def fetch_resource(method, neutron_context, controller, collection, resource, resource_id, parent_id=None): field_list = [] if method == 'PUT': attrs = controller.resource_info if not attrs: # this isn't a request for a normal resource. it could be # an action like removing a network from a dhcp agent. # return None and assume the custom controller for this will # handle the necessary logic. return field_list = [name for (name, value) in attrs.items() if (value.get('required_by_policy') or value.get('primary_key') or 'default' not in value)] plugin = manager.NeutronManager.get_plugin_for_resource(collection) if plugin: if utils.is_member_action(controller): getter = controller.parent_controller.plugin_shower else: getter = controller.plugin_shower getter_args = [neutron_context, resource_id] if parent_id: getter_args.append(parent_id) return getter(*getter_args, fields=field_list) else: # Some legit resources, like quota, do not have a plugin yet. # Retrieving the original object is nevertheless important # for policy checks. return _custom_getter(resource, resource_id)
def before(self, state): if state.request.method not in ('POST', 'PUT', 'DELETE'): return resource = state.request.context.get('resource') if not resource: return if utils.is_member_action(utils.get_controller(state)): return action = pecan_constants.ACTION_MAP.get(state.request.method) event = '%s.%s.start' % (resource, action) if action in ('create', 'update'): # notifier just gets plain old body without any treatment other # than the population of the object ID being operated on try: payload = state.request.json.copy() if not payload: return except ValueError: return if action == 'update': payload['id'] = state.request.context.get('resource_id') elif action == 'delete': resource_id = state.request.context.get('resource_id') payload = {resource + '_id': resource_id} self._notifier.info(state.request.context.get('neutron_context'), event, payload)
def after(self, state): resource_name = state.request.context.get('resource') collection_name = state.request.context.get('collection') neutron_context = state.request.context.get('neutron_context') action = pecan_constants.ACTION_MAP.get(state.request.method) if not action or action not in ('create', 'update', 'delete'): return if utils.is_member_action(utils.get_controller(state)): return if not resource_name: LOG.debug("Skipping NotifierHook processing as there was no " "resource associated with the request") return if state.response.status_int > 300: LOG.debug( "No notification will be sent due to unsuccessful " "status code: %s", state.response.status_int) return original = {} if (action in ('delete', 'update') and state.request.context.get('original_resources', [])): # We only need the original resource for updates and deletes original = state.request.context.get('original_resources')[0] if action == 'delete': # The object has been deleted, so we must notify the agent with the # data of the original object as the payload, but we do not need # to pass it in as the original result = {resource_name: original} original = {} else: if not state.response.body: result = {} else: result = state.response.json notifier_method = '%s.%s.end' % (resource_name, action) notifier_action = utils.get_controller(state).plugin_handlers[action] registry.publish(resource_name, events.BEFORE_RESPONSE, self, payload=events.APIEventPayload( neutron_context, notifier_method, notifier_action, request_body=state.request.body, states=( original, result, ), collection_name=collection_name)) if action == 'delete': resource_id = state.request.context.get('resource_id') result[resource_name + '_id'] = resource_id self._notifier.info(neutron_context, notifier_method, result)
def after(self, state): resource_name = state.request.context.get('resource') collection_name = state.request.context.get('collection') neutron_context = state.request.context.get('neutron_context') action = pecan_constants.ACTION_MAP.get(state.request.method) if not action or action not in ('create', 'update', 'delete'): return if utils.is_member_action(utils.get_controller(state)): return if not resource_name: LOG.debug("Skipping NotifierHook processing as there was no " "resource associated with the request") return if state.response.status_int > 300: LOG.debug("No notification will be sent due to unsuccessful " "status code: %s", state.response.status_int) return original = {} if (action in ('delete', 'update') and state.request.context.get('original_resources', [])): # We only need the original resource for updates and deletes original = state.request.context.get('original_resources')[0] if action == 'delete': # The object has been deleted, so we must notify the agent with the # data of the original object as the payload, but we do not need # to pass it in as the original result = {resource_name: original} original = {} else: if not state.response.body: result = {} else: result = state.response.json notifier_method = '%s.%s.end' % (resource_name, action) notifier_action = utils.get_controller(state).plugin_handlers[action] registry.publish(resource_name, events.BEFORE_RESPONSE, self, payload=events.APIEventPayload( neutron_context, notifier_method, notifier_action, request_body=state.request.body, states=(original, result,), collection_name=collection_name)) if action == 'delete': resource_id = state.request.context.get('resource_id') result[resource_name + '_id'] = resource_id self._notifier.info(neutron_context, notifier_method, result)
def before(self, state): # This hook should be run only for PUT,POST and DELETE methods and for # requests targeting a neutron resource resources = state.request.context.get('resources', []) if state.request.method not in ('POST', 'PUT', 'DELETE'): return # As this routine will likely alter the resources, do a shallow copy resources_copy = resources[:] neutron_context = state.request.context.get('neutron_context') resource = state.request.context.get('resource') # If there is no resource for this request, don't bother running authZ # policies if not resource: return controller = utils.get_controller(state) if not controller or utils.is_member_action(controller): return collection = state.request.context.get('collection') needs_prefetch = (state.request.method == 'PUT' or state.request.method == 'DELETE') policy.init() action = controller.plugin_handlers[ pecan_constants.ACTION_MAP[state.request.method]] # NOTE(salv-orlando): As bulk updates are not supported, in case of PUT # requests there will be only a single item to process, and its # identifier would have been already retrieved by the lookup process; # in the case of DELETE requests there won't be any item to process in # the request body original_resources = [] if needs_prefetch: try: item = resources_copy.pop() except IndexError: # Ops... this was a delete after all! item = {} resource_id = state.request.context.get('resource_id') parent_id = state.request.context.get('parent_id') method = state.request.method resource_obj = fetch_resource(method, neutron_context, controller, collection, resource, resource_id, parent_id=parent_id) if resource_obj: original_resources.append(resource_obj) obj = copy.copy(resource_obj) obj.update(item) obj[const.ATTRIBUTES_TO_UPDATE] = item.keys() # Put back the item in the list so that policies could be # enforced resources_copy.append(obj) # TODO(salv-orlando): as other hooks might need to prefetch resources, # store them in the request context. However, this should be done in a # separate hook which is conveniently called before all other hooks state.request.context['original_resources'] = original_resources for item in resources_copy: try: policy.enforce( neutron_context, action, item, pluralized=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. orig_item_tenant_id = item.get('tenant_id') if (needs_prefetch and (neutron_context.tenant_id != orig_item_tenant_id or orig_item_tenant_id is None)): ctxt.reraise = False msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg)
def after(self, state): resource_name = state.request.context.get('resource') collection_name = state.request.context.get('collection') neutron_context = state.request.context.get('neutron_context') if not resource_name: LOG.debug("Skipping NotifierHook processing as there was no " "resource associated with the request") return action = pecan_constants.ACTION_MAP.get(state.request.method) if not action or action == 'get': LOG.debug("No notification will be sent for action: %s", action) return if utils.is_member_action(utils.get_controller(state)): return if state.response.status_int > 300: LOG.debug("No notification will be sent due to unsuccessful " "status code: %s", state.response.status_int) return if action == 'delete': # The object has been deleted, so we must notify the agent with the # data of the original object data = {collection_name: state.request.context.get('original_resources', [])} else: try: data = jsonutils.loads(state.response.body) except ValueError: if not state.response.body: data = {} resources = [] if data: if resource_name in data: resources = [data[resource_name]] elif collection_name in data: # This was a bulk request resources = data[collection_name] # Send a notification only if a resource can be identified in the # response. This means that for operations such as add_router_interface # no notification will be sent if cfg.CONF.dhcp_agent_notification and data: self._notify_dhcp_agent( neutron_context, resource_name, action, resources) if cfg.CONF.notify_nova_on_port_data_changes: orig = {} if action == 'update': orig = state.request.context.get('original_resources')[0] elif action == 'delete': # NOTE(kevinbenton): the nova notifier is a bit strange because # it expects the original to be in the last argument on a # delete rather than in the 'original_obj' position resources = ( state.request.context.get('original_resources') or []) for resource in resources: self._nova_notify(action, resource_name, orig, {resource_name: resource}) event = '%s.%s.end' % (resource_name, action) if action == 'delete': resource_id = state.request.context.get('resource_id') payload = {resource_name + '_id': resource_id} elif action in ('create', 'update'): if not resources: # create/update did not complete so no notification return if len(resources) > 1: payload = {collection_name: resources} else: payload = {resource_name: resources[0]} else: return self._notifier.info(neutron_context, event, payload)
def after(self, state): resource_name = state.request.context.get('resource') collection_name = state.request.context.get('collection') neutron_context = state.request.context.get('neutron_context') if not resource_name: LOG.debug("Skipping NotifierHook processing as there was no " "resource associated with the request") return action = pecan_constants.ACTION_MAP.get(state.request.method) if not action or action == 'get': LOG.debug("No notification will be sent for action: %s", action) return if utils.is_member_action(utils.get_controller(state)): return if state.response.status_int > 300: LOG.debug( "No notification will be sent due to unsuccessful " "status code: %s", state.response.status_int) return if action == 'delete': # The object has been deleted, so we must notify the agent with the # data of the original object data = { collection_name: state.request.context.get('original_resources', []) } else: try: data = jsonutils.loads(state.response.body) except ValueError: if not state.response.body: data = {} resources = [] if data: if resource_name in data: resources = [data[resource_name]] elif collection_name in data: # This was a bulk request resources = data[collection_name] # Send a notification only if a resource can be identified in the # response. This means that for operations such as add_router_interface # no notification will be sent if cfg.CONF.dhcp_agent_notification and data: self._notify_dhcp_agent(neutron_context, resource_name, action, resources) if cfg.CONF.notify_nova_on_port_data_changes: orig = {} if action == 'update': orig = state.request.context.get('original_resources')[0] elif action == 'delete': # NOTE(kevinbenton): the nova notifier is a bit strange because # it expects the original to be in the last argument on a # delete rather than in the 'original_obj' position resources = (state.request.context.get('original_resources') or []) for resource in resources: self._nova_notify(action, resource_name, orig, {resource_name: resource}) event = '%s.%s.end' % (resource_name, action) if action == 'delete': resource_id = state.request.context.get('resource_id') payload = {resource_name + '_id': resource_id} elif action in ('create', 'update'): if not resources: # create/update did not complete so no notification return if len(resources) > 1: payload = {collection_name: resources} else: payload = {resource_name: resources[0]} else: return self._notifier.info(neutron_context, event, payload)