Beispiel #1
0
    def __init__(self, region_name=None):
        # Check if objects are cached and try to use those
        self.os_clients_dict = collections.defaultdict(dict)
        self._token = None

        self.region_name = region_name

        if ('keystone' in self.os_clients_dict) and self._is_token_valid():
            self.keystone_client = self.os_clients_dict['keystone']
        else:
            self.keystone_client = KeystoneClient()
            self.os_clients_dict['keystone'] = self.keystone_client
        if region_name in self.os_clients_dict and \
                self._is_token_valid():
            LOG.info('Using cached OS client objects %s' % region_name)
            self.sysinv_client = self.os_clients_dict[region_name]['sysinv']
        else:
            # Create new objects and cache them
            LOG.debug("Creating fresh OS Clients objects %s" % region_name)
            self.os_clients_dict[region_name] = collections.defaultdict(dict)

            try:
                self.sysinv_client = SysinvClient(region_name,
                                                  self.keystone_client.session)
                self.os_clients_dict[region_name][
                    'sysinv'] = self.sysinv_client
            except Exception as exception:
                LOG.error('sysinv_client region %s error: %s' %
                          (region_name, exception.message))
 def get_all_regions_for_project(self, project_id):
     try:
         # Retrieve regions based on endpoint filter for the project.
         region_lists = self._get_filtered_regions(project_id)
         if not region_lists:
             # If endpoint filter is not used for the project, then
             # return all regions
             region_lists = \
                 KeystoneClient().endpoint_cache.get_all_regions()
         # nova, cinder, and neutron have no endpoints in consts.CLOUD_0
         if consts.CLOUD_0 in region_lists:
             region_lists.remove(consts.CLOUD_0)
         return region_lists
     except Exception as exception:
         LOG.error('Error Occurred: %s', exception.message)
         raise
Beispiel #3
0
    def __init__(self, region_name=consts.VIRTUAL_MASTER_CLOUD, auth_url=None):
        # Check if objects are cached and try to use those
        self.os_clients_dict = collections.defaultdict(dict)
        self._identity_tokens = {}

        self.region_name = region_name

        if ((region_name in self.os_clients_dict)
                and ('keystone' in self.os_clients_dict[region_name])
                and self._is_token_valid(region_name)):
            self.keystone_client = \
                self.os_clients_dict[region_name]['keystone']
        else:
            LOG.info("get new keystone client for subcloud %s", region_name)
            self.keystone_client = KeystoneClient(region_name, auth_url)
            self.os_clients_dict[region_name]['keystone'] = \
                self.keystone_client
        if ((region_name in self.os_clients_dict)
                and ('sysinv' in self.os_clients_dict[region_name])
                and self._is_token_valid(region_name)):
            LOG.info('Using cached OS client objects %s' % region_name)
            self.sysinv_client = self.os_clients_dict[region_name]['sysinv']
        else:
            # Create new objects and cache them
            LOG.debug("Creating fresh OS Clients objects %s" % region_name)
            self.os_clients_dict[region_name] = collections.defaultdict(dict)

            try:
                self.sysinv_client = SysinvClient(region_name,
                                                  self.keystone_client.session)
                self.os_clients_dict[region_name][
                    'sysinv'] = self.sysinv_client
            except Exception as exception:
                LOG.error('sysinv_client region %s error: %s' %
                          (region_name, exception.message))
Beispiel #4
0
    def _delete_subcloud_routes(self, context, subcloud):
        """Delete the routes to this subcloud"""

        keystone_client = KeystoneClient()
        # Delete subcloud's identity endpoints
        keystone_client.delete_endpoints(subcloud.name)

        # Delete the route to this subcloud on the management interface on
        # both controllers.
        management_subnet = netaddr.IPNetwork(subcloud.management_subnet)
        session = keystone_client.endpoint_cache.get_session_from_token(
            context.auth_token, context.project)
        sysinv_client = SysinvClient(consts.DEFAULT_REGION_NAME, session)
        controllers = sysinv_client.get_controller_hosts()
        for controller in controllers:
            management_interface = sysinv_client.get_management_interface(
                controller.hostname)
            if management_interface is not None:
                sysinv_client.delete_route(
                    management_interface.uuid, str(management_subnet.ip),
                    management_subnet.prefixlen,
                    str(netaddr.IPAddress(
                        subcloud.systemcontroller_gateway_ip)), 1)
    def __init__(self, region_name=consts.VIRTUAL_MASTER_CLOUD, auth_url=None):
        # Check if objects are cached and try to use those
        self.region_name = region_name

        if (region_name in OpenStackDriver._identity_tokens
                and (region_name in OpenStackDriver.os_clients_dict) and
            ('keystone' in OpenStackDriver.os_clients_dict[region_name])
                and self._is_token_valid(self.region_name)):
            self.keystone_client = \
                OpenStackDriver.os_clients_dict[region_name]['keystone']
        else:
            LOG.info("get new keystone client for %s" % region_name)
            self.keystone_client = KeystoneClient(region_name, auth_url)
            OpenStackDriver.os_clients_dict[region_name]['keystone'] = \
                self.keystone_client

        self.disabled_quotas = self._get_disabled_quotas(region_name)
        if region_name in OpenStackDriver.os_clients_dict and \
                self._is_token_valid(region_name):
            LOG.info('Using cached OS client objects %s' % region_name)
            self.sysinv_client = OpenStackDriver.os_clients_dict[region_name][
                'sysinv']
            self.nova_client = OpenStackDriver.os_clients_dict[region_name][
                'nova']
            self.cinder_client = OpenStackDriver.os_clients_dict[region_name][
                'cinder']
            self.neutron_client = OpenStackDriver.os_clients_dict[region_name][
                'neutron']
            self.fm_client = OpenStackDriver.os_clients_dict[region_name]['fm']
        else:
            # Create new objects and cache them
            LOG.info("Creating fresh OS Clients objects %s" % region_name)
            OpenStackDriver.os_clients_dict[
                region_name] = collections.defaultdict(dict)

            try:
                self.sysinv_client = SysinvClient(region_name,
                                                  self.keystone_client.session)
                OpenStackDriver.os_clients_dict[region_name][
                    'sysinv'] = self.sysinv_client
            except Exception as exception:
                LOG.error('sysinv_client region %s error: %s' %
                          (region_name, exception.message))

            try:
                self.neutron_client = NeutronClient(
                    region_name,
                    self.disabled_quotas,
                    self.keystone_client.session,
                    endpoint_type=consts.KS_ENDPOINT_DEFAULT)
                OpenStackDriver.os_clients_dict[region_name][
                    'neutron'] = self.neutron_client
            except Exception as exception:
                LOG.error('neutron_client region %s error: %s' %
                          (region_name, exception.message))

            try:
                self.nova_client = NovaClient(
                    region_name,
                    self.keystone_client.session,
                    endpoint_type=consts.KS_ENDPOINT_DEFAULT,
                    disabled_quotas=self.disabled_quotas)
                OpenStackDriver.os_clients_dict[region_name][
                    'nova'] = self.nova_client
            except Exception as exception:
                LOG.error('nova_client region %s error: %s' %
                          (region_name, exception.message))

            try:
                self.cinder_client = CinderClient(
                    region_name,
                    self.disabled_quotas,
                    self.keystone_client.session,
                    endpoint_type=consts.KS_ENDPOINT_DEFAULT)
                OpenStackDriver.os_clients_dict[region_name][
                    'cinder'] = self.cinder_client
            except Exception as exception:
                LOG.error('cinder_client region %s error: %s' %
                          (region_name, exception.message))

            try:
                self.fm_client = FmClient(
                    region_name,
                    self.keystone_client.session,
                    endpoint_type=consts.KS_ENDPOINT_DEFAULT)
                OpenStackDriver.os_clients_dict[region_name][
                    'fm'] = self.fm_client
            except Exception as exception:
                LOG.error('fm_client region %s error: %s' %
                          (region_name, exception.message))
class OpenStackDriver(object):

    os_clients_dict = collections.defaultdict(dict)
    _identity_tokens = {}

    @lockutils.synchronized('dcorch-openstackdriver')
    def __init__(self, region_name=consts.VIRTUAL_MASTER_CLOUD, auth_url=None):
        # Check if objects are cached and try to use those
        self.region_name = region_name

        if (region_name in OpenStackDriver._identity_tokens
                and (region_name in OpenStackDriver.os_clients_dict) and
            ('keystone' in OpenStackDriver.os_clients_dict[region_name])
                and self._is_token_valid(self.region_name)):
            self.keystone_client = \
                OpenStackDriver.os_clients_dict[region_name]['keystone']
        else:
            LOG.info("get new keystone client for %s" % region_name)
            self.keystone_client = KeystoneClient(region_name, auth_url)
            OpenStackDriver.os_clients_dict[region_name]['keystone'] = \
                self.keystone_client

        self.disabled_quotas = self._get_disabled_quotas(region_name)
        if region_name in OpenStackDriver.os_clients_dict and \
                self._is_token_valid(region_name):
            LOG.info('Using cached OS client objects %s' % region_name)
            self.sysinv_client = OpenStackDriver.os_clients_dict[region_name][
                'sysinv']
            self.nova_client = OpenStackDriver.os_clients_dict[region_name][
                'nova']
            self.cinder_client = OpenStackDriver.os_clients_dict[region_name][
                'cinder']
            self.neutron_client = OpenStackDriver.os_clients_dict[region_name][
                'neutron']
            self.fm_client = OpenStackDriver.os_clients_dict[region_name]['fm']
        else:
            # Create new objects and cache them
            LOG.info("Creating fresh OS Clients objects %s" % region_name)
            OpenStackDriver.os_clients_dict[
                region_name] = collections.defaultdict(dict)

            try:
                self.sysinv_client = SysinvClient(region_name,
                                                  self.keystone_client.session)
                OpenStackDriver.os_clients_dict[region_name][
                    'sysinv'] = self.sysinv_client
            except Exception as exception:
                LOG.error('sysinv_client region %s error: %s' %
                          (region_name, exception.message))

            try:
                self.neutron_client = NeutronClient(
                    region_name,
                    self.disabled_quotas,
                    self.keystone_client.session,
                    endpoint_type=consts.KS_ENDPOINT_DEFAULT)
                OpenStackDriver.os_clients_dict[region_name][
                    'neutron'] = self.neutron_client
            except Exception as exception:
                LOG.error('neutron_client region %s error: %s' %
                          (region_name, exception.message))

            try:
                self.nova_client = NovaClient(
                    region_name,
                    self.keystone_client.session,
                    endpoint_type=consts.KS_ENDPOINT_DEFAULT,
                    disabled_quotas=self.disabled_quotas)
                OpenStackDriver.os_clients_dict[region_name][
                    'nova'] = self.nova_client
            except Exception as exception:
                LOG.error('nova_client region %s error: %s' %
                          (region_name, exception.message))

            try:
                self.cinder_client = CinderClient(
                    region_name,
                    self.disabled_quotas,
                    self.keystone_client.session,
                    endpoint_type=consts.KS_ENDPOINT_DEFAULT)
                OpenStackDriver.os_clients_dict[region_name][
                    'cinder'] = self.cinder_client
            except Exception as exception:
                LOG.error('cinder_client region %s error: %s' %
                          (region_name, exception.message))

            try:
                self.fm_client = FmClient(
                    region_name,
                    self.keystone_client.session,
                    endpoint_type=consts.KS_ENDPOINT_DEFAULT)
                OpenStackDriver.os_clients_dict[region_name][
                    'fm'] = self.fm_client
            except Exception as exception:
                LOG.error('fm_client region %s error: %s' %
                          (region_name, exception.message))

    @classmethod
    @lockutils.synchronized('dcorch-openstackdriver')
    def delete_region_clients(cls, region_name, clear_token=False):
        LOG.warn("delete_region_clients=%s, clear_token=%s" %
                 (region_name, clear_token))
        if region_name in cls.os_clients_dict:
            del cls.os_clients_dict[region_name]
        if clear_token:
            OpenStackDriver._identity_tokens[region_name] = None

    def get_enabled_projects(self, id_only=True):
        try:
            return self.keystone_client.get_enabled_projects(id_only)
        except Exception as exception:
            LOG.error('Error Occurred: %s', exception.message)

    def get_project_by_name(self, projectname):
        try:
            return self.keystone_client.get_project_by_name(projectname)
        except Exception as exception:
            LOG.error('Error Occurred : %s', exception.message)

    def get_project_by_id(self, projectid):
        try:
            return self.keystone_client.get_project_by_id(projectid)
        except Exception as exception:
            LOG.error('Error Occurred : %s', exception.message)

    def get_enabled_users(self, id_only=True):
        try:
            return self.keystone_client.get_enabled_users(id_only)
        except Exception as exception:
            LOG.error('Error Occurred : %s', exception.message)

    def get_user_by_name(self, username):
        try:
            return self.keystone_client.get_user_by_name(username)
        except Exception as exception:
            LOG.error('Error Occurred : %s', exception.message)

    def get_user_by_id(self, userid):
        try:
            return self.keystone_client.get_user_by_id(userid)
        except Exception as exception:
            LOG.error('Error Occurred : %s', exception.message)

    def get_resource_usages(self, project_id, user_id):
        # If one of the resources is unavailable we still want to return
        # any usage information we have for the others.
        nova_usages = {}
        neutron_usages = {}
        cinder_usages = {}
        try:
            nova_usages = self.nova_client.get_resource_usages(
                project_id, user_id)
            if user_id is None:
                # neutron/cinder don't do per-user quotas/usage
                neutron_usages = self.neutron_client.get_resource_usages(
                    project_id)
                cinder_usages = self.cinder_client.get_resource_usages(
                    project_id)
        except (exceptions.ConnectionRefused, exceptions.NotAuthorized,
                exceptions.TimeOut) as ex:
            # Delete the cached objects for that region
            LOG.error('Error Occurred: %s', ex.message)
            del OpenStackDriver.os_clients_dict[self.region_name]
        except Exception as exception:
            LOG.error('Error Occurred: %s', exception.message)
        return nova_usages, neutron_usages, cinder_usages

    def get_quota_limits(self, project_id, user_id):
        # If one of the resources is unavailable we still want to return
        # any limit information we have for the others.
        nova_limits = {}
        neutron_limits = {}
        cinder_limits = {}
        try:
            nova_limits = self.nova_client.get_quota_limits(
                project_id, user_id)
            if user_id is None:
                # neutron/cinder don't do per-user quotas/usage
                neutron_limits = self.neutron_client.get_quota_limits(
                    project_id)
                cinder_limits = self.cinder_client.get_quota_limits(project_id)
        except (exceptions.ConnectionRefused, exceptions.NotAuthorized,
                exceptions.TimeOut) as ex:
            LOG.error('Error Occurred: %s', ex.message)
            # Delete the cached objects for that region
            del OpenStackDriver.os_clients_dict[self.region_name]
        except Exception as exception:
            LOG.error('Error Occurred: %s', exception.message)
        return nova_limits, neutron_limits, cinder_limits

    def write_quota_limits(self, project_id, user_id, limits_to_write):
        try:
            self.nova_client.update_quota_limits(project_id, user_id,
                                                 **limits_to_write['nova'])
            # Only nova supports per-user quotas.
            if user_id is None:
                self.cinder_client.update_quota_limits(
                    project_id, **limits_to_write['cinder'])
                self.neutron_client.update_quota_limits(
                    project_id, limits_to_write['neutron'])
        except (exceptions.ConnectionRefused, exceptions.NotAuthorized,
                exceptions.TimeOut) as ex:
            LOG.error('Error Occurred: %s', ex.message)
            # Delete the cached objects for that region
            del OpenStackDriver.os_clients_dict[self.region_name]
        except Exception as exception:
            LOG.error('Error Occurred: %s', exception.message)

    def delete_quota_limits(self, project_id):
        try:
            self.nova_client.delete_quota_limits(project_id)
            self.neutron_client.delete_quota_limits(project_id)
            self.cinder_client.delete_quota_limits(project_id)
        except (exceptions.ConnectionRefused, exceptions.NotAuthorized,
                exceptions.TimeOut):
            # Delete the cached objects for that region
            del OpenStackDriver.os_clients_dict[self.region_name]
        except Exception as exception:
            LOG.error('Error Occurred: %s', exception.message)

    def _get_disabled_quotas(self, region):
        disabled_quotas = []
        if not self.keystone_client.is_service_enabled('volume') and \
                not self.keystone_client.is_service_enabled('volumev2'):
            disabled_quotas.extend(consts.CINDER_QUOTA_FIELDS)
        # Neutron
        if not self.keystone_client.is_service_enabled('network'):
            disabled_quotas.extend(consts.NEUTRON_QUOTA_FIELDS)
        else:
            disabled_quotas.extend(['floating_ips', 'fixed_ips'])
            disabled_quotas.extend(['security_groups', 'security_group_rules'])
        return disabled_quotas

    def get_all_regions_for_project(self, project_id):
        try:
            # Retrieve regions based on endpoint filter for the project.
            region_lists = self._get_filtered_regions(project_id)
            if not region_lists:
                # If endpoint filter is not used for the project, then
                # return all regions
                region_lists = \
                    KeystoneClient().endpoint_cache.get_all_regions()
            # nova, cinder, and neutron have no endpoints in consts.CLOUD_0
            if consts.CLOUD_0 in region_lists:
                region_lists.remove(consts.CLOUD_0)
            return region_lists
        except Exception as exception:
            LOG.error('Error Occurred: %s', exception.message)
            raise

    def _get_filtered_regions(self, project_id):
        return self.keystone_client.get_filtered_region(project_id)

    def _is_token_valid(self, region_name):
        try:
            keystone = \
                self.os_clients_dict[region_name]['keystone'].keystone_client
            if (not OpenStackDriver._identity_tokens
                    or region_name not in OpenStackDriver._identity_tokens
                    or not OpenStackDriver._identity_tokens[region_name]):
                identity_token = \
                    keystone.tokens.validate(keystone.session.get_token())
                OpenStackDriver._identity_tokens[region_name] = identity_token
                LOG.info("Got new token for subcloud %s, expires_at=%s" %
                         (region_name, identity_token['expires_at']))
                # Reset the cached dictionary
                OpenStackDriver.os_clients_dict[region_name] = \
                    collections.defaultdict(dict)
                return False
            keystone.tokens.validate(
                OpenStackDriver._identity_tokens[region_name])
        except Exception as exception:
            LOG.info('_is_token_valid handle: %s', exception.message)
            # Reset the cached dictionary
            OpenStackDriver.os_clients_dict[region_name] = \
                collections.defaultdict(dict)
            OpenStackDriver._identity_tokens[region_name] = None
            return False

        identity_token = OpenStackDriver._identity_tokens[region_name]
        expiry_time = timeutils.normalize_time(
            timeutils.parse_isotime(identity_token['expires_at']))
        if timeutils.is_soon(expiry_time, STALE_TOKEN_DURATION):
            LOG.info("The cached keystone token for subcloud %s will "
                     "expire soon %s" %
                     (region_name, identity_token['expires_at']))
            # Reset the cached dictionary
            OpenStackDriver.os_clients_dict[region_name] = \
                collections.defaultdict(dict)
            OpenStackDriver._identity_tokens[region_name] = None
            return False
        else:
            return True
Beispiel #7
0
 def _get_management_address_pool(self, context):
     """Get the system controller's management address pool"""
     session = KeystoneClient().endpoint_cache.get_session_from_token(
         context.auth_token, context.project)
     sysinv_client = SysinvClient(consts.DEFAULT_REGION_NAME, session)
     return sysinv_client.get_management_address_pool()
Beispiel #8
0
    def _periodic_subcloud_audit_loop(self):
        """Audit availability of subclouds loop."""
        # We will be running in our own green thread here.
        LOG.info('Triggered subcloud audit.')

        # For each subcloud, if at least one service is active in
        # each service of servicegroup-list then declare the subcloud online.

        for subcloud in db_api.subcloud_get_all(self.context):
            subcloud_name = subcloud.name
            subcloud_id = subcloud.id
            management_state = subcloud.management_state
            avail_status_current = subcloud.availability_status
            audit_fail_count = subcloud.audit_fail_count

            # Set defaults to None and disabled so we will still set disabled
            # status if we encounter an error.

            sysinv_client = None
            svc_groups = None
            avail_to_set = consts.AVAILABILITY_OFFLINE

            try:
                ks_client = KeystoneClient(subcloud_name)
                sysinv_client = SysinvClient(subcloud_name, ks_client.session)
            except (keystone_exceptions.EndpointNotFound,
                    keystone_exceptions.ConnectFailure, IndexError) as e:
                if avail_status_current == consts.AVAILABILITY_OFFLINE:
                    LOG.info("Identity or Platform endpoint for %s not "
                             "found, ignoring for offline "
                             "subcloud." % subcloud_name)
                    continue
                else:
                    LOG.error("Identity or Platform endpoint for online "
                              "subcloud: %s not found." % subcloud_name)

            except Exception as e:
                LOG.exception(e)

            if sysinv_client:
                try:
                    svc_groups = sysinv_client.get_service_groups()
                except Exception as e:
                    svc_groups = None
                    LOG.warn('Cannot retrieve service groups for '
                             'subcloud:%s, %s' % (subcloud_name, e))

            if svc_groups:
                active_sgs = []
                inactive_sgs = []

                # Build 2 lists, 1 of active service groups,
                # one with non-active.
                for sg in svc_groups:
                    if sg.state != consts.SERVICE_GROUP_STATUS_ACTIVE:
                        inactive_sgs.append(sg.service_group_name)
                    else:
                        active_sgs.append(sg.service_group_name)

                # Create a list of service groups that are only present
                # in non-active list
                inactive_only = [
                    sg for sg in inactive_sgs if sg not in active_sgs
                ]

                # An empty inactive only list and a non-empty active list
                # means we're good to go.
                if not inactive_only and active_sgs:
                    avail_to_set = \
                        consts.AVAILABILITY_ONLINE
                else:
                    LOG.info("Subcloud:%s has non-active "
                             "service groups: %s" %
                             (subcloud_name, inactive_only))

            if avail_to_set == consts.AVAILABILITY_OFFLINE:
                if audit_fail_count < consts.AVAIL_FAIL_COUNT_MAX:
                    audit_fail_count = audit_fail_count + 1

                if (avail_status_current == consts.AVAILABILITY_ONLINE) and \
                        (audit_fail_count < consts.AVAIL_FAIL_COUNT_TO_ALARM):
                    # Do not set offline until we have failed audit
                    # the requisite number of times
                    avail_to_set = consts.AVAILABILITY_ONLINE
            else:
                # In the case of a one off blip, we may need to set the
                # fail count back to 0
                audit_fail_count = 0

            if avail_to_set != avail_status_current:

                if avail_to_set == consts.AVAILABILITY_ONLINE:
                    audit_fail_count = 0

                LOG.info('Setting new availability status: %s '
                         'on subcloud: %s' % (avail_to_set, subcloud_name))

                entity_instance_id = "subcloud=%s" % subcloud_name
                fault = self.fm_api.get_fault(
                    fm_const.FM_ALARM_ID_DC_SUBCLOUD_OFFLINE,
                    entity_instance_id)

                if fault and (avail_to_set == consts.AVAILABILITY_ONLINE):
                    try:
                        self.fm_api.clear_fault(
                            fm_const.FM_ALARM_ID_DC_SUBCLOUD_OFFLINE,
                            entity_instance_id)
                    except Exception as e:
                        LOG.exception(e)

                elif not fault and \
                        (avail_to_set == consts.AVAILABILITY_OFFLINE):
                    try:
                        fault = fm_api.Fault(
                            alarm_id=fm_const.FM_ALARM_ID_DC_SUBCLOUD_OFFLINE,
                            alarm_state=fm_const.FM_ALARM_STATE_SET,
                            entity_type_id=fm_const.FM_ENTITY_TYPE_SUBCLOUD,
                            entity_instance_id=entity_instance_id,
                            severity=fm_const.FM_ALARM_SEVERITY_CRITICAL,
                            reason_text=('%s is offline' % subcloud_name),
                            alarm_type=fm_const.FM_ALARM_TYPE_0,
                            probable_cause=fm_const.ALARM_PROBABLE_CAUSE_29,
                            proposed_repair_action="Wait for subcloud to "
                            "become online; if "
                            "problem persists contact "
                            "next level of support.",
                            service_affecting=True)

                        self.fm_api.set_fault(fault)
                    except Exception as e:
                        LOG.exception(e)

                try:
                    db_api.subcloud_update(self.context,
                                           subcloud_id,
                                           management_state=None,
                                           availability_status=avail_to_set,
                                           software_version=None,
                                           description=None,
                                           location=None,
                                           audit_fail_count=audit_fail_count)
                except exceptions.SubcloudNotFound:
                    # slim possibility subcloud could have been deleted since
                    # we found it in db, ignore this benign error.
                    LOG.info('Ignoring SubcloudNotFound when attempting state'
                             ' update: %s' % subcloud_name)
                    continue

                try:
                    self.dcorch_rpc_client.\
                        update_subcloud_states(self.context,
                                               subcloud_name,
                                               management_state,
                                               avail_to_set)

                    LOG.info('Notifying dcorch, subcloud:%s management: %s, '
                             'availability:%s' %
                             (subcloud_name, management_state, avail_to_set))
                except Exception as e:
                    LOG.exception(e)
                    LOG.warn('Problem informing dcorch of subcloud '
                             'state change, subcloud: %s' % subcloud_name)

                if avail_to_set == consts.AVAILABILITY_OFFLINE:
                    # Subcloud is going offline, set all endpoint statuses to
                    # unknown.
                    try:
                        self.subcloud_manager.update_subcloud_endpoint_status(
                            self.context,
                            subcloud_name=subcloud_name,
                            endpoint_type=None,
                            sync_status=consts.SYNC_STATUS_UNKNOWN)
                    except exceptions.SubcloudNotFound:
                        LOG.info('Ignoring SubcloudNotFound when attempting '
                                 'sync_status update: %s' % subcloud_name)
                        continue

            elif audit_fail_count != subcloud.audit_fail_count:

                try:
                    db_api.subcloud_update(self.context,
                                           subcloud_id,
                                           management_state=None,
                                           availability_status=None,
                                           software_version=None,
                                           description=None,
                                           location=None,
                                           audit_fail_count=audit_fail_count)
                except exceptions.SubcloudNotFound:
                    # slim possibility subcloud could have been deleted since
                    # we found it in db, ignore this benign error.
                    LOG.info('Ignoring SubcloudNotFound when attempting '
                             'audit_fail_count update: %s' % subcloud_name)
                    continue
Beispiel #9
0
    def add_subcloud(self, context, payload):
        """Add subcloud and notify orchestrators.

        :param context: request context object
        :param name: name of subcloud to add
        :param payload: subcloud configuration
        """
        LOG.info("Adding subcloud %s." % payload['name'])

        try:
            subcloud = db_api.subcloud_get_by_name(context, payload['name'])
        except exceptions.SubcloudNameNotFound:
            pass
        else:
            raise exceptions.BadRequest(
                resource='subcloud',
                msg='Subcloud with that name already exists')

        # Subcloud is added with software version that matches system
        # controller.
        software_version = SW_VERSION
        try:
            subcloud = db_api.subcloud_create(
                context, payload['name'], payload.get('description'),
                payload.get('location'), software_version,
                payload['management-subnet'], payload['management-gateway-ip'],
                payload['management-start-ip'], payload['management-end-ip'],
                payload['systemcontroller-gateway-ip'])
        except Exception as e:
            LOG.exception(e)
            raise e

        # Populate the subcloud status table with all endpoints
        for endpoint in dcorch_consts.ENDPOINT_TYPES_LIST:
            db_api.subcloud_status_create(context, subcloud.id, endpoint)

        try:
            # Create a new route to this subcloud on the management interface
            # on both controllers.
            m_ks_client = KeystoneClient()
            subcloud_subnet = netaddr.IPNetwork(payload['management-subnet'])
            session = m_ks_client.endpoint_cache.get_session_from_token(
                context.auth_token, context.project)
            sysinv_client = SysinvClient(consts.DEFAULT_REGION_NAME, session)
            controllers = sysinv_client.get_controller_hosts()
            for controller in controllers:
                management_interface = sysinv_client.get_management_interface(
                    controller.hostname)
                if management_interface is not None:
                    sysinv_client.create_route(
                        management_interface.uuid, str(subcloud_subnet.ip),
                        subcloud_subnet.prefixlen,
                        payload['systemcontroller-gateway-ip'], 1)

            # Create identity endpoints to this subcloud on the
            # management-start-ip of the subcloud which will be allocated
            # as the floating Management IP of the Subcloud if the
            # Address Pool is not shared. Incase the endpoint entry
            # is incorrect, or the management IP of the subcloud is changed
            # in the future, it will not go managed or will show up as
            # out of sync. To fix this use Openstack endpoint commands
            # on the SystemController to change the subcloud endpoint
            ks_service_id = None
            for service in m_ks_client.services_list:
                if service.type == dcorch_consts.ENDPOINT_TYPE_IDENTITY:
                    ks_service_id = service.id
                    break
            else:
                raise exceptions.BadRequest(
                    resource='subcloud',
                    msg='No Identity service found on SystemController')

            identity_endpoint_ip = payload['management-start-ip']

            if netaddr.IPAddress(identity_endpoint_ip).version == 6:
                identity_endpoint_url = \
                    "http://[{}]:5000/v3".format(identity_endpoint_ip)
            else:
                identity_endpoint_url = \
                    "http://{}:5000/v3".format(identity_endpoint_ip)

            for iface in ['internal', 'admin']:
                m_ks_client.keystone_client.endpoints.create(
                    ks_service_id,
                    identity_endpoint_url,
                    interface=iface,
                    region=subcloud.name)

            # Inform orchestrator that subcloud has been added
            self.dcorch_rpc_client.add_subcloud(context, subcloud.name,
                                                subcloud.software_version)

            # Regenerate the addn_hosts_dc file
            self._create_addn_hosts_dc(context)

            return db_api.subcloud_db_model_to_dict(subcloud)

        except Exception as e:
            LOG.exception(e)
            # If we failed to create the subcloud, clean up anything we may
            # have done.
            self._delete_subcloud_routes(context, subcloud)
            db_api.subcloud_destroy(context, subcloud.id)
            raise e
Beispiel #10
0
    def delete_subcloud(self, context, subcloud_id):
        """Delete subcloud and notify orchestrators.

        :param context: request context object.
        :param subcloud_id: id of subcloud to delete
        """
        LOG.info("Deleting subcloud %s." % subcloud_id)

        # Retrieve the subcloud details from the database
        subcloud = db_api.subcloud_get(context, subcloud_id)

        # Semantic checking
        if subcloud.management_state != consts.MANAGEMENT_UNMANAGED:
            raise exceptions.SubcloudNotUnmanaged()

        if subcloud.availability_status == \
                consts.AVAILABILITY_ONLINE:
            raise exceptions.SubcloudNotOffline()

        # Inform orchestrators that subcloud has been deleted
        try:
            self.dcorch_rpc_client.del_subcloud(context, subcloud.name)
        except RemoteError as e:
            if "SubcloudNotFound" in e:
                pass

        # We only delete subcloud endpoints, region and user information
        # in the Central Region. The subcloud is already unmanaged and powered
        # down so is not accessible. Therefore set up a session with the
        # Central Region Keystone ONLY.
        keystone_client = KeystoneClient()

        # Delete keystone endpoints for subcloud
        keystone_client.delete_endpoints(subcloud.name)
        keystone_client.delete_region(subcloud.name)

        # Delete the routes to this subcloud
        self._delete_subcloud_routes(context, subcloud)

        # Remove the subcloud from the database
        try:
            db_api.subcloud_destroy(context, subcloud_id)
        except Exception as e:
            LOG.exception(e)
            raise e

        # Clear the offline fault associated with this subcloud as we
        # are deleting it. Note that endpoint out-of-sync alarms should
        # have been cleared when the subcloud was unmanaged and the endpoint
        # sync statuses were set to unknown.
        entity_instance_id = "subcloud=%s" % subcloud.name

        try:
            subcloud_offline = fm_const.FM_ALARM_ID_DC_SUBCLOUD_OFFLINE
            fault = self.fm_api.get_fault(subcloud_offline, entity_instance_id)

            if fault:
                self.fm_api.clear_fault(subcloud_offline, entity_instance_id)
        except Exception as e:
            LOG.info("Problem clearing offline fault for "
                     "subcloud %s" % subcloud.name)
            LOG.exception(e)

        # Regenerate the addn_hosts_dc file
        self._create_addn_hosts_dc(context)
Beispiel #11
0
    def _periodic_patch_audit_loop(self):
        """Audit patch status of subclouds loop."""

        # We are running in our own green thread here.
        LOG.info('Triggered patch audit.')

        try:
            ks_client = KeystoneClient()
        except Exception:
            LOG.warn('Failure initializing KeystoneClient, exiting audit.')
            return

        # First query RegionOne to determine what patches should be applied
        # to the system.
        patching_client = PatchingClient(consts.DEFAULT_REGION_NAME,
                                         ks_client.session)
        regionone_patches = patching_client.query()
        LOG.debug("regionone_patches: %s" % regionone_patches)

        # Build lists of patches that should be applied or committed in all
        # subclouds, based on their state in RegionOne. Check repostate
        # (not patchstate) as we only care if the patch has been applied to
        # the repo (not whether it is installed on the hosts).
        applied_patch_ids = list()
        committed_patch_ids = list()
        for patch_id in regionone_patches.keys():
            if regionone_patches[patch_id]['repostate'] == \
                    patching_v1.PATCH_STATE_APPLIED:
                applied_patch_ids.append(patch_id)
            elif regionone_patches[patch_id]['repostate'] == \
                    patching_v1.PATCH_STATE_COMMITTED:
                committed_patch_ids.append(patch_id)
        LOG.debug("RegionOne applied_patch_ids: %s" % applied_patch_ids)
        LOG.debug("RegionOne committed_patch_ids: %s" % committed_patch_ids)

        # For each subcloud, check whether the patches match the target.
        for subcloud in db_api.subcloud_get_all(self.context):
            # Only audit patching on subclouds that are managed and online
            if (subcloud.management_state != consts.MANAGEMENT_MANAGED
                    or subcloud.availability_status !=
                    consts.AVAILABILITY_ONLINE):
                continue

            try:
                sc_ks_client = KeystoneClient(subcloud.name)
            except (keystone_exceptions.EndpointNotFound, IndexError) as e:
                LOG.warn(
                    "Identity endpoint for online subcloud % not found. %" %
                    (subcloud.name, e))
                continue

            try:
                patching_client = PatchingClient(subcloud.name,
                                                 sc_ks_client.session)
            except keystone_exceptions.EndpointNotFound:
                LOG.warn(
                    "Patching endpoint for online subcloud %s not found." %
                    subcloud.name)
                continue

            try:
                sysinv_client = SysinvClient(subcloud.name,
                                             sc_ks_client.session)
            except keystone_exceptions.EndpointNotFound:
                LOG.warn("Sysinv endpoint for online subcloud %s not found." %
                         subcloud.name)
                continue

            # Retrieve all the patches that are present in this subcloud.
            try:
                subcloud_patches = patching_client.query()
                LOG.debug("Patches for subcloud %s: %s" %
                          (subcloud.name, subcloud_patches))
            except Exception:
                LOG.warn('Cannot retrieve patches for subcloud: %s' %
                         subcloud.name)
                continue

            # Determine which loads are present in this subcloud. During an
            # upgrade, there will be more than one load installed.
            installed_loads = list()
            try:
                loads = sysinv_client.get_loads()
            except Exception:
                LOG.warn('Cannot retrieve loads for subcloud: %s' %
                         subcloud.name)
                continue
            for load in loads:
                installed_loads.append(load.software_version)

            out_of_sync = False

            # Check that all patches in this subcloud are in the correct
            # state, based on the state of the patch in RegionOne. For the
            # subcloud, we use the patchstate because we care whether the
            # patch is installed on the hosts.
            for patch_id in subcloud_patches.keys():
                if subcloud_patches[patch_id]['patchstate'] == \
                        patching_v1.PATCH_STATE_APPLIED:
                    if patch_id not in applied_patch_ids:
                        if patch_id not in committed_patch_ids:
                            LOG.debug("Patch %s should not be applied in %s" %
                                      (patch_id, subcloud.name))
                        else:
                            LOG.debug("Patch %s should be committed in %s" %
                                      (patch_id, subcloud.name))
                        out_of_sync = True
                elif subcloud_patches[patch_id]['patchstate'] == \
                        patching_v1.PATCH_STATE_COMMITTED:
                    if patch_id not in committed_patch_ids:
                        LOG.warn("Patch %s should not be committed in %s" %
                                 (patch_id, subcloud.name))
                        out_of_sync = True
                else:
                    # In steady state, all patches should either be applied
                    # or committed in each subcloud. Patches in other
                    # states mean a sync is required.
                    out_of_sync = True

            # Check that all applied or committed patches in RegionOne are
            # present in the subcloud.
            for patch_id in applied_patch_ids:
                if regionone_patches[patch_id]['sw_version'] in \
                        installed_loads and patch_id not in subcloud_patches:
                    LOG.debug("Patch %s missing from %s" %
                              (patch_id, subcloud.name))
                    out_of_sync = True
            for patch_id in committed_patch_ids:
                if regionone_patches[patch_id]['sw_version'] in \
                        installed_loads and patch_id not in subcloud_patches:
                    LOG.debug("Patch %s missing from %s" %
                              (patch_id, subcloud.name))
                    out_of_sync = True

            if out_of_sync:
                LOG.debug("Subcloud %s is out-of-sync for patching" %
                          subcloud.name)
                self.subcloud_manager.update_subcloud_endpoint_status(
                    self.context,
                    subcloud_name=subcloud.name,
                    endpoint_type=dcorch_consts.ENDPOINT_TYPE_PATCHING,
                    sync_status=consts.SYNC_STATUS_OUT_OF_SYNC)
            else:
                LOG.debug("Subcloud %s is in-sync for patching" %
                          subcloud.name)
                self.subcloud_manager.update_subcloud_endpoint_status(
                    self.context,
                    subcloud_name=subcloud.name,
                    endpoint_type=dcorch_consts.ENDPOINT_TYPE_PATCHING,
                    sync_status=consts.SYNC_STATUS_IN_SYNC)