def _delete(self, request, id, **kwargs): action = self._plugin_handlers[self.DELETE] # Check authz policy.init() parent_id = kwargs.get(self._parent_id_name) obj = self._item(request, id, parent_id=parent_id) try: policy.enforce(request.context, action, obj, pluralized=self._collection) except oslo_policy.PolicyNotAuthorized: # To avoid giving away information, pretend that it # doesn't exist msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) obj_deleter = getattr(self._plugin, action) obj_deleter(request.context, id, **kwargs) # A delete operation usually alters resource usage, so mark affected # usage trackers as dirty resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + '.delete.end' self._notifier.info(request.context, notifier_method, {self._resource + '_id': id}) result = {self._resource: self._view(request.context, obj)} self._send_nova_notification(action, {}, result) self._send_dhcp_notification(request.context, result, notifier_method)
def _handle_action(request, id, **kwargs): arg_list = [request.context, id] # Ensure policy engine is initialized policy.init() # Fetch the resource and verify if the user can access it try: parent_id = kwargs.get(self._parent_id_name) resource = self._item(request, id, do_authz=True, field_list=None, parent_id=parent_id) except oslo_policy.PolicyNotAuthorized: msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) body = copy.deepcopy(kwargs.pop('body', None)) # Explicit comparison with None to distinguish from {} if body is not None: arg_list.append(body) # It is ok to raise a 403 because accessibility to the # object was checked earlier in this method policy.enforce(request.context, name, resource, pluralized=self._collection) ret_value = getattr(self._plugin, name)(*arg_list, **kwargs) # It is simply impossible to predict whether one of this # actions alters resource usage. For instance a tenant port # is created when a router interface is added. Therefore it is # important to mark as dirty resources whose counters have # been altered by this operation resource_registry.set_resources_dirty(request.context) return ret_value
def notify(create_result): # Ensure usage trackers for all resources affected by this API # operation are marked as dirty # Commit the reservation(s) for reservation in reservations: quota.QUOTAS.commit_reservation(request.context, reservation.reservation_id) resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + '.create.end' self._notifier.info(request.context, notifier_method, create_result) registry.publish(self._resource, events.BEFORE_RESPONSE, self, payload=events.APIEventPayload( request.context, notifier_method, action, request_body=body, states=( {}, create_result, ), collection_name=self._collection)) return create_result
def _update(self, request, id, body, **kwargs): body = Controller.prepare_request_body( request.context, copy.deepcopy(body), False, self._resource, self._attr_info, allow_bulk=self._allow_bulk ) action = self._plugin_handlers[self.UPDATE] # Load object to check authz # but pass only attributes in the original body and required # by the policy engine to the policy 'brain' field_list = [ name for (name, value) in six.iteritems(self._attr_info) if (value.get("required_by_policy") or value.get("primary_key") or "default" not in value) ] # Ensure policy engine is initialized policy.init() parent_id = kwargs.get(self._parent_id_name) orig_obj = self._item(request, id, field_list=field_list, parent_id=parent_id) orig_object_copy = copy.copy(orig_obj) orig_obj.update(body[self._resource]) # Make a list of attributes to be updated to inform the policy engine # which attributes are set explicitly so that it can distinguish them # from the ones that are set to their default values. orig_obj[n_const.ATTRIBUTES_TO_UPDATE] = body[self._resource].keys() try: policy.enforce(request.context, action, orig_obj, pluralized=self._collection) except oslo_policy.PolicyNotAuthorized: with excutils.save_and_reraise_exception() as ctxt: # If a tenant is modifying it's own object, it's safe to return # a 403. Otherwise, pretend that it doesn't exist to avoid # giving away information. if request.context.tenant_id != orig_obj["tenant_id"]: ctxt.reraise = False msg = _("The resource could not be found.") raise webob.exc.HTTPNotFound(msg) obj_updater = getattr(self._plugin, action) kwargs = {self._resource: body} if parent_id: kwargs[self._parent_id_name] = parent_id obj = obj_updater(request.context, id, **kwargs) # Usually an update operation does not alter resource usage, but as # there might be side effects it might be worth checking for changes # in resource usage here as well (e.g: a tenant port is created when a # router interface is added) resource_registry.set_resources_dirty(request.context) result = {self._resource: self._view(request.context, obj)} notifier_method = self._resource + ".update.end" self._notifier.info(request.context, notifier_method, result) registry.notify( self._resource, events.BEFORE_RESPONSE, self, context=request.context, data=result, method_name=notifier_method, action=action, original=orig_object_copy, ) return result
def _delete(self, request, id, **kwargs): action = self._plugin_handlers[self.DELETE] # Check authz policy.init() parent_id = kwargs.get(self._parent_id_name) obj = self._item(request, id, parent_id=parent_id) try: policy.enforce(request.context, action, obj, pluralized=self._collection) except oslo_policy.PolicyNotAuthorized: # To avoid giving away information, pretend that it # doesn't exist msg = _("The resource could not be found.") raise webob.exc.HTTPNotFound(msg) obj_deleter = getattr(self._plugin, action) obj_deleter(request.context, id, **kwargs) # A delete operation usually alters resource usage, so mark affected # usage trackers as dirty resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + ".delete.end" result = {self._resource: self._view(request.context, obj)} notifier_payload = {self._resource + "_id": id} notifier_payload.update(result) self._notifier.info(request.context, notifier_method, notifier_payload) registry.notify( self._resource, events.BEFORE_RESPONSE, self, context=request.context, data=result, method_name=notifier_method, action=action, original={}, )
def _update(self, request, id, body, **kwargs): body = Controller.prepare_request_body(request.context, body, False, self._resource, self._attr_info, allow_bulk=self._allow_bulk) action = self._plugin_handlers[self.UPDATE] # Load object to check authz # but pass only attributes in the original body and required # by the policy engine to the policy 'brain' field_list = [name for (name, value) in six.iteritems(self._attr_info) if (value.get('required_by_policy') or value.get('primary_key') or 'default' not in value)] # Ensure policy engine is initialized policy.init() parent_id = kwargs.get(self._parent_id_name) orig_obj = self._item(request, id, field_list=field_list, parent_id=parent_id) orig_object_copy = copy.copy(orig_obj) orig_obj.update(body[self._resource]) # Make a list of attributes to be updated to inform the policy engine # which attributes are set explicitly so that it can distinguish them # from the ones that are set to their default values. orig_obj[n_const.ATTRIBUTES_TO_UPDATE] = body[self._resource].keys() try: policy.enforce(request.context, action, orig_obj, pluralized=self._collection) except oslo_policy.PolicyNotAuthorized: with excutils.save_and_reraise_exception() as ctxt: # If a tenant is modifying its own object, it's safe to return # a 403. Otherwise, pretend that it doesn't exist to avoid # giving away information. orig_obj_tenant_id = orig_obj.get("tenant_id") if (request.context.tenant_id != orig_obj_tenant_id or orig_obj_tenant_id is None): ctxt.reraise = False msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) obj_updater = getattr(self._plugin, action) kwargs = {self._resource: body} if parent_id: kwargs[self._parent_id_name] = parent_id obj = obj_updater(request.context, id, **kwargs) # Usually an update operation does not alter resource usage, but as # there might be side effects it might be worth checking for changes # in resource usage here as well (e.g: a tenant port is created when a # router interface is added) resource_registry.set_resources_dirty(request.context) result = {self._resource: self._view(request.context, obj)} notifier_method = self._resource + '.update.end' self._notifier.info(request.context, notifier_method, result) registry.notify(self._resource, events.BEFORE_RESPONSE, self, context=request.context, data=result, method_name=notifier_method, action=action, original=orig_object_copy) return result
def test_set_resources_dirty_no_tracked_resource(self): ctx = context.Context('user_id', 'tenant_id', is_admin=False, is_advsvc=False) with mock.patch('neutron.quota.resource.' 'TrackedResource.mark_dirty') as mock_mark_dirty: self.registry.register_resource_by_name('meh') resource_registry.set_resources_dirty(ctx) self.assertEqual(0, mock_mark_dirty.call_count)
def test_set_resources_dirty_invoked_with_tracking_disabled(self): cfg.CONF.set_override("track_quota_usage", False, group="QUOTAS") # DietTestCase does not automatically cleans configuration overrides self.addCleanup(cfg.CONF.reset) with mock.patch("neutron.quota.resource." "TrackedResource.mark_dirty") as mock_mark_dirty: self.registry.set_tracked_resource("meh", test_quota.MehModel) self.registry.register_resource_by_name("meh") resource_registry.set_resources_dirty(mock.ANY) self.assertEqual(0, mock_mark_dirty.call_count)
def test_set_resources_dirty(self): ctx = context.Context("user_id", "tenant_id", is_admin=False, is_advsvc=False) with mock.patch("neutron.quota.resource." "TrackedResource.mark_dirty") as mock_mark_dirty: self.registry.set_tracked_resource("meh", test_quota.MehModel) self.registry.register_resource_by_name("meh") res = self.registry.get_resource("meh") # This ensures dirty is true res._dirty_tenants.add("tenant_id") resource_registry.set_resources_dirty(ctx) mock_mark_dirty.assert_called_once_with(ctx)
def test_set_resources_dirty_no_dirty_resource(self): ctx = context.Context("user_id", "tenant_id", is_admin=False, is_advsvc=False) with mock.patch("neutron.quota.resource." "TrackedResource.mark_dirty") as mock_mark_dirty: self.registry.set_tracked_resource("meh", test_quota.MehModel) self.registry.register_resource_by_name("meh") res = self.registry.get_resource("meh") # This ensures dirty is false res._dirty_tenants.clear() resource_registry.set_resources_dirty(ctx) self.assertEqual(0, mock_mark_dirty.call_count)
def test_set_resources_dirty_invoked_with_tracking_disabled(self): cfg.CONF.set_override('track_quota_usage', False, group='QUOTAS') # DietTestCase does not automatically cleans configuration overrides self.addCleanup(cfg.CONF.reset) with mock.patch('neutron.quota.resource.' 'TrackedResource.mark_dirty') as mock_mark_dirty: self.registry.set_tracked_resource('meh', test_quota.MehModel) self.registry.register_resource_by_name('meh') resource_registry.set_resources_dirty(mock.ANY) self.assertEqual(0, mock_mark_dirty.call_count)
def test_set_resources_dirty(self): ctx = context.Context('user_id', 'tenant_id', is_admin=False, is_advsvc=False) with mock.patch('neutron.quota.resource.' 'TrackedResource.mark_dirty') as mock_mark_dirty: self.registry.set_tracked_resource('meh', test_quota.MehModel) self.registry.register_resource_by_name('meh') res = self.registry.get_resource('meh') # This ensures dirty is true res._dirty_tenants.add('tenant_id') resource_registry.set_resources_dirty(ctx) mock_mark_dirty.assert_called_once_with(ctx, nested=True)
def after(self, state): # Commit reservation(s) reservations = state.request.context.get('reservations') if not reservations: return neutron_context = state.request.context.get('neutron_context') with db_api.context_manager.writer.using(neutron_context): # Commit the reservation(s) for reservation in reservations: quota.QUOTAS.commit_reservation( neutron_context, reservation.reservation_id) resource_registry.set_resources_dirty(neutron_context)
def test_set_resources_dirty(self): ctx = context.Context('user_id', 'tenant_id', is_admin=False, is_advsvc=False) with mock.patch('neutron.quota.resource.' 'TrackedResource.mark_dirty') as mock_mark_dirty: self.registry.set_tracked_resource('meh', test_quota.MehModel) self.registry.register_resource_by_name('meh') res = self.registry.get_resource('meh') # This ensures dirty is true res._dirty_tenants.add('tenant_id') resource_registry.set_resources_dirty(ctx) mock_mark_dirty.assert_called_once_with(ctx)
def test_set_resources_dirty_no_dirty_resource(self): ctx = context.Context('user_id', 'tenant_id', is_admin=False, is_advsvc=False) with mock.patch('neutron.quota.resource.' 'TrackedResource.mark_dirty') as mock_mark_dirty: self.registry.set_tracked_resource('meh', test_quota.MehModel) self.registry.register_resource_by_name('meh') res = self.registry.get_resource('meh') # This ensures dirty is false res._dirty_tenants.clear() resource_registry.set_resources_dirty(ctx) self.assertEqual(0, mock_mark_dirty.call_count)
def after(self, state): # Commit reservation(s) reservations = state.request.context.get('reservations') if not reservations: return neutron_context = state.request.context.get('neutron_context') with db_api.context_manager.writer.using(neutron_context): # Commit the reservation(s) for reservation in reservations: quota.QUOTAS.commit_reservation(neutron_context, reservation.reservation_id) resource_registry.set_resources_dirty(neutron_context)
def notify(create_result): # Ensure usage trackers for all resources affected by this API # operation are marked as dirty # TODO(salv-orlando): This operation will happen in a single # transaction with reservation commit once that is implemented resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + '.create.end' self._notifier.info(request.context, notifier_method, create_result) self._send_dhcp_notification(request.context, create_result, notifier_method) return create_result
def _delete(self, request, id, **kwargs): action = self._plugin_handlers[self.DELETE] # Check authz policy.init() parent_id = kwargs.get(self._parent_id_name) obj = self._item(request, id, parent_id=parent_id) try: policy.enforce(request.context, action, obj, pluralized=self._collection) except oslo_policy.PolicyNotAuthorized: # To avoid giving away information, pretend that it # doesn't exist if policy does not authorize SHOW with excutils.save_and_reraise_exception() as ctxt: if not policy.check(request.context, self._plugin_handlers[self.SHOW], obj, pluralized=self._collection): ctxt.reraise = False msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) obj_deleter = getattr(self._plugin, action) obj_deleter(request.context, id, **kwargs) # A delete operation usually alters resource usage, so mark affected # usage trackers as dirty resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + '.delete.end' result = {self._resource: self._view(request.context, obj)} notifier_payload = {self._resource + '_id': id} notifier_payload.update(result) self._notifier.info(request.context, notifier_method, notifier_payload) registry.publish(self._resource, events.BEFORE_RESPONSE, self, payload=events.APIEventPayload( request.context, notifier_method, action, states=( {}, obj, result, ), collection_name=self._collection))
def notify(create_result): # Ensure usage trackers for all resources affected by this API # operation are marked as dirty with request.context.session.begin(): # Commit the reservation(s) for reservation in reservations: quota.QUOTAS.commit_reservation(request.context, reservation.reservation_id) resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + '.create.end' self._notifier.info(request.context, notifier_method, create_result) self._send_dhcp_notification(request.context, create_result, notifier_method) return create_result
def _call_on_drivers(self, method_name, context, continue_on_failure=False, raise_db_retriable=False): super(MechanismManager, self)._call_on_drivers( method_name, context, continue_on_failure=False, raise_db_retriable=False) if method_name.endswith('_precommit'): # This does the same thing as: # https://github.com/openstack/neutron/blob/newton-eol/neutron/ # api/v2/base.py#L489 # but from within the scope of the plugin's transaction, such # that if it fails, everything that happened prior to this in # precommit phase can also be rolled back. resource_name = method_name.replace('_precommit', '').replace( 'create_', '').replace('update_', '').replace( 'delete_', '') tracked_resource = resource_registry.get_resource(resource_name) tracked_resource._dirty_tenants.add(context.current['tenant_id']) resource_registry.set_resources_dirty(context._plugin_context)
def notify(create_result): # Ensure usage trackers for all resources affected by this API # operation are marked as dirty with request.context.session.begin(): # Commit the reservation(s) for reservation in reservations: quota.QUOTAS.commit_reservation( request.context, reservation.reservation_id) resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + '.create.end' self._notifier.info(request.context, notifier_method, create_result) self._send_dhcp_notification(request.context, create_result, notifier_method) return create_result
def notify(create_result): # Ensure usage trackers for all resources affected by this API # operation are marked as dirty with request.context.session.begin(): # Commit the reservation(s) for reservation in reservations: quota.QUOTAS.commit_reservation( request.context, reservation.reservation_id) resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + '.create.end' self._notifier.info(request.context, notifier_method, create_result) registry.notify(self._resource, events.BEFORE_RESPONSE, self, context=request.context, data=create_result, method_name=notifier_method, collection=self._collection) return create_result
def _delete(self, request, id, **kwargs): action = self._plugin_handlers[self.DELETE] # Check authz policy.init() parent_id = kwargs.get(self._parent_id_name) obj = self._item(request, id, parent_id=parent_id) try: policy.enforce(request.context, action, obj, pluralized=self._collection) except oslo_policy.PolicyNotAuthorized: # To avoid giving away information, pretend that it # doesn't exist if policy does not authorize SHOW with excutils.save_and_reraise_exception() as ctxt: if not policy.check(request.context, self._plugin_handlers[self.SHOW], obj, pluralized=self._collection): ctxt.reraise = False msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) obj_deleter = getattr(self._plugin, action) obj_deleter(request.context, id, **kwargs) # A delete operation usually alters resource usage, so mark affected # usage trackers as dirty resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + '.delete.end' result = {self._resource: self._view(request.context, obj)} notifier_payload = {self._resource + '_id': id} notifier_payload.update(result) self._notifier.info(request.context, notifier_method, notifier_payload) registry.publish(self._resource, events.BEFORE_RESPONSE, self, payload=events.APIEventPayload( request.context, notifier_method, action, states=({}, obj, result,), collection_name=self._collection))
def after(self, state): neutron_context = state.request.context.get('neutron_context') if not neutron_context: return collection = state.request.context.get('collection') resource = state.request.context.get('resource') if state.request.method == 'GET' and collection: # resync on list operations to preserve behavior of old API resource_registry.resync_resource( neutron_context, resource, neutron_context.tenant_id) # Commit reservation(s) reservations = state.request.context.get('reservations') or [] if not reservations and state.request.method != 'DELETE': return with db_api.CONTEXT_WRITER.using(neutron_context): # Commit the reservation(s) for reservation in reservations: quota.QUOTAS.commit_reservation( neutron_context, reservation.reservation_id) resource_registry.set_resources_dirty(neutron_context)
def notify(create_result): # Ensure usage trackers for all resources affected by this API # operation are marked as dirty with request.context.session.begin(): # Commit the reservation(s) for reservation in reservations: quota.QUOTAS.commit_reservation( request.context, reservation.reservation_id) resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + '.create.end' self._notifier.info(request.context, notifier_method, create_result) registry.notify(self._resource, events.BEFORE_RESPONSE, self, context=request.context, data=create_result, method_name=notifier_method, collection=self._collection, action=action, original={}) return create_result
def after(self, state): neutron_context = state.request.context.get('neutron_context') if not neutron_context: return collection = state.request.context.get('collection') resource = state.request.context.get('resource') if state.request.method == 'GET' and collection: # resync on list operations to preserve behavior of old API resource_registry.resync_resource(neutron_context, resource, neutron_context.tenant_id) # Commit reservation(s) reservations = state.request.context.get('reservations') or [] if not reservations and state.request.method != 'DELETE': return with db_api.CONTEXT_WRITER.using(neutron_context): # Commit the reservation(s) for reservation in reservations: quota.QUOTAS.commit_reservation(neutron_context, reservation.reservation_id) resource_registry.set_resources_dirty(neutron_context)
def notify(create_result): # Ensure usage trackers for all resources affected by this API # operation are marked as dirty with db_api.CONTEXT_WRITER.using(request.context): # Commit the reservation(s) for reservation in reservations: quota.QUOTAS.commit_reservation( request.context, reservation.reservation_id) resource_registry.set_resources_dirty(request.context) notifier_method = self._resource + '.create.end' self._notifier.info(request.context, notifier_method, create_result) registry.publish(self._resource, events.BEFORE_RESPONSE, self, payload=events.APIEventPayload( request.context, notifier_method, action, request_body=body, states=({}, create_result,), collection_name=self._collection)) return create_result
def update(self, request, id, body=None, **kwargs): """Updates the specified entity's attributes.""" parent_id = kwargs.get(self._parent_id_name) try: payload = body.copy() except AttributeError: msg = _("Invalid format: %s") % request.body raise exceptions.BadRequest(resource='body', msg=msg) payload['id'] = id self._notifier.info(request.context, self._resource + '.update.start', payload) body = Controller.prepare_request_body(request.context, body, False, self._resource, self._attr_info, allow_bulk=self._allow_bulk) action = self._plugin_handlers[self.UPDATE] # Load object to check authz # but pass only attributes in the original body and required # by the policy engine to the policy 'brain' field_list = [name for (name, value) in six.iteritems(self._attr_info) if (value.get('required_by_policy') or value.get('primary_key') or 'default' not in value)] # Ensure policy engine is initialized policy.init() orig_obj = self._item(request, id, field_list=field_list, parent_id=parent_id) orig_object_copy = copy.copy(orig_obj) orig_obj.update(body[self._resource]) # Make a list of attributes to be updated to inform the policy engine # which attributes are set explicitly so that it can distinguish them # from the ones that are set to their default values. orig_obj[const.ATTRIBUTES_TO_UPDATE] = body[self._resource].keys() try: policy.enforce(request.context, action, orig_obj, pluralized=self._collection) except oslo_policy.PolicyNotAuthorized: with excutils.save_and_reraise_exception() as ctxt: # If a tenant is modifying it's own object, it's safe to return # a 403. Otherwise, pretend that it doesn't exist to avoid # giving away information. if request.context.tenant_id != orig_obj['tenant_id']: ctxt.reraise = False msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) obj_updater = getattr(self._plugin, action) kwargs = {self._resource: body} if parent_id: kwargs[self._parent_id_name] = parent_id obj = obj_updater(request.context, id, **kwargs) # Usually an update operation does not alter resource usage, but as # there might be side effects it might be worth checking for changes # in resource usage here as well (e.g: a tenant port is created when a # router interface is added) resource_registry.set_resources_dirty(request.context) result = {self._resource: self._view(request.context, obj)} notifier_method = self._resource + '.update.end' self._notifier.info(request.context, notifier_method, result) self._send_dhcp_notification(request.context, result, notifier_method) self._send_nova_notification(action, orig_object_copy, result) return result
def _update(self, request, id, body, **kwargs): body = Controller.prepare_request_body(request.context, body, False, self._resource, self._attr_info, allow_bulk=self._allow_bulk) action = self._plugin_handlers[self.UPDATE] # Load object to check authz # but pass only attributes in the original body and required # by the policy engine to the policy 'brain' field_list = [name for (name, value) in self._attr_info.items() if (value.get('required_by_policy') or value.get('primary_key') or 'default' not in value)] # Ensure policy engine is initialized policy.init() parent_id = kwargs.get(self._parent_id_name) # If the parent_id exist, we should get orig_obj with # self._parent_id_name field. if parent_id and self._parent_id_name not in field_list: field_list.append(self._parent_id_name) orig_obj = self._item(request, id, field_list=field_list, parent_id=parent_id) orig_object_copy = copy.copy(orig_obj) orig_obj.update(body[self._resource]) # Make a list of attributes to be updated to inform the policy engine # which attributes are set explicitly so that it can distinguish them # from the ones that are set to their default values. orig_obj[n_const.ATTRIBUTES_TO_UPDATE] = body[self._resource].keys() # Then get the ext_parent_id, format to ext_parent_parent_resource_id if self._parent_id_name in orig_obj: self._set_parent_id_into_ext_resources_request( request, orig_obj, parent_id) try: policy.enforce(request.context, action, orig_obj, pluralized=self._collection) except oslo_policy.PolicyNotAuthorized: # To avoid giving away information, pretend that it # doesn't exist if policy does not authorize SHOW with excutils.save_and_reraise_exception() as ctxt: if not policy.check(request.context, self._plugin_handlers[self.SHOW], orig_obj, pluralized=self._collection): ctxt.reraise = False msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) obj_updater = getattr(self._plugin, action) kwargs = {self._resource: body} if parent_id: kwargs[self._parent_id_name] = parent_id obj = obj_updater(request.context, id, **kwargs) # Usually an update operation does not alter resource usage, but as # there might be side effects it might be worth checking for changes # in resource usage here as well (e.g: a tenant port is created when a # router interface is added) resource_registry.set_resources_dirty(request.context) result = {self._resource: self._view(request.context, obj)} notifier_method = self._resource + '.update.end' self._notifier.info(request.context, notifier_method, result) registry.publish(self._resource, events.BEFORE_RESPONSE, self, payload=events.APIEventPayload( request.context, notifier_method, action, request_body=body, states=(orig_object_copy, result,), collection_name=self._collection)) return result
def _update(self, request, id, body, **kwargs): try: body = Controller.prepare_request_body(request.context, body, False, self._resource, self._attr_info, allow_bulk=self._allow_bulk) except Exception as e: LOG.warning( "An exception happened while processing the request " "body. The exception message is [%s].", e) raise e action = self._plugin_handlers[self.UPDATE] # Load object to check authz # but pass only attributes in the original body and required # by the policy engine to the policy 'brain' field_list = [ name for (name, value) in self._attr_info.items() if (value.get('required_by_policy') or value.get('primary_key') or 'default' not in value) ] # Ensure policy engine is initialized policy.init() parent_id = kwargs.get(self._parent_id_name) # If the parent_id exist, we should get orig_obj with # self._parent_id_name field. if parent_id and self._parent_id_name not in field_list: field_list.append(self._parent_id_name) orig_obj = self._item(request, id, field_list=field_list, parent_id=parent_id) orig_object_copy = copy.copy(orig_obj) orig_obj.update(body[self._resource]) # Make a list of attributes to be updated to inform the policy engine # which attributes are set explicitly so that it can distinguish them # from the ones that are set to their default values. orig_obj[constants.ATTRIBUTES_TO_UPDATE] = body[self._resource].keys() # Then get the ext_parent_id, format to ext_parent_parent_resource_id if self._parent_id_name in orig_obj: self._set_parent_id_into_ext_resources_request( request, orig_obj, parent_id) try: policy.enforce(request.context, action, orig_obj, pluralized=self._collection) except oslo_policy.PolicyNotAuthorized: # To avoid giving away information, pretend that it # doesn't exist if policy does not authorize SHOW with excutils.save_and_reraise_exception() as ctxt: if not policy.check(request.context, self._plugin_handlers[self.SHOW], orig_obj, pluralized=self._collection): ctxt.reraise = False msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) if self._native_bulk and hasattr(self._plugin, "%s_bulk" % action): obj_updater = getattr(self._plugin, "%s_bulk" % action) else: obj_updater = getattr(self._plugin, action) kwargs = {self._resource: body} if parent_id: kwargs[self._parent_id_name] = parent_id obj = obj_updater(request.context, id, **kwargs) # Usually an update operation does not alter resource usage, but as # there might be side effects it might be worth checking for changes # in resource usage here as well (e.g: a tenant port is created when a # router interface is added) resource_registry.set_resources_dirty(request.context) result = {self._resource: self._view(request.context, obj)} notifier_method = self._resource + '.update.end' self._notifier.info(request.context, notifier_method, result) registry.publish(self._resource, events.BEFORE_RESPONSE, self, payload=events.APIEventPayload( request.context, notifier_method, action, request_body=body, states=( orig_object_copy, result, ), collection_name=self._collection)) return result