예제 #1
0
파일: upgrade.py 프로젝트: arenadata/adcm
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))}
예제 #2
0
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)))
예제 #3
0
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, ''
예제 #4
0
 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
예제 #5
0
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))}
예제 #6
0
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)
예제 #7
0
파일: api.py 프로젝트: dimuha-rs/adcm
 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
예제 #8
0
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)
예제 #9
0
파일: api.py 프로젝트: dimuha-rs/adcm
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()
예제 #10
0
파일: api.py 프로젝트: dimuha-rs/adcm
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)
예제 #11
0
파일: api.py 프로젝트: dimuha-rs/adcm
 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
예제 #12
0
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)
예제 #13
0
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')
예제 #14
0
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')
예제 #15
0
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)))
예제 #16
0
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, ''
예제 #17
0
파일: api.py 프로젝트: dimuha-rs/adcm
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
예제 #18
0
파일: api.py 프로젝트: dimuha-rs/adcm
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
예제 #19
0
 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
예제 #20
0
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)
예제 #21
0
파일: api.py 프로젝트: dimuha-rs/adcm
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