def do_upgrade(obj: Union[Cluster, HostProvider], upgrade: Upgrade) -> dict: old_proto = obj.prototype check_license(obj.prototype.bundle) check_license(upgrade.bundle) ok, msg = check_upgrade(obj, upgrade) if not ok: return err('UPGRADE_ERROR', msg) log.info('upgrade %s version %s (upgrade #%s)', obj_ref(obj), old_proto.version, upgrade.id) if obj.prototype.type == 'cluster': new_proto = Prototype.objects.get(bundle=upgrade.bundle, type='cluster') elif obj.prototype.type == 'provider': new_proto = Prototype.objects.get(bundle=upgrade.bundle, type='provider') else: return err('UPGRADE_ERROR', 'can upgrade only cluster or host provider') with transaction.atomic(): obj.prototype = new_proto obj.before_upgrade['state'] = obj.state if upgrade.state_on_success: obj.state = upgrade.state_on_success obj.save() switch_config(obj, new_proto, old_proto) if obj.prototype.type == 'cluster': switch_services(upgrade, obj) elif obj.prototype.type == 'provider': switch_hosts(upgrade, obj) cm.issue.update_hierarchy_issues(obj) log.info('upgrade %s OK to version %s', obj_ref(obj), obj.prototype.version) cm.status_api.post_event( 'upgrade', obj.prototype.type, obj.id, 'version', str(obj.prototype.version) ) return {'id': obj.id, 'upgradable': bool(get_upgrade(obj))}
def check_multi_bind(actual_import, cluster, service, export_cluster, export_service, cb_list=None): if actual_import.multibind: return if cb_list is None: cb_list = ClusterBind.objects.filter(cluster=cluster, service=service) for cb in cb_list: if cb.source_service: source_proto = cb.source_service.prototype else: source_proto = cb.source_cluster.prototype if export_service: if source_proto == export_service.prototype: msg = 'can not multi bind {} to {}' err('BIND_ERROR', msg.format(proto_ref(source_proto), obj_ref(cluster))) else: if source_proto == export_cluster.prototype: msg = 'can not multi bind {} to {}' err('BIND_ERROR', msg.format(proto_ref(source_proto), obj_ref(cluster)))
def check_upgrade_import(obj, upgrade): # pylint: disable=too-many-branches def get_export(cbind): if cbind.source_service: return cbind.source_service else: return cbind.source_cluster def get_import(cbind): # pylint: disable=redefined-outer-name if cbind.service: return cbind.service else: return cbind.cluster if obj.prototype.type != 'cluster': return True, '' for cbind in ClusterBind.objects.filter(cluster=obj): export = get_export(cbind) impr_obj = get_import(cbind) try: proto = Prototype.objects.get(bundle=upgrade.bundle, name=impr_obj.prototype.name, type=impr_obj.prototype.type) except Prototype.DoesNotExist: msg = 'Upgrade does not have new version of {} required for import' return False, msg.format(proto_ref(impr_obj.prototype)) try: pi = PrototypeImport.objects.get(prototype=proto, name=export.prototype.name) if not version_in(export.prototype.version, pi): msg = 'Import "{}" of {} versions ({}, {}) does not match export version: {} ({})' return (False, msg.format(export.prototype.name, proto_ref(proto), pi.min_version, pi.max_version, export.prototype.version, obj_ref(export))) except PrototypeImport.DoesNotExist: # msg = 'New version of {} does not have import "{}"' # ADCM-1507 # return False, msg.format(proto_ref(proto), export.prototype.name) cbind.delete() for cbind in ClusterBind.objects.filter(source_cluster=obj): export = get_export(cbind) try: proto = Prototype.objects.get(bundle=upgrade.bundle, name=export.prototype.name, type=export.prototype.type) except Prototype.DoesNotExist: msg = 'Upgrade does not have new version of {} required for export' return False, msg.format(proto_ref(export.prototype)) import_obj = get_import(cbind) pi = PrototypeImport.objects.get(prototype=import_obj.prototype, name=export.prototype.name) if not version_in(proto.version, pi): msg = 'Export of {} does not match import versions: ({}, {}) ({})' return (False, msg.format(proto_ref(proto), pi.min_version, pi.max_version, obj_ref(import_obj))) return True, ''
def get_export_service(b, export_cluster): export_co = None if 'service_id' in b['export_id']: export_co = ClusterObject.obj.get(id=b['export_id']['service_id']) if export_co.cluster != export_cluster: msg = 'export {} is not belong to {}' err('BIND_ERROR', msg.format(obj_ref(export_co), obj_ref(export_cluster))) return export_co
def do_upgrade(obj, upgrade): old_proto = obj.prototype check_license(obj.prototype.bundle) check_license(upgrade.bundle) ok, msg = check_upgrade(obj, upgrade) if not ok: return err('UPGRADE_ERROR', msg) log.info('upgrade %s version %s (upgrade #%s)', obj_ref(obj), old_proto.version, upgrade.id) if obj.prototype.type == 'cluster': new_proto = Prototype.objects.get(bundle=upgrade.bundle, type='cluster') elif obj.prototype.type == 'provider': new_proto = Prototype.objects.get(bundle=upgrade.bundle, type='provider') else: return err('UPGRADE_ERROR', 'can upgrade only cluster or host provider') with transaction.atomic(): obj.prototype = new_proto if upgrade.state_on_success: obj.state = upgrade.state_on_success obj.save() switch_config(obj, new_proto, old_proto) if obj.prototype.type == 'cluster': for p in Prototype.objects.filter(bundle=upgrade.bundle, type='service'): try: co = ClusterObject.objects.get(cluster=obj, prototype__name=p.name) switch_service(co, p) switch_components(obj, co, p) except ClusterObject.DoesNotExist: # co.delete() ?! pass switch_hc(obj, upgrade) elif obj.prototype.type == 'provider': for p in Prototype.objects.filter(bundle=upgrade.bundle, type='host'): for host in Host.objects.filter(provider=obj, prototype__name=p.name): switch_service(host, p) cm.issue.save_issue(obj) log.info('upgrade %s OK to version %s', obj_ref(obj), obj.prototype.version) cm.status_api.post_event('upgrade', obj.prototype.type, obj.id, 'version', str(obj.prototype.version)) return {'id': obj.id, 'upgradable': bool(get_upgrade(obj))}
def unlock_obj(obj, event): if obj.stack: stack = obj.stack else: log.warning('no stack in %s for unlock', obj_ref(obj)) return try: state = stack.pop() except IndexError: log.warning('empty stack in %s for unlock', obj_ref(obj)) return log.debug('unlock %s, stack: %s', obj_ref(obj), stack) obj.stack = stack api.set_object_state(obj, state, event)
def get_export_service(b, export_cluster): export_co = None if 'service_id' in b['export_id']: try: export_co = ClusterObject.objects.get( id=b['export_id']['service_id']) except ClusterObject.DoesNotExist: msg = 'export service with id #{} not found' err('BIND_ERROR', msg.format(b['export_id']['service_id'])) if export_co.cluster != export_cluster: msg = 'export {} is not belong to {}' err('BIND_ERROR', msg.format(obj_ref(export_co), obj_ref(export_cluster))) return export_co
def set_action_state(action, task, obj, state): if not obj: log.warning('empty object for action %s of task #%s', action.name, task.id) return msg = 'action "%s" of task #%s will set %s state to "%s"' log.info(msg, action.name, task.id, obj_ref(obj), state) api.push_obj(obj, state)
def delete_host(host): cluster = host.cluster if cluster: msg = 'Host #{} "{}" belong to {}' err('HOST_CONFLICT', msg.format(host.id, host.fqdn, obj_ref(cluster))) host_id = host.id host.delete() cm.status_api.post_event('delete', 'host', host_id) cm.status_api.load_service_map()
def delete_host_provider(provider): hosts = Host.objects.filter(provider=provider) if hosts: msg = 'There is host #{} "{}" of host {}' err('PROVIDER_CONFLICT', msg.format(hosts[0].id, hosts[0].fqdn, obj_ref(provider))) provider_id = provider.id provider.delete() cm.status_api.post_event('delete', 'provider', provider_id)
def get_pi(import_id, import_obj): try: pi = PrototypeImport.objects.get(id=import_id) except PrototypeImport.DoesNotExist: err('BIND_ERROR', 'Import with id #{} does not found'.format(import_id)) if pi.prototype != import_obj.prototype: msg = 'Import #{} does not belong to {}' err('BIND_ERROR', msg.format(import_id, obj_ref(import_obj))) return pi
def lock_obj(obj, event): stack = obj.stack if not stack: stack = [obj.state] elif stack[-1] != obj.state: stack.append(obj.state) log.debug('lock %s, stack: %s', obj_ref(obj), stack) obj.stack = stack api.set_object_state(obj, config.Job.LOCKED, event)
def delete_host(host, cancel_tasks=True): cluster = host.cluster if cluster: msg = 'Host #{} "{}" belong to {}' err('HOST_CONFLICT', msg.format(host.id, host.fqdn, obj_ref(cluster))) if cancel_tasks: _cancel_locking_tasks(host) host_id = host.id host.delete() cm.status_api.post_event('delete', 'host', host_id) load_service_map() log.info(f'host #{host_id} is deleted')
def delete_host_provider(provider, cancel_tasks=True): hosts = Host.objects.filter(provider=provider) if hosts: msg = 'There is host #{} "{}" of host {}' err('PROVIDER_CONFLICT', msg.format(hosts[0].id, hosts[0].fqdn, obj_ref(provider))) if cancel_tasks: _cancel_locking_tasks(provider) provider_id = provider.id provider.delete() cm.status_api.post_event('delete', 'provider', provider_id) log.info(f'host provider #{provider_id} is deleted')
def check_import_default(import_obj, export_obj): pi = PrototypeImport.objects.get(prototype=import_obj.prototype, name=export_obj.prototype.name) if not pi.default: return cl = ConfigLog.objects.get(obj_ref=import_obj.config, id=import_obj.config.current) if not cl.attr: return for name in json.loads(pi.default): if name in cl.attr: if 'active' in cl.attr[name] and not cl.attr[name]['active']: msg = 'Default import "{}" for {} is inactive' err('BIND_ERROR', msg.format(name, obj_ref(import_obj)))
def check_upgrade(obj, upgrade): issue = cm.issue.get_issue(obj) if not cm.issue.issue_to_bool(issue): return False, '{} has issue: {}'.format(obj_ref(obj), issue) check_list = [ check_upgrade_version, check_upgrade_edition, check_upgrade_state, check_upgrade_import ] for func in check_list: ok, msg = func(obj, upgrade) if not ok: return False, msg return True, ''
def bind(cluster, service, export_cluster, export_service_id): # pylint: disable=too-many-branches ''' Adapter between old and new bind interface /api/.../bind/ -> /api/.../import/ bind() -> multi_bind() ''' export_service = None if export_service_id: try: export_service = ClusterObject.objects.get(cluster=export_cluster, id=export_service_id) if not PrototypeExport.objects.filter( prototype=export_service.prototype): err('BIND_ERROR', '{} do not have exports'.format(obj_ref(export_service))) except ClusterObject.DoesNotExist: msg = 'service #{} does not exists or does not belong to cluster # {}' err('SERVICE_NOT_FOUND', msg.format(export_service_id, export_cluster.id)) name = export_service.prototype.name else: if not PrototypeExport.objects.filter( prototype=export_cluster.prototype): err('BIND_ERROR', '{} does not have exports'.format(obj_ref(export_cluster))) name = export_cluster.prototype.name import_obj = cluster if service: import_obj = service try: pi = PrototypeImport.objects.get(prototype=import_obj.prototype, name=name) except PrototypeImport.DoesNotExist: err('BIND_ERROR', '{} does not have appropriate import'.format(obj_ref(import_obj))) except MultipleObjectsReturned: err('BIND_ERROR', 'Old api does not support multi bind. Go to /api/v1/.../import/') bind_list = [] for imp in get_import(cluster, service): for exp in imp['exports']: if exp['binded']: bind_list.append({ 'import_id': imp['id'], 'export_id': exp['id'] }) item = {'import_id': pi.id, 'export_id': {'cluster_id': export_cluster.id}} if export_service: item['export_id']['service_id'] = export_service.id bind_list.append(item) multi_bind(cluster, service, bind_list) res = { 'export_cluster_id': export_cluster.id, 'export_cluster_name': export_cluster.name, 'export_cluster_prototype_name': export_cluster.prototype.name, } if export_service: res['export_service_id'] = export_service.id return res
def check_hc(cluster, hc_in): # pylint: disable=too-many-branches def check_sub(sub_key, sub_type, item): if sub_key not in item: msg = '"{}" sub-field of hostcomponent is required' raise AdcmEx('INVALID_INPUT', msg.format(sub_key)) if not isinstance(item[sub_key], sub_type): msg = '"{}" sub-field of hostcomponent should be "{}"' raise AdcmEx('INVALID_INPUT', msg.format(sub_key, sub_type)) seen = {} if not isinstance(hc_in, list): raise AdcmEx('INVALID_INPUT', 'hostcomponent should be array') for item in hc_in: for sub_key, sub_type in (('service_id', int), ('host_id', int), ('component_id', int)): check_sub(sub_key, sub_type, item) key = (item.get('service_id', ''), item.get('host_id', ''), item.get('component_id', '')) if key not in seen: seen[key] = 1 else: msg = 'duplicate ({}) in host service list' raise AdcmEx('INVALID_INPUT', msg.format(item)) host_comp_list = [] for item in hc_in: try: host = Host.objects.get(id=item['host_id']) except Host.DoesNotExist: msg = 'No host #{}'.format(item['host_id']) raise AdcmEx('HOST_NOT_FOUND', msg) from None try: service = ClusterObject.objects.get(id=item['service_id'], cluster=cluster) except ClusterObject.DoesNotExist: msg = 'No service #{} in {}'.format(item['service_id'], obj_ref(cluster)) raise AdcmEx('SERVICE_NOT_FOUND', msg) from None try: comp = ServiceComponent.objects.get(id=item['component_id'], cluster=cluster, service=service) except ServiceComponent.DoesNotExist: msg = 'No component #{} in {} '.format(item['component_id'], obj_ref(service)) raise AdcmEx('COMPONENT_NOT_FOUND', msg) from None if not host.cluster: msg = 'host #{} {} does not belong to any cluster'.format( host.id, host.fqdn) raise AdcmEx("FOREIGN_HOST", msg) if host.cluster.id != cluster.id: msg = 'host {} (cluster #{}) does not belong to cluster #{}' raise AdcmEx("FOREIGN_HOST", msg.format(host.fqdn, host.cluster.id, cluster.id)) host_comp_list.append((service, host, comp)) for service in ClusterObject.objects.filter(cluster=cluster): cm.issue.check_component_constraint( service, [i for i in host_comp_list if i[0] == service]) cm.issue.check_component_requires(host_comp_list) cm.issue.check_bound_components(host_comp_list) return host_comp_list
def get_pi(import_id, import_obj): pi = PrototypeImport.obj.get(id=import_id) if pi.prototype != import_obj.prototype: msg = 'Import #{} does not belong to {}' err('BIND_ERROR', msg.format(import_id, obj_ref(import_obj))) return pi
def multi_bind(cluster, service, bind_list): # pylint: disable=too-many-locals,too-many-statements def get_pi(import_id, import_obj): pi = PrototypeImport.obj.get(id=import_id) if pi.prototype != import_obj.prototype: msg = 'Import #{} does not belong to {}' err('BIND_ERROR', msg.format(import_id, obj_ref(import_obj))) return pi def get_export_service(b, export_cluster): export_co = None if 'service_id' in b['export_id']: export_co = ClusterObject.obj.get(id=b['export_id']['service_id']) if export_co.cluster != export_cluster: msg = 'export {} is not belong to {}' err('BIND_ERROR', msg.format(obj_ref(export_co), obj_ref(export_cluster))) return export_co def cook_key(cluster, service): if service: return f'{cluster.id}.{service.id}' return str(cluster.id) check_bind_post(bind_list) import_obj = get_bind_obj(cluster, service) old_bind = {} cb_list = ClusterBind.objects.filter(cluster=cluster, service=service) for cb in cb_list: old_bind[cook_key(cb.source_cluster, cb.source_service)] = cb new_bind = {} for b in bind_list: pi = get_pi(b['import_id'], import_obj) export_cluster = Cluster.obj.get(id=b['export_id']['cluster_id']) export_obj = export_cluster export_co = get_export_service(b, export_cluster) if export_co: export_obj = export_co if cook_key(export_cluster, export_co) in new_bind: err('BIND_ERROR', 'Bind list has duplicates') if pi.name != export_obj.prototype.name: msg = 'Export {} does not match import name "{}"' err('BIND_ERROR', msg.format(obj_ref(export_obj), pi.name)) if not version_in(export_obj.prototype.version, pi): msg = 'Import "{}" of {} versions ({}, {}) does not match export version: {} ({})' err( 'BIND_ERROR', msg.format( export_obj.prototype.name, proto_ref(pi.prototype), pi.min_version, pi.max_version, export_obj.prototype.version, obj_ref(export_obj), ), ) cbind = ClusterBind( cluster=cluster, service=service, source_cluster=export_cluster, source_service=export_co, ) new_bind[cook_key(export_cluster, export_co)] = (pi, cbind, export_obj) with transaction.atomic(): for key, value in old_bind.items(): if key in new_bind: continue export_obj = get_bind_obj(value.source_cluster, value.source_service) check_import_default(import_obj, export_obj) value.delete() log.info('unbind %s from %s', obj_ref(export_obj), obj_ref(import_obj)) for key, value in new_bind.items(): if key in old_bind: continue (pi, cb, export_obj) = value check_multi_bind(pi, cluster, service, cb.source_cluster, cb.source_service) cb.save() log.info('bind %s to %s', obj_ref(export_obj), obj_ref(import_obj)) cm.issue.update_hierarchy_issues(cluster) return get_import(cluster, service)
def set_object_state(obj, state, event): obj.state = state obj.save() event.set_object_state(obj.prototype.type, obj.id, state) log.info('set %s state to "%s"', obj_ref(obj), state) return obj