From ca3ade76162d1d1e7b9d1018b18ad7e5c4c930d2 Mon Sep 17 00:00:00 2001 From: Chris Dent Date: Fri, 8 Mar 2019 17:37:13 +0000 Subject: [PATCH] Move Inventory and InventoryList to own file The Inventory and InventoryList classes are moved to an inventory.py file. Tests and other callers are udpated to reflect the new location. A new unit.objects.test_inventory is created. A functional.db.test_inventory is _not_ created because though there are inventory related tests within test_resource_provider they are within a ResourceProvider-related class and teasing out the ones that are for the sake of just Inventory versus for both Inventory and ResourceProvider is not entirely clear. Change-Id: Ia1ad18585194236647ec2c83480254531a619e91 --- placement/handlers/inventory.py | 15 +- placement/handlers/reshaper.py | 7 +- placement/objects/inventory.py | 107 ++++++++++++++ placement/objects/reshaper.py | 6 +- placement/objects/resource_provider.py | 90 +----------- .../tests/functional/db/test_allocation.py | 18 +-- placement/tests/functional/db/test_base.py | 9 +- placement/tests/functional/db/test_reshape.py | 45 +++--- .../functional/db/test_resource_class.py | 7 +- .../functional/db/test_resource_provider.py | 39 ++--- placement/tests/functional/db/test_usage.py | 10 +- .../tests/unit/objects/test_inventory.py | 134 ++++++++++++++++++ .../unit/objects/test_resource_provider.py | 112 --------------- 13 files changed, 325 insertions(+), 274 deletions(-) create mode 100644 placement/objects/inventory.py create mode 100644 placement/tests/unit/objects/test_inventory.py diff --git a/placement/handlers/inventory.py b/placement/handlers/inventory.py index 41723125c..c5ff8a782 100644 --- a/placement/handlers/inventory.py +++ b/placement/handlers/inventory.py @@ -24,6 +24,7 @@ from placement import errors from placement import exception from placement.i18n import _ from placement import microversion +from placement.objects import inventory as inv_obj from placement.objects import resource_provider as rp_obj from placement.policies import inventory as policies from placement.schemas import inventory as schema @@ -82,7 +83,7 @@ def make_inventory_object(resource_provider, resource_class, **data): # 0) for non-negative integers. It's not clear if that is # duplication or decoupling so leaving it as this for now. try: - inventory = rp_obj.Inventory( + inventory = inv_obj.Inventory( resource_provider=resource_provider, resource_class=resource_class, **data) except (ValueError, TypeError) as exc: @@ -165,8 +166,8 @@ def _validate_inventory_capacity(version, inventories): else: op = operator.lt exc_class = exception.InvalidInventoryCapacityReservedCanBeTotal - if isinstance(inventories, rp_obj.Inventory): - inventories = rp_obj.InventoryList(objects=[inventories]) + if isinstance(inventories, inv_obj.Inventory): + inventories = inv_obj.InventoryList(objects=[inventories]) for inventory in inventories: if op(inventory.capacity, 0): raise exc_class( @@ -271,7 +272,7 @@ def get_inventories(req): _("No resource provider with uuid %(uuid)s found : %(error)s") % {'uuid': uuid, 'error': exc}) - inv_list = rp_obj.InventoryList.get_all_by_resource_provider(context, rp) + inv_list = inv_obj.InventoryList.get_all_by_resource_provider(context, rp) return _send_inventories(req, rp, inv_list) @@ -295,7 +296,7 @@ def get_inventory(req): _("No resource provider with uuid %(uuid)s found : %(error)s") % {'uuid': uuid, 'error': exc}) - inv_list = rp_obj.InventoryList.get_all_by_resource_provider(context, rp) + inv_list = inv_obj.InventoryList.get_all_by_resource_provider(context, rp) inventory = inv_list.find(resource_class) if not inventory: @@ -339,7 +340,7 @@ def set_inventories(req): inventory = make_inventory_object( resource_provider, res_class, **inventory_data) inv_list.append(inventory) - inventories = rp_obj.InventoryList(objects=inv_list) + inventories = inv_obj.InventoryList(objects=inv_list) try: _validate_inventory_capacity( @@ -390,7 +391,7 @@ def delete_inventories(req): resource_provider = rp_obj.ResourceProvider.get_by_uuid( context, uuid) - inventories = rp_obj.InventoryList(objects=[]) + inventories = inv_obj.InventoryList(objects=[]) try: resource_provider.set_inventory(inventories) diff --git a/placement/handlers/reshaper.py b/placement/handlers/reshaper.py index 87548f0ac..f45c2f359 100644 --- a/placement/handlers/reshaper.py +++ b/placement/handlers/reshaper.py @@ -30,6 +30,7 @@ from placement.handlers import allocation from placement.handlers import inventory from placement.i18n import _ from placement import microversion +from placement.objects import inventory as inv_obj from placement.objects import reshaper from placement.objects import resource_provider as rp_obj from placement.policies import reshaper as policies @@ -78,10 +79,10 @@ def reshape(req): for res_class, raw_inventory in inventory_data['inventories'].items(): inv_data = copy.copy(inventory.INVENTORY_DEFAULTS) inv_data.update(raw_inventory) - inv_obj = inventory.make_inventory_object( + inv_object = inventory.make_inventory_object( resource_provider, res_class, **inv_data) - inv_list.append(inv_obj) - inventory_by_rp[resource_provider] = rp_obj.InventoryList( + inv_list.append(inv_object) + inventory_by_rp[resource_provider] = inv_obj.InventoryList( objects=inv_list) # Make the consumer objects associated with the allocations. diff --git a/placement/objects/inventory.py b/placement/objects/inventory.py new file mode 100644 index 000000000..3dd504143 --- /dev/null +++ b/placement/objects/inventory.py @@ -0,0 +1,107 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import six +import sqlalchemy as sa + +from placement.db.sqlalchemy import models +from placement import db_api +from placement.objects import common as common_obj +from placement import resource_class_cache as rc_cache + + +_INV_TBL = models.Inventory.__table__ + + +class Inventory(object): + + # kwargs included because some constructors pass resource_class_id + # but it is not used. + def __init__(self, id=None, resource_provider=None, resource_class=None, + total=None, reserved=0, min_unit=1, max_unit=1, step_size=1, + allocation_ratio=1.0, updated_at=None, created_at=None, + **kwargs): + self.id = id + self.resource_provider = resource_provider + self.resource_class = resource_class + self.total = total + self.reserved = reserved + self.min_unit = min_unit + self.max_unit = max_unit + self.step_size = step_size + self.allocation_ratio = allocation_ratio + self.updated_at = updated_at + self.created_at = created_at + + @property + def capacity(self): + """Inventory capacity, adjusted by allocation_ratio.""" + return int((self.total - self.reserved) * self.allocation_ratio) + + +class InventoryList(common_obj.ObjectList): + ITEM_CLS = Inventory + + def find(self, res_class): + """Return the inventory record from the list of Inventory records that + matches the supplied resource class, or None. + + :param res_class: An integer or string representing a resource + class. If the value is a string, the method first + looks up the resource class identifier from the + string. + """ + if not isinstance(res_class, six.string_types): + raise ValueError + + for inv_rec in self.objects: + if inv_rec.resource_class == res_class: + return inv_rec + + @classmethod + def get_all_by_resource_provider(cls, context, rp): + db_inv = _get_inventory_by_provider_id(context, rp.id) + # Build up a list of Inventory objects, setting the Inventory object + # fields to the same-named database record field we got from + # _get_inventory_by_provider_id(). We already have the ResourceProvider + # object so we just pass that object to the Inventory object + # constructor as-is + objs = [ + Inventory( + resource_provider=rp, + resource_class=rc_cache.RC_CACHE.string_from_id( + rec['resource_class_id']), + **rec) + for rec in db_inv + ] + inv_list = cls(objects=objs) + return inv_list + + +@db_api.placement_context_manager.reader +def _get_inventory_by_provider_id(ctx, rp_id): + inv = sa.alias(_INV_TBL, name="i") + cols = [ + inv.c.resource_class_id, + inv.c.total, + inv.c.reserved, + inv.c.min_unit, + inv.c.max_unit, + inv.c.step_size, + inv.c.allocation_ratio, + inv.c.updated_at, + inv.c.created_at, + ] + sel = sa.select(cols) + sel = sel.where(inv.c.resource_provider_id == rp_id) + + return [dict(r) for r in ctx.session.execute(sel)] diff --git a/placement/objects/reshaper.py b/placement/objects/reshaper.py index 58f758007..c5a67cc85 100644 --- a/placement/objects/reshaper.py +++ b/placement/objects/reshaper.py @@ -13,7 +13,7 @@ from oslo_log import log as logging from placement import db_api from placement.objects import allocation as alloc_obj -from placement.objects import resource_provider as rp_obj +from placement.objects import inventory as inv_obj LOG = logging.getLogger(__name__) @@ -84,7 +84,7 @@ def reshape(ctx, inventories, allocations): # with the original inventory list. inv_by_rc = { inv.resource_class: inv for inv in - rp_obj.InventoryList.get_all_by_resource_provider(ctx, rp)} + inv_obj.InventoryList.get_all_by_resource_provider(ctx, rp)} # Now add each inventory in the new inventory list. If an inventory for # that resource class existed in the original inventory list, it is # overwritten. @@ -92,7 +92,7 @@ def reshape(ctx, inventories, allocations): inv_by_rc[inv.resource_class] = inv # Set the interim inventory structure. rp.set_inventory( - rp_obj.InventoryList(objects=list(inv_by_rc.values()))) + inv_obj.InventoryList(objects=list(inv_by_rc.values()))) # NOTE(jaypipes): The above inventory replacements will have # incremented the resource provider generations, so we need to look in diff --git a/placement/objects/resource_provider.py b/placement/objects/resource_provider.py index 86c064747..ac340758c 100644 --- a/placement/objects/resource_provider.py +++ b/placement/objects/resource_provider.py @@ -35,6 +35,7 @@ from placement import db_api from placement import exception from placement.i18n import _ from placement.objects import common as common_obj +from placement.objects import inventory as inv_obj from placement.objects import rp_candidates from placement.objects import trait as trait_obj from placement import resource_class_cache as rc_cache @@ -197,7 +198,7 @@ def _add_inventory(context, rp, inventory): cannot be found in the DB. """ rc_id = rc_cache.RC_CACHE.id_from_string(inventory.resource_class) - inv_list = InventoryList(objects=[inventory]) + inv_list = inv_obj.InventoryList(objects=[inventory]) _add_inventory_to_provider( context, rp, inv_list, set([rc_id])) rp.increment_generation() @@ -211,7 +212,7 @@ def _update_inventory(context, rp, inventory): cannot be found in the DB. """ rc_id = rc_cache.RC_CACHE.id_from_string(inventory.resource_class) - inv_list = InventoryList(objects=[inventory]) + inv_list = inv_obj.InventoryList(objects=[inventory]) exceeded = _update_inventory_for_provider( context, rp, inv_list, set([rc_id])) rp.increment_generation() @@ -1485,91 +1486,6 @@ class ResourceProviderList(common_obj.ObjectList): return cls._set_objects(context, resource_providers) -class Inventory(object): - - # kwargs included because some constructors pass resource_class_id - # but it is not used. - def __init__(self, id=None, resource_provider=None, resource_class=None, - total=None, reserved=0, min_unit=1, max_unit=1, step_size=1, - allocation_ratio=1.0, updated_at=None, created_at=None, - **kwargs): - self.id = id - self.resource_provider = resource_provider - self.resource_class = resource_class - self.total = total - self.reserved = reserved - self.min_unit = min_unit - self.max_unit = max_unit - self.step_size = step_size - self.allocation_ratio = allocation_ratio - self.updated_at = updated_at - self.created_at = created_at - - @property - def capacity(self): - """Inventory capacity, adjusted by allocation_ratio.""" - return int((self.total - self.reserved) * self.allocation_ratio) - - -@db_api.placement_context_manager.reader -def _get_inventory_by_provider_id(ctx, rp_id): - inv = sa.alias(_INV_TBL, name="i") - cols = [ - inv.c.resource_class_id, - inv.c.total, - inv.c.reserved, - inv.c.min_unit, - inv.c.max_unit, - inv.c.step_size, - inv.c.allocation_ratio, - inv.c.updated_at, - inv.c.created_at, - ] - sel = sa.select(cols) - sel = sel.where(inv.c.resource_provider_id == rp_id) - - return [dict(r) for r in ctx.session.execute(sel)] - - -class InventoryList(common_obj.ObjectList): - ITEM_CLS = Inventory - - def find(self, res_class): - """Return the inventory record from the list of Inventory records that - matches the supplied resource class, or None. - - :param res_class: An integer or string representing a resource - class. If the value is a string, the method first - looks up the resource class identifier from the - string. - """ - if not isinstance(res_class, six.string_types): - raise ValueError - - for inv_rec in self.objects: - if inv_rec.resource_class == res_class: - return inv_rec - - @classmethod - def get_all_by_resource_provider(cls, context, rp): - db_inv = _get_inventory_by_provider_id(context, rp.id) - # Build up a list of Inventory objects, setting the Inventory object - # fields to the same-named database record field we got from - # _get_inventory_by_provider_id(). We already have the ResourceProvider - # object so we just pass that object to the Inventory object - # constructor as-is - objs = [ - Inventory( - resource_provider=rp, - resource_class=rc_cache.RC_CACHE.string_from_id( - rec['resource_class_id']), - **rec) - for rec in db_inv - ] - inv_list = cls(objects=objs) - return inv_list - - @db_api.placement_context_manager.reader def get_provider_ids_having_any_trait(ctx, traits): """Returns a set of resource provider internal IDs that have ANY of the diff --git a/placement/tests/functional/db/test_allocation.py b/placement/tests/functional/db/test_allocation.py index 6b2558186..58aaaf8c6 100644 --- a/placement/tests/functional/db/test_allocation.py +++ b/placement/tests/functional/db/test_allocation.py @@ -17,7 +17,7 @@ from oslo_utils.fixture import uuidsentinel from placement import exception from placement.objects import allocation as alloc_obj from placement.objects import consumer as consumer_obj -from placement.objects import resource_provider as rp_obj +from placement.objects import inventory as inv_obj from placement.objects import usage as usage_obj from placement.tests.functional.db import test_base as tb @@ -138,14 +138,14 @@ class TestAllocationListCreateDelete(tb.PlacementDbBaseTestCase): # Now the allocations will still fail because max_unit 1 self.assertRaises(exception.InvalidAllocationConstraintsViolated, alloc_obj.replace_all, self.ctx, allocation_list) - inv1 = rp_obj.Inventory(resource_provider=rp1, - resource_class=rp1_class, - total=1024, max_unit=max_unit) - rp1.set_inventory(rp_obj.InventoryList(objects=[inv1])) - inv2 = rp_obj.Inventory(resource_provider=rp2, - resource_class=rp2_class, - total=255, reserved=2, max_unit=max_unit) - rp2.set_inventory(rp_obj.InventoryList(objects=[inv2])) + inv1 = inv_obj.Inventory(resource_provider=rp1, + resource_class=rp1_class, + total=1024, max_unit=max_unit) + rp1.set_inventory(inv_obj.InventoryList(objects=[inv1])) + inv2 = inv_obj.Inventory(resource_provider=rp2, + resource_class=rp2_class, + total=255, reserved=2, max_unit=max_unit) + rp2.set_inventory(inv_obj.InventoryList(objects=[inv2])) # Now we can finally allocate. alloc_obj.replace_all(self.ctx, allocation_list) diff --git a/placement/tests/functional/db/test_base.py b/placement/tests/functional/db/test_base.py index 9ce3b41aa..50f944c3e 100644 --- a/placement/tests/functional/db/test_base.py +++ b/placement/tests/functional/db/test_base.py @@ -20,6 +20,7 @@ from oslo_utils import uuidutils from placement import exception from placement.objects import allocation as alloc_obj from placement.objects import consumer as consumer_obj +from placement.objects import inventory as inv_obj from placement.objects import project as project_obj from placement.objects import resource_provider as rp_obj from placement.objects import trait as trait_obj @@ -58,8 +59,8 @@ def create_provider(context, name, *aggs, **kwargs): def add_inventory(rp, rc, total, **kwargs): kwargs.setdefault('max_unit', total) - inv = rp_obj.Inventory(rp._context, resource_provider=rp, - resource_class=rc, total=total, **kwargs) + inv = inv_obj.Inventory(rp._context, resource_provider=rp, + resource_class=rc, total=total, **kwargs) rp.add_inventory(inv) return inv @@ -141,8 +142,8 @@ class PlacementDbBaseTestCase(base.TestCase): def _make_allocation(self, inv_dict, alloc_dict): alloc_dict = copy.copy(alloc_dict) rp = self._create_provider('allocation_resource_provider') - disk_inv = rp_obj.Inventory(resource_provider=rp, **inv_dict) - inv_list = rp_obj.InventoryList(objects=[disk_inv]) + disk_inv = inv_obj.Inventory(resource_provider=rp, **inv_dict) + inv_list = inv_obj.InventoryList(objects=[disk_inv]) rp.set_inventory(inv_list) consumer_id = alloc_dict.pop('consumer_id') consumer = ensure_consumer( diff --git a/placement/tests/functional/db/test_reshape.py b/placement/tests/functional/db/test_reshape.py index 22ca1f6ce..e3fd4563e 100644 --- a/placement/tests/functional/db/test_reshape.py +++ b/placement/tests/functional/db/test_reshape.py @@ -14,6 +14,7 @@ from oslo_utils.fixture import uuidsentinel as uuids from placement import exception from placement.objects import allocation as alloc_obj from placement.objects import consumer as consumer_obj +from placement.objects import inventory as inv_obj from placement.objects import reshaper from placement.objects import resource_provider as rp_obj from placement.tests.functional.db import test_base as tb @@ -103,31 +104,31 @@ class ReshapeTestCase(tb.PlacementDbBaseTestCase): # storage provider. after_inventories = { # cn1 keeps the RAM only - cn1: rp_obj.InventoryList(objects=[ - rp_obj.Inventory( + cn1: inv_obj.InventoryList(objects=[ + inv_obj.Inventory( resource_provider=cn1, resource_class='MEMORY_MB', total=32768, reserved=0, max_unit=32768, min_unit=1, step_size=1, allocation_ratio=1.0), ]), # each NUMA node gets half of the CPUs - cn1_numa0: rp_obj.InventoryList(objects=[ - rp_obj.Inventory( + cn1_numa0: inv_obj.InventoryList(objects=[ + inv_obj.Inventory( resource_provider=cn1_numa0, resource_class='VCPU', total=8, reserved=0, max_unit=8, min_unit=1, step_size=1, allocation_ratio=1.0), ]), - cn1_numa1: rp_obj.InventoryList(objects=[ - rp_obj.Inventory( + cn1_numa1: inv_obj.InventoryList(objects=[ + inv_obj.Inventory( resource_provider=cn1_numa1, resource_class='VCPU', total=8, reserved=0, max_unit=8, min_unit=1, step_size=1, allocation_ratio=1.0), ]), # The sharing provider gets a bunch of disk - ss: rp_obj.InventoryList(objects=[ - rp_obj.Inventory( + ss: inv_obj.InventoryList(objects=[ + inv_obj.Inventory( resource_provider=ss, resource_class='DISK_GB', total=100000, reserved=0, max_unit=1000, min_unit=1, step_size=1, @@ -172,24 +173,24 @@ class ReshapeTestCase(tb.PlacementDbBaseTestCase): # providers in the AFTER scenario # The root compute node should only have MEMORY_MB, nothing else - cn1_inv = rp_obj.InventoryList.get_all_by_resource_provider( + cn1_inv = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, cn1) self.assertEqual(1, len(cn1_inv)) self.assertEqual('MEMORY_MB', cn1_inv[0].resource_class) self.assertEqual(32768, cn1_inv[0].total) # Each NUMA node should only have half the original VCPU, nothing else - numa0_inv = rp_obj.InventoryList.get_all_by_resource_provider( + numa0_inv = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, cn1_numa0) self.assertEqual(1, len(numa0_inv)) self.assertEqual('VCPU', numa0_inv[0].resource_class) self.assertEqual(8, numa0_inv[0].total) - numa1_inv = rp_obj.InventoryList.get_all_by_resource_provider( + numa1_inv = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, cn1_numa1) self.assertEqual(1, len(numa1_inv)) self.assertEqual('VCPU', numa1_inv[0].resource_class) self.assertEqual(8, numa1_inv[0].total) # The sharing storage provider should only have DISK_GB, nothing else - ss_inv = rp_obj.InventoryList.get_all_by_resource_provider( + ss_inv = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, ss) self.assertEqual(1, len(ss_inv)) self.assertEqual('DISK_GB', ss_inv[0].resource_class) @@ -279,31 +280,31 @@ class ReshapeTestCase(tb.PlacementDbBaseTestCase): # storage provider. after_inventories = { # cn1 keeps the RAM only - cn1: rp_obj.InventoryList(objects=[ - rp_obj.Inventory( + cn1: inv_obj.InventoryList(objects=[ + inv_obj.Inventory( resource_provider=cn1, resource_class='MEMORY_MB', total=32768, reserved=0, max_unit=32768, min_unit=1, step_size=1, allocation_ratio=1.0), ]), # each NUMA node gets half of the CPUs - cn1_numa0: rp_obj.InventoryList(objects=[ - rp_obj.Inventory( + cn1_numa0: inv_obj.InventoryList(objects=[ + inv_obj.Inventory( resource_provider=cn1_numa0, resource_class='VCPU', total=8, reserved=0, max_unit=8, min_unit=1, step_size=1, allocation_ratio=1.0), ]), - cn1_numa1: rp_obj.InventoryList(objects=[ - rp_obj.Inventory( + cn1_numa1: inv_obj.InventoryList(objects=[ + inv_obj.Inventory( resource_provider=cn1_numa1, resource_class='VCPU', total=8, reserved=0, max_unit=8, min_unit=1, step_size=1, allocation_ratio=1.0), ]), # The sharing provider gets a bunch of disk - ss: rp_obj.InventoryList(objects=[ - rp_obj.Inventory( + ss: inv_obj.InventoryList(objects=[ + inv_obj.Inventory( resource_provider=ss, resource_class='DISK_GB', total=100000, reserved=0, max_unit=1000, min_unit=1, step_size=1, @@ -337,8 +338,8 @@ class ReshapeTestCase(tb.PlacementDbBaseTestCase): # generation was validated and the actual call to reshape() ss_threadB = rp_obj.ResourceProvider.get_by_uuid(self.ctx, ss.uuid) # Reduce the amount of storage to 2000, from 100000. - new_ss_inv = rp_obj.InventoryList(objects=[ - rp_obj.Inventory( + new_ss_inv = inv_obj.InventoryList(objects=[ + inv_obj.Inventory( resource_provider=ss_threadB, resource_class='DISK_GB', total=2000, reserved=0, max_unit=1000, min_unit=1, step_size=1, allocation_ratio=1.0)]) diff --git a/placement/tests/functional/db/test_resource_class.py b/placement/tests/functional/db/test_resource_class.py index cf955e13c..f33d44b91 100644 --- a/placement/tests/functional/db/test_resource_class.py +++ b/placement/tests/functional/db/test_resource_class.py @@ -17,6 +17,7 @@ from oslo_utils.fixture import uuidsentinel import placement from placement import exception +from placement.objects import inventory as inv_obj from placement.objects import resource_class as rc_obj from placement.objects import resource_provider as rp_obj from placement.tests.functional.db import test_base as tb @@ -221,18 +222,18 @@ class ResourceClassTestCase(tb.PlacementDbBaseTestCase): uuid=uuidsentinel.rp, ) rp.create() - inv = rp_obj.Inventory( + inv = inv_obj.Inventory( resource_provider=rp, resource_class='CUSTOM_IRON_NFV', total=1, ) - inv_list = rp_obj.InventoryList(objects=[inv]) + inv_list = inv_obj.InventoryList(objects=[inv]) rp.set_inventory(inv_list) self.assertRaises(exception.ResourceClassInUse, rc.destroy) - rp.set_inventory(rp_obj.InventoryList(objects=[])) + rp.set_inventory(inv_obj.InventoryList(objects=[])) rc.destroy() rc_list = rc_obj.get_all(self.ctx) rc_ids = (r.id for r in rc_list) diff --git a/placement/tests/functional/db/test_resource_provider.py b/placement/tests/functional/db/test_resource_provider.py index b53b74155..b7836fa4e 100644 --- a/placement/tests/functional/db/test_resource_provider.py +++ b/placement/tests/functional/db/test_resource_provider.py @@ -21,6 +21,7 @@ from placement.db.sqlalchemy import models from placement import exception from placement.objects import allocation as alloc_obj from placement.objects import consumer as consumer_obj +from placement.objects import inventory as inv_obj from placement.objects import resource_provider as rp_obj from placement.objects import trait as trait_obj from placement.objects import usage as usage_obj @@ -523,11 +524,11 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): tb.add_inventory(resource_provider, tb.DISK_INVENTORY['resource_class'], tb.DISK_INVENTORY['total']) - inventories = rp_obj.InventoryList.get_all_by_resource_provider( + inventories = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, resource_provider) self.assertEqual(1, len(inventories)) resource_provider.destroy() - inventories = rp_obj.InventoryList.get_all_by_resource_provider( + inventories = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, resource_provider) self.assertEqual(0, len(inventories)) @@ -576,7 +577,7 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): tb.add_inventory(rp, 'VCPU', 12) self.allocate_from_provider(rp, 'VCPU', 1) - inv = rp_obj.Inventory( + inv = inv_obj.Inventory( resource_provider=rp, resource_class='MEMORY_MB', total=1024, @@ -587,7 +588,7 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): allocation_ratio=1.0, ) - inv_list = rp_obj.InventoryList(objects=[inv]) + inv_list = inv_obj.InventoryList(objects=[inv]) self.assertRaises(exception.InventoryInUse, rp.set_inventory, inv_list) @@ -611,7 +612,7 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): # Update our inventory to over-subscribe us after the above allocation disk_inv.total = 400 - rp.set_inventory(rp_obj.InventoryList(objects=[disk_inv, vcpu_inv])) + rp.set_inventory(inv_obj.InventoryList(objects=[disk_inv, vcpu_inv])) # We should succeed, but have logged a warning for going over on disk mock_log.warning.assert_called_once_with( @@ -634,7 +635,7 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): self.assertEqual(saved_generation + 2, rp.generation) saved_generation = rp.generation - new_inv_list = rp_obj.InventoryList.get_all_by_resource_provider( + new_inv_list = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, rp) self.assertEqual(2, len(new_inv_list)) resource_classes = [inv.resource_class for inv in new_inv_list] @@ -642,14 +643,14 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): self.assertIn(orc.DISK_GB, resource_classes) # reset list to just disk_inv - inv_list = rp_obj.InventoryList(objects=[disk_inv]) + inv_list = inv_obj.InventoryList(objects=[disk_inv]) rp.set_inventory(inv_list) # generation has bumped self.assertEqual(saved_generation + 1, rp.generation) saved_generation = rp.generation - new_inv_list = rp_obj.InventoryList.get_all_by_resource_provider( + new_inv_list = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, rp) self.assertEqual(1, len(new_inv_list)) resource_classes = [inv.resource_class for inv in new_inv_list] @@ -658,7 +659,7 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): self.assertEqual(1024, new_inv_list[0].total) # update existing disk inv to new settings - disk_inv = rp_obj.Inventory( + disk_inv = inv_obj.Inventory( resource_provider=rp, resource_class=orc.DISK_GB, total=2048, @@ -673,7 +674,7 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): self.assertEqual(saved_generation + 1, rp.generation) saved_generation = rp.generation - new_inv_list = rp_obj.InventoryList.get_all_by_resource_provider( + new_inv_list = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, rp) self.assertEqual(1, len(new_inv_list)) self.assertEqual(2048, new_inv_list[0].total) @@ -685,7 +686,7 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): self.assertEqual(saved_generation + 1, rp.generation) saved_generation = rp.generation - new_inv_list = rp_obj.InventoryList.get_all_by_resource_provider( + new_inv_list = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, rp) result = new_inv_list.find(orc.DISK_GB) self.assertIsNone(result) @@ -693,13 +694,13 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): orc.DISK_GB) # check inventory list is empty - inv_list = rp_obj.InventoryList.get_all_by_resource_provider( + inv_list = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, rp) self.assertEqual(0, len(inv_list)) # add some inventory rp.add_inventory(vcpu_inv) - inv_list = rp_obj.InventoryList.get_all_by_resource_provider( + inv_list = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, rp) self.assertEqual(1, len(inv_list)) @@ -738,9 +739,9 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): def test_update_inventory_not_found(self): rp = self._create_provider(uuidsentinel.rp_name) - disk_inv = rp_obj.Inventory(resource_provider=rp, - resource_class='DISK_GB', - total=2048) + disk_inv = inv_obj.Inventory(resource_provider=rp, + resource_class='DISK_GB', + total=2048) error = self.assertRaises(exception.NotFound, rp.update_inventory, disk_inv) self.assertIn('No inventory of class DISK_GB found', @@ -756,7 +757,7 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): # attempt to set inventory to less than currently allocated # amounts new_total = 1 - disk_inv = rp_obj.Inventory( + disk_inv = inv_obj.Inventory( resource_provider=rp, resource_class=orc.DISK_GB, total=new_total) rp.update_inventory(disk_inv) @@ -765,7 +766,7 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): self.ctx, rp.uuid) self.assertEqual(allocation.used, usages[0].usage) - inv_list = rp_obj.InventoryList.get_all_by_resource_provider( + inv_list = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, rp) self.assertEqual(new_total, inv_list[0].total) mock_log.warning.assert_called_once_with( @@ -792,7 +793,7 @@ class ResourceProviderTestCase(tb.PlacementDbBaseTestCase): # Get inventories for the first resource provider and validate # the inventory records have a matching resource provider - got_inv = rp_obj.InventoryList.get_all_by_resource_provider( + got_inv = inv_obj.InventoryList.get_all_by_resource_provider( self.ctx, rp1) for inv in got_inv: self.assertEqual(rp1.id, inv.resource_provider.id) diff --git a/placement/tests/functional/db/test_usage.py b/placement/tests/functional/db/test_usage.py index 130aa2ba4..86f93fa4c 100644 --- a/placement/tests/functional/db/test_usage.py +++ b/placement/tests/functional/db/test_usage.py @@ -13,7 +13,7 @@ import os_resource_classes as orc from oslo_utils.fixture import uuidsentinel -from placement.objects import resource_provider as rp_obj +from placement.objects import inventory as inv_obj from placement.objects import usage as usage_obj from placement.tests.functional.db import test_base as tb @@ -31,10 +31,10 @@ class UsageListTestCase(tb.PlacementDbBaseTestCase): def test_get_all_one_allocation(self): db_rp, _ = self._make_allocation(tb.DISK_INVENTORY, tb.DISK_ALLOCATION) - inv = rp_obj.Inventory(resource_provider=db_rp, - resource_class=orc.DISK_GB, - total=1024) - inv_list = rp_obj.InventoryList(objects=[inv]) + inv = inv_obj.Inventory(resource_provider=db_rp, + resource_class=orc.DISK_GB, + total=1024) + inv_list = inv_obj.InventoryList(objects=[inv]) db_rp.set_inventory(inv_list) usages = usage_obj.get_all_by_resource_provider_uuid( diff --git a/placement/tests/unit/objects/test_inventory.py b/placement/tests/unit/objects/test_inventory.py new file mode 100644 index 000000000..bd0f31bc7 --- /dev/null +++ b/placement/tests/unit/objects/test_inventory.py @@ -0,0 +1,134 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +import os_resource_classes as orc +from oslo_utils.fixture import uuidsentinel as uuids +from oslo_utils import timeutils + +from placement.objects import inventory +from placement.objects import resource_provider +from placement.tests.unit.objects import base + + +_RESOURCE_CLASS_NAME = 'DISK_GB' +_RESOURCE_CLASS_ID = 2 +_RESOURCE_PROVIDER_ID = 1 +_RESOURCE_PROVIDER_UUID = uuids.resource_provider + +VCPU_ID = orc.STANDARDS.index( + orc.VCPU) + +_INVENTORY_ID = 2 +_INVENTORY_DB = { + 'id': _INVENTORY_ID, + 'resource_provider_id': _RESOURCE_PROVIDER_ID, + 'resource_class_id': _RESOURCE_CLASS_ID, + 'total': 16, + 'reserved': 2, + 'min_unit': 1, + 'max_unit': 8, + 'step_size': 1, + 'allocation_ratio': 1.0, + 'updated_at': None, + 'created_at': timeutils.utcnow(with_timezone=True), +} + + +class TestInventoryNoDB(base.TestCase): + + @mock.patch('placement.resource_class_cache.ensure_rc_cache', + side_effect=base.fake_ensure_cache) + @mock.patch('placement.objects.inventory._get_inventory_by_provider_id') + def test_get_all_by_resource_provider(self, mock_get, mock_ensure_cache): + mock_ensure_cache(self.context) + expected = [dict(_INVENTORY_DB, + resource_provider_id=_RESOURCE_PROVIDER_ID), + dict(_INVENTORY_DB, + id=_INVENTORY_DB['id'] + 1, + resource_provider_id=_RESOURCE_PROVIDER_ID)] + mock_get.return_value = expected + rp = resource_provider.ResourceProvider(self.context, + id=_RESOURCE_PROVIDER_ID, + uuid=_RESOURCE_PROVIDER_UUID) + objs = inventory.InventoryList.get_all_by_resource_provider( + self.context, rp) + self.assertEqual(2, len(objs)) + self.assertEqual(_INVENTORY_DB['id'], objs[0].id) + self.assertEqual(_INVENTORY_DB['id'] + 1, objs[1].id) + self.assertEqual(_RESOURCE_PROVIDER_ID, objs[0].resource_provider.id) + + def test_set_defaults(self): + rp = resource_provider.ResourceProvider(self.context, + id=_RESOURCE_PROVIDER_ID, + uuid=_RESOURCE_PROVIDER_UUID) + kwargs = dict(resource_provider=rp, + resource_class=_RESOURCE_CLASS_NAME, + total=16) + inv = inventory.Inventory(self.context, **kwargs) + + self.assertEqual(0, inv.reserved) + self.assertEqual(1, inv.min_unit) + self.assertEqual(1, inv.max_unit) + self.assertEqual(1, inv.step_size) + self.assertEqual(1.0, inv.allocation_ratio) + + def test_capacity(self): + rp = resource_provider.ResourceProvider(self.context, + id=_RESOURCE_PROVIDER_ID, + uuid=_RESOURCE_PROVIDER_UUID) + kwargs = dict(resource_provider=rp, + resource_class=_RESOURCE_CLASS_NAME, + total=16, + reserved=16) + inv = inventory.Inventory(self.context, **kwargs) + + self.assertEqual(0, inv.capacity) + inv.reserved = 15 + self.assertEqual(1, inv.capacity) + inv.allocation_ratio = 2.0 + self.assertEqual(2, inv.capacity) + + +class TestInventoryList(base.TestCase): + + def test_find(self): + rp = resource_provider.ResourceProvider( + self.context, uuid=uuids.rp_uuid) + inv_list = inventory.InventoryList( + objects=[ + inventory.Inventory( + resource_provider=rp, + resource_class=orc.VCPU, + total=24), + inventory.Inventory( + resource_provider=rp, + resource_class=orc.MEMORY_MB, + total=10240), + ]) + + found = inv_list.find(orc.MEMORY_MB) + self.assertIsNotNone(found) + self.assertEqual(10240, found.total) + + found = inv_list.find(orc.VCPU) + self.assertIsNotNone(found) + self.assertEqual(24, found.total) + + found = inv_list.find(orc.DISK_GB) + self.assertIsNone(found) + + # Try an integer resource class identifier... + self.assertRaises(ValueError, inv_list.find, VCPU_ID) + + # Use an invalid string... + self.assertIsNone(inv_list.find('HOUSE')) diff --git a/placement/tests/unit/objects/test_resource_provider.py b/placement/tests/unit/objects/test_resource_provider.py index c0d58b5e2..88f3ae9ab 100644 --- a/placement/tests/unit/objects/test_resource_provider.py +++ b/placement/tests/unit/objects/test_resource_provider.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import mock -import os_resource_classes as orc from oslo_utils.fixture import uuidsentinel as uuids from oslo_utils import timeutils import six @@ -21,12 +19,7 @@ from placement.objects import resource_provider from placement.tests.unit.objects import base -_RESOURCE_CLASS_NAME = 'DISK_GB' _RESOURCE_CLASS_ID = 2 -IPV4_ADDRESS_ID = orc.STANDARDS.index( - orc.IPV4_ADDRESS) -VCPU_ID = orc.STANDARDS.index( - orc.VCPU) _RESOURCE_PROVIDER_ID = 1 _RESOURCE_PROVIDER_UUID = uuids.resource_provider @@ -55,20 +48,6 @@ _RESOURCE_PROVIDER_DB2 = { } -_INVENTORY_ID = 2 -_INVENTORY_DB = { - 'id': _INVENTORY_ID, - 'resource_provider_id': _RESOURCE_PROVIDER_ID, - 'resource_class_id': _RESOURCE_CLASS_ID, - 'total': 16, - 'reserved': 2, - 'min_unit': 1, - 'max_unit': 8, - 'step_size': 1, - 'allocation_ratio': 1.0, - 'updated_at': None, - 'created_at': timeutils.utcnow(with_timezone=True), -} _ALLOCATION_ID = 2 _ALLOCATION_DB = { 'id': _ALLOCATION_ID, @@ -119,94 +98,3 @@ class TestResourceProviderNoDB(base.TestCase): obj = resource_provider.ResourceProvider(context=self.context) self.assertRaises(exception.ObjectActionError, obj.create) - - -class TestInventoryNoDB(base.TestCase): - - @mock.patch('placement.resource_class_cache.ensure_rc_cache', - side_effect=base.fake_ensure_cache) - @mock.patch('placement.objects.resource_provider.' - '_get_inventory_by_provider_id') - def test_get_all_by_resource_provider(self, mock_get, mock_ensure_cache): - mock_ensure_cache(self.context) - expected = [dict(_INVENTORY_DB, - resource_provider_id=_RESOURCE_PROVIDER_ID), - dict(_INVENTORY_DB, - id=_INVENTORY_DB['id'] + 1, - resource_provider_id=_RESOURCE_PROVIDER_ID)] - mock_get.return_value = expected - rp = resource_provider.ResourceProvider(self.context, - id=_RESOURCE_PROVIDER_ID, - uuid=_RESOURCE_PROVIDER_UUID) - objs = resource_provider.InventoryList.get_all_by_resource_provider( - self.context, rp) - self.assertEqual(2, len(objs)) - self.assertEqual(_INVENTORY_DB['id'], objs[0].id) - self.assertEqual(_INVENTORY_DB['id'] + 1, objs[1].id) - self.assertEqual(_RESOURCE_PROVIDER_ID, objs[0].resource_provider.id) - - def test_set_defaults(self): - rp = resource_provider.ResourceProvider(self.context, - id=_RESOURCE_PROVIDER_ID, - uuid=_RESOURCE_PROVIDER_UUID) - kwargs = dict(resource_provider=rp, - resource_class=_RESOURCE_CLASS_NAME, - total=16) - inv = resource_provider.Inventory(self.context, **kwargs) - - self.assertEqual(0, inv.reserved) - self.assertEqual(1, inv.min_unit) - self.assertEqual(1, inv.max_unit) - self.assertEqual(1, inv.step_size) - self.assertEqual(1.0, inv.allocation_ratio) - - def test_capacity(self): - rp = resource_provider.ResourceProvider(self.context, - id=_RESOURCE_PROVIDER_ID, - uuid=_RESOURCE_PROVIDER_UUID) - kwargs = dict(resource_provider=rp, - resource_class=_RESOURCE_CLASS_NAME, - total=16, - reserved=16) - inv = resource_provider.Inventory(self.context, **kwargs) - - self.assertEqual(0, inv.capacity) - inv.reserved = 15 - self.assertEqual(1, inv.capacity) - inv.allocation_ratio = 2.0 - self.assertEqual(2, inv.capacity) - - -class TestInventoryList(base.TestCase): - - def test_find(self): - rp = resource_provider.ResourceProvider( - self.context, uuid=uuids.rp_uuid) - inv_list = resource_provider.InventoryList( - objects=[ - resource_provider.Inventory( - resource_provider=rp, - resource_class=orc.VCPU, - total=24), - resource_provider.Inventory( - resource_provider=rp, - resource_class=orc.MEMORY_MB, - total=10240), - ]) - - found = inv_list.find(orc.MEMORY_MB) - self.assertIsNotNone(found) - self.assertEqual(10240, found.total) - - found = inv_list.find(orc.VCPU) - self.assertIsNotNone(found) - self.assertEqual(24, found.total) - - found = inv_list.find(orc.DISK_GB) - self.assertIsNone(found) - - # Try an integer resource class identifier... - self.assertRaises(ValueError, inv_list.find, VCPU_ID) - - # Use an invalid string... - self.assertIsNone(inv_list.find('HOUSE'))