def test_update(self): """Tests the scenario where a user supplies a different project/user ID for an allocation's consumer and we call Consumer.update() to save that information to the consumers table. """ # First, create the consumer with the "fake-user" and "fake-project" # user/project in the base test class's setUp c = consumer_obj.Consumer(self.ctx, uuid=uuids.consumer, user=self.user_obj, project=self.project_obj) c.create() c = consumer_obj.Consumer.get_by_uuid(self.ctx, uuids.consumer) self.assertEqual(self.project_obj.id, c.project.id) self.assertEqual(self.user_obj.id, c.user.id) # Now change the consumer's project and user to a different project another_user = user_obj.User(self.ctx, external_id='another-user') another_user.create() another_proj = project_obj.Project(self.ctx, external_id='another-project') another_proj.create() c.project = another_proj c.user = another_user c.update() c = consumer_obj.Consumer.get_by_uuid(self.ctx, uuids.consumer) self.assertEqual(another_proj.id, c.project.id) self.assertEqual(another_user.id, c.user.id)
def test_create_and_get(self): p = project_obj.Project(self.ctx, external_id='another-project') p.create() p = project_obj.Project.get_by_external_id(self.ctx, 'another-project') # Project ID == 1 is fake-project created in setup self.assertEqual(2, p.id) self.assertRaises(exception.ProjectExists, p.create)
def start_fixture(self): super(AllocationFixture, self).start_fixture() # For use creating and querying allocations/usages os.environ['ALT_USER_ID'] = uuidutils.generate_uuid() project_id = os.environ['PROJECT_ID'] user_id = os.environ['USER_ID'] alt_user_id = os.environ['ALT_USER_ID'] user = user_obj.User(self.context, external_id=user_id) user.create() alt_user = user_obj.User(self.context, external_id=alt_user_id) alt_user.create() project = project_obj.Project(self.context, external_id=project_id) project.create() # Stealing from the super rp_name = os.environ['RP_NAME'] rp_uuid = os.environ['RP_UUID'] # Create the rp with VCPU and DISK_GB inventory rp = tb.create_provider(self.context, rp_name, uuid=rp_uuid) tb.add_inventory(rp, 'DISK_GB', 2048, step_size=10, min_unit=10, max_unit=1000) tb.add_inventory(rp, 'VCPU', 10, max_unit=10) # Create a first consumer for the DISK_GB allocations consumer1 = tb.ensure_consumer(self.context, user, project) tb.set_allocation(self.context, rp, consumer1, {'DISK_GB': 1000}) os.environ['CONSUMER_0'] = consumer1.uuid # Create a second consumer for the VCPU allocations consumer2 = tb.ensure_consumer(self.context, user, project) tb.set_allocation(self.context, rp, consumer2, {'VCPU': 6}) os.environ['CONSUMER_ID'] = consumer2.uuid # Create a consumer object for a different user alt_consumer = tb.ensure_consumer(self.context, alt_user, project) os.environ['ALT_CONSUMER_ID'] = alt_consumer.uuid # Create a couple of allocations for a different user. tb.set_allocation(self.context, rp, alt_consumer, { 'DISK_GB': 20, 'VCPU': 1 }) # The ALT_RP_XXX variables are for a resource provider that has # not been created in the Allocation fixture os.environ['ALT_RP_UUID'] = uuidutils.generate_uuid() os.environ['ALT_RP_NAME'] = uuidutils.generate_uuid()
def test_create_and_get(self): u = user_obj.User(self.ctx, external_id='fake-user') u.create() p = project_obj.Project(self.ctx, external_id='fake-project') p.create() c = consumer_obj.Consumer( self.ctx, uuid=uuids.consumer, user=u, project=p) c.create() c = consumer_obj.Consumer.get_by_uuid(self.ctx, uuids.consumer) self.assertEqual(1, c.id) self.assertEqual(1, c.project.id) self.assertEqual(1, c.user.id) self.assertRaises(exception.ConsumerExists, c.create)
def setUp(self): super(PlacementDbBaseTestCase, self).setUp() # we use context in some places and ctx in other. We should only use # context, but let's paper over that for now. self.ctx = self.context self.user_obj = user_obj.User(self.ctx, external_id='fake-user') self.user_obj.create() self.project_obj = project_obj.Project(self.ctx, external_id='fake-project') self.project_obj.create() # For debugging purposes, populated by _create_provider and used by # _validate_allocation_requests to make failure results more readable. self.rp_uuid_to_name = {}
def ensure_consumer(ctx, consumer_uuid, project_id, user_id): """Ensures there are records in the consumers, projects and users table for the supplied external identifiers. Returns a populated Consumer object containing Project and User sub-objects :param ctx: The request context. :param consumer_uuid: The uuid of the consumer of the resources. :param project_id: The external ID of the project consuming the resources. :param user_id: The external ID of the user consuming the resources. """ if project_id is None: project_id = CONF.placement.incomplete_consumer_project_id user_id = CONF.placement.incomplete_consumer_user_id try: proj = project_obj.Project.get_by_external_id(ctx, project_id) except exception.NotFound: # Auto-create the project if we found no record of it... try: proj = project_obj.Project(ctx, external_id=project_id) proj.create() except exception.ProjectExists: # No worries, another thread created this project already proj = project_obj.Project.get_by_external_id(ctx, project_id) try: user = user_obj.User.get_by_external_id(ctx, user_id) except exception.NotFound: # Auto-create the user if we found no record of it... try: user = user_obj.User(ctx, external_id=user_id) user.create() except exception.UserExists: # No worries, another thread created this user already user = user_obj.User.get_by_external_id(ctx, user_id) try: consumer = consumer_obj.Consumer.get_by_uuid(ctx, consumer_uuid) except exception.NotFound: # No such consumer. This is common for new allocations. Create the # consumer record try: consumer = consumer_obj.Consumer(ctx, uuid=consumer_uuid, project=proj, user=user) consumer.create() except exception.ConsumerExists: # No worries, another thread created this user already consumer = consumer_obj.Consumer.get_by_uuid(ctx, consumer_uuid) return consumer
def setUp(self): super(PlacementDbBaseTestCase, self).setUp() self.useFixture(fixtures.Database()) self.placement_db = self.useFixture( fixtures.Database(database='placement')) self.ctx = context.RequestContext('fake-user', 'fake-project') self.user_obj = user_obj.User(self.ctx, external_id='fake-user') self.user_obj.create() self.project_obj = project_obj.Project(self.ctx, external_id='fake-project') self.project_obj.create() # Do database syncs, such as traits sync. deploy.update_database() # For debugging purposes, populated by _create_provider and used by # _validate_allocation_requests to make failure results more readable. self.rp_uuid_to_name = {}
def test_existing_project_user_no_existing_consumer(self): """Check that if we find an existing project and user, that we use those found objects in creating the consumer. """ proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id) self.mock_project_get.return_value = proj user = user_obj.User(self.ctx, id=1, external_id=self.user_id) self.mock_user_get.return_value = user self.mock_consumer_get.side_effect = exception.NotFound util.ensure_consumer(self.ctx, self.consumer_id, self.project_id, self.user_id) self.mock_project_create.assert_not_called() self.mock_user_create.assert_not_called() self.mock_consumer_create.assert_called_once()
def _from_db_object(ctx, target, source): target.id = source['id'] target.uuid = source['uuid'] target.created_at = source['created_at'] target.updated_at = source['updated_at'] target.project = project_obj.Project( ctx, id=source['project_id'], external_id=source['project_external_id']) target.user = user_obj.User(ctx, id=source['user_id'], external_id=source['user_external_id']) target._context = ctx target.obj_reset_changes() return target
def test_existing_project_no_existing_consumer_before_gen_success(self): """Check that if we find an existing project and user, that we use those found objects in creating the consumer. Do not require a consumer generation before the appropriate microversion. """ proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id) self.mock_project_get.return_value = proj user = user_obj.User(self.ctx, id=1, external_id=self.user_id) self.mock_user_get.return_value = user self.mock_consumer_get.side_effect = exception.NotFound consumer_gen = None # should be ignored util.ensure_consumer(self.ctx, self.consumer_id, self.project_id, self.user_id, consumer_gen, self.before_version) self.mock_project_create.assert_not_called() self.mock_user_create.assert_not_called() self.mock_consumer_create.assert_called_once()
def test_create_with_id_fails(self, mock_rc_cache): rp = resource_provider.ResourceProvider(context=self.context, uuid=_RESOURCE_PROVIDER_UUID, name=_RESOURCE_PROVIDER_NAME) self.project = project_obj.Project(self.context, external_id='fake-project') self.user = user_obj.User(self.context, external_id='fake-user') self.consumer = consumer_obj.Consumer(self.context, uuid=uuids.fake_instance, project=self.project, user=self.user) obj = resource_provider.Allocation(context=self.context, id=99, resource_provider=rp, resource_class=_RESOURCE_CLASS_NAME, consumer=self.consumer, used=8) alloc_list = resource_provider.AllocationList(self.context, objects=[obj]) self.assertRaises(exception.ObjectActionError, alloc_list.create_all)
def test_existing_consumer_after_gen_fail(self): """Tests that we require a consumer_generation after the appropriate microversion and that when the consumer already exists, then we raise a 400 when there is a mismatch on the existing generation. """ proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id) self.mock_project_get.return_value = proj user = user_obj.User(self.ctx, id=1, external_id=self.user_id) self.mock_user_get.return_value = user consumer = consumer_obj.Consumer(self.ctx, id=1, project=proj, user=user, generation=42) self.mock_consumer_get.return_value = consumer consumer_gen = 2 # should NOT be ignored (and 2 is NOT expected) self.assertRaises(webob.exc.HTTPConflict, util.ensure_consumer, self.ctx, self.consumer_id, self.project_id, self.user_id, consumer_gen, self.after_version)
def test_existing_consumer_after_gen_matches_supplied_gen(self): """Tests that we require a consumer_generation after the appropriate microversion and that when the consumer already exists, then we ensure a matching generation is supplied """ proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id) self.mock_project_get.return_value = proj user = user_obj.User(self.ctx, id=1, external_id=self.user_id) self.mock_user_get.return_value = user consumer = consumer_obj.Consumer(self.ctx, id=1, project=proj, user=user, generation=2) self.mock_consumer_get.return_value = consumer consumer_gen = 2 # should NOT be ignored (and 2 is expected) util.ensure_consumer(self.ctx, self.consumer_id, self.project_id, self.user_id, consumer_gen, self.after_version) self.mock_project_create.assert_not_called() self.mock_user_create.assert_not_called() self.mock_consumer_create.assert_not_called()
def start_fixture(self): super(AllocationFixture, self).start_fixture() self.context = context.get_admin_context() # For use creating and querying allocations/usages os.environ['ALT_USER_ID'] = uuidutils.generate_uuid() project_id = os.environ['PROJECT_ID'] user_id = os.environ['USER_ID'] alt_user_id = os.environ['ALT_USER_ID'] user = user_obj.User(self.context, external_id=user_id) user.create() alt_user = user_obj.User(self.context, external_id=alt_user_id) alt_user.create() project = project_obj.Project(self.context, external_id=project_id) project.create() # Stealing from the super rp_name = os.environ['RP_NAME'] rp_uuid = os.environ['RP_UUID'] rp = rp_obj.ResourceProvider(self.context, name=rp_name, uuid=rp_uuid) rp.create() # Create a first consumer for the DISK_GB consumer_id = uuidutils.generate_uuid() consumer = consumer_obj.Consumer(self.context, uuid=consumer_id, user=user, project=project) consumer.create() # Create some DISK_GB inventory and allocations. inventory = rp_obj.Inventory(self.context, resource_provider=rp, resource_class='DISK_GB', total=2048, step_size=10, min_unit=10, max_unit=600) inventory.obj_set_defaults() rp.add_inventory(inventory) alloc1 = rp_obj.Allocation(self.context, resource_provider=rp, resource_class='DISK_GB', consumer=consumer, used=500) alloc2 = rp_obj.Allocation(self.context, resource_provider=rp, resource_class='DISK_GB', consumer=consumer, used=500) alloc_list = rp_obj.AllocationList(self.context, objects=[alloc1, alloc2]) alloc_list.create_all() # Create a second consumer for the VCPU consumer_id = uuidutils.generate_uuid() consumer = consumer_obj.Consumer(self.context, uuid=consumer_id, user=user, project=project) consumer.create() # This consumer is referenced from the gabbits os.environ['CONSUMER_ID'] = consumer_id # Create some VCPU inventory and allocations. inventory = rp_obj.Inventory(self.context, resource_provider=rp, resource_class='VCPU', total=10, max_unit=4) inventory.obj_set_defaults() rp.add_inventory(inventory) alloc1 = rp_obj.Allocation(self.context, resource_provider=rp, resource_class='VCPU', consumer=consumer, used=2) alloc2 = rp_obj.Allocation(self.context, resource_provider=rp, resource_class='VCPU', consumer=consumer, used=4) alloc_list = rp_obj.AllocationList(self.context, objects=[alloc1, alloc2]) alloc_list.create_all() # Create a consumer object for a different user alt_consumer_id = uuidutils.generate_uuid() alt_consumer = consumer_obj.Consumer(self.context, uuid=alt_consumer_id, user=alt_user, project=project) alt_consumer.create() os.environ['ALT_CONSUMER_ID'] = alt_consumer_id # Create a couple of allocations for a different user. alloc1 = rp_obj.Allocation(self.context, resource_provider=rp, resource_class='DISK_GB', consumer=alt_consumer, used=20) alloc2 = rp_obj.Allocation(self.context, resource_provider=rp, resource_class='VCPU', consumer=alt_consumer, used=1) alloc_list = rp_obj.AllocationList(self.context, objects=[alloc1, alloc2]) alloc_list.create_all() # The ALT_RP_XXX variables are for a resource provider that has # not been created in the Allocation fixture os.environ['ALT_RP_UUID'] = uuidutils.generate_uuid() os.environ['ALT_RP_NAME'] = uuidutils.generate_uuid()
def test_create_and_get(self): p = project_obj.Project(self.ctx, external_id='fake-project') p.create() p = project_obj.Project.get_by_external_id(self.ctx, 'fake-project') self.assertEqual(1, p.id) self.assertRaises(exception.ProjectExists, p.create)
def ensure_consumer(ctx, consumer_uuid, project_id, user_id, consumer_generation, want_version): """Ensures there are records in the consumers, projects and users table for the supplied external identifiers. Returns a populated Consumer object containing Project and User sub-objects :param ctx: The request context. :param consumer_uuid: The uuid of the consumer of the resources. :param project_id: The external ID of the project consuming the resources. :param user_id: The external ID of the user consuming the resources. :param consumer_generation: The generation provided by the user for this consumer. :param want_version: the microversion matcher. :raises webob.exc.HTTPConflict if consumer generation is required and there was a mismatch """ requires_consumer_generation = want_version.matches((1, 28)) if project_id is None: project_id = CONF.placement.incomplete_consumer_project_id user_id = CONF.placement.incomplete_consumer_user_id try: proj = project_obj.Project.get_by_external_id(ctx, project_id) except exception.NotFound: # Auto-create the project if we found no record of it... try: proj = project_obj.Project(ctx, external_id=project_id) proj.create() except exception.ProjectExists: # No worries, another thread created this project already proj = project_obj.Project.get_by_external_id(ctx, project_id) try: user = user_obj.User.get_by_external_id(ctx, user_id) except exception.NotFound: # Auto-create the user if we found no record of it... try: user = user_obj.User(ctx, external_id=user_id) user.create() except exception.UserExists: # No worries, another thread created this user already user = user_obj.User.get_by_external_id(ctx, user_id) try: consumer = consumer_obj.Consumer.get_by_uuid(ctx, consumer_uuid) if requires_consumer_generation: if consumer.generation != consumer_generation: raise webob.exc.HTTPConflict( _('consumer generation conflict - ' 'expected %(expected_gen)s but got %(got_gen)s') % { 'expected_gen': consumer.generation, 'got_gen': consumer_generation, }) except exception.NotFound: # If we are attempting to modify or create allocations after 1.26, we # need a consumer generation specified. The user must have specified # None for the consumer generation if we get here, since there was no # existing consumer with this UUID and therefore the user should be # indicating that they expect the consumer did not exist. if requires_consumer_generation: if consumer_generation is not None: raise webob.exc.HTTPConflict( _('consumer generation conflict - ' 'expected null but got %s') % consumer_generation) # No such consumer. This is common for new allocations. Create the # consumer record try: consumer = consumer_obj.Consumer(ctx, uuid=consumer_uuid, project=proj, user=user) consumer.create() except exception.ConsumerExists: # No worries, another thread created this user already consumer = consumer_obj.Consumer.get_by_uuid(ctx, consumer_uuid) return consumer
def ensure_consumer(ctx, consumer_uuid, project_id, user_id, consumer_generation, want_version): """Ensures there are records in the consumers, projects and users table for the supplied external identifiers. Returns a tuple containing the populated Consumer object containing Project and User sub-objects and a boolean indicating whether a new Consumer object was created (as opposed to an existing consumer record retrieved) :note: If the supplied project or user external identifiers do not match an existing consumer's project and user identifiers, the existing consumer's project and user IDs are updated to reflect the supplied ones. :param ctx: The request context. :param consumer_uuid: The uuid of the consumer of the resources. :param project_id: The external ID of the project consuming the resources. :param user_id: The external ID of the user consuming the resources. :param consumer_generation: The generation provided by the user for this consumer. :param want_version: the microversion matcher. :raises webob.exc.HTTPConflict if consumer generation is required and there was a mismatch """ created_new_consumer = False requires_consumer_generation = want_version.matches((1, 28)) if project_id is None: project_id = CONF.placement.incomplete_consumer_project_id user_id = CONF.placement.incomplete_consumer_user_id try: proj = project_obj.Project.get_by_external_id(ctx, project_id) except exception.NotFound: # Auto-create the project if we found no record of it... try: proj = project_obj.Project(ctx, external_id=project_id) proj.create() except exception.ProjectExists: # No worries, another thread created this project already proj = project_obj.Project.get_by_external_id(ctx, project_id) try: user = user_obj.User.get_by_external_id(ctx, user_id) except exception.NotFound: # Auto-create the user if we found no record of it... try: user = user_obj.User(ctx, external_id=user_id) user.create() except exception.UserExists: # No worries, another thread created this user already user = user_obj.User.get_by_external_id(ctx, user_id) try: consumer = consumer_obj.Consumer.get_by_uuid(ctx, consumer_uuid) if requires_consumer_generation: if consumer.generation != consumer_generation: raise webob.exc.HTTPConflict( _('consumer generation conflict - ' 'expected %(expected_gen)s but got %(got_gen)s') % { 'expected_gen': consumer.generation, 'got_gen': consumer_generation, }, comment=errors.CONCURRENT_UPDATE) # NOTE(jaypipes): The user may have specified a different project and # user external ID than the one that we had for the consumer. If this # is the case, go ahead and modify the consumer record with the # newly-supplied project/user information, but do not bump the consumer # generation (since it will be bumped in the # AllocationList.replace_all() method). # # TODO(jaypipes): This means that there may be a partial update. # Imagine a scenario where a user calls POST /allocations, and the # payload references two consumers. The first consumer is a new # consumer and is auto-created. The second consumer is an existing # consumer, but contains a different project or user ID than the # existing consumer's record. If the eventual call to # AllocationList.replace_all() fails for whatever reason (say, a # resource provider generation conflict or out of resources failure), # we will end up deleting the auto-created consumer but we MAY not undo # the changes to the second consumer's project and user ID. I say MAY # and not WILL NOT because I'm not sure that the exception that gets # raised from AllocationList.replace_all() will cause the context # manager's transaction to rollback automatically. I believe that the # same transaction context is used for both util.ensure_consumer() and # AllocationList.replace_all() within the same HTTP request, but need # to test this to be 100% certain... if (project_id != consumer.project.external_id or user_id != consumer.user.external_id): LOG.debug( "Supplied project or user ID for consumer %s was " "different than existing record. Updating consumer " "record.", consumer_uuid) consumer.project = proj consumer.user = user consumer.update() except exception.NotFound: # If we are attempting to modify or create allocations after 1.26, we # need a consumer generation specified. The user must have specified # None for the consumer generation if we get here, since there was no # existing consumer with this UUID and therefore the user should be # indicating that they expect the consumer did not exist. if requires_consumer_generation: if consumer_generation is not None: raise webob.exc.HTTPConflict( _('consumer generation conflict - ' 'expected null but got %s') % consumer_generation, comment=errors.CONCURRENT_UPDATE) # No such consumer. This is common for new allocations. Create the # consumer record try: consumer = consumer_obj.Consumer(ctx, uuid=consumer_uuid, project=proj, user=user) consumer.create() created_new_consumer = True except exception.ConsumerExists: # No worries, another thread created this user already consumer = consumer_obj.Consumer.get_by_uuid(ctx, consumer_uuid) return consumer, created_new_consumer