def test_no_object_time_fields_more(self): # An unsaved ovo will not have the created_at or updated_at fields # present on the object at all. now = timeutils.utcnow(with_timezone=True) more = now + datetime.timedelta(seconds=300) with mock.patch('oslo_utils.timeutils.utcnow') as mock_utc: mock_utc.return_value = now chosen_time = util.pick_last_modified(more, self.resource_provider) self.assertEqual(more, chosen_time) mock_utc.assert_called_once_with(with_timezone=True)
def _serialize_resource_classes(environ, rcs, want_version): output = [] last_modified = None get_last_modified = want_version.matches((1, 15)) for rc in rcs: if get_last_modified: last_modified = util.pick_last_modified(last_modified, rc) data = _serialize_resource_class(environ, rc) output.append(data) last_modified = last_modified or timeutils.utcnow(with_timezone=True) return ({"resource_classes": output}, last_modified)
def _serialize_providers(environ, resource_providers, want_version): output = [] last_modified = None get_last_modified = want_version.matches((1, 15)) for provider in resource_providers: if get_last_modified: last_modified = util.pick_last_modified(last_modified, provider) provider_data = _serialize_provider(environ, provider, want_version) output.append(provider_data) last_modified = last_modified or timeutils.utcnow(with_timezone=True) return ({"resource_providers": output}, last_modified)
def create_resource_provider(req): """POST to create a resource provider. On success return a 201 response with an empty body and a location header pointing to the newly created resource provider. """ context = req.environ['placement.context'] context.can(policies.CREATE) schema = rp_schema.POST_RESOURCE_PROVIDER_SCHEMA want_version = req.environ[microversion.MICROVERSION_ENVIRON] if want_version.matches((1, 14)): schema = rp_schema.POST_RP_SCHEMA_V1_14 data = util.extract_json(req.body, schema) try: if data.get('uuid'): # Normalize UUID with no proper dashes into dashed one # with format {8}-{4}-{4}-{4}-{12} data['uuid'] = str(uuidlib.UUID(data['uuid'])) else: data['uuid'] = uuidutils.generate_uuid() resource_provider = rp_obj.ResourceProvider(context, **data) resource_provider.create() except db_exc.DBDuplicateEntry as exc: # Whether exc.columns has one or two entries (in the event # of both fields being duplicates) appears to be database # dependent, so going with the complete solution here. duplicate = ', '.join(['%s: %s' % (column, data[column]) for column in exc.columns]) raise webob.exc.HTTPConflict( _('Conflicting resource provider %(duplicate)s already exists.') % {'duplicate': duplicate}, comment=errors.DUPLICATE_NAME) except exception.ObjectActionError as exc: raise webob.exc.HTTPBadRequest( _('Unable to create resource provider "%(name)s", %(rp_uuid)s: ' '%(error)s') % {'name': data['name'], 'rp_uuid': data['uuid'], 'error': exc}) req.response.location = util.resource_provider_url( req.environ, resource_provider) if want_version.matches(min_version=(1, 20)): req.response.body = encodeutils.to_utf8(jsonutils.dumps( _serialize_provider(req.environ, resource_provider, want_version))) req.response.content_type = 'application/json' modified = util.pick_last_modified(None, resource_provider) req.response.last_modified = modified req.response.cache_control = 'no-cache' else: req.response.status = 201 req.response.content_type = None return req.response
def _serialize_inventories(inventories, generation): """Turn a list of inventories in a dict by resource class.""" inventories_by_class = {inventory.resource_class: inventory for inventory in inventories} inventories_dict = {} last_modified = None for resource_class, inventory in inventories_by_class.items(): last_modified = util.pick_last_modified(last_modified, inventory) inventories_dict[resource_class] = _serialize_inventory( inventory, generation=None) return ({'resource_provider_generation': generation, 'inventories': inventories_dict}, last_modified)
def _serialize_traits(traits, want_version): last_modified = None get_last_modified = want_version.matches((1, 15)) trait_names = [] for trait in traits: if get_last_modified: last_modified = util.pick_last_modified(last_modified, trait) trait_names.append(trait.name) # If there were no traits, set last_modified to now last_modified = last_modified or timeutils.utcnow(with_timezone=True) return {'traits': trait_names}, last_modified
def _send_inventory(req, resource_provider, inventory, status=200): """Send a JSON representation of one single inventory.""" response = req.response response.status = status response.body = encodeutils.to_utf8(jsonutils.dumps(_serialize_inventory( inventory, generation=resource_provider.generation))) response.content_type = 'application/json' want_version = req.environ[microversion.MICROVERSION_ENVIRON] if want_version.matches((1, 15)): modified = util.pick_last_modified(None, inventory) response.last_modified = modified response.cache_control = 'no-cache' return response
def _last_modified_from_allocations(allocations, want_version): """Given a set of allocation objects, returns the last modified timestamp. """ # NOTE(cdent): The last_modified for an allocation will always be # based off the created_at column because allocations are only # ever inserted, never updated. last_modified = None # Only calculate last-modified if we are using a microversion that # supports it. get_last_modified = want_version and want_version.matches((1, 15)) for allocation in allocations: if get_last_modified: last_modified = util.pick_last_modified(last_modified, allocation) last_modified = last_modified or timeutils.utcnow(with_timezone=True) return last_modified
def _allocations_dict(allocations, key_fetcher, resource_provider=None, want_version=None): """Turn allocations into a dict of resources keyed by key_fetcher.""" allocation_data = collections.defaultdict(dict) # NOTE(cdent): The last_modified for an allocation will always be # based off the created_at column because allocations are only # ever inserted, never updated. last_modified = None # Only calculate last-modified if we are using a microversion that # supports it. get_last_modified = want_version and want_version.matches((1, 15)) for allocation in allocations: if get_last_modified: last_modified = util.pick_last_modified(last_modified, allocation) key = key_fetcher(allocation) if 'resources' not in allocation_data[key]: allocation_data[key]['resources'] = {} resource_class = allocation.resource_class allocation_data[key]['resources'][resource_class] = allocation.used if not resource_provider: generation = allocation.resource_provider.generation allocation_data[key]['generation'] = generation result = {'allocations': allocation_data} if resource_provider: result['resource_provider_generation'] = resource_provider.generation else: if allocations and want_version and want_version.matches((1, 12)): # We're looking at a list of allocations by consumer id so # project and user are consistent across the list result['project_id'] = allocations[0].project_id result['user_id'] = allocations[0].user_id last_modified = last_modified or timeutils.utcnow(with_timezone=True) return result, last_modified
def get_resource_provider(req): """Get a single resource provider. On success return a 200 with an application/json body representing the resource provider. """ want_version = req.environ[microversion.MICROVERSION_ENVIRON] uuid = util.wsgi_path_item(req.environ, 'uuid') # The containing application will catch a not found here. context = req.environ['placement.context'] resource_provider = rp_obj.ResourceProvider.get_by_uuid( context, uuid) response = req.response response.body = encodeutils.to_utf8(jsonutils.dumps( _serialize_provider(req.environ, resource_provider, want_version))) response.content_type = 'application/json' if want_version.matches((1, 15)): modified = util.pick_last_modified(None, resource_provider) response.last_modified = modified response.cache_control = 'no-cache' return response
def get_resource_class(req): """Get a single resource class. On success return a 200 with an application/json body representing the resource class. """ name = util.wsgi_path_item(req.environ, 'name') context = req.environ['placement.context'] want_version = req.environ[microversion.MICROVERSION_ENVIRON] # The containing application will catch a not found here. rc = rp_obj.ResourceClass.get_by_name(context, name) req.response.body = encodeutils.to_utf8( jsonutils.dumps(_serialize_resource_class(req.environ, rc))) req.response.content_type = 'application/json' if want_version.matches((1, 15)): req.response.cache_control = 'no-cache' # Non-custom resource classes will return None from pick_last_modified, # so the 'or' causes utcnow to be used. last_modified = util.pick_last_modified( None, rc) or timeutils.utcnow(with_timezone=True) req.response.last_modified = last_modified return req.response
def test_last_modified_same(self): now = timeutils.utcnow(with_timezone=True) self.resource_provider.updated_at = now self.resource_provider.created_at = now chosen_time = util.pick_last_modified(now, self.resource_provider) self.assertEqual(now, chosen_time)
def create_resource_provider(req): """POST to create a resource provider. On success return a 201 response with an empty body (microversions 1.0 - 1.19) or a 200 response with a payload representing the newly created resource provider (microversions 1.20 - latest), and a location header pointing to the resource provider. """ context = req.environ['placement.context'] context.can(policies.CREATE) schema = rp_schema.POST_RESOURCE_PROVIDER_SCHEMA want_version = req.environ[microversion.MICROVERSION_ENVIRON] if want_version.matches((1, 14)): schema = rp_schema.POST_RP_SCHEMA_V1_14 data = util.extract_json(req.body, schema) try: if data.get('uuid'): # Normalize UUID with no proper dashes into dashed one # with format {8}-{4}-{4}-{4}-{12} data['uuid'] = str(uuidlib.UUID(data['uuid'])) else: data['uuid'] = uuidutils.generate_uuid() resource_provider = rp_obj.ResourceProvider(context, **data) resource_provider.create() except db_exc.DBDuplicateEntry as exc: # Whether exc.columns has one or two entries (in the event # of both fields being duplicates) appears to be database # dependent, so going with the complete solution here. duplicates = [] for column in exc.columns: # For MySQL, this is error 1062: # # Duplicate entry '%s' for key %d # # The 'key' value is captured in 'DBDuplicateEntry.columns'. # Despite the name, this isn't always a column name. While MySQL # 5.x does indeed use the name of the column, 8.x uses the name of # the constraint. oslo.db should probably fix this, but until that # happens we need to handle both cases if column == 'uniq_resource_providers0uuid': duplicates.append(f'uuid: {data["uuid"]}') elif column == 'uniq_resource_providers0name': duplicates.append(f'name: {data["name"]}') else: duplicates.append(f'{column}: {data[column]}') raise webob.exc.HTTPConflict( 'Conflicting resource provider %(duplicate)s already exists.' % {'duplicate': ', '.join(duplicates)}, comment=errors.DUPLICATE_NAME) except exception.ObjectActionError as exc: raise webob.exc.HTTPBadRequest( 'Unable to create resource provider "%(name)s", %(rp_uuid)s: ' '%(error)s' % { 'name': data['name'], 'rp_uuid': data['uuid'], 'error': exc }) req.response.location = util.resource_provider_url(req.environ, resource_provider) if want_version.matches(min_version=(1, 20)): req.response.body = encodeutils.to_utf8( jsonutils.dumps( _serialize_provider(req.environ, resource_provider, want_version))) req.response.content_type = 'application/json' modified = util.pick_last_modified(None, resource_provider) req.response.last_modified = modified req.response.cache_control = 'no-cache' else: req.response.status = 201 req.response.content_type = None return req.response