Пример #1
0
class WorkflowsController(rest.RestController, hooks.HookController):
    # TODO(nmakhotkin): Have a discussion with pecan/WSME folks in order
    # to have requests and response of different content types. Then
    # delete ContentTypeHook.
    __hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]

    validate = validation.SpecValidationController(
        spec_parser.get_workflow_list_spec_from_yaml)

    @pecan.expose()
    def _lookup(self, identifier, sub_resource, *remainder):
        LOG.info(
            "Lookup subcontrollers of WorkflowsController, "
            "sub_resource: %s, remainder: %s.", sub_resource, remainder)

        if sub_resource == 'members':
            if not uuidutils.is_uuid_like(identifier):
                raise exc.WorkflowException(
                    "Only support UUID as resource identifier in resource "
                    "sharing feature.")

            # We don't check workflow's existence here, since a user may query
            # members of a workflow, which doesn't belong to him/her.
            return member.MembersController('workflow', identifier), remainder

        return super(WorkflowsController,
                     self)._lookup(identifier, sub_resource, *remainder)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.Workflow, wtypes.text)
    def get(self, identifier):
        """Return the named workflow."""
        acl.enforce('workflows:get', context.ctx())

        LOG.info("Fetch workflow [identifier=%s]", identifier)

        db_model = db_api.get_workflow_definition(identifier)

        return resources.Workflow.from_dict(db_model.to_dict())

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def put(self, identifier=None):
        """Update one or more workflows.

        :param identifier: Optional. If provided, it's UUID of a workflow.
            Only one workflow can be updated with identifier param.

        The text is allowed to have definitions of multiple workflows. In this
        case they all will be updated.
        """
        acl.enforce('workflows:update', context.ctx())

        definition = pecan.request.text
        scope = pecan.request.GET.get('scope', 'private')

        if scope not in resources.SCOPE_TYPES.values:
            raise exc.InvalidModelException(
                "Scope must be one of the following: %s; actual: "
                "%s" % (resources.SCOPE_TYPES.values, scope))

        LOG.info("Update workflow(s) [definition=%s]", definition)

        db_wfs = workflows.update_workflows(definition,
                                            scope=scope,
                                            identifier=identifier)

        models_dicts = [db_wf.to_dict() for db_wf in db_wfs]
        workflow_list = [
            resources.Workflow.from_dict(wf) for wf in models_dicts
        ]

        return (workflow_list[0].to_json() if identifier else
                resources.Workflows(workflows=workflow_list).to_json())

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def post(self):
        """Create a new workflow.

        NOTE: The text is allowed to have definitions
            of multiple workflows. In this case they all will be created.
        """
        acl.enforce('workflows:create', context.ctx())

        definition = pecan.request.text
        scope = pecan.request.GET.get('scope', 'private')
        pecan.response.status = 201

        if scope not in resources.SCOPE_TYPES.values:
            raise exc.InvalidModelException(
                "Scope must be one of the following: %s; actual: "
                "%s" % (resources.SCOPE_TYPES.values, scope))

        LOG.info("Create workflow(s) [definition=%s]", definition)

        db_wfs = workflows.create_workflows(definition, scope=scope)
        models_dicts = [db_wf.to_dict() for db_wf in db_wfs]

        workflow_list = [
            resources.Workflow.from_dict(wf) for wf in models_dicts
        ]

        return resources.Workflows(workflows=workflow_list).to_json()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
    def delete(self, identifier):
        """Delete a workflow."""
        acl.enforce('workflows:delete', context.ctx())
        LOG.info("Delete workflow [identifier=%s]", identifier)

        with db_api.transaction():
            db_api.delete_workflow_definition(identifier)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.Workflows, types.uuid, int,
                         types.uniquelist, types.list, types.uniquelist,
                         wtypes.text, wtypes.text, wtypes.text, wtypes.text,
                         resources.SCOPE_TYPES, types.uuid, wtypes.text,
                         wtypes.text, bool)
    def get_all(self,
                marker=None,
                limit=None,
                sort_keys='created_at',
                sort_dirs='asc',
                fields='',
                name=None,
                input=None,
                definition=None,
                tags=None,
                scope=None,
                project_id=None,
                created_at=None,
                updated_at=None,
                all_projects=False):
        """Return a list of workflows.

        :param marker: Optional. Pagination marker for large data sets.
        :param limit: Optional. Maximum number of resources to return in a
                      single result. Default value is None for backward
                      compatibility.
        :param sort_keys: Optional. Columns to sort results by.
                          Default: created_at.
        :param sort_dirs: Optional. Directions to sort corresponding to
                          sort_keys, "asc" or "desc" can be chosen.
                          Default: asc.
        :param fields: Optional. A specified list of fields of the resource to
                       be returned. 'id' will be included automatically in
                       fields if it's provided, since it will be used when
                       constructing 'next' link.
        :param name: Optional. Keep only resources with a specific name.
        :param input: Optional. Keep only resources with a specific input.
        :param definition: Optional. Keep only resources with a specific
                           definition.
        :param tags: Optional. Keep only resources containing specific tags.
        :param scope: Optional. Keep only resources with a specific scope.
        :param project_id: Optional. The same as the requester project_id
                           or different if the scope is public.
        :param created_at: Optional. Keep only resources created at a specific
                           time and date.
        :param updated_at: Optional. Keep only resources with specific latest
                           update time and date.
        :param all_projects: Optional. Get resources of all projects.
        """
        acl.enforce('workflows:list', context.ctx())

        if all_projects:
            acl.enforce('workflows:list:all_projects', context.ctx())

        filters = filter_utils.create_filters_from_request_params(
            created_at=created_at,
            name=name,
            scope=scope,
            tags=tags,
            updated_at=updated_at,
            input=input,
            definition=definition,
            project_id=project_id)

        LOG.info(
            "Fetch workflows. marker=%s, limit=%s, sort_keys=%s, "
            "sort_dirs=%s, fields=%s, filters=%s, all_projects=%s", marker,
            limit, sort_keys, sort_dirs, fields, filters, all_projects)

        return rest_utils.get_all(resources.Workflows,
                                  resources.Workflow,
                                  db_api.get_workflow_definitions,
                                  db_api.get_workflow_definition_by_id,
                                  marker=marker,
                                  limit=limit,
                                  sort_keys=sort_keys,
                                  sort_dirs=sort_dirs,
                                  fields=fields,
                                  all_projects=all_projects,
                                  **filters)
Пример #2
0
class WorkbooksController(rest.RestController, hooks.HookController):
    __hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]

    validate = validation.SpecValidationController(
        spec_parser.get_workbook_spec_from_yaml)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.Workbook, wtypes.text)
    def get(self, name):
        """Return the named workbook."""
        acl.enforce('workbooks:get', context.ctx())

        LOG.info("Fetch workbook [name=%s]" % name)

        db_model = db_api.get_workbook(name)

        return resources.Workbook.from_dict(db_model.to_dict())

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def put(self):
        """Update a workbook."""
        acl.enforce('workbooks:update', context.ctx())

        definition = pecan.request.text

        LOG.info("Update workbook [definition=%s]" % definition)

        wb_db = workbooks.update_workbook_v2(definition)

        return resources.Workbook.from_dict(wb_db.to_dict()).to_json()

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def post(self):
        """Create a new workbook."""
        acl.enforce('workbooks:create', context.ctx())

        definition = pecan.request.text

        LOG.info("Create workbook [definition=%s]" % definition)

        wb_db = workbooks.create_workbook_v2(definition)
        pecan.response.status = 201

        return resources.Workbook.from_dict(wb_db.to_dict()).to_json()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
    def delete(self, name):
        """Delete the named workbook."""
        acl.enforce('workbooks:delete', context.ctx())

        LOG.info("Delete workbook [name=%s]" % name)

        db_api.delete_workbook(name)

    @wsme_pecan.wsexpose(resources.Workbooks, types.uuid, int,
                         types.uniquelist, types.list, types.uniquelist,
                         wtypes.text, wtypes.text, wtypes.text,
                         resources.SCOPE_TYPES, wtypes.text, types.uniquelist,
                         wtypes.text)
    def get_all(self,
                marker=None,
                limit=None,
                sort_keys='created_at',
                sort_dirs='asc',
                fields='',
                created_at=None,
                definition=None,
                name=None,
                scope=None,
                tag=None,
                tags=None,
                updated_at=None):
        """Return a list of workbooks.

        :param marker: Optional. Pagination marker for large data sets.
        :param limit: Optional. Maximum number of resources to return in a
                      single result. Default value is None for backward
                      compatibility.
        :param sort_keys: Optional. Columns to sort results by.
                          Default: created_at.
        :param sort_dirs: Optional. Directions to sort corresponding to
                          sort_keys, "asc" or "desc" can be chosen.
                          Default: asc.
        :param fields: Optional. A specified list of fields of the resource to
                       be returned. 'id' will be included automatically in
                       fields if it's provided, since it will be used when
                       constructing 'next' link.
        :param name: Optional. Keep only resources with a specific name.
        :param definition: Optional. Keep only resources with a specific
                           definition.
        :param tag: Optional. Keep only resources with a specific tag. If it is
                    used with 'tags', it will be appended to the list of
                    matching tags.
        :param tags: Optional. Keep only resources containing specific tags.
        :param scope: Optional. Keep only resources with a specific scope.
        :param created_at: Optional. Keep only resources created at a specific
                           time and date.
        :param updated_at: Optional. Keep only resources with specific latest
                           update time and date.

        Where project_id is the same as the requester or
        project_id is different but the scope is public.
        """
        acl.enforce('workbooks:list', context.ctx())

        if tag is not None:
            if tags is None:
                tags = [tag]
            else:
                tags.append(tag)

        filters = rest_utils.filters_to_dict(created_at=created_at,
                                             definition=definition,
                                             name=name,
                                             scope=scope,
                                             tags=tags,
                                             updated_at=updated_at)

        LOG.info(
            "Fetch workbooks. marker=%s, limit=%s, sort_keys=%s, "
            "sort_dirs=%s, fields=%s, filters=%s", marker, limit, sort_keys,
            sort_dirs, fields, filters)

        return rest_utils.get_all(resources.Workbooks,
                                  resources.Workbook,
                                  db_api.get_workbooks,
                                  db_api.get_workbook,
                                  marker=marker,
                                  limit=limit,
                                  sort_keys=sort_keys,
                                  sort_dirs=sort_dirs,
                                  fields=fields,
                                  **filters)
Пример #3
0
class ActionsController(rest.RestController, hooks.HookController):
    # TODO(nmakhotkin): Have a discussion with pecan/WSME folks in order
    # to have requests and response of different content types. Then
    # delete ContentTypeHook.
    __hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]

    validate = validation.SpecValidationController(
        spec_parser.get_action_list_spec_from_yaml)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(Action, wtypes.text)
    def get(self, name):
        """Return the named action."""
        LOG.info("Fetch action [name=%s]" % name)

        db_model = db_api.get_action_definition(name)

        return Action.from_dict(db_model.to_dict())

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def put(self):
        """Update one or more actions.

        NOTE: This text is allowed to have definitions
            of multiple actions. In this case they all will be updated.
        """
        definition = pecan.request.text
        LOG.info("Update action(s) [definition=%s]" % definition)
        scope = pecan.request.GET.get('scope', 'private')

        if scope not in SCOPE_TYPES.values:
            raise exc.InvalidModelException(
                "Scope must be one of the following: %s; actual: "
                "%s" % (SCOPE_TYPES.values, scope))

        db_acts = actions.update_actions(definition, scope=scope)
        models_dicts = [db_act.to_dict() for db_act in db_acts]

        action_list = [Action.from_dict(act) for act in models_dicts]

        return Actions(actions=action_list).to_string()

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def post(self):
        """Create a new action.

        NOTE: This text is allowed to have definitions
            of multiple actions. In this case they all will be created.
        """
        definition = pecan.request.text
        scope = pecan.request.GET.get('scope', 'private')
        pecan.response.status = 201

        if scope not in SCOPE_TYPES.values:
            raise exc.InvalidModelException(
                "Scope must be one of the following: %s; actual: "
                "%s" % (SCOPE_TYPES.values, scope))

        LOG.info("Create action(s) [definition=%s]" % definition)

        db_acts = actions.create_actions(definition, scope=scope)
        models_dicts = [db_act.to_dict() for db_act in db_acts]

        action_list = [Action.from_dict(act) for act in models_dicts]

        return Actions(actions=action_list).to_string()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
    def delete(self, name):
        """Delete the named action."""
        LOG.info("Delete action [name=%s]" % name)

        with db_api.transaction():
            db_model = db_api.get_action_definition(name)

            if db_model.is_system:
                msg = "Attempt to delete a system action: %s" % name
                raise exc.DataAccessException(msg)

            db_api.delete_action_definition(name)

    @wsme_pecan.wsexpose(Actions, types.uuid, int, types.uniquelist,
                         types.list)
    def get_all(self,
                marker=None,
                limit=None,
                sort_keys='name',
                sort_dirs='asc'):
        """Return all actions.

        :param marker: Optional. Pagination marker for large data sets.
        :param limit: Optional. Maximum number of resources to return in a
                      single result. Default value is None for backward
                      compatibility.
        :param sort_keys: Optional. Columns to sort results by.
                          Default: name.
        :param sort_dirs: Optional. Directions to sort corresponding to
                          sort_keys, "asc" or "desc" can be choosed.
                          Default: asc.

        Where project_id is the same as the requester or
        project_id is different but the scope is public.
        """
        LOG.info(
            "Fetch actions. marker=%s, limit=%s, sort_keys=%s, "
            "sort_dirs=%s", marker, limit, sort_keys, sort_dirs)

        rest_utils.validate_query_params(limit, sort_keys, sort_dirs)

        marker_obj = None

        if marker:
            marker_obj = db_api.get_action_definition_by_id(marker)

        db_action_defs = db_api.get_action_definitions(limit=limit,
                                                       marker=marker_obj,
                                                       sort_keys=sort_keys,
                                                       sort_dirs=sort_dirs)

        actions_list = [
            Action.from_dict(db_model.to_dict()) for db_model in db_action_defs
        ]

        return Actions.convert_with_links(actions_list,
                                          limit,
                                          pecan.request.host_url,
                                          sort_keys=','.join(sort_keys),
                                          sort_dirs=','.join(sort_dirs))
Пример #4
0
class WorkbooksController(rest.RestController, hooks.HookController):
    __hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]

    validate = validation.SpecValidationController(
        spec_parser.get_workbook_spec_from_yaml)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.Workbook, wtypes.text)
    def get(self, name):
        """Return the named workbook.

        :param name: Name of workbook to retrieve
        """
        acl.enforce('workbooks:get', context.ctx())

        LOG.debug("Fetch workbook [name=%s]", name)

        # Use retries to prevent possible failures.
        r = rest_utils.create_db_retry_object()
        db_model = r.call(db_api.get_workbook, name)

        return resources.Workbook.from_db_model(db_model)

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def put(self):
        """Update a workbook."""
        acl.enforce('workbooks:update', context.ctx())

        definition = pecan.request.text
        scope = pecan.request.GET.get('scope', 'private')

        resources.Workbook.validate_scope(scope)

        LOG.debug("Update workbook [definition=%s]", definition)

        wb_db = rest_utils.rest_retry_on_db_error(
            workbooks.update_workbook_v2)(definition, scope=scope)

        return resources.Workbook.from_db_model(wb_db).to_json()

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def post(self):
        """Create a new workbook."""
        acl.enforce('workbooks:create', context.ctx())

        definition = pecan.request.text
        scope = pecan.request.GET.get('scope', 'private')

        resources.Workbook.validate_scope(scope)

        LOG.debug("Create workbook [definition=%s]", definition)

        wb_db = rest_utils.rest_retry_on_db_error(
            workbooks.create_workbook_v2)(definition, scope=scope)

        pecan.response.status = 201

        return resources.Workbook.from_db_model(wb_db).to_json()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
    def delete(self, name):
        """Delete the named workbook.

        :param name: Name of workbook to delete
        """
        acl.enforce('workbooks:delete', context.ctx())

        LOG.debug("Delete workbook [name=%s]", name)

        rest_utils.rest_retry_on_db_error(db_api.delete_workbook)(name)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.Workbooks, types.uuid, int,
                         types.uniquelist, types.list, types.uniquelist,
                         wtypes.text, wtypes.text, wtypes.text,
                         resources.SCOPE_TYPES, wtypes.text, wtypes.text)
    def get_all(self,
                marker=None,
                limit=None,
                sort_keys='created_at',
                sort_dirs='asc',
                fields='',
                created_at=None,
                definition=None,
                name=None,
                scope=None,
                tags=None,
                updated_at=None):
        """Return a list of workbooks.

        :param marker: Optional. Pagination marker for large data sets.
        :param limit: Optional. Maximum number of resources to return in a
                      single result. Default value is None for backward
                      compatibility.
        :param sort_keys: Optional. Columns to sort results by.
                          Default: created_at.
        :param sort_dirs: Optional. Directions to sort corresponding to
                          sort_keys, "asc" or "desc" can be chosen.
                          Default: asc.
        :param fields: Optional. A specified list of fields of the resource to
                       be returned. 'id' will be included automatically in
                       fields if it's provided, since it will be used when
                       constructing 'next' link.
        :param name: Optional. Keep only resources with a specific name.
        :param definition: Optional. Keep only resources with a specific
                           definition.
        :param tags: Optional. Keep only resources containing specific tags.
        :param scope: Optional. Keep only resources with a specific scope.
        :param created_at: Optional. Keep only resources created at a specific
                           time and date.
        :param updated_at: Optional. Keep only resources with specific latest
                           update time and date.
        """
        acl.enforce('workbooks:list', context.ctx())

        filters = filter_utils.create_filters_from_request_params(
            created_at=created_at,
            definition=definition,
            name=name,
            scope=scope,
            tags=tags,
            updated_at=updated_at)

        LOG.debug(
            "Fetch workbooks. marker=%s, limit=%s, sort_keys=%s, "
            "sort_dirs=%s, fields=%s, filters=%s", marker, limit, sort_keys,
            sort_dirs, fields, filters)

        return rest_utils.get_all(resources.Workbooks,
                                  resources.Workbook,
                                  db_api.get_workbooks,
                                  db_api.get_workbook,
                                  marker=marker,
                                  limit=limit,
                                  sort_keys=sort_keys,
                                  sort_dirs=sort_dirs,
                                  fields=fields,
                                  **filters)
Пример #5
0
class ActionsController(rest.RestController, hooks.HookController):
    # TODO(nmakhotkin): Have a discussion with pecan/WSME folks in order
    # to have requests and response of different content types. Then
    # delete ContentTypeHook.
    __hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]

    validate = validation.SpecValidationController(
        spec_parser.get_action_list_spec_from_yaml)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.Action, wtypes.text)
    def get(self, identifier):
        """Return the named action.

        :param identifier: ID or name of the Action to get.
        """

        acl.enforce('actions:get', context.ctx())

        LOG.debug("Fetch action [identifier=%s]", identifier)

        # Use retries to prevent possible failures.
        db_model = rest_utils.rest_retry_on_db_error(
            db_api.get_action_definition
        )(identifier)

        return resources.Action.from_db_model(db_model)

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def put(self, identifier=None):
        """Update one or more actions.

        :param identifier: Optional. If provided, it's UUID or name of an
            action. Only one action can be updated with identifier param.

        NOTE: This text is allowed to have definitions
            of multiple actions. In this case they all will be updated.
        """
        acl.enforce('actions:update', context.ctx())

        definition = pecan.request.text

        LOG.debug("Update action(s) [definition=%s]", definition)

        scope = pecan.request.GET.get('scope', 'private')
        resources.Action.validate_scope(scope)

        @rest_utils.rest_retry_on_db_error
        def _update_actions():
            with db_api.transaction():
                return actions.update_actions(
                    definition,
                    scope=scope,
                    identifier=identifier
                )

        db_acts = _update_actions()

        action_list = [
            resources.Action.from_db_model(db_act) for db_act in db_acts
        ]

        return resources.Actions(actions=action_list).to_json()

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def post(self):
        """Create a new action.

        NOTE: This text is allowed to have definitions
            of multiple actions. In this case they all will be created.
        """
        acl.enforce('actions:create', context.ctx())

        definition = pecan.request.text
        scope = pecan.request.GET.get('scope', 'private')
        pecan.response.status = 201

        resources.Action.validate_scope(scope)

        LOG.debug("Create action(s) [definition=%s]", definition)

        @rest_utils.rest_retry_on_db_error
        def _create_action_definitions():
            with db_api.transaction():
                return actions.create_actions(definition, scope=scope)

        db_acts = _create_action_definitions()

        action_list = [
            resources.Action.from_db_model(db_act) for db_act in db_acts
        ]

        return resources.Actions(actions=action_list).to_json()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
    def delete(self, identifier):
        """Delete the named action.

        :param identifier: Name or UUID of the action to delete.
        """
        acl.enforce('actions:delete', context.ctx())

        LOG.debug("Delete action [identifier=%s]", identifier)

        @rest_utils.rest_retry_on_db_error
        def _delete_action_definition():
            with db_api.transaction():
                db_model = db_api.get_action_definition(identifier)

                if db_model.is_system:
                    msg = "Attempt to delete a system action: %s" % identifier
                    raise exc.DataAccessException(msg)

                db_api.delete_action_definition(identifier)

        _delete_action_definition()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.Actions, types.uuid, int, types.uniquelist,
                         types.list, types.uniquelist, wtypes.text,
                         wtypes.text, resources.SCOPE_TYPES, wtypes.text,
                         wtypes.text, wtypes.text, wtypes.text, wtypes.text,
                         wtypes.text)
    def get_all(self, marker=None, limit=None, sort_keys='name',
                sort_dirs='asc', fields='', created_at=None, name=None,
                scope=None, tags=None, updated_at=None,
                description=None, definition=None, is_system=None, input=None):
        """Return all actions.

        :param marker: Optional. Pagination marker for large data sets.
        :param limit: Optional. Maximum number of resources to return in a
                      single result. Default value is None for backward
                      compatibility.
        :param sort_keys: Optional. Columns to sort results by.
                          Default: name.
        :param sort_dirs: Optional. Directions to sort corresponding to
                          sort_keys, "asc" or "desc" can be chosen.
                          Default: asc.
        :param fields: Optional. A specified list of fields of the resource to
                       be returned. 'id' will be included automatically in
                       fields if it's provided, since it will be used when
                       constructing 'next' link.
        :param name: Optional. Keep only resources with a specific name.
        :param scope: Optional. Keep only resources with a specific scope.
        :param definition: Optional. Keep only resources with a specific
                           definition.
        :param is_system: Optional. Keep only system actions or ad-hoc
                          actions (if False).
        :param input: Optional. Keep only resources with a specific input.
        :param description: Optional. Keep only resources with a specific
                            description.
        :param tags: Optional. Keep only resources containing specific tags.
        :param created_at: Optional. Keep only resources created at a specific
                           time and date.
        :param updated_at: Optional. Keep only resources with specific latest
                           update time and date.
        """
        acl.enforce('actions:list', context.ctx())

        filters = filter_utils.create_filters_from_request_params(
            created_at=created_at,
            name=name,
            scope=scope,
            tags=tags,
            updated_at=updated_at,
            description=description,
            definition=definition,
            is_system=is_system,
            input=input
        )

        LOG.debug("Fetch actions. marker=%s, limit=%s, sort_keys=%s, "
                  "sort_dirs=%s, filters=%s", marker, limit, sort_keys,
                  sort_dirs, filters)

        return rest_utils.get_all(
            resources.Actions,
            resources.Action,
            db_api.get_action_definitions,
            db_api.get_action_definition_by_id,
            marker=marker,
            limit=limit,
            sort_keys=sort_keys,
            sort_dirs=sort_dirs,
            fields=fields,
            **filters
        )
Пример #6
0
class WorkflowsController(rest.RestController, hooks.HookController):
    # TODO(nmakhotkin): Have a discussion with pecan/WSME folks in order
    # to have requests and response of different content types. Then
    # delete ContentTypeHook.
    __hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]

    validate = validation.SpecValidationController(
        spec_parser.get_workflow_list_spec_from_yaml)

    @pecan.expose()
    def _lookup(self, identifier, sub_resource, *remainder):
        LOG.debug(
            "Lookup subcontrollers of WorkflowsController, "
            "sub_resource: %s, remainder: %s.", sub_resource, remainder)

        if sub_resource == 'members':
            if not uuidutils.is_uuid_like(identifier):
                raise exc.WorkflowException(
                    "Only support UUID as resource identifier in resource "
                    "sharing feature.")

            # We don't check workflow's existence here, since a user may query
            # members of a workflow, which doesn't belong to him/her.
            return member.MembersController('workflow', identifier), remainder

        return super(WorkflowsController,
                     self)._lookup(identifier, sub_resource, *remainder)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.Workflow, wtypes.text, wtypes.text)
    def get(self, identifier, namespace=''):
        """Return the named workflow.

        :param identifier: Name or UUID of the workflow to retrieve.
        :param namespace: Optional. Namespace of the workflow to retrieve.
        """
        acl.enforce('workflows:get', context.ctx())

        LOG.debug("Fetch workflow [identifier=%s]", identifier)

        # Use retries to prevent possible failures.
        r = rest_utils.create_db_retry_object()
        db_model = r.call(db_api.get_workflow_definition,
                          identifier,
                          namespace=namespace)

        return resources.Workflow.from_db_model(db_model)

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def put(self, identifier=None, namespace=''):
        """Update one or more workflows.

        :param identifier: Optional. If provided, it's UUID of a workflow.
            Only one workflow can be updated with identifier param.
        :param namespace: Optional. If provided, it's the namespace of the
                          workflow/workflows. Currently, namespace cannot be
                          changed.

        The text is allowed to have definitions of multiple workflows. In such
        case, they all will be updated.
        """
        acl.enforce('workflows:update', context.ctx())

        # NOTE(rakhmerov): We can't use normal method arguments to access
        # request data because it will break dynamic sub controller lookup
        # functionality (see _lookup() above) so we have to get the data
        # directly from the request object.

        definition = pecan.request.text
        scope = pecan.request.GET.get('scope', 'private')

        # If "skip_validation" is present in the query string parameters
        # then workflow language validation will be disabled.
        skip_validation = 'skip_validation' in pecan.request.GET

        resources.Workflow.validate_scope(scope)

        if scope == 'public':
            acl.enforce('workflows:publicize', context.ctx())

        LOG.debug("Update workflow(s) [definition=%s]", definition)

        db_wfs = rest_utils.rest_retry_on_db_error(workflows.update_workflows)(
            definition,
            scope=scope,
            identifier=identifier,
            namespace=namespace,
            validate=not skip_validation)

        workflow_list = [
            resources.Workflow.from_db_model(db_wf) for db_wf in db_wfs
        ]

        return (workflow_list[0].to_json() if identifier else
                resources.Workflows(workflows=workflow_list).to_json())

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def post(self, namespace=''):
        """Create a new workflow.

        :param namespace: Optional. The namespace to create the workflow
            in. Workflows with the same name can be added to a given
            project if they are in two different namespaces.

        The text is allowed to have definitions of multiple workflows.
        In such case, they all will be created.
        """
        acl.enforce('workflows:create', context.ctx())

        # NOTE(rakhmerov): We can't use normal method arguments to access
        # request data because it will break dynamic sub controller lookup
        # functionality (see _lookup() above) so we have to get the data
        # directly from the request object.

        definition = pecan.request.text
        scope = pecan.request.GET.get('scope', 'private')

        # If "skip_validation" is present in the query string parameters
        # then workflow language validation will be disabled.
        skip_validation = 'skip_validation' in pecan.request.GET

        pecan.response.status = 201

        resources.Workflow.validate_scope(scope)

        if scope == 'public':
            acl.enforce('workflows:publicize', context.ctx())

        LOG.debug("Create workflow(s) [definition=%s]", definition)

        db_wfs = rest_utils.rest_retry_on_db_error(workflows.create_workflows)(
            definition,
            scope=scope,
            namespace=namespace,
            validate=not skip_validation)

        workflow_list = [
            resources.Workflow.from_db_model(db_wf) for db_wf in db_wfs
        ]

        return resources.Workflows(workflows=workflow_list).to_json()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(None, wtypes.text, wtypes.text, status_code=204)
    def delete(self, identifier, namespace=''):
        """Delete a workflow.

        :param identifier: Name or ID of workflow to delete.
        :param namespace: Optional. Namespace of the workflow to delete.
        """
        acl.enforce('workflows:delete', context.ctx())

        LOG.debug("Delete workflow [identifier=%s, namespace=%s]", identifier,
                  namespace)

        @rest_utils.rest_retry_on_db_error
        def _delete_workflow_definition():
            with db_api.transaction():
                db_api.delete_workflow_definition(identifier, namespace)

        _delete_workflow_definition()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.Workflows, types.uuid, int,
                         types.uniquelist, types.list, types.uniquelist,
                         wtypes.text, wtypes.text, wtypes.text, wtypes.text,
                         resources.SCOPE_TYPES, types.uuid, wtypes.text,
                         wtypes.text, bool, wtypes.text)
    def get_all(self,
                marker=None,
                limit=None,
                sort_keys='created_at',
                sort_dirs='asc',
                fields='',
                name=None,
                input=None,
                definition=None,
                tags=None,
                scope=None,
                project_id=None,
                created_at=None,
                updated_at=None,
                all_projects=False,
                namespace=None):
        """Return a list of workflows.

        :param marker: Optional. Pagination marker for large data sets.
        :param limit: Optional. Maximum number of resources to return in a
                      single result. Default value is None for backward
                      compatibility.
        :param sort_keys: Optional. Columns to sort results by.
                          Default: created_at.
        :param sort_dirs: Optional. Directions to sort corresponding to
                          sort_keys, "asc" or "desc" can be chosen.
                          Default: asc.
        :param fields: Optional. A specified list of fields of the resource to
                       be returned. 'id' will be included automatically in
                       fields if it's provided, since it will be used when
                       constructing 'next' link.
        :param name: Optional. Keep only resources with a specific name.
        :param namespace: Optional. Keep only resources with a specific
                          namespace
        :param input: Optional. Keep only resources with a specific input.
        :param definition: Optional. Keep only resources with a specific
                           definition.
        :param tags: Optional. Keep only resources containing specific tags.
        :param scope: Optional. Keep only resources with a specific scope.
        :param project_id: Optional. The same as the requester project_id
                           or different if the scope is public.
        :param created_at: Optional. Keep only resources created at a specific
                           time and date.
        :param updated_at: Optional. Keep only resources with specific latest
                           update time and date.
        :param all_projects: Optional. Get resources of all projects.
        """
        acl.enforce('workflows:list', context.ctx())

        if all_projects:
            acl.enforce('workflows:list:all_projects', context.ctx())

        filters = filter_utils.create_filters_from_request_params(
            created_at=created_at,
            name=name,
            scope=scope,
            tags=tags,
            updated_at=updated_at,
            input=input,
            definition=definition,
            project_id=project_id,
            namespace=namespace)

        LOG.debug(
            "Fetch workflows. marker=%s, limit=%s, sort_keys=%s, "
            "sort_dirs=%s, fields=%s, filters=%s, all_projects=%s", marker,
            limit, sort_keys, sort_dirs, fields, filters, all_projects)

        return rest_utils.get_all(resources.Workflows,
                                  resources.Workflow,
                                  db_api.get_workflow_definitions,
                                  db_api.get_workflow_definition_by_id,
                                  marker=marker,
                                  limit=limit,
                                  sort_keys=sort_keys,
                                  sort_dirs=sort_dirs,
                                  fields=fields,
                                  all_projects=all_projects,
                                  **filters)
Пример #7
0
class CodeSourcesController(rest.RestController, hooks.HookController):
    __hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def post(self, name, scope='private', namespace=''):
        """Creates new code sources.

        :param name: Code source name (i.e. the name of the module).
        :param scope: Optional. Scope (private or public).
        :param namespace: Optional. The namespace to create the code sources
            in.
        """

        acl.enforce('code_sources:create', context.ctx())

        # Extract content directly from the request.
        content = pecan.request.text

        LOG.debug('Creating code source [names=%s, scope=%s, namespace=%s]',
                  name, scope, namespace)

        db_model = rest_utils.rest_retry_on_db_error(
            db_api.create_code_source)({
                'name': name,
                'content': content,
                'namespace': namespace,
                'scope': scope,
                'version': 1,
            })

        pecan.response.status = 201

        return resources.CodeSource.from_db_model(db_model).to_json()

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def put(self, identifier, scope='private', namespace=''):
        """Update code source.

        :param identifier: Identifier (name or ID) of the code source.
        :param scope: Scope (private or public) of the code source.
        :param namespace: Optional. The namespace of the code source.
        """
        acl.enforce('code_sources:update', context.ctx())

        LOG.debug(
            'Updating code source [identifier(name or id)=%s, scope=%s,'
            ' namespace=%s]', identifier, scope, namespace)

        content = pecan.request.text

        db_model = rest_utils.rest_retry_on_db_error(
            db_api.update_code_source)(identifier=identifier,
                                       namespace=namespace,
                                       values={
                                           'scope': scope,
                                           'content': content
                                       })

        return resources.CodeSource.from_db_model(db_model).to_json()

    @wsme_pecan.wsexpose(resources.CodeSources, types.uuid, int,
                         types.uniquelist, types.list, types.uniquelist,
                         wtypes.text, wtypes.text, resources.SCOPE_TYPES,
                         types.uuid, wtypes.text, wtypes.text, bool,
                         wtypes.text)
    def get_all(self,
                marker=None,
                limit=None,
                sort_keys='created_at',
                sort_dirs='asc',
                fields='',
                name=None,
                tags=None,
                scope=None,
                project_id=None,
                created_at=None,
                updated_at=None,
                all_projects=False,
                namespace=None):
        """Return a list of Code Sources.

        :param marker: Optional. Pagination marker for large data sets.
        :param limit: Optional. Maximum number of resources to return in a
                      single result. Default value is None for backward
                      compatibility.
        :param sort_keys: Optional. Columns to sort results by.
                          Default: created_at.
        :param sort_dirs: Optional. Directions to sort corresponding to
                          sort_keys, "asc" or "desc" can be chosen.
                          Default: asc.
        :param fields: Optional. A specified list of fields of the resource to
                       be returned. 'id' will be included automatically in
                       fields if it's provided, since it will be used when
                       constructing 'next' link.
        :param name: Optional. Keep only resources with a specific name.
        :param namespace: Optional. Keep only resources with a specific
                          namespace
        :param tags: Optional. Keep only resources containing specific tags.
        :param scope: Optional. Keep only resources with a specific scope.
        :param project_id: Optional. The same as the requester project_id
                           or different if the scope is public.
        :param created_at: Optional. Keep only resources created at a specific
                           time and date.
        :param updated_at: Optional. Keep only resources with specific latest
                           update time and date.
        :param all_projects: Optional. Get resources of all projects.
        """

        acl.enforce('code_sources:list', context.ctx())

        filters = filter_utils.create_filters_from_request_params(
            created_at=created_at,
            name=name,
            scope=scope,
            tags=tags,
            updated_at=updated_at,
            project_id=project_id,
            namespace=namespace)

        LOG.debug(
            "Fetch code sources. marker=%s, limit=%s, sort_keys=%s, "
            "sort_dirs=%s, fields=%s, filters=%s, all_projects=%s", marker,
            limit, sort_keys, sort_dirs, fields, filters, all_projects)

        return rest_utils.get_all(resources.CodeSources,
                                  resources.CodeSource,
                                  db_api.get_code_sources,
                                  db_api.get_code_source,
                                  marker=marker,
                                  limit=limit,
                                  sort_keys=sort_keys,
                                  sort_dirs=sort_dirs,
                                  fields=fields,
                                  all_projects=all_projects,
                                  **filters)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.CodeSource, wtypes.text, wtypes.text)
    def get(self, identifier, namespace=''):
        """Return a code source.

        :param identifier: Name or UUID of the code source to retrieve.
        :param namespace: Optional. Namespace of the code source to retrieve.
        """

        acl.enforce('code_sources:get', context.ctx())

        LOG.debug('Fetch code source [identifier=%s, namespace=%s]',
                  identifier, namespace)

        db_model = rest_utils.rest_retry_on_db_error(db_api.get_code_source)(
            identifier=identifier, namespace=namespace)

        return resources.CodeSource.from_db_model(db_model)

    @rest_utils.wrap_pecan_controller_exception
    @wsme_pecan.wsexpose(None, wtypes.text, wtypes.text, status_code=204)
    def delete(self, identifier, namespace=''):
        """Delete a code source.

        :param identifier: Name or ID of Code Source to delete.
        :param namespace: Optional. Namespace of the Code Source to delete.
        """

        acl.enforce('code_sources:delete', context.ctx())

        LOG.debug('Delete code source [identifier=%s, namespace=%s]',
                  identifier, namespace)

        rest_utils.rest_retry_on_db_error(db_api.delete_code_source)(
            identifier=identifier, namespace=namespace)
Пример #8
0
class ActionsController(rest.RestController, hooks.HookController):
    # TODO(nmakhotkin): Have a discussion with pecan/WSME folks in order
    # to have requests and response of different content types. Then
    # delete ContentTypeHook.
    __hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]

    validate = validation.SpecValidationController(
        spec_parser.get_action_list_spec_from_yaml)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.Action, wtypes.text, wtypes.text)
    def get(self, identifier, namespace=''):
        """Return the named action.

        :param identifier: ID or name of the Action to get.
        :param namespace: The namespace of the action.
        """

        acl.enforce('actions:get', context.ctx())

        LOG.debug("Fetch action [identifier=%s]", identifier)

        action_provider = action_service.get_system_action_provider()

        # Here we assume that the action search might involve DB operations
        # so we need to apply the regular retrying logic as everywhere else.
        action_desc = rest_utils.rest_retry_on_db_error(action_provider.find)(
            identifier, namespace=namespace)

        if action_desc is None:
            # TODO(rakhmerov): We need to change exception class so that
            # it's not DB specific. But it should be associated with the
            # same HTTP code.
            raise exc.DBEntityNotFoundError(
                'Action not found [name=%s, namespace=%s]' %
                (identifier, namespace))

        return _action_descriptor_to_resource(action_desc)

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def put(self, identifier=None, namespace=''):
        """Update one or more actions.

        :param identifier: Optional. If provided, it's UUID or name of an
            action. Only one action can be updated with identifier param.
        :param namespace: Optional. If provided, it's the namespace that
            the action is under.

        NOTE: This text is allowed to have definitions
            of multiple actions. In this case they all will be updated.
        """
        acl.enforce('actions:update', context.ctx())

        definition = pecan.request.text

        LOG.debug("Update action(s) [definition=%s]", definition)

        namespace = namespace or ''

        scope = pecan.request.GET.get('scope', 'private')

        resources.Action.validate_scope(scope)

        if scope == 'public':
            acl.enforce('actions:publicize', context.ctx())

        @rest_utils.rest_retry_on_db_error
        def _update_actions():
            with db_api.transaction():
                return adhoc_actions.update_actions(definition,
                                                    scope=scope,
                                                    identifier=identifier,
                                                    namespace=namespace)

        db_acts = _update_actions()

        action_list = [
            resources.Action.from_db_model(db_act) for db_act in db_acts
        ]

        return resources.Actions(actions=action_list).to_json()

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def post(self, namespace=''):
        """Create a new action.

        :param namespace: Optional. The namespace to create the ad-hoc action
            in. actions with the same name can be added to a given
            project if they are in two different namespaces.
            (default namespace is '')

        NOTE: This text is allowed to have definitions
            of multiple actions. In this case they all will be created.
        """
        acl.enforce('actions:create', context.ctx())

        namespace = namespace or ''

        definition = pecan.request.text
        scope = pecan.request.GET.get('scope', 'private')
        pecan.response.status = 201

        resources.Action.validate_scope(scope)

        if scope == 'public':
            acl.enforce('actions:publicize', context.ctx())

        LOG.debug("Create action(s) [definition=%s]", definition)

        @rest_utils.rest_retry_on_db_error
        def _create_action_definitions():
            with db_api.transaction():
                return adhoc_actions.create_actions(definition,
                                                    scope=scope,
                                                    namespace=namespace)

        db_acts = _create_action_definitions()

        action_list = [
            resources.Action.from_db_model(db_act) for db_act in db_acts
        ]

        return resources.Actions(actions=action_list).to_json()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(None, wtypes.text, wtypes.text, status_code=204)
    def delete(self, identifier, namespace=''):
        """Delete the named action.

        :param identifier: Name or UUID of the action to delete.
        :param namespace: The namespace of which the action is in.
        """
        acl.enforce('actions:delete', context.ctx())

        LOG.debug("Delete action [identifier=%s]", identifier)

        @rest_utils.rest_retry_on_db_error
        def _delete_action_definition():
            with db_api.transaction():
                db_model = db_api.get_action_definition(identifier,
                                                        namespace=namespace)

                if db_model.is_system:
                    raise exc.DataAccessException(
                        "Attempt to delete a system action: %s" % identifier)

                db_api.delete_action_definition(identifier,
                                                namespace=namespace)

        _delete_action_definition()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.Actions, types.uuid, int, types.uniquelist,
                         types.list, types.uniquelist, wtypes.text,
                         wtypes.text, resources.SCOPE_TYPES, wtypes.text,
                         wtypes.text, wtypes.text, wtypes.text, wtypes.text,
                         wtypes.text, wtypes.text)
    def get_all(self,
                marker=None,
                limit=None,
                sort_keys='name',
                sort_dirs='asc',
                fields='',
                created_at=None,
                name=None,
                scope=None,
                tags=None,
                updated_at=None,
                description=None,
                definition=None,
                is_system=None,
                input=None,
                namespace=''):
        """Return all actions.

        :param marker: Optional. Pagination marker for large data sets.
        :param limit: Optional. Maximum number of resources to return in a
                      single result. Default value is None for backward
                      compatibility.
        :param sort_keys: Optional. Columns to sort results by.
                          Default: name.
        :param sort_dirs: Optional. Directions to sort corresponding to
                          sort_keys, "asc" or "desc" can be chosen.
                          Default: asc.
        :param fields: Optional. A specified list of fields of the resource to
                       be returned. 'id' will be included automatically in
                       fields if it's provided, since it will be used when
                       constructing 'next' link.
        :param name: Optional. Keep only resources with a specific name.
        :param scope: Optional. Keep only resources with a specific scope.
        :param definition: Optional. Keep only resources with a specific
                           definition.
        :param is_system: Optional. Keep only system actions or ad-hoc
                          actions (if False).
        :param input: Optional. Keep only resources with a specific input.
        :param description: Optional. Keep only resources with a specific
                            description.
        :param tags: Optional. Keep only resources containing specific tags.
        :param created_at: Optional. Keep only resources created at a specific
                           time and date.
        :param updated_at: Optional. Keep only resources with specific latest
                           update time and date.
        :param namespace: Optional. The namespace of the action.
        """
        acl.enforce('actions:list', context.ctx())

        filters = filter_utils.create_filters_from_request_params(
            created_at=created_at,
            name=name,
            scope=scope,
            tags=tags,
            updated_at=updated_at,
            description=description,
            definition=definition,
            is_system=is_system,
            input=input,
            namespace=namespace)

        LOG.debug(
            "Fetch actions. marker=%s, limit=%s, sort_keys=%s, "
            "sort_dirs=%s, filters=%s", marker, limit, sort_keys, sort_dirs,
            filters)

        sort_keys = ['name'] if sort_keys is None else sort_keys
        sort_dirs = ['asc'] if sort_dirs is None else sort_dirs
        fields = [] if fields is None else fields

        if fields and 'name' not in fields:
            fields.insert(0, 'name')

        rest_utils.validate_query_params(limit, sort_keys, sort_dirs)

        action_provider = action_service.get_system_action_provider()

        # Here we assume that the action search might involve DB operations
        # so we need to apply the regular retrying logic as everywhere else.
        action_descriptors = rest_utils.rest_retry_on_db_error(
            action_provider.find_all)(namespace=namespace,
                                      limit=limit,
                                      sort_fields=sort_keys,
                                      sort_dirs=sort_dirs,
                                      filters=filters)

        # We can't guarantee that at this point the collection of action
        # descriptors is properly filtered and sorted.

        # Apply filters.
        action_descriptors = filter(
            lambda a_d: filter_utils.match_filters(a_d, filters),
            action_descriptors)

        # Apply sorting.
        def compare_(a_d1, a_d2):
            # TODO(rakhmerov): Implement properly
            return 0

        action_descriptors = sorted(action_descriptors,
                                    key=functools.cmp_to_key(compare_))

        if limit and limit > 0:
            action_descriptors = action_descriptors[0:limit]

        action_resources = [
            _action_descriptor_to_resource(a_d) for a_d in action_descriptors
        ]

        # TODO(rakhmerov): Fix pagination so that it doesn't work with
        # the 'id' field as a marker. We can't use IDs anymore. "name"
        # seems a good candidate for this.
        return resources.Actions.convert_with_links(
            action_resources,
            limit,
            pecan.request.application_url,
            sort_keys=','.join(sort_keys),
            sort_dirs=','.join(sort_dirs),
            **filters)
Пример #9
0
class WorkflowsController(rest.RestController, hooks.HookController):
    # TODO(nmakhotkin): Have a discussion with pecan/WSME folks in order
    # to have requests and response of different content types. Then
    # delete ContentTypeHook.
    __hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]

    validate = validation.SpecValidationController(
        spec_parser.get_workflow_list_spec_from_yaml)

    @pecan.expose()
    def _lookup(self, identifier, sub_resource, *remainder):
        LOG.info(
            "Lookup subcontrollers of WorkflowsController, "
            "sub_resource: %s, remainder: %s.", sub_resource, remainder)

        if sub_resource == 'members':
            if not uuidutils.is_uuid_like(identifier):
                raise exc.WorkflowException(
                    "Only support UUID as resource identifier in resource "
                    "sharing feature.")

            # We don't check workflow's existence here, since a user may query
            # members of a workflow, which doesn't belong to him/her.
            return member.MembersController('workflow', identifier), remainder

        return super(WorkflowsController,
                     self)._lookup(identifier, sub_resource, *remainder)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(Workflow, wtypes.text)
    def get(self, identifier):
        """Return the named workflow."""
        LOG.info("Fetch workflow [identifier=%s]" % identifier)

        db_model = db_api.get_workflow_definition(identifier)

        return Workflow.from_dict(db_model.to_dict())

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def put(self, identifier=None):
        """Update one or more workflows.

        :param identifier: Optional. If provided, it's UUID of a workflow.
            Only one workflow can be updated with identifier param.

        The text is allowed to have definitions of multiple workflows. In this
        case they all will be updated.
        """
        definition = pecan.request.text
        scope = pecan.request.GET.get('scope', 'private')

        if scope not in SCOPE_TYPES.values:
            raise exc.InvalidModelException(
                "Scope must be one of the following: %s; actual: "
                "%s" % (SCOPE_TYPES.values, scope))

        LOG.info("Update workflow(s) [definition=%s]" % definition)

        db_wfs = workflows.update_workflows(definition,
                                            scope=scope,
                                            identifier=identifier)

        models_dicts = [db_wf.to_dict() for db_wf in db_wfs]
        workflow_list = [Workflow.from_dict(wf) for wf in models_dicts]

        return (workflow_list[0].to_string() if identifier else Workflows(
            workflows=workflow_list).to_string())

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def post(self):
        """Create a new workflow.

        NOTE: The text is allowed to have definitions
            of multiple workflows. In this case they all will be created.
        """
        definition = pecan.request.text
        scope = pecan.request.GET.get('scope', 'private')
        pecan.response.status = 201

        if scope not in SCOPE_TYPES.values:
            raise exc.InvalidModelException(
                "Scope must be one of the following: %s; actual: "
                "%s" % (SCOPE_TYPES.values, scope))

        LOG.info("Create workflow(s) [definition=%s]" % definition)

        db_wfs = workflows.create_workflows(definition, scope=scope)
        models_dicts = [db_wf.to_dict() for db_wf in db_wfs]

        workflow_list = [Workflow.from_dict(wf) for wf in models_dicts]

        return Workflows(workflows=workflow_list).to_string()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
    def delete(self, identifier):
        """Delete a workflow."""
        LOG.info("Delete workflow [identifier=%s]" % identifier)

        with db_api.transaction():
            db_api.delete_workflow_definition(identifier)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(Workflows, types.uuid, int, types.uniquelist,
                         types.list, types.uniquelist)
    def get_all(self,
                marker=None,
                limit=None,
                sort_keys='created_at',
                sort_dirs='asc',
                fields=''):
        """Return a list of workflows.

        :param marker: Optional. Pagination marker for large data sets.
        :param limit: Optional. Maximum number of resources to return in a
                      single result. Default value is None for backward
                      compatibility.
        :param sort_keys: Optional. Columns to sort results by.
                          Default: created_at.
        :param sort_dirs: Optional. Directions to sort corresponding to
                          sort_keys, "asc" or "desc" can be choosed.
                          Default: asc.
        :param fields: Optional. A specified list of fields of the resource to
                       be returned. 'id' will be included automatically in
                       fields if it's provided, since it will be used when
                       constructing 'next' link.

        Where project_id is the same as the requester or
        project_id is different but the scope is public.
        """
        LOG.info(
            "Fetch workflows. marker=%s, limit=%s, sort_keys=%s, "
            "sort_dirs=%s, fields=%s", marker, limit, sort_keys, sort_dirs,
            fields)

        if fields and 'id' not in fields:
            fields.insert(0, 'id')

        rest_utils.validate_query_params(limit, sort_keys, sort_dirs)
        rest_utils.validate_fields(fields, Workflow.get_fields())

        marker_obj = None

        if marker:
            marker_obj = db_api.get_workflow_definition_by_id(marker)

        db_workflows = db_api.get_workflow_definitions(limit=limit,
                                                       marker=marker_obj,
                                                       sort_keys=sort_keys,
                                                       sort_dirs=sort_dirs,
                                                       fields=fields)

        workflows_list = []

        for data in db_workflows:
            workflow_dict = (dict(zip(fields, data))
                             if fields else data.to_dict())
            workflows_list.append(Workflow.from_dict(workflow_dict))

        return Workflows.convert_with_links(
            workflows_list,
            limit,
            pecan.request.host_url,
            sort_keys=','.join(sort_keys),
            sort_dirs=','.join(sort_dirs),
            fields=','.join(fields) if fields else '')
Пример #10
0
class DynamicActionsController(rest.RestController, hooks.HookController):
    __hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]

    @rest_utils.wrap_pecan_controller_exception
    @wsme_pecan.wsexpose(resources.DynamicAction,
                         body=resources.DynamicAction,
                         status_code=201)
    def post(self, dyn_action):
        """Creates new dynamic action.

        :param dyn_action: Dynamic action to create.
        """
        acl.enforce('dynamic_actions:create', context.ctx())

        LOG.debug('Creating dynamic action [action=%s]', dyn_action)

        if not dyn_action.code_source_id and not dyn_action.code_source_name:
            raise exc.InputException(
                "Either 'code_source_id' or 'code_source_name'"
                " must be provided.")

        code_source = db_api.get_code_source(dyn_action.code_source_id
                                             or dyn_action.code_source_name,
                                             namespace=dyn_action.namespace)

        # TODO(rakhmerov): Ideally we also need to check if the specified
        # class exists in the specified code source. But probably it's not
        # a controller responsibility.

        db_model = rest_utils.rest_retry_on_db_error(
            db_api.create_dynamic_action_definition)({
                'name':
                dyn_action.name,
                'namespace':
                dyn_action.namespace,
                'class_name':
                dyn_action.class_name,
                'code_source_id':
                code_source.id,
                'code_source_name':
                code_source.name
            })

        return resources.DynamicAction.from_db_model(db_model)

    @rest_utils.wrap_pecan_controller_exception
    @wsme_pecan.wsexpose(resources.DynamicAction, body=resources.DynamicAction)
    def put(self, dyn_action):
        """Update dynamic action.

        :param dyn_action: Dynamic action to create.
        """
        acl.enforce('dynamic_actions:update', context.ctx())

        LOG.debug('Updating dynamic action [action=%s]', dyn_action)

        if not dyn_action.id and not dyn_action.name:
            raise exc.InputException("Either 'name' or 'id' must be provided.")

        values = {'class_name': dyn_action.class_name}

        if dyn_action.scope:
            values['scope'] = dyn_action.scope

        # A client may also want to update a source code.
        if dyn_action.code_source_id or dyn_action.code_source_name:
            code_source = db_api.get_code_source(
                dyn_action.code_source_id or dyn_action.code_source_name,
                namespace=dyn_action.namespace)

            values['code_source_id'] = code_source.id
            values['code_source_name'] = code_source.name

        # TODO(rakhmerov): Ideally we also need to check if the specified
        # class exists in the specified code source. But probably it's not
        # a controller responsibility.

        db_model = rest_utils.rest_retry_on_db_error(
            db_api.update_dynamic_action_definition)(
                dyn_action.id or dyn_action.name,
                values,
                namespace=dyn_action.namespace)

        return resources.DynamicAction.from_db_model(db_model)

    @wsme_pecan.wsexpose(resources.DynamicActions, types.uuid, int,
                         types.uniquelist, types.list, types.uniquelist,
                         wtypes.text, wtypes.text, resources.SCOPE_TYPES,
                         types.uuid, wtypes.text, wtypes.text, bool,
                         wtypes.text)
    def get_all(self,
                marker=None,
                limit=None,
                sort_keys='created_at',
                sort_dirs='asc',
                fields='',
                name=None,
                tags=None,
                scope=None,
                project_id=None,
                created_at=None,
                updated_at=None,
                all_projects=False,
                namespace=None):
        """Return a list of Actions.

        :param marker: Optional. Pagination marker for large data sets.
        :param limit: Optional. Maximum number of resources to return in a
                      single result. Default value is None for backward
                      compatibility.
        :param sort_keys: Optional. Columns to sort results by.
                          Default: created_at.
        :param sort_dirs: Optional. Directions to sort corresponding to
                          sort_keys, "asc" or "desc" can be chosen.
                          Default: asc.
        :param fields: Optional. A specified list of fields of the resource to
                       be returned. 'id' will be included automatically in
                       fields if it's provided, since it will be used when
                       constructing 'next' link.
        :param name: Optional. Keep only resources with a specific name.
        :param namespace: Optional. Keep only resources with a specific
                          namespace
        :param input: Optional. Keep only resources with a specific input.
        :param definition: Optional. Keep only resources with a specific
                           definition.
        :param tags: Optional. Keep only resources containing specific tags.
        :param scope: Optional. Keep only resources with a specific scope.
        :param project_id: Optional. The same as the requester project_id
                           or different if the scope is public.
        :param created_at: Optional. Keep only resources created at a specific
                           time and date.
        :param updated_at: Optional. Keep only resources with specific latest
                           update time and date.
        :param all_projects: Optional. Get resources of all projects.
        """

        acl.enforce('dynamic_actions:list', context.ctx())

        filters = filter_utils.create_filters_from_request_params(
            created_at=created_at,
            name=name,
            scope=scope,
            tags=tags,
            updated_at=updated_at,
            project_id=project_id,
            namespace=namespace)

        LOG.debug(
            "Fetch dynamic actions. marker=%s, limit=%s, sort_keys=%s, "
            "sort_dirs=%s, fields=%s, filters=%s, all_projects=%s", marker,
            limit, sort_keys, sort_dirs, fields, filters, all_projects)

        return rest_utils.get_all(resources.DynamicActions,
                                  resources.DynamicAction,
                                  db_api.get_dynamic_action_definitions,
                                  db_api.get_dynamic_action_definition,
                                  marker=marker,
                                  limit=limit,
                                  sort_keys=sort_keys,
                                  sort_dirs=sort_dirs,
                                  fields=fields,
                                  all_projects=all_projects,
                                  **filters)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(resources.DynamicAction, wtypes.text, wtypes.text)
    def get(self, identifier, namespace=''):
        """Return the named action.

        :param identifier: Name or UUID of the action to retrieve.
        :param namespace: Optional. Namespace of the action to retrieve.
        """
        acl.enforce('dynamic_actions:get', context.ctx())

        LOG.debug('Fetch dynamic action [identifier=%s, namespace=%s]',
                  identifier, namespace)

        db_model = rest_utils.rest_retry_on_db_error(
            db_api.get_dynamic_action_definition)(identifier=identifier,
                                                  namespace=namespace)

        return resources.DynamicAction.from_db_model(db_model)

    @rest_utils.wrap_pecan_controller_exception
    @wsme_pecan.wsexpose(None, wtypes.text, wtypes.text, status_code=204)
    def delete(self, identifier, namespace=''):
        """Delete a dynamic action.

        :param identifier: Name or ID of the action to delete.
        :param namespace: Optional. Namespace of the action to delete.
        """

        acl.enforce('dynamic_actions:delete', context.ctx())

        LOG.debug('Delete dynamic action [identifier=%s, namespace=%s]',
                  identifier, namespace)

        rest_utils.rest_retry_on_db_error(
            db_api.delete_dynamic_action_definition)(identifier=identifier,
                                                     namespace=namespace)
Пример #11
0
class WorkbooksController(rest.RestController, hooks.HookController):
    __hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]

    validate = validation.SpecValidationController(
        spec_parser.get_workbook_spec_from_yaml)

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(Workbook, wtypes.text)
    def get(self, name):
        """Return the named workbook."""
        LOG.info("Fetch workbook [name=%s]" % name)

        db_model = db_api.get_workbook(name)

        return Workbook.from_dict(db_model.to_dict())

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def put(self):
        """Update a workbook."""
        definition = pecan.request.text
        LOG.info("Update workbook [definition=%s]" % definition)

        wb_db = workbooks.update_workbook_v2(definition)

        return Workbook.from_dict(wb_db.to_dict()).to_string()

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def post(self):
        """Create a new workbook."""
        definition = pecan.request.text
        LOG.info("Create workbook [definition=%s]" % definition)

        wb_db = workbooks.create_workbook_v2(definition)
        pecan.response.status = 201

        return Workbook.from_dict(wb_db.to_dict()).to_string()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
    def delete(self, name):
        """Delete the named workbook."""
        LOG.info("Delete workbook [name=%s]" % name)

        db_api.delete_workbook(name)

    @wsme_pecan.wsexpose(Workbooks)
    def get_all(self):
        """Return all workbooks.

        Where project_id is the same as the requestor or
        project_id is different but the scope is public.
        """
        LOG.info("Fetch workbooks.")

        workbooks_list = [
            Workbook.from_dict(db_model.to_dict())
            for db_model in db_api.get_workbooks()
        ]

        return Workbooks(workbooks=workbooks_list)
Пример #12
0
class ActionsController(rest.RestController, hooks.HookController):
    # TODO(nmakhotkin): Have a discussion with pecan/WSME folks in order
    # to have requests and response of different content types. Then
    # delete ContentTypeHook.
    __hooks__ = [ct_hook.ContentTypeHook("application/json", ['POST', 'PUT'])]

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(Action, wtypes.text)
    def get(self, name):
        """Return the named action."""
        LOG.info("Fetch action [name=%s]" % name)

        db_model = db_api.get_action_definition(name)

        return Action.from_dict(db_model.to_dict())

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def put(self):
        """Update one or more actions.

        NOTE: This text is allowed to have definitions
            of multiple actions. In this case they all will be updated.
        """
        definition = pecan.request.text
        LOG.info("Update action(s) [definition=%s]" % definition)

        db_acts = actions.update_actions(definition)
        models_dicts = [db_act.to_dict() for db_act in db_acts]

        action_list = [Action.from_dict(act) for act in models_dicts]

        return Actions(actions=action_list).to_string()

    @rest_utils.wrap_pecan_controller_exception
    @pecan.expose(content_type="text/plain")
    def post(self):
        """Create a new action.

        NOTE: This text is allowed to have definitions
            of multiple actions. In this case they all will be created.
        """
        definition = pecan.request.text
        pecan.response.status = 201

        LOG.info("Create action(s) [definition=%s]" % definition)

        db_acts = actions.create_actions(definition)
        models_dicts = [db_act.to_dict() for db_act in db_acts]

        action_list = [Action.from_dict(act) for act in models_dicts]

        return Actions(actions=action_list).to_string()

    @rest_utils.wrap_wsme_controller_exception
    @wsme_pecan.wsexpose(None, wtypes.text, status_code=204)
    def delete(self, name):
        """Delete the named action."""
        LOG.info("Delete action [name=%s]" % name)

        with db_api.transaction():
            db_model = db_api.get_action_definition(name)

            if db_model.is_system:
                msg = "Attempt to delete a system action: %s" % name
                raise exc.DataAccessException(msg)

            db_api.delete_action_definition(name)

    @wsme_pecan.wsexpose(Actions)
    def get_all(self):
        """Return all actions.

        Where project_id is the same as the requester or
        project_id is different but the scope is public.
        """
        LOG.info("Fetch actions.")

        action_list = [
            Action.from_dict(db_model.to_dict())
            for db_model in db_api.get_action_definitions()
        ]

        return Actions(actions=action_list)