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): 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): neutron_context = state.request.context.get('neutron_context') resource = state.request.context.get('resource') collection = state.request.context.get('collection') controller = utils.get_controller(state) if not resource: # can't filter a resource we don't recognize return # NOTE(kevinbenton): extension listing isn't controlled by policy if resource == 'extension': return try: data = state.response.json except ValueError: return if state.request.method not in pecan_constants.ACTION_MAP: return if not data or (resource not in data and collection not in data): return policy.init() is_single = resource in data action_type = pecan_constants.ACTION_MAP[state.request.method] if action_type == 'get': action = controller.plugin_handlers[controller.SHOW] else: action = controller.plugin_handlers[action_type] key = resource if is_single else collection to_process = [data[resource]] if is_single else data[collection] # in the single case, we enforce which raises on violation # in the plural case, we just check so violating items are hidden policy_method = policy.enforce if is_single else policy.check plugin = manager.NeutronManager.get_plugin_for_resource(collection) try: resp = [ self._get_filtered_item(state.request, controller, resource, collection, item) for item in to_process if (state.request.method != 'GET' or policy_method(neutron_context, action, item, plugin=plugin, pluralized=collection)) ] except oslo_policy.PolicyNotAuthorized: # This exception must be explicitly caught as the exception # translation hook won't be called if an error occurs in the # 'after' handler. Instead of raising an HTTPForbidden exception, # we have to set the status_code here to prevent the catch_errors # middleware from turning this into a 500. state.response.status_code = 403 return if is_single: resp = resp[0] state.response.json = {key: resp}
def after(self, state): neutron_context = state.request.context.get('neutron_context') resource = state.request.context.get('resource') collection = state.request.context.get('collection') controller = utils.get_controller(state) if not resource: # can't filter a resource we don't recognize return # NOTE(kevinbenton): extension listing isn't controlled by policy if resource == 'extension': return try: data = state.response.json except ValueError: return if state.request.method not in pecan_constants.ACTION_MAP: return if not data or (resource not in data and collection not in data): return policy.init() is_single = resource in data action_type = pecan_constants.ACTION_MAP[state.request.method] if action_type == 'get': action = controller.plugin_handlers[controller.SHOW] else: action = controller.plugin_handlers[action_type] key = resource if is_single else collection to_process = [data[resource]] if is_single else data[collection] # in the single case, we enforce which raises on violation # in the plural case, we just check so violating items are hidden policy_method = policy.enforce if is_single else policy.check plugin = manager.NeutronManager.get_plugin_for_resource(collection) try: resp = [self._get_filtered_item(state.request, controller, resource, collection, item) for item in to_process if (state.request.method != 'GET' or policy_method(neutron_context, action, item, plugin=plugin, pluralized=collection))] except oslo_policy.PolicyNotAuthorized: # This exception must be explicitly caught as the exception # translation hook won't be called if an error occurs in the # 'after' handler. Instead of raising an HTTPForbidden exception, # we have to set the status_code here to prevent the catch_errors # middleware from turning this into a 500. state.response.status_code = 403 return if is_single: resp = resp[0] state.response.json = {key: resp}
def before(self, state): if state.request.method not in ('POST', 'PUT'): return resource = state.request.context.get('resource') collection = state.request.context.get('collection') neutron_context = state.request.context['neutron_context'] is_create = state.request.method == 'POST' if not resource: return if not state.request.body: return try: json_data = jsonutils.loads(state.request.body) if not isinstance(json_data, dict): raise ValueError() except ValueError: msg = _("Body contains invalid data") raise webob.exc.HTTPBadRequest(msg) # Raw data are consumed by member actions such as add_router_interface state.request.context['request_data'] = json_data if not (resource in json_data or collection in json_data): # there is no resource in the request. This can happen when a # member action is being processed or on agent scheduler operations return # Prepare data to be passed to the plugin from request body controller = utils.get_controller(state) try: data = v2_base.Controller.prepare_request_body( neutron_context, json_data, is_create, resource, controller.resource_info, allow_bulk=is_create) except Exception as e: LOG.warning( "An exception happened while processing the request " "body. The exception message is [%s].", e) raise e if collection in data: state.request.context['resources'] = [ item[resource] for item in data[collection] ] state.request.context['is_bulk'] = True else: state.request.context['resources'] = [data[resource]] state.request.context['is_bulk'] = False
def after(self, state): neutron_context = state.request.context.get('neutron_context') resource = state.request.context.get('resource') collection = state.request.context.get('collection') controller = utils.get_controller(state) if not resource: # can't filter a resource we don't recognize return # NOTE(kevinbenton): extension listing isn't controlled by policy if resource == 'extension': return try: data = state.response.json except ValueError: return if state.request.method not in pecan_constants.ACTION_MAP: return action = '%s_%s' % (pecan_constants.ACTION_MAP[state.request.method], resource) if not data or (resource not in data and collection not in data): return is_single = resource in data key = resource if is_single else collection to_process = [data[resource]] if is_single else data[collection] # in the single case, we enforce which raises on violation # in the plural case, we just check so violating items are hidden policy_method = policy.enforce if is_single else policy.check plugin = manager.NeutronManager.get_plugin_for_resource(resource) try: resp = [ self._get_filtered_item(state.request, controller, resource, collection, item) for item in to_process if (state.request.method != 'GET' or policy_method(neutron_context, action, item, plugin=plugin, pluralized=collection)) ] except oslo_policy.PolicyNotAuthorized as e: # This exception must be explicitly caught as the exception # translation hook won't be called if an error occurs in the # 'after' handler. raise webob.exc.HTTPForbidden(str(e)) if is_single: resp = resp[0] state.response.json = {key: resp}
def after(self, state): neutron_context = state.request.context.get('neutron_context') resource = state.request.context.get('resource') collection = state.request.context.get('collection') controller = utils.get_controller(state) if not resource: # can't filter a resource we don't recognize return # NOTE(kevinbenton): extension listing isn't controlled by policy if resource == 'extension': return try: data = state.response.json except ValueError: return if state.request.method not in pecan_constants.ACTION_MAP: return action = '%s_%s' % (pecan_constants.ACTION_MAP[state.request.method], resource) if not data or (resource not in data and collection not in data): return is_single = resource in data key = resource if is_single else collection to_process = [data[resource]] if is_single else data[collection] # in the single case, we enforce which raises on violation # in the plural case, we just check so violating items are hidden policy_method = policy.enforce if is_single else policy.check plugin = manager.NeutronManager.get_plugin_for_resource(collection) try: resp = [self._get_filtered_item(state.request, controller, resource, collection, item) for item in to_process if (state.request.method != 'GET' or policy_method(neutron_context, action, item, plugin=plugin, pluralized=collection))] except oslo_policy.PolicyNotAuthorized as e: # This exception must be explicitly caught as the exception # translation hook won't be called if an error occurs in the # 'after' handler. raise webob.exc.HTTPForbidden(str(e)) if is_single: resp = resp[0] state.response.json = {key: resp}
def before(self, state): if state.request.method not in ('POST', 'PUT'): return resource = state.request.context.get('resource') collection = state.request.context.get('collection') neutron_context = state.request.context['neutron_context'] is_create = state.request.method == 'POST' if not resource: return try: json_data = jsonutils.loads(state.request.body) except ValueError: LOG.debug("No JSON Data in %(method)s request for %(collection)s", { 'method': state.request.method, 'collections': collection }) return # Raw data are consumed by member actions such as add_router_interface state.request.context['request_data'] = json_data if not (resource in json_data or collection in json_data): # there is no resource in the request. This can happen when a # member action is being processed or on agent scheduler operations return # Prepare data to be passed to the plugin from request body controller = utils.get_controller(state) data = v2_base.Controller.prepare_request_body( neutron_context, json_data, is_create, resource, controller.resource_info, allow_bulk=is_create) if collection in data: state.request.context['resources'] = [ item[resource] for item in data[collection] ] state.request.context['is_bulk'] = True else: state.request.context['resources'] = [data[resource]] state.request.context['is_bulk'] = False
def before(self, state): state.request.context['query_params'] = {} if state.request.method != 'GET': return collection = state.request.context.get('collection') if not collection: return controller = utils.get_controller(state) combined_fields, added_fields = _set_fields(state, controller) filters = _set_filters(state, controller) query_params = {'fields': combined_fields, 'filters': filters} pagination_helper = _get_pagination_helper(state.request, controller) sorting_helper = _get_sorting_helper(state.request, controller) sorting_helper.update_args(query_params) sorting_helper.update_fields(query_params.get('fields', []), added_fields) pagination_helper.update_args(query_params) pagination_helper.update_fields(query_params.get('fields', []), added_fields) state.request.context['query_params'] = query_params
def before(self, state): if state.request.method not in ('POST', 'PUT'): return resource = state.request.context.get('resource') collection = state.request.context.get('collection') neutron_context = state.request.context['neutron_context'] is_create = state.request.method == 'POST' if not resource: return if not state.request.body: return try: json_data = jsonutils.loads(state.request.body) if not isinstance(json_data, dict): raise ValueError() except ValueError: msg = _("Body contains invalid data") raise webob.exc.HTTPBadRequest(msg) # Raw data are consumed by member actions such as add_router_interface state.request.context['request_data'] = json_data if not (resource in json_data or collection in json_data): # there is no resource in the request. This can happen when a # member action is being processed or on agent scheduler operations return # Prepare data to be passed to the plugin from request body controller = utils.get_controller(state) data = v2_base.Controller.prepare_request_body( neutron_context, json_data, is_create, resource, controller.resource_info, allow_bulk=is_create) if collection in data: state.request.context['resources'] = [item[resource] for item in data[collection]] state.request.context['is_bulk'] = True else: state.request.context['resources'] = [data[resource]] state.request.context['is_bulk'] = False
def before(self, state): if state.request.method not in ('POST', 'PUT'): return resource = state.request.context.get('resource') collection = state.request.context.get('collection') neutron_context = state.request.context['neutron_context'] is_create = state.request.method == 'POST' if not resource: return try: json_data = jsonutils.loads(state.request.body) except ValueError: LOG.debug("No JSON Data in %(method)s request for %(collection)s", {'method': state.request.method, 'collections': collection}) return # Raw data are consumed by member actions such as add_router_interface state.request.context['request_data'] = json_data if not (resource in json_data or collection in json_data): # there is no resource in the request. This can happen when a # member action is being processed or on agent scheduler operations return # Prepare data to be passed to the plugin from request body controller = utils.get_controller(state) data = v2_base.Controller.prepare_request_body( neutron_context, json_data, is_create, resource, controller.resource_info, allow_bulk=is_create) if collection in data: state.request.context['resources'] = [item[resource] for item in data[collection]] state.request.context['is_bulk'] = True else: state.request.context['resources'] = [data[resource]] state.request.context['is_bulk'] = False
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)