Exemple #1
0
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)
Exemple #2
0
 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)
Exemple #4
0
    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)
Exemple #5
0
    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)
Exemple #6
0
    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)
Exemple #7
0
    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)