def test_parse_cpu_spec_valid_syntax_works(self): cpuset_ids = hw.parse_cpu_spec("1") self.assertEqual(set([1]), cpuset_ids) cpuset_ids = hw.parse_cpu_spec("1,2") self.assertEqual(set([1, 2]), cpuset_ids) cpuset_ids = hw.parse_cpu_spec(", , 1 , ,, 2, ,") self.assertEqual(set([1, 2]), cpuset_ids) cpuset_ids = hw.parse_cpu_spec("1-1") self.assertEqual(set([1]), cpuset_ids) cpuset_ids = hw.parse_cpu_spec(" 1 - 1, 1 - 2 , 1 -3") self.assertEqual(set([1, 2, 3]), cpuset_ids) cpuset_ids = hw.parse_cpu_spec("1,^2") self.assertEqual(set([1]), cpuset_ids) cpuset_ids = hw.parse_cpu_spec("1-2, ^1") self.assertEqual(set([2]), cpuset_ids) cpuset_ids = hw.parse_cpu_spec("1-3,5,^2") self.assertEqual(set([1, 3, 5]), cpuset_ids) cpuset_ids = hw.parse_cpu_spec(" 1 - 3 , ^2, 5") self.assertEqual(set([1, 3, 5]), cpuset_ids) cpuset_ids = hw.parse_cpu_spec(" 1,1, ^1") self.assertEqual(set([]), cpuset_ids)
def _migrate_legacy_object(cls, context, instance_uuid, primitive): """Convert a pre-Liberty object to a real o.vo. Handle an unversioned object created prior to Liberty, by transforming to a versioned object and saving back the serialized version of this. :param context: RequestContext :param instance_uuid: The UUID of the instance this topology is associated with. :param primitive: A serialized representation of the legacy object. :returns: A serialized representation of the updated object. """ obj = cls( instance_uuid=instance_uuid, cells=[ InstanceNUMACell( id=cell.get('id'), cpuset=hardware.parse_cpu_spec(cell.get('cpus', '')), pcpuset=set(), memory=cell.get('mem', {}).get('total', 0), pagesize=cell.get('pagesize'), ) for cell in primitive.get('cells', []) ], ) db_obj = jsonutils.dumps(obj.obj_to_primitive()) values = { 'numa_topology': db_obj, } db.instance_extra_update_by_uuid(context, instance_uuid, values) return obj
def _from_dict(cls, data_dict): # NOTE(sahid): Used as legacy, could be renamed in # _legacy_from_dict_ to the future to avoid confusing. cpuset = hardware.parse_cpu_spec(data_dict.get('cpus', '')) memory = data_dict.get('mem', {}).get('total', 0) cell_id = data_dict.get('id') pagesize = data_dict.get('pagesize') return cls(id=cell_id, cpuset=cpuset, memory=memory, pagesize=pagesize)
def _from_dict(cls, data_dict): cpuset = hardware.parse_cpu_spec( data_dict.get('cpus', '')) cpu_usage = data_dict.get('cpu_usage', 0) memory = data_dict.get('mem', {}).get('total', 0) memory_usage = data_dict.get('mem', {}).get('used', 0) cell_id = data_dict.get('id') return cls(id=cell_id, cpuset=cpuset, memory=memory, cpu_usage=cpu_usage, memory_usage=memory_usage)
def _from_dict(cls, data_dict): cpuset = hardware.parse_cpu_spec( data_dict.get('cpus', '')) cpu_usage = data_dict.get('cpu_usage', 0) memory = data_dict.get('mem', {}).get('total', 0) memory_usage = data_dict.get('mem', {}).get('used', 0) cell_id = data_dict.get('id') return cls(id=cell_id, cpuset=cpuset, memory=memory, cpu_usage=cpu_usage, memory_usage=memory_usage, mempages=[], pinned_cpus=set([]), siblings=[])
def _set_numa_cpus(self, image_props): hw_numa_cpus = [] hw_numa_cpus_set = False for cellid in range(ImageMetaProps.NUMA_NODES_MAX): cpuprop = "hw_numa_cpus.%d" % cellid if cpuprop not in image_props: break hw_numa_cpus.append(hardware.parse_cpu_spec(image_props[cpuprop])) hw_numa_cpus_set = True del image_props[cpuprop] if hw_numa_cpus_set: self.hw_numa_cpus = hw_numa_cpus
def obj_from_db_obj(cls, db_obj): if 'nova_object.name' in db_obj: obj_topology = cls.obj_from_primitive(db_obj) else: # NOTE(sahid): This compatibility code needs to stay until we can # guarantee that all compute nodes are using RPC API => 3.40. cell = db_obj['cells'][0] ram_ratio = cell['mem']['limit'] / float(cell['mem']['total']) cpu_ratio = cell['cpu_limit'] / float( len(hardware.parse_cpu_spec(cell['cpus']))) obj_topology = NUMATopologyLimits(cpu_allocation_ratio=cpu_ratio, ram_allocation_ratio=ram_ratio) return obj_topology
def obj_from_db_obj(cls, db_obj): if 'nova_object.name' in db_obj: obj_topology = cls.obj_from_primitive(db_obj) else: # NOTE(sahid): This compatibility code needs to stay until we can # guarantee that all compute nodes are using RPC API => 3.40. cell = db_obj['cells'][0] ram_ratio = cell['mem']['limit'] / float(cell['mem']['total']) cpu_ratio = cell['cpu_limit'] / float(len(hardware.parse_cpu_spec( cell['cpus']))) obj_topology = NUMATopologyLimits( cpu_allocation_ratio=cpu_ratio, ram_allocation_ratio=ram_ratio) return obj_topology
def _set_numa_cpus(self, image_props): nodes = int(image_props.get("hw_numa_nodes", "1")) hw_numa_cpus = [None for i in range(nodes)] hw_numa_cpus_set = False for cellid in range(nodes): cpuprop = "hw_numa_cpus.%d" % cellid if cpuprop in image_props: hw_numa_cpus[cellid] = \ hardware.parse_cpu_spec(image_props[cpuprop]) hw_numa_cpus_set = True del image_props[cpuprop] if hw_numa_cpus_set: self.hw_numa_cpus = hw_numa_cpus
def get_pinset(self): """ Parse the vcpu_pin_set line from nova.conf using nova's parse_cpu_spec() function """ broken = None try: oc_pin_set, broken = host.ssh( "sudo crudini --get /etc/nova/nova.conf DEFAULT vcpu_pin_set | cat 2>&1" ) line = "".join(oc_pin_set).rstrip() self.pinset_list = parse_cpu_spec(line) self.pinset_line = line except: log.error("[%s] Unable to parse vcpu_pin_set: %s" % (self, line)) pass return broken
def from_legacy_object(cls, primitive: str): """Convert a pre-Liberty object to a (serialized) real o.vo. :param primitive: A serialized representation of the legacy object. :returns: A serialized representation of the updated object. """ topology = cls(cells=[ NUMACell( id=cell.get('id'), cpuset=hardware.parse_cpu_spec(cell.get('cpus', '')), cpu_usage=cell.get('cpu_usage', 0), memory=cell.get('mem', {}).get('total', 0), memory_usage=cell.get('mem', {}).get('used', 0), mempages=[], pinned_cpus=set(), siblings=[], ) for cell in jsonutils.loads(primitive).get('cells', []) ], ) return topology._to_json()
def _validate_cache_node(flavor): # Split a list into evenly sized chunks def chunk(it, size): it = iter(it) return iter(lambda: tuple(islice(it, size)), ()) specs = flavor.extra_specs KEYS = ['hw:cache_l3', 'hw:cache_l3_code', 'hw:cache_l3_data'] for this_key in KEYS: this_prefix = this_key + '.' for key in specs: if key.startswith(this_prefix): # Check that we have specified dedicated cpus if specs.get(CPU_POLICY_KEY) != \ fields.CPUAllocationPolicy.DEDICATED: msg = (_('%(K)s is not permitted when %(P)s is set to' ' shared.') % {'K': this_key, 'P': CPU_POLICY_KEY}) raise webob.exc.HTTPConflict(explanation=msg) # Virtual numa node must be valid suffix = key.split(this_prefix, 1)[1] try: vnode = int(suffix) except ValueError: msg = _('%s virtual numa node number must be an ' 'integer') % this_key raise webob.exc.HTTPBadRequest(explanation=msg) if vnode < 0: msg = _('%s virtual numa node number must be greater ' 'than or equal to 0') % this_key raise webob.exc.HTTPBadRequest(explanation=msg) # Cache size must be valid and positive try: value = int(specs[key]) except ValueError: msg = _('%s must be an integer') % key raise webob.exc.HTTPBadRequest(explanation=msg) if value <= 0: msg = _('%s must be positive') % key raise webob.exc.HTTPBadRequest(explanation=msg) # Check that we can properly parse hw:cache_vcpus.x cpulist, # and that vcpus are within valid range. flavor_cpuset = set(range(flavor.vcpus)) cache_key = 'hw:cache_vcpus' cache_prefix = cache_key + '.' for key in specs: if key.startswith(cache_prefix): suffix = key.split(cache_prefix, 1)[1] try: vnode = int(suffix) except ValueError: msg = _('%s virtual numa node number must be an ' 'integer') % cache_key raise webob.exc.HTTPBadRequest(explanation=msg) if vnode < 0: msg = _('%s virtual numa node number must be greater ' 'than or equal to 0') % cache_key raise webob.exc.HTTPBadRequest(explanation=msg) try: value = specs[key] cpuset_ids = hardware.parse_cpu_spec(value) except Exception as e: msg = (_("Invalid %(K)s '%(V)s'; reason: %(R)s.") % {'K': key, 'V': value, 'R': e.format_message() }) raise webob.exc.HTTPBadRequest(explanation=msg) if not cpuset_ids: msg = (_("Invalid %(K)s '%(V)s'; reason: %(R)s.") % {'K': key, 'V': value, 'R': 'no vcpus specified' }) raise webob.exc.HTTPBadRequest(explanation=msg) if not cpuset_ids.issubset(flavor_cpuset): msg = _('%(K)s value (%(V)s) must be a subset of vcpus ' '(%(S)s)') \ % {'K': key, 'V': value, 'S': utils.list_to_range(list(flavor_cpuset))} raise webob.exc.HTTPBadRequest(explanation=msg) # Check whether hw:cache_vcpus.x are subset of hw:numa_cpus.x cpus_key = 'hw:numa_cpus.' + suffix if cpus_key in specs: try: cpus_value = specs[cpus_key] numa_cpuset = hardware.parse_cpu_spec(cpus_value) except Exception as e: msg = (_("Invalid %(K)s '%(V)s'; reason: %(R)s.") % {'K': cpus_key, 'V': cpus_value, 'R': e.format_message() }) raise webob.exc.HTTPBadRequest(explanation=msg) else: NUMA_NODES_KEY = 'hw:numa_nodes' try: hw_numa_nodes = int(specs.get(NUMA_NODES_KEY, 1)) except ValueError: msg = _('hw:numa_nodes value must be an integer') raise webob.exc.HTTPBadRequest(explanation=msg) if vnode >= hw_numa_nodes: msg = (_('%(K)s must use vnode id less than the ' 'specified hw:numa_nodes value %(N)s.') % {'K': this_key, 'N': hw_numa_nodes}) raise webob.exc.HTTPBadRequest(explanation=msg) chunk_size = flavor.vcpus / hw_numa_nodes numa_cpus = list(chunk(range(flavor.vcpus), chunk_size)) try: numa_cpuset = set(numa_cpus[vnode]) except IndexError: msg = _('%s virtual numa node number must be subset ' 'of numa nodes') % vnode raise webob.exc.HTTPBadRequest(explanation=msg) if not cpuset_ids.issubset(numa_cpuset): msg = (_('%(K)s value (%(V)s) must be a subset of ' 'vcpus (%(S)s)') % {'K': cache_key, 'V': value, 'S': utils.list_to_range(list(numa_cpuset)) }) raise webob.exc.HTTPBadRequest(explanation=msg) # Check that we have specified dedicated cpus if specs.get(CPU_POLICY_KEY) != \ fields.CPUAllocationPolicy.DEDICATED: msg = (_('%(K)s is not permitted when %(P)s is set to' ' shared.') % {'K': key, 'P': CPU_POLICY_KEY}) raise webob.exc.HTTPConflict(explanation=msg)