Ejemplo n.º 1
0
    def detail(self, goal=None, marker=None, limit=None,
               sort_key='id', sort_dir='asc'):
        """Retrieve a list of strategies with detail.

        :param goal: goal UUID or name to filter by.
        :param marker: pagination marker for large data sets.
        :param limit: maximum number of resources to return in a single result.
        :param sort_key: column to sort results by. Default: id.
        :param sort_dir: direction to sort. "asc" or "desc". Default: asc.
        """
        context = pecan.request.context
        policy.enforce(context, 'strategy:detail',
                       action='strategy:detail')
        # NOTE(lucasagomes): /detail should only work agaist collections
        parent = pecan.request.path.split('/')[:-1][-1]
        if parent != "strategies":
            raise exception.HTTPNotFound
        expand = True
        resource_url = '/'.join(['strategies', 'detail'])

        filters = {}
        if goal:
            if common_utils.is_uuid_like(goal):
                filters['goal_uuid'] = goal
            else:
                filters['goal_name'] = goal

        return self._get_strategies_collection(
            filters, marker, limit, sort_key, sort_dir, expand, resource_url)
Ejemplo n.º 2
0
    def detail(self, marker=None, limit=None,
               sort_key='id', sort_dir='asc', action_plan_uuid=None,
               audit_uuid=None):
        """Retrieve a list of actions with detail.

        :param marker: pagination marker for large data sets.
        :param limit: maximum number of resources to return in a single result.
        :param sort_key: column to sort results by. Default: id.
        :param sort_dir: direction to sort. "asc" or "desc". Default: asc.
        :param action_plan_uuid: Optional UUID of an action plan,
           to get only actions for that action plan.
        :param audit_uuid: Optional UUID of an audit,
           to get only actions for that audit.
        """
        context = pecan.request.context
        policy.enforce(context, 'action:detail',
                       action='action:detail')

        # NOTE(lucasagomes): /detail should only work agaist collections
        parent = pecan.request.path.split('/')[:-1][-1]
        if parent != "actions":
            raise exception.HTTPNotFound

        if action_plan_uuid and audit_uuid:
            raise exception.ActionFilterCombinationProhibited

        expand = True
        resource_url = '/'.join(['actions', 'detail'])
        return self._get_actions_collection(
            marker, limit, sort_key, sort_dir, expand, resource_url,
            action_plan_uuid=action_plan_uuid, audit_uuid=audit_uuid)
Ejemplo n.º 3
0
    def get_all(self, goal=None, strategy=None, marker=None,
                limit=None, sort_key='id', sort_dir='asc'):
        """Retrieve a list of audit templates.

        :param goal: goal UUID or name to filter by
        :param strategy: strategy UUID or name to filter by
        :param marker: pagination marker for large data sets.
        :param limit: maximum number of resources to return in a single result.
        :param sort_key: column to sort results by. Default: id.
        :param sort_dir: direction to sort. "asc" or "desc". Default: asc.
        """
        context = pecan.request.context
        policy.enforce(context, 'audit_template:get_all',
                       action='audit_template:get_all')
        filters = {}
        if goal:
            if common_utils.is_uuid_like(goal):
                filters['goal_uuid'] = goal
            else:
                filters['goal_name'] = goal

        if strategy:
            if common_utils.is_uuid_like(strategy):
                filters['strategy_uuid'] = strategy
            else:
                filters['strategy_name'] = strategy

        return self._get_audit_templates_collection(
            filters, marker, limit, sort_key, sort_dir)
Ejemplo n.º 4
0
    def delete(self, audit_uuid):
        """Delete a audit.

        :param audit_uuid: UUID of a audit.
        """
        context = pecan.request.context
        audit_to_delete = api_utils.get_resource('Audit', audit_uuid)
        policy.enforce(context, 'audit:update', audit_to_delete,
                       action='audit:update')

        audit_to_delete.soft_delete()
Ejemplo n.º 5
0
    def delete(self, action_plan_uuid):
        """Delete an action plan.

        :param action_plan_uuid: UUID of a action.
        """
        context = pecan.request.context
        action_plan = api_utils.get_resource('ActionPlan', action_plan_uuid)
        policy.enforce(context, 'action_plan:delete', action_plan,
                       action='action_plan:delete')

        action_plan.soft_delete()
Ejemplo n.º 6
0
    def post(self, audit_p):
        """Create a new audit.

        :param audit_p: a audit within the request body.
        """
        context = pecan.request.context
        policy.enforce(context, 'audit:create',
                       action='audit:create')

        audit = audit_p.as_audit()
        if self.from_audits:
            raise exception.OperationNotPermitted

        if not audit._audit_template_uuid:
            raise exception.Invalid(
                message=_('The audit template UUID or name specified is '
                          'invalid'))

        audit_template = objects.AuditTemplate.get(pecan.request.context,
                                                   audit._audit_template_uuid)
        strategy_id = audit_template.strategy_id
        no_schema = True
        if strategy_id is not None:
            # validate parameter when predefined strategy in audit template
            strategy = objects.Strategy.get(pecan.request.context, strategy_id)
            schema = strategy.parameters_spec
            if schema:
                # validate input parameter with default value feedback
                no_schema = False
                utils.DefaultValidatingDraft4Validator(schema).validate(
                    audit.parameters)

        if no_schema and audit.parameters:
            raise exception.Invalid(_('Specify parameters but no predefined '
                                      'strategy for audit template, or no '
                                      'parameter spec in predefined strategy'))

        audit_dict = audit.as_dict()
        context = pecan.request.context
        new_audit = objects.Audit(context, **audit_dict)
        new_audit.create(context)

        # Set the HTTP Location Header
        pecan.response.location = link.build_url('audits', new_audit.uuid)

        # trigger decision-engine to run the audit

        if new_audit.audit_type == objects.audit.AuditType.ONESHOT.value:
            dc_client = rpcapi.DecisionEngineAPI()
            dc_client.trigger_audit(context, new_audit.uuid)

        return Audit.convert_with_links(new_audit)
Ejemplo n.º 7
0
    def get_one(self, audit_uuid):
        """Retrieve information about the given audit.

        :param audit_uuid: UUID of a audit.
        """
        if self.from_audits:
            raise exception.OperationNotPermitted

        context = pecan.request.context
        rpc_audit = api_utils.get_resource('Audit', audit_uuid)
        policy.enforce(context, 'audit:get', rpc_audit, action='audit:get')

        return Audit.convert_with_links(rpc_audit)
Ejemplo n.º 8
0
    def delete(self, audit_template):
        """Delete a audit template.

        :param audit template_uuid: UUID or name of an audit template.
        """
        context = pecan.request.context
        audit_template_to_delete = api_utils.get_resource('AuditTemplate',
                                                          audit_template)
        policy.enforce(context, 'audit_template:update',
                       audit_template_to_delete,
                       action='audit_template:update')

        audit_template_to_delete.soft_delete()
Ejemplo n.º 9
0
    def get_one(self, action_uuid):
        """Retrieve information about the given action.

        :param action_uuid: UUID of a action.
        """
        if self.from_actions:
            raise exception.OperationNotPermitted

        context = pecan.request.context
        action = api_utils.get_resource('Action', action_uuid)
        policy.enforce(context, 'action:get', action, action='action:get')

        return Action.convert_with_links(action)
Ejemplo n.º 10
0
    def get_one(self, strategy):
        """Retrieve information about the given strategy.

        :param strategy: UUID or name of the strategy.
        """
        if self.from_strategies:
            raise exception.OperationNotPermitted

        context = pecan.request.context
        rpc_strategy = api_utils.get_resource('Strategy', strategy)
        policy.enforce(context, 'strategy:get', rpc_strategy,
                       action='strategy:get')

        return Strategy.convert_with_links(rpc_strategy)
Ejemplo n.º 11
0
    def get_one(self, audit_template):
        """Retrieve information about the given audit template.

        :param audit audit_template: UUID or name of an audit template.
        """
        if self.from_audit_templates:
            raise exception.OperationNotPermitted

        context = pecan.request.context
        rpc_audit_template = api_utils.get_resource('AuditTemplate',
                                                    audit_template)
        policy.enforce(context, 'audit_template:get', rpc_audit_template,
                       action='audit_template:get')

        return AuditTemplate.convert_with_links(rpc_audit_template)
Ejemplo n.º 12
0
    def get_all(self, marker=None, limit=None, sort_key='id',
                sort_dir='asc'):
        """Retrieve a list of Scoring Engines.

        :param marker: pagination marker for large data sets.
        :param limit: maximum number of resources to return in a single result.
        :param sort_key: column to sort results by. Default: name.
        :param sort_dir: direction to sort. "asc" or "desc". Default: asc.
        """
        context = pecan.request.context
        policy.enforce(context, 'scoring_engine:get_all',
                       action='scoring_engine:get_all')

        return self._get_scoring_engines_collection(
            marker, limit, sort_key, sort_dir)
Ejemplo n.º 13
0
    def get_one(self, scoring_engine):
        """Retrieve information about the given Scoring Engine.

        :param scoring_engine_name: The name of the Scoring Engine.
        """
        context = pecan.request.context
        policy.enforce(context, 'scoring_engine:get',
                       action='scoring_engine:get')

        if self.from_scoring_engines:
            raise exception.OperationNotPermitted

        rpc_scoring_engine = api_utils.get_resource(
            'ScoringEngine', scoring_engine)

        return ScoringEngine.convert_with_links(rpc_scoring_engine)
Ejemplo n.º 14
0
    def get_all(self, audit_template=None, marker=None, limit=None,
                sort_key='id', sort_dir='asc'):
        """Retrieve a list of audits.

        :param audit_template: Optional UUID or name of an audit
        :param marker: pagination marker for large data sets.
        :param limit: maximum number of resources to return in a single result.
        :param sort_key: column to sort results by. Default: id.
        :param sort_dir: direction to sort. "asc" or "desc". Default: asc.
         template, to get only audits for that audit template.
        """
        context = pecan.request.context
        policy.enforce(context, 'audit:get_all',
                       action='audit:get_all')
        return self._get_audits_collection(marker, limit, sort_key,
                                           sort_dir,
                                           audit_template=audit_template)
Ejemplo n.º 15
0
    def patch(self, audit_template, patch):
        """Update an existing audit template.

        :param audit template_uuid: UUID of a audit template.
        :param patch: a json PATCH document to apply to this audit template.
        """
        if self.from_audit_templates:
            raise exception.OperationNotPermitted

        context = pecan.request.context
        audit_template_to_update = api_utils.get_resource('AuditTemplate',
                                                          audit_template)
        policy.enforce(context, 'audit_template:update',
                       audit_template_to_update,
                       action='audit_template:update')

        if common_utils.is_uuid_like(audit_template):
            audit_template_to_update = objects.AuditTemplate.get_by_uuid(
                pecan.request.context,
                audit_template)
        else:
            audit_template_to_update = objects.AuditTemplate.get_by_name(
                pecan.request.context,
                audit_template)

        try:
            audit_template_dict = audit_template_to_update.as_dict()
            audit_template = AuditTemplate(**api_utils.apply_jsonpatch(
                audit_template_dict, patch))
        except api_utils.JSONPATCH_EXCEPTIONS as e:
            raise exception.PatchError(patch=patch, reason=e)

        # Update only the fields that have changed
        for field in objects.AuditTemplate.fields:
            try:
                patch_val = getattr(audit_template, field)
            except AttributeError:
                # Ignore fields that aren't exposed in the API
                continue
            if patch_val == wtypes.Unset:
                patch_val = None
            if audit_template_to_update[field] != patch_val:
                audit_template_to_update[field] = patch_val

        audit_template_to_update.save()
        return AuditTemplate.convert_with_links(audit_template_to_update)
Ejemplo n.º 16
0
    def detail(self, marker=None, limit=None, sort_key='id', sort_dir='asc'):
        """Retrieve a list of Scoring Engines with detail.

        :param marker: pagination marker for large data sets.
        :param limit: maximum number of resources to return in a single result.
        :param sort_key: column to sort results by. Default: name.
        :param sort_dir: direction to sort. "asc" or "desc". Default: asc.
        """
        context = pecan.request.context
        policy.enforce(context, 'scoring_engine:detail',
                       action='scoring_engine:detail')

        parent = pecan.request.path.split('/')[:-1][-1]
        if parent != "scoring_engines":
            raise exception.HTTPNotFound
        expand = True
        resource_url = '/'.join(['scoring_engines', 'detail'])
        return self._get_scoring_engines_collection(
            marker, limit, sort_key, sort_dir, expand, resource_url)
Ejemplo n.º 17
0
    def detail(self, goal=None, strategy=None, marker=None,
               limit=None, sort_key='id', sort_dir='asc'):
        """Retrieve a list of audit templates with detail.

        :param goal: goal UUID or name to filter by
        :param strategy: strategy UUID or name to filter by
        :param marker: pagination marker for large data sets.
        :param limit: maximum number of resources to return in a single result.
        :param sort_key: column to sort results by. Default: id.
        :param sort_dir: direction to sort. "asc" or "desc". Default: asc.
        """
        context = pecan.request.context
        policy.enforce(context, 'audit_template:detail',
                       action='audit_template:detail')

        # NOTE(lucasagomes): /detail should only work agaist collections
        parent = pecan.request.path.split('/')[:-1][-1]
        if parent != "audit_templates":
            raise exception.HTTPNotFound

        filters = {}
        if goal:
            if common_utils.is_uuid_like(goal):
                filters['goal_uuid'] = goal
            else:
                filters['goal_name'] = goal

        if strategy:
            if common_utils.is_uuid_like(strategy):
                filters['strategy_uuid'] = strategy
            else:
                filters['strategy_name'] = strategy

        expand = True
        resource_url = '/'.join(['audit_templates', 'detail'])
        return self._get_audit_templates_collection(filters, marker, limit,
                                                    sort_key, sort_dir, expand,
                                                    resource_url)
Ejemplo n.º 18
0
    def get_all(self, marker=None, limit=None,
                sort_key='id', sort_dir='asc', goal=None,
                strategy=None, host_aggregate=None):
        """Retrieve a list of audits.

        :param marker: pagination marker for large data sets.
        :param limit: maximum number of resources to return in a single result.
        :param sort_key: column to sort results by. Default: id.
        :param sort_dir: direction to sort. "asc" or "desc". Default: asc.
         id.
        :param goal: goal UUID or name to filter by
        :param strategy: strategy UUID or name to filter by
        :param host_aggregate: Optional host_aggregate
        """

        context = pecan.request.context
        policy.enforce(context, 'audit:get_all',
                       action='audit:get_all')

        return self._get_audits_collection(marker, limit, sort_key,
                                           sort_dir, goal=goal,
                                           strategy=strategy,
                                           host_aggregate=host_aggregate)
Ejemplo n.º 19
0
    def detail(self,
               marker=None,
               limit=None,
               sort_key='id',
               sort_dir='asc',
               audit_uuid=None,
               strategy=None):
        """Retrieve a list of action_plans with detail.

        :param marker: pagination marker for large data sets.
        :param limit: maximum number of resources to return in a single result.
        :param sort_key: column to sort results by. Default: id.
        :param sort_dir: direction to sort. "asc" or "desc". Default: asc.
        :param audit_uuid: Optional UUID of an audit, to get only actions
            for that audit.
        :param strategy: strategy UUID or name to filter by
        """
        context = pecan.request.context
        policy.enforce(context,
                       'action_plan:detail',
                       action='action_plan:detail')

        # NOTE(lucasagomes): /detail should only work agaist collections
        parent = pecan.request.path.split('/')[:-1][-1]
        if parent != "action_plans":
            raise exception.HTTPNotFound

        expand = True
        resource_url = '/'.join(['action_plans', 'detail'])
        return self._get_action_plans_collection(marker,
                                                 limit,
                                                 sort_key,
                                                 sort_dir,
                                                 expand,
                                                 resource_url,
                                                 audit_uuid=audit_uuid,
                                                 strategy=strategy)
Ejemplo n.º 20
0
    def post(self, audit_template_postdata):
        """Create a new audit template.

        :param audit_template_postdata: the audit template POST data
                                        from the request body.
        """
        if self.from_audit_templates:
            raise exception.OperationNotPermitted

        context = pecan.request.context
        policy.enforce(context, 'audit_template:create',
                       action='audit_template:create')

        context = pecan.request.context
        audit_template = audit_template_postdata.as_audit_template()
        audit_template_dict = audit_template.as_dict()
        new_audit_template = objects.AuditTemplate(context,
                                                   **audit_template_dict)
        new_audit_template.create(context)

        # Set the HTTP Location Header
        pecan.response.location = link.build_url('audit_templates',
                                                 new_audit_template.uuid)
        return AuditTemplate.convert_with_links(new_audit_template)
Ejemplo n.º 21
0
    def get_all(self, marker=None, limit=None,
                sort_key='id', sort_dir='asc', action_plan_uuid=None,
                audit_uuid=None):
        """Retrieve a list of actions.

        :param marker: pagination marker for large data sets.
        :param limit: maximum number of resources to return in a single result.
        :param sort_key: column to sort results by. Default: id.
        :param sort_dir: direction to sort. "asc" or "desc". Default: asc.
        :param action_plan_uuid: Optional UUID of an action plan,
           to get only actions for that action plan.
        :param audit_uuid: Optional UUID of an audit,
           to get only actions for that audit.
        """
        context = pecan.request.context
        policy.enforce(context, 'action:get_all',
                       action='action:get_all')

        if action_plan_uuid and audit_uuid:
            raise exception.ActionFilterCombinationProhibited

        return self._get_actions_collection(
            marker, limit, sort_key, sort_dir,
            action_plan_uuid=action_plan_uuid, audit_uuid=audit_uuid)
Ejemplo n.º 22
0
    def detail(self, audit_template=None, marker=None, limit=None,
               sort_key='id', sort_dir='asc'):
        """Retrieve a list of audits with detail.

        :param audit_template: Optional UUID or name of an audit
        :param marker: pagination marker for large data sets.
        :param limit: maximum number of resources to return in a single result.
        :param sort_key: column to sort results by. Default: id.
        :param sort_dir: direction to sort. "asc" or "desc". Default: asc.
        """
        context = pecan.request.context
        policy.enforce(context, 'audit:detail',
                       action='audit:detail')
        # NOTE(lucasagomes): /detail should only work agaist collections
        parent = pecan.request.path.split('/')[:-1][-1]
        if parent != "audits":
            raise exception.HTTPNotFound

        expand = True
        resource_url = '/'.join(['audits', 'detail'])
        return self._get_audits_collection(marker, limit,
                                           sort_key, sort_dir, expand,
                                           resource_url,
                                           audit_template=audit_template)
Ejemplo n.º 23
0
    def post(self, audit_template_postdata):
        """Create a new audit template.

        :param audit_template_postdata: the audit template POST data
                                        from the request body.
        """
        if self.from_audit_templates:
            raise exception.OperationNotPermitted

        context = pecan.request.context
        policy.enforce(context, 'audit_template:create',
                       action='audit_template:create')

        context = pecan.request.context
        audit_template = audit_template_postdata.as_audit_template()
        audit_template_dict = audit_template.as_dict()
        new_audit_template = objects.AuditTemplate(context,
                                                   **audit_template_dict)
        new_audit_template.create(context)

        # Set the HTTP Location Header
        pecan.response.location = link.build_url('audit_templates',
                                                 new_audit_template.uuid)
        return AuditTemplate.convert_with_links(new_audit_template)
Ejemplo n.º 24
0
    def patch(self, action_plan_uuid, patch):
        """Update an existing action plan.

        :param action_plan_uuid: UUID of a action plan.
        :param patch: a json PATCH document to apply to this action plan.
        """
        if self.from_actionsPlans:
            raise exception.OperationNotPermitted

        context = pecan.request.context
        action_plan_to_update = api_utils.get_resource('ActionPlan',
                                                       action_plan_uuid,
                                                       eager=True)
        policy.enforce(context,
                       'action_plan:update',
                       action_plan_to_update,
                       action='action_plan:update')

        try:
            action_plan_dict = action_plan_to_update.as_dict()
            action_plan = ActionPlan(
                **api_utils.apply_jsonpatch(action_plan_dict, patch))
        except api_utils.JSONPATCH_EXCEPTIONS as e:
            raise exception.PatchError(patch=patch, reason=e)

        launch_action_plan = False
        cancel_action_plan = False

        # transitions that are allowed via PATCH
        allowed_patch_transitions = [
            (ap_objects.State.RECOMMENDED, ap_objects.State.PENDING),
            (ap_objects.State.RECOMMENDED, ap_objects.State.CANCELLED),
            (ap_objects.State.ONGOING, ap_objects.State.CANCELLING),
            (ap_objects.State.PENDING, ap_objects.State.CANCELLED),
        ]

        # todo: improve this in blueprint watcher-api-validation
        if hasattr(action_plan, 'state'):
            transition = (action_plan_to_update.state, action_plan.state)
            if transition not in allowed_patch_transitions:
                error_message = _("State transition not allowed: "
                                  "(%(initial_state)s -> %(new_state)s)")
                raise exception.PatchError(
                    patch=patch,
                    reason=error_message %
                    dict(initial_state=action_plan_to_update.state,
                         new_state=action_plan.state))

            if action_plan.state == ap_objects.State.PENDING:
                launch_action_plan = True
            if action_plan.state == ap_objects.State.CANCELLED:
                cancel_action_plan = True

        # Update only the fields that have changed
        for field in objects.ActionPlan.fields:
            try:
                patch_val = getattr(action_plan, field)
            except AttributeError:
                # Ignore fields that aren't exposed in the API
                continue
            if patch_val == wtypes.Unset:
                patch_val = None
            if action_plan_to_update[field] != patch_val:
                action_plan_to_update[field] = patch_val

            if (field == 'state'
                    and patch_val == objects.action_plan.State.PENDING):
                launch_action_plan = True

        action_plan_to_update.save()

        # NOTE: if action plan is cancelled from pending or recommended
        # state update action state here only
        if cancel_action_plan:
            filters = {'action_plan_uuid': action_plan.uuid}
            actions = objects.Action.list(pecan.request.context,
                                          filters=filters,
                                          eager=True)
            for a in actions:
                a.state = objects.action.State.CANCELLED
                a.save()

        if launch_action_plan:
            self.applier_client.launch_action_plan(pecan.request.context,
                                                   action_plan.uuid)

        action_plan_to_update = objects.ActionPlan.get_by_uuid(
            pecan.request.context, action_plan_uuid)
        return ActionPlan.convert_with_links(action_plan_to_update)
Ejemplo n.º 25
0
    def patch(self, action_plan_uuid, patch):
        """Update an existing action plan.

        :param action_plan_uuid: UUID of a action plan.
        :param patch: a json PATCH document to apply to this action plan.
        """
        launch_action_plan = True
        if self.from_actionsPlans:
            raise exception.OperationNotPermitted

        context = pecan.request.context
        action_plan_to_update = api_utils.get_resource('ActionPlan',
                                                       action_plan_uuid)
        policy.enforce(context, 'action_plan:update', action_plan_to_update,
                       action='action_plan:update')

        try:
            action_plan_dict = action_plan_to_update.as_dict()
            action_plan = ActionPlan(**api_utils.apply_jsonpatch(
                action_plan_dict, patch))
        except api_utils.JSONPATCH_EXCEPTIONS as e:
            raise exception.PatchError(patch=patch, reason=e)

        launch_action_plan = False

        # transitions that are allowed via PATCH
        allowed_patch_transitions = [
            (ap_objects.State.RECOMMENDED,
             ap_objects.State.PENDING),
            (ap_objects.State.RECOMMENDED,
             ap_objects.State.CANCELLED),
            (ap_objects.State.ONGOING,
             ap_objects.State.CANCELLED),
            (ap_objects.State.PENDING,
             ap_objects.State.CANCELLED),
        ]

        # todo: improve this in blueprint watcher-api-validation
        if hasattr(action_plan, 'state'):
            transition = (action_plan_to_update.state, action_plan.state)
            if transition not in allowed_patch_transitions:
                error_message = _("State transition not allowed: "
                                  "(%(initial_state)s -> %(new_state)s)")
                raise exception.PatchError(
                    patch=patch,
                    reason=error_message % dict(
                        initial_state=action_plan_to_update.state,
                        new_state=action_plan.state))

            if action_plan.state == ap_objects.State.PENDING:
                launch_action_plan = True

        # Update only the fields that have changed
        for field in objects.ActionPlan.fields:
            try:
                patch_val = getattr(action_plan, field)
            except AttributeError:
                # Ignore fields that aren't exposed in the API
                continue
            if patch_val == wtypes.Unset:
                patch_val = None
            if action_plan_to_update[field] != patch_val:
                action_plan_to_update[field] = patch_val

            if (field == 'state'
                    and patch_val == objects.action_plan.State.PENDING):
                launch_action_plan = True

        action_plan_to_update.save()

        if launch_action_plan:
            applier_client = rpcapi.ApplierAPI()
            applier_client.launch_action_plan(pecan.request.context,
                                              action_plan.uuid)

        action_plan_to_update = objects.ActionPlan.get_by_uuid(
            pecan.request.context,
            action_plan_uuid)
        return ActionPlan.convert_with_links(action_plan_to_update)