def create_resource_class(req): """POST to create a resource class. On success return a 201 response with an empty body and a location header pointing to the newly created resource class. """ context = req.environ['placement.context'] data = util.extract_json(req.body, POST_RC_SCHEMA_V1_2) try: rc = rp_obj.ResourceClass(context, name=data['name']) rc.create() except exception.ResourceClassExists: raise webob.exc.HTTPConflict( _('Conflicting resource class already exists: %(name)s') % {'name': data['name']}) except exception.MaxDBRetriesExceeded: raise webob.exc.HTTPConflict( _('Max retries of DB transaction exceeded attempting ' 'to create resource class: %(name)s, please' 'try again.') % {'name': data['name']}) req.response.location = util.resource_class_url(req.environ, rc) req.response.status = 201 req.response.content_type = None return req.response
def update_resource_class(req): """PUT to create or validate the existence of single resource class. On a successful create return 201. Return 204 if the class already exists. If the resource class is not a custom resource class, return a 400. 409 might be a better choice, but 400 aligns with previous code. """ name = util.wsgi_path_item(req.environ, 'name') context = req.environ['placement.context'] # Use JSON validation to validation resource class name. util.extract_json('{"name": "%s"}' % name, PUT_RC_SCHEMA_V1_2) status = 204 try: rc = rp_obj.ResourceClass.get_by_name(context, name) except exception.NotFound: try: rc = rp_obj.ResourceClass(context, name=name) rc.create() status = 201 # We will not see ResourceClassCannotUpdateStandard because # that was already caught when validating the {name}. except exception.ResourceClassExists: # Someone just now created the class, so stick with 204 pass req.response.status = status req.response.content_type = None req.response.location = util.resource_class_url(req.environ, rc) return req.response
def test_cannot_create_requires_name(self): rc = resource_provider.ResourceClass(self.context) exc = self.assertRaises(exception.ObjectActionError, rc.create) self.assertIn('name is required', str(exc))
def test_cannot_create_with_id(self): rc = resource_provider.ResourceClass(self.context, id=1, name='CUSTOM_IRON_NFV') exc = self.assertRaises(exception.ObjectActionError, rc.create) self.assertIn('already created', str(exc))
def setUp(self): super(TestPlacementURLs, self).setUp() self.resource_provider = rp_obj.ResourceProvider( name=uuidsentinel.rp_name, uuid=uuidsentinel.rp_uuid) self.resource_class = rp_obj.ResourceClass( name='CUSTOM_BAREMETAL_GOLD', id=1000)
def test_local_with_shared_custom_resource(self): """Create some resource providers that can satisfy the request for resources with local VCPU and MEMORY_MB but rely on a shared resource provider to satisfy a custom resource requirement and verify that the allocation requests returned by AllocationCandidates have the custom resource served up by the shared custom resource provider and VCPU/MEMORY_MB by the compute node providers """ # The aggregate that will be associated to everything... agg_uuid = uuids.agg # Create two compute node providers with VCPU, RAM and NO local # CUSTOM_MAGIC resources, associated with the aggregate. for name in ('cn1', 'cn2'): cn = self._create_provider(name, agg_uuid) _add_inventory(cn, fields.ResourceClass.VCPU, 24, allocation_ratio=16.0) _add_inventory(cn, fields.ResourceClass.MEMORY_MB, 1024, min_unit=64, allocation_ratio=1.5) # Create a custom resource called MAGIC magic_rc = rp_obj.ResourceClass( self.ctx, name='CUSTOM_MAGIC', ) magic_rc.create() # Create the shared provider that serves CUSTOM_MAGIC, associated with # the same aggregate magic_p = self._create_provider('shared custom resource provider', agg_uuid) _add_inventory(magic_p, magic_rc.name, 2048, reserved=1024, min_unit=10) # Mark the magic provider as having inventory shared among any provider # associated via aggregate _set_traits(magic_p, "MISC_SHARES_VIA_AGGREGATE") # The resources we will request requested_resources = { fields.ResourceClass.VCPU: 1, fields.ResourceClass.MEMORY_MB: 64, magic_rc.name: 512, } alloc_cands = self._get_allocation_candidates( resources=requested_resources) # Verify the allocation requests that are returned. There should be 2 # allocation requests, one for each compute node, containing 3 # resources in each allocation request, one each for VCPU, RAM, and # MAGIC. The amounts of the requests should correspond to the requested # resource amounts in the filter:resources dict passed to # AllocationCandidates.get_by_filters(). The providers for VCPU and # MEMORY_MB should be the compute nodes while the provider for the # MAGIC should be the shared custom resource provider. expected = [ [('cn1', fields.ResourceClass.VCPU, 1), ('cn1', fields.ResourceClass.MEMORY_MB, 64), ('shared custom resource provider', magic_rc.name, 512)], [('cn2', fields.ResourceClass.VCPU, 1), ('cn2', fields.ResourceClass.MEMORY_MB, 64), ('shared custom resource provider', magic_rc.name, 512)], ] self._validate_allocation_requests(expected, alloc_cands)
def test_get_provider_ids_matching_all(self): # These RPs are named based on whether we expect them to be 'incl'uded # or 'excl'uded in the result. # No inventory records. This one should never show up in a result. self._create_provider('no_inventory') # Inventory of adequate CPU and memory, no allocations against it. excl_big_cm_noalloc = self._create_provider('big_cm_noalloc') _add_inventory(excl_big_cm_noalloc, fields.ResourceClass.VCPU, 15) _add_inventory(excl_big_cm_noalloc, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048) # Adequate inventory, no allocations against it. incl_biginv_noalloc = self._create_provider('biginv_noalloc') _add_inventory(incl_biginv_noalloc, fields.ResourceClass.VCPU, 15) _add_inventory(incl_biginv_noalloc, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048) _add_inventory(incl_biginv_noalloc, fields.ResourceClass.DISK_GB, 2000) # No allocations, but inventory unusable. Try to hit all the possible # reasons for exclusion. # VCPU min_unit too high excl_badinv_min_unit = self._create_provider('badinv_min_unit') _add_inventory(excl_badinv_min_unit, fields.ResourceClass.VCPU, 12, min_unit=6) _add_inventory(excl_badinv_min_unit, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048) _add_inventory(excl_badinv_min_unit, fields.ResourceClass.DISK_GB, 2000) # MEMORY_MB max_unit too low excl_badinv_max_unit = self._create_provider('badinv_max_unit') _add_inventory(excl_badinv_max_unit, fields.ResourceClass.VCPU, 15) _add_inventory(excl_badinv_max_unit, fields.ResourceClass.MEMORY_MB, 4096, max_unit=512) _add_inventory(excl_badinv_max_unit, fields.ResourceClass.DISK_GB, 2000) # DISK_GB unsuitable step_size excl_badinv_step_size = self._create_provider('badinv_step_size') _add_inventory(excl_badinv_step_size, fields.ResourceClass.VCPU, 15) _add_inventory(excl_badinv_step_size, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048) _add_inventory(excl_badinv_step_size, fields.ResourceClass.DISK_GB, 2000, step_size=7) # Not enough total VCPU excl_badinv_total = self._create_provider('badinv_total') _add_inventory(excl_badinv_total, fields.ResourceClass.VCPU, 4) _add_inventory(excl_badinv_total, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048) _add_inventory(excl_badinv_total, fields.ResourceClass.DISK_GB, 2000) # Too much reserved MEMORY_MB excl_badinv_reserved = self._create_provider('badinv_reserved') _add_inventory(excl_badinv_reserved, fields.ResourceClass.VCPU, 15) _add_inventory(excl_badinv_reserved, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048, reserved=3500) _add_inventory(excl_badinv_reserved, fields.ResourceClass.DISK_GB, 2000) # DISK_GB allocation ratio blows it up excl_badinv_alloc_ratio = self._create_provider('badinv_alloc_ratio') _add_inventory(excl_badinv_alloc_ratio, fields.ResourceClass.VCPU, 15) _add_inventory(excl_badinv_alloc_ratio, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048) _add_inventory(excl_badinv_alloc_ratio, fields.ResourceClass.DISK_GB, 2000, allocation_ratio=0.5) # Inventory consumed in one RC, but available in the others excl_1invunavail = self._create_provider('1invunavail') _add_inventory(excl_1invunavail, fields.ResourceClass.VCPU, 10) _allocate_from_provider(excl_1invunavail, fields.ResourceClass.VCPU, 7) _add_inventory(excl_1invunavail, fields.ResourceClass.MEMORY_MB, 4096) _allocate_from_provider(excl_1invunavail, fields.ResourceClass.MEMORY_MB, 1024) _add_inventory(excl_1invunavail, fields.ResourceClass.DISK_GB, 2000) _allocate_from_provider(excl_1invunavail, fields.ResourceClass.DISK_GB, 400) # Inventory all consumed excl_allused = self._create_provider('allused') _add_inventory(excl_allused, fields.ResourceClass.VCPU, 10) _allocate_from_provider(excl_allused, fields.ResourceClass.VCPU, 7) _add_inventory(excl_allused, fields.ResourceClass.MEMORY_MB, 4000) _allocate_from_provider(excl_allused, fields.ResourceClass.MEMORY_MB, 1500) _allocate_from_provider(excl_allused, fields.ResourceClass.MEMORY_MB, 2000) _add_inventory(excl_allused, fields.ResourceClass.DISK_GB, 1500) _allocate_from_provider(excl_allused, fields.ResourceClass.DISK_GB, 1) # Inventory available in requested classes, but unavailable in others incl_extra_full = self._create_provider('extra_full') _add_inventory(incl_extra_full, fields.ResourceClass.VCPU, 20) _allocate_from_provider(incl_extra_full, fields.ResourceClass.VCPU, 15) _add_inventory(incl_extra_full, fields.ResourceClass.MEMORY_MB, 4096) _allocate_from_provider(incl_extra_full, fields.ResourceClass.MEMORY_MB, 1024) _add_inventory(incl_extra_full, fields.ResourceClass.DISK_GB, 2000) _allocate_from_provider(incl_extra_full, fields.ResourceClass.DISK_GB, 400) _add_inventory(incl_extra_full, fields.ResourceClass.PCI_DEVICE, 4) _allocate_from_provider(incl_extra_full, fields.ResourceClass.PCI_DEVICE, 1) _allocate_from_provider(incl_extra_full, fields.ResourceClass.PCI_DEVICE, 3) # Inventory available in a unrequested classes, not in requested ones excl_extra_avail = self._create_provider('extra_avail') # Incompatible step size _add_inventory(excl_extra_avail, fields.ResourceClass.VCPU, 10, step_size=3) # Not enough left after reserved + used _add_inventory(excl_extra_avail, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048, reserved=2048) _allocate_from_provider(excl_extra_avail, fields.ResourceClass.MEMORY_MB, 1040) # Allocation ratio math _add_inventory(excl_extra_avail, fields.ResourceClass.DISK_GB, 2000, allocation_ratio=0.5) _add_inventory(excl_extra_avail, fields.ResourceClass.IPV4_ADDRESS, 48) custom_special = rp_obj.ResourceClass(self.ctx, name='CUSTOM_SPECIAL') custom_special.create() _add_inventory(excl_extra_avail, 'CUSTOM_SPECIAL', 100) _allocate_from_provider(excl_extra_avail, 'CUSTOM_SPECIAL', 99) resources = { fields.ResourceClass.STANDARD.index(fields.ResourceClass.VCPU): 5, fields.ResourceClass.STANDARD.index( fields.ResourceClass.MEMORY_MB): 1024, fields.ResourceClass.STANDARD.index( fields.ResourceClass.DISK_GB): 1500 } # Run it! res = rp_obj._get_provider_ids_matching_all(self.ctx, resources, {}) # We should get all the incl_* RPs expected = [incl_biginv_noalloc, incl_extra_full] self.assertEqual(set(rp.id for rp in expected), set(res)) # Now request that the providers must have a set of required traits and # that this results in no results returned, since we haven't yet # associated any traits with the providers avx2_t = rp_obj.Trait.get_by_name(self.ctx, os_traits.HW_CPU_X86_AVX2) # _get_provider_ids_matching_all()'s required_traits argument is a map, # keyed by trait name, of the trait internal ID req_traits = {os_traits.HW_CPU_X86_AVX2: avx2_t.id} res = rp_obj._get_provider_ids_matching_all(self.ctx, resources, req_traits) self.assertEqual([], res) # OK, now add the trait to one of the providers and verify that # provider now shows up in our results incl_biginv_noalloc.set_traits([avx2_t]) res = rp_obj._get_provider_ids_matching_all(self.ctx, resources, req_traits) self.assertEqual([incl_biginv_noalloc.id], res)
def test_get_provider_ids_matching_all(self): # These RPs are named based on whether we expect them to be 'incl'uded # or 'excl'uded in the result. # No inventory records. This one should never show up in a result. self._create_provider('no_inventory') # Inventory of adequate CPU and memory, no allocations against it. excl_big_cm_noalloc = self._create_provider('big_cm_noalloc') _add_inventory(excl_big_cm_noalloc, fields.ResourceClass.VCPU, 15) _add_inventory(excl_big_cm_noalloc, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048) # Adequate inventory, no allocations against it. incl_biginv_noalloc = self._create_provider('biginv_noalloc') _add_inventory(incl_biginv_noalloc, fields.ResourceClass.VCPU, 15) _add_inventory(incl_biginv_noalloc, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048) _add_inventory(incl_biginv_noalloc, fields.ResourceClass.DISK_GB, 2000) # No allocations, but inventory unusable. Try to hit all the possible # reasons for exclusion. # VCPU min_unit too high excl_badinv_min_unit = self._create_provider('badinv_min_unit') _add_inventory(excl_badinv_min_unit, fields.ResourceClass.VCPU, 12, min_unit=6) _add_inventory(excl_badinv_min_unit, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048) _add_inventory(excl_badinv_min_unit, fields.ResourceClass.DISK_GB, 2000) # MEMORY_MB max_unit too low excl_badinv_max_unit = self._create_provider('badinv_max_unit') _add_inventory(excl_badinv_max_unit, fields.ResourceClass.VCPU, 15) _add_inventory(excl_badinv_max_unit, fields.ResourceClass.MEMORY_MB, 4096, max_unit=512) _add_inventory(excl_badinv_max_unit, fields.ResourceClass.DISK_GB, 2000) # DISK_GB unsuitable step_size excl_badinv_step_size = self._create_provider('badinv_step_size') _add_inventory(excl_badinv_step_size, fields.ResourceClass.VCPU, 15) _add_inventory(excl_badinv_step_size, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048) _add_inventory(excl_badinv_step_size, fields.ResourceClass.DISK_GB, 2000, step_size=7) # Not enough total VCPU excl_badinv_total = self._create_provider('badinv_total') _add_inventory(excl_badinv_total, fields.ResourceClass.VCPU, 4) _add_inventory(excl_badinv_total, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048) _add_inventory(excl_badinv_total, fields.ResourceClass.DISK_GB, 2000) # Too much reserved MEMORY_MB excl_badinv_reserved = self._create_provider('badinv_reserved') _add_inventory(excl_badinv_reserved, fields.ResourceClass.VCPU, 15) _add_inventory(excl_badinv_reserved, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048, reserved=3500) _add_inventory(excl_badinv_reserved, fields.ResourceClass.DISK_GB, 2000) # DISK_GB allocation ratio blows it up excl_badinv_alloc_ratio = self._create_provider('badinv_alloc_ratio') _add_inventory(excl_badinv_alloc_ratio, fields.ResourceClass.VCPU, 15) _add_inventory(excl_badinv_alloc_ratio, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048) _add_inventory(excl_badinv_alloc_ratio, fields.ResourceClass.DISK_GB, 2000, allocation_ratio=0.5) # Inventory consumed in one RC, but available in the others excl_1invunavail = self._create_provider('1invunavail') _add_inventory(excl_1invunavail, fields.ResourceClass.VCPU, 10) _allocate_from_provider(excl_1invunavail, fields.ResourceClass.VCPU, 7) _add_inventory(excl_1invunavail, fields.ResourceClass.MEMORY_MB, 4096) _allocate_from_provider(excl_1invunavail, fields.ResourceClass.MEMORY_MB, 1024) _add_inventory(excl_1invunavail, fields.ResourceClass.DISK_GB, 2000) _allocate_from_provider(excl_1invunavail, fields.ResourceClass.DISK_GB, 400) # Inventory all consumed excl_allused = self._create_provider('allused') _add_inventory(excl_allused, fields.ResourceClass.VCPU, 10) _allocate_from_provider(excl_allused, fields.ResourceClass.VCPU, 7) _add_inventory(excl_allused, fields.ResourceClass.MEMORY_MB, 4000) _allocate_from_provider(excl_allused, fields.ResourceClass.MEMORY_MB, 1500) _allocate_from_provider(excl_allused, fields.ResourceClass.MEMORY_MB, 2000) _add_inventory(excl_allused, fields.ResourceClass.DISK_GB, 1500) _allocate_from_provider(excl_allused, fields.ResourceClass.DISK_GB, 1) # Inventory available in requested classes, but unavailable in others incl_extra_full = self._create_provider('extra_full') _add_inventory(incl_extra_full, fields.ResourceClass.VCPU, 20) _allocate_from_provider(incl_extra_full, fields.ResourceClass.VCPU, 15) _add_inventory(incl_extra_full, fields.ResourceClass.MEMORY_MB, 4096) _allocate_from_provider(incl_extra_full, fields.ResourceClass.MEMORY_MB, 1024) _add_inventory(incl_extra_full, fields.ResourceClass.DISK_GB, 2000) _allocate_from_provider(incl_extra_full, fields.ResourceClass.DISK_GB, 400) _add_inventory(incl_extra_full, fields.ResourceClass.PCI_DEVICE, 4) _allocate_from_provider(incl_extra_full, fields.ResourceClass.PCI_DEVICE, 1) _allocate_from_provider(incl_extra_full, fields.ResourceClass.PCI_DEVICE, 3) # Inventory available in a unrequested classes, not in requested ones excl_extra_avail = self._create_provider('extra_avail') # Incompatible step size _add_inventory(excl_extra_avail, fields.ResourceClass.VCPU, 10, step_size=3) # Not enough left after reserved + used _add_inventory(excl_extra_avail, fields.ResourceClass.MEMORY_MB, 4096, max_unit=2048, reserved=2048) _allocate_from_provider(excl_extra_avail, fields.ResourceClass.MEMORY_MB, 1040) # Allocation ratio math _add_inventory(excl_extra_avail, fields.ResourceClass.DISK_GB, 2000, allocation_ratio=0.5) _add_inventory(excl_extra_avail, fields.ResourceClass.IPV4_ADDRESS, 48) custom_special = rp_obj.ResourceClass(self.ctx, name='CUSTOM_SPECIAL') custom_special.create() _add_inventory(excl_extra_avail, 'CUSTOM_SPECIAL', 100) _allocate_from_provider(excl_extra_avail, 'CUSTOM_SPECIAL', 99) resources = { fields.ResourceClass.STANDARD.index(fields.ResourceClass.VCPU): 5, fields.ResourceClass.STANDARD.index( fields.ResourceClass.MEMORY_MB): 1024, fields.ResourceClass.STANDARD.index( fields.ResourceClass.DISK_GB): 1500 } # Run it! res = rp_obj._get_provider_ids_matching_all(self.ctx, resources) # We should get all the incl_* RPs expected = [incl_biginv_noalloc, incl_extra_full] self.assertEqual(set(rp.id for rp in expected), set(res))