def _resource_classes_sync(ctx): # Create a set of all resource class in the os_resource_classes library. sel = sa.select([_RC_TBL.c.name]) res = ctx.session.execute(sel).fetchall() db_classes = [r[0] for r in res if not orc.is_custom(r[0])] LOG.debug("Found existing resource classes in db: %s", db_classes) # Determine those resource clases which are in os_resource_classes but not # currently in the database, and insert them. batch_args = [{ 'name': six.text_type(name), 'id': index } for index, name in enumerate(orc.STANDARDS) if name not in db_classes] ins = _RC_TBL.insert() if batch_args: conn = ctx.session.connection() if conn.engine.dialect.name == 'mysql': # We need to do a literal insert of 0 to preserve the order # of the resource class ids from the previous style of # managing them. In some mysql settings a 0 is the same as # "give me a default key". conn.execute("SET SESSION SQL_MODE='NO_AUTO_VALUE_ON_ZERO'") try: ctx.session.execute(ins, batch_args) LOG.debug("Synced resource_classes from os_resource_classes: %s", batch_args) except db_exc.DBDuplicateEntry: pass # some other process sync'd, just ignore
def _resource_classes_sync(context): # Create a set of all resource class in the os_resource_classes library. query = """ MATCH (rc:RESOURCE_CLASS) RETURN rc.name AS name """ result = context.tx.run(query).data() db_std_classes = [] if result: db_std_classes = [ res["name"] for res in result if not orc.is_custom(res["name"]) ] LOG.debug("Found existing resource classes in db: %s", db_std_classes) # Determine those resource clases which are in os_resource_classes but not # currently in the database, and insert them. missing_rc_names = [ name for name in orc.STANDARDS if name not in db_std_classes ] for rc_name in missing_rc_names: query = """ MERGE (rc:RESOURCE_CLASS {name: '%s', created_at: timestamp(), updated_at: timestamp()}) """ % rc_name try: result = context.tx.run(query).data() except db.ClientError: pass # some other process sync'd, just ignore except db.TransientError as e: LOG.error("Transient errror creating Resource Class '%s': %s" % (rc_name, e))
def _validate_rc(provider): # Check that resource classes are custom additional_inventories = provider.get("inventories", {}).get("additional", []) all_inventory_conflicts = [] for inventory in additional_inventories: inventory_conflicts = [ rc for rc in inventory if not os_resource_classes.is_custom(rc) ] all_inventory_conflicts += inventory_conflicts if all_inventory_conflicts: # sort for more predictable message for testing message = _("Invalid resource class, only custom resource classes " "are allowed: %s") % ', '.join( sorted(all_inventory_conflicts)) raise nova_exc.ProviderConfigException(error=message) return additional_inventories
def _get_usage( context: 'nova.context.RequestContext', project_id: str, resource_names: ty.List[str], ) -> ty.Dict[str, int]: """Called by oslo_limit's enforcer""" if not limit_utils.use_unified_limits(): raise NotImplementedError("Unified limits support is disabled") count_servers = False resource_classes = [] for resource in resource_names: if resource == "servers": count_servers = True continue if not resource.startswith("class:"): raise ValueError("Unknown resource type: %s" % resource) # Temporarily strip resource class prefix as placement does not use it. # Example: limit resource 'class:VCPU' will be returned as 'VCPU' from # placement. r_class = resource.lstrip("class:") if r_class in orc.STANDARDS or orc.is_custom(r_class): resource_classes.append(r_class) else: raise ValueError("Unknown resource class: %s" % r_class) if not count_servers and len(resource_classes) == 0: raise ValueError("no resources to check") resource_counts = {} if count_servers: # TODO(melwitt): Change this to count servers from placement once nova # is using placement consumer types and is able to differentiate # between "instance" allocations vs "migration" allocations. if not quota.is_qfd_populated(context): LOG.error('Must migrate all instance mappings before using ' 'unified limits') raise ValueError("must first migrate instance mappings") mappings = objects.InstanceMappingList.get_counts(context, project_id) resource_counts['servers'] = mappings['project']['instances'] try: usages = _get_placement_usages(context, project_id) except exception.UsagesRetrievalFailed as e: msg = ("Failed to retrieve usages from placement while enforcing " "%s quota limits." % ", ".join(resource_names)) LOG.error(msg + " Error: " + str(e)) raise exception.UsagesRetrievalFailed(msg) # Use legacy behavior VCPU = VCPU + PCPU if configured. if CONF.workarounds.unified_limits_count_pcpu_as_vcpu: # If PCPU is in resource_classes, that means it was specified in the # flavor explicitly. In that case, we expect it to have its own limit # registered and we should not fold it into VCPU. if orc.PCPU in usages and orc.PCPU not in resource_classes: usages[orc.VCPU] = (usages.get(orc.VCPU, 0) + usages.get(orc.PCPU, 0)) for resource_class in resource_classes: # Need to add back resource class prefix that was stripped earlier resource_name = 'class:' + resource_class # Placement doesn't know about classes with zero usage # so default to zero to tell oslo.limit usage is zero resource_counts[resource_name] = usages.get(resource_class, 0) return resource_counts