From 4bcf17a7b55ed028d85493c18e480525fcbfbf35 Mon Sep 17 00:00:00 2001 From: Renat Akhmerov Date: Wed, 20 Jul 2016 12:25:31 +0700 Subject: [PATCH] Move the remainder of REST resources to resources.py Change-Id: I2112c0c46cbbdad06305856635d93b68be17624e --- mistral/api/controllers/v2/cron_trigger.py | 90 ++---- mistral/api/controllers/v2/environment.py | 79 ++---- mistral/api/controllers/v2/member.py | 67 ++--- mistral/api/controllers/v2/resources.py | 267 ++++++++++++++++++ mistral/api/controllers/v2/service.py | 35 +-- mistral/api/controllers/v2/task.py | 1 + mistral/api/controllers/v2/workbook.py | 72 ++--- mistral/api/controllers/v2/workflow.py | 117 ++------ mistral/tests/unit/api/v2/test_environment.py | 4 +- 9 files changed, 384 insertions(+), 348 deletions(-) diff --git a/mistral/api/controllers/v2/cron_trigger.py b/mistral/api/controllers/v2/cron_trigger.py index df3b4ee4b..0bbccae76 100644 --- a/mistral/api/controllers/v2/cron_trigger.py +++ b/mistral/api/controllers/v2/cron_trigger.py @@ -18,7 +18,7 @@ from wsme import types as wtypes import wsmeext.pecan as wsme_pecan from mistral.api import access_control as acl -from mistral.api.controllers import resource +from mistral.api.controllers.v2 import resources from mistral.api.controllers.v2 import types from mistral import context from mistral.db.v2 import api as db_api @@ -26,78 +26,31 @@ from mistral.services import triggers from mistral.utils import rest_utils LOG = logging.getLogger(__name__) -SCOPE_TYPES = wtypes.Enum(str, 'private', 'public') - - -class CronTrigger(resource.Resource): - """CronTrigger resource.""" - - id = wtypes.text - name = wtypes.text - workflow_name = wtypes.text - workflow_id = wtypes.text - workflow_input = types.jsontype - workflow_params = types.jsontype - - scope = SCOPE_TYPES - - pattern = wtypes.text - remaining_executions = wtypes.IntegerType(minimum=1) - first_execution_time = wtypes.text - next_execution_time = wtypes.text - - created_at = wtypes.text - updated_at = wtypes.text - - @classmethod - def sample(cls): - return cls(id='123e4567-e89b-12d3-a456-426655440000', - name='my_trigger', - workflow_name='my_wf', - workflow_id='123e4567-e89b-12d3-a456-426655441111', - workflow_input={}, - workflow_params={}, - scope='private', - pattern='* * * * *', - remaining_executions=42, - created_at='1970-01-01T00:00:00.000000', - updated_at='1970-01-01T00:00:00.000000') - - -class CronTriggers(resource.ResourceList): - """A collection of cron triggers.""" - - cron_triggers = [CronTrigger] - - def __init__(self, **kwargs): - self._type = 'cron_triggers' - - super(CronTriggers, self).__init__(**kwargs) - - @classmethod - def sample(cls): - return cls(cron_triggers=[CronTrigger.sample()]) class CronTriggersController(rest.RestController): @rest_utils.wrap_wsme_controller_exception - @wsme_pecan.wsexpose(CronTrigger, wtypes.text) + @wsme_pecan.wsexpose(resources.CronTrigger, wtypes.text) def get(self, name): """Returns the named cron_trigger.""" - acl.enforce('cron_triggers:get', context.ctx()) + LOG.info('Fetch cron trigger [name=%s]' % name) db_model = db_api.get_cron_trigger(name) - return CronTrigger.from_dict(db_model.to_dict()) + return resources.CronTrigger.from_dict(db_model.to_dict()) @rest_utils.wrap_wsme_controller_exception - @wsme_pecan.wsexpose(CronTrigger, body=CronTrigger, status_code=201) + @wsme_pecan.wsexpose( + resources.CronTrigger, + body=resources.CronTrigger, + status_code=201 + ) def post(self, cron_trigger): """Creates a new cron trigger.""" - acl.enforce('cron_triggers:create', context.ctx()) + LOG.info('Create cron trigger: %s' % cron_trigger) values = cron_trigger.to_dict() @@ -113,21 +66,22 @@ class CronTriggersController(rest.RestController): workflow_id=values.get('workflow_id') ) - return CronTrigger.from_dict(db_model.to_dict()) + return resources.CronTrigger.from_dict(db_model.to_dict()) @rest_utils.wrap_wsme_controller_exception @wsme_pecan.wsexpose(None, wtypes.text, status_code=204) def delete(self, name): """Delete cron trigger.""" acl.enforce('cron_triggers:delete', context.ctx()) + LOG.info("Delete cron trigger [name=%s]" % name) db_api.delete_cron_trigger(name) - @wsme_pecan.wsexpose(CronTriggers, types.uuid, int, types.uniquelist, - types.list, types.uniquelist, wtypes.text, - wtypes.text, types.uuid, types.jsontype, - types.jsontype, SCOPE_TYPES, wtypes.text, + @wsme_pecan.wsexpose(resources.CronTriggers, types.uuid, int, + types.uniquelist, types.list, types.uniquelist, + wtypes.text, wtypes.text, types.uuid, types.jsontype, + types.jsontype, resources.SCOPE_TYPES, wtypes.text, wtypes.IntegerType(minimum=1), wtypes.text, wtypes.text, wtypes.text, wtypes.text) def get_all(self, marker=None, limit=None, sort_keys='created_at', @@ -191,13 +145,15 @@ class CronTriggersController(rest.RestController): next_execution_time=next_execution_time ) - LOG.info("Fetch cron triggers. marker=%s, limit=%s, sort_keys=%s, " - "sort_dirs=%s, filters=%s", marker, limit, sort_keys, - sort_dirs, filters) + LOG.info( + "Fetch cron triggers. marker=%s, limit=%s, sort_keys=%s, " + "sort_dirs=%s, filters=%s", + marker, limit, sort_keys, sort_dirs, filters + ) return rest_utils.get_all( - CronTriggers, - CronTrigger, + resources.CronTriggers, + resources.CronTrigger, db_api.get_cron_triggers, db_api.get_cron_trigger, resource_function=None, diff --git a/mistral/api/controllers/v2/environment.py b/mistral/api/controllers/v2/environment.py index 84d7eef77..b5c5165aa 100644 --- a/mistral/api/controllers/v2/environment.py +++ b/mistral/api/controllers/v2/environment.py @@ -13,7 +13,6 @@ # limitations under the License. import json -import uuid from oslo_log import log as logging from pecan import rest @@ -21,7 +20,7 @@ from wsme import types as wtypes import wsmeext.pecan as wsme_pecan from mistral.api import access_control as acl -from mistral.api.controllers import resource +from mistral.api.controllers.v2 import resources from mistral.api.controllers.v2 import types from mistral import context from mistral.db.v2 import api as db_api @@ -30,58 +29,13 @@ from mistral.utils import rest_utils LOG = logging.getLogger(__name__) -SCOPE_TYPES = wtypes.Enum(str, 'private', 'public') - -SAMPLE = { - 'server': 'localhost', - 'database': 'temp', - 'timeout': 600, - 'verbose': True -} - - -class Environment(resource.Resource): - """Environment resource.""" - - id = wtypes.text - name = wtypes.text - description = wtypes.text - variables = types.jsontype - scope = SCOPE_TYPES - created_at = wtypes.text - updated_at = wtypes.text - - @classmethod - def sample(cls): - return cls(id=str(uuid.uuid4()), - name='sample', - description='example environment entry', - variables=SAMPLE, - scope='private', - created_at='1970-01-01T00:00:00.000000', - updated_at='1970-01-01T00:00:00.000000') - - -class Environments(resource.ResourceList): - """A collection of Environment resources.""" - - environments = [Environment] - - def __init__(self, **kwargs): - self._type = 'environments' - - super(Environments, self).__init__(**kwargs) - - @classmethod - def sample(cls): - return cls(environments=[Environment.sample()]) class EnvironmentController(rest.RestController): - @wsme_pecan.wsexpose(Environments, types.uuid, int, types.uniquelist, - types.list, types.uniquelist, wtypes.text, - wtypes.text, types.jsontype, SCOPE_TYPES, wtypes.text, - wtypes.text) + @wsme_pecan.wsexpose(resources.Environments, types.uuid, int, + types.uniquelist, types.list, types.uniquelist, + wtypes.text, wtypes.text, types.jsontype, + resources.SCOPE_TYPES, wtypes.text, wtypes.text) def get_all(self, marker=None, limit=None, sort_keys='created_at', sort_dirs='asc', fields='', name=None, description=None, variables=None, scope=None, created_at=None, updated_at=None): @@ -131,8 +85,8 @@ class EnvironmentController(rest.RestController): sort_dirs, filters) return rest_utils.get_all( - Environments, - Environment, + resources.Environments, + resources.Environment, db_api.get_environments, db_api.get_environment, resource_function=None, @@ -145,21 +99,27 @@ class EnvironmentController(rest.RestController): ) @rest_utils.wrap_wsme_controller_exception - @wsme_pecan.wsexpose(Environment, wtypes.text) + @wsme_pecan.wsexpose(resources.Environment, wtypes.text) def get(self, name): """Return the named environment.""" acl.enforce('environments:get', context.ctx()) + LOG.info("Fetch environment [name=%s]" % name) db_model = db_api.get_environment(name) - return Environment.from_dict(db_model.to_dict()) + return resources.Environment.from_dict(db_model.to_dict()) @rest_utils.wrap_wsme_controller_exception - @wsme_pecan.wsexpose(Environment, body=Environment, status_code=201) + @wsme_pecan.wsexpose( + resources.Environment, + body=resources.Environment, + status_code=201 + ) def post(self, env): """Create a new environment.""" acl.enforce('environments:create', context.ctx()) + LOG.info("Create environment [env=%s]" % env) self._validate_environment( @@ -169,10 +129,10 @@ class EnvironmentController(rest.RestController): db_model = db_api.create_environment(env.to_dict()) - return Environment.from_dict(db_model.to_dict()) + return resources.Environment.from_dict(db_model.to_dict()) @rest_utils.wrap_wsme_controller_exception - @wsme_pecan.wsexpose(Environment, body=Environment) + @wsme_pecan.wsexpose(resources.Environment, body=resources.Environment) def put(self, env): """Update an environment.""" acl.enforce('environments:update', context.ctx()) @@ -194,13 +154,14 @@ class EnvironmentController(rest.RestController): db_model = db_api.update_environment(env.name, env.to_dict()) - return Environment.from_dict(db_model.to_dict()) + return resources.Environment.from_dict(db_model.to_dict()) @rest_utils.wrap_wsme_controller_exception @wsme_pecan.wsexpose(None, wtypes.text, status_code=204) def delete(self, name): """Delete the named environment.""" acl.enforce('environments:delete', context.ctx()) + LOG.info("Delete environment [name=%s]" % name) db_api.delete_environment(name) diff --git a/mistral/api/controllers/v2/member.py b/mistral/api/controllers/v2/member.py index d392bccf9..e586d1ad3 100644 --- a/mistral/api/controllers/v2/member.py +++ b/mistral/api/controllers/v2/member.py @@ -11,8 +11,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + import functools -import uuid from oslo_config import cfg from oslo_log import log as logging @@ -21,8 +21,7 @@ from wsme import types as wtypes import wsmeext.pecan as wsme_pecan from mistral.api import access_control as acl -from mistral.api.controllers import resource -from mistral.api.controllers.v2 import types +from mistral.api.controllers.v2 import resources from mistral import context from mistral.db.v2 import api as db_api from mistral import exceptions as exc @@ -33,38 +32,6 @@ LOG = logging.getLogger(__name__) CONF = cfg.CONF -class Member(resource.Resource): - id = types.uuid - resource_id = wtypes.text - resource_type = wtypes.text - project_id = wtypes.text - member_id = wtypes.text - status = wtypes.Enum(str, 'pending', 'accepted', 'rejected') - created_at = wtypes.text - updated_at = wtypes.text - - @classmethod - def sample(cls): - return cls( - id=str(uuid.uuid4()), - resource_id=str(uuid.uuid4()), - resource_type='workflow', - project_id='40a908dbddfe48ad80a87fb30fa70a03', - member_id='a7eb669e9819420ea4bd1453e672c0a7', - status='accepted', - created_at='1970-01-01T00:00:00.000000', - updated_at='1970-01-01T00:00:00.000000' - ) - - -class Members(resource.ResourceList): - members = [Member] - - @classmethod - def sample(cls): - return cls(members=[Member.sample()]) - - def auth_enable_check(func): @functools.wraps(func) def wrapped(*args, **kwargs): @@ -86,10 +53,11 @@ class MembersController(rest.RestController): @rest_utils.wrap_pecan_controller_exception @auth_enable_check - @wsme_pecan.wsexpose(Member, wtypes.text) + @wsme_pecan.wsexpose(resources.Member, wtypes.text) def get(self, member_id): """Shows resource member details.""" acl.enforce('members:get', context.ctx()) + LOG.info( "Fetch resource member [resource_id=%s, resource_type=%s, " "member_id=%s].", @@ -104,14 +72,15 @@ class MembersController(rest.RestController): member_id ).to_dict() - return Member.from_dict(member_dict) + return resources.Member.from_dict(member_dict) @rest_utils.wrap_pecan_controller_exception @auth_enable_check - @wsme_pecan.wsexpose(Members) + @wsme_pecan.wsexpose(resources.Members) def get_all(self): """Return all members with whom the resource has been shared.""" acl.enforce('members:list', context.ctx()) + LOG.info( "Fetch resource members [resource_id=%s, resource_type=%s].", self.resource_id, @@ -122,16 +91,24 @@ class MembersController(rest.RestController): self.resource_id, self.type ) - members = [Member.from_dict(member.to_dict()) for member in db_members] + members = [ + resources.Member.from_dict(member.to_dict()) + for member in db_members + ] - return Members(members=members) + return resources.Members(members=members) @rest_utils.wrap_pecan_controller_exception @auth_enable_check - @wsme_pecan.wsexpose(Member, body=Member, status_code=201) + @wsme_pecan.wsexpose( + resources.Member, + body=resources.Member, + status_code=201 + ) def post(self, member_info): """Shares the resource to a new member.""" acl.enforce('members:create', context.ctx()) + LOG.info( "Share resource to a member. [resource_id=%s, " "resource_type=%s, member_info=%s].", @@ -159,14 +136,15 @@ class MembersController(rest.RestController): db_member = db_api.create_resource_member(resource_member) - return Member.from_dict(db_member.to_dict()) + return resources.Member.from_dict(db_member.to_dict()) @rest_utils.wrap_pecan_controller_exception @auth_enable_check - @wsme_pecan.wsexpose(Member, wtypes.text, body=Member) + @wsme_pecan.wsexpose(resources.Member, wtypes.text, body=resources.Member) def put(self, member_id, member_info): """Sets the status for a resource member.""" acl.enforce('members:update', context.ctx()) + LOG.info( "Update resource member status. [resource_id=%s, " "member_id=%s, member_info=%s].", @@ -186,7 +164,7 @@ class MembersController(rest.RestController): {'status': member_info.status} ) - return Member.from_dict(db_member.to_dict()) + return resources.Member.from_dict(db_member.to_dict()) @rest_utils.wrap_pecan_controller_exception @auth_enable_check @@ -194,6 +172,7 @@ class MembersController(rest.RestController): def delete(self, member_id): """Deletes a member from the member list of a resource.""" acl.enforce('members:delete', context.ctx()) + LOG.info( "Delete resource member. [resource_id=%s, " "resource_type=%s, member_id=%s].", diff --git a/mistral/api/controllers/v2/resources.py b/mistral/api/controllers/v2/resources.py index ec3852840..a26f322a7 100644 --- a/mistral/api/controllers/v2/resources.py +++ b/mistral/api/controllers/v2/resources.py @@ -22,6 +22,123 @@ from mistral.workflow import states SCOPE_TYPES = wtypes.Enum(str, 'private', 'public') +class Workbook(resource.Resource): + """Workbook resource.""" + + id = wtypes.text + name = wtypes.text + + definition = wtypes.text + "workbook definition in Mistral v2 DSL" + tags = [wtypes.text] + scope = SCOPE_TYPES + "'private' or 'public'" + + created_at = wtypes.text + updated_at = wtypes.text + + @classmethod + def sample(cls): + return cls(id='123e4567-e89b-12d3-a456-426655440000', + name='book', + definition='HERE GOES' + 'WORKBOOK DEFINITION IN MISTRAL DSL v2', + tags=['large', 'expensive'], + scope='private', + created_at='1970-01-01T00:00:00.000000', + updated_at='1970-01-01T00:00:00.000000') + + +class Workbooks(resource.ResourceList): + """A collection of Workbooks.""" + + workbooks = [Workbook] + + def __init__(self, **kwargs): + self._type = 'workbooks' + + super(Workbooks, self).__init__(**kwargs) + + @classmethod + def sample(cls): + return cls(workbooks=[Workbook.sample()]) + + +class Workflow(resource.Resource): + """Workflow resource.""" + + id = wtypes.text + name = wtypes.text + input = wtypes.text + + definition = wtypes.text + "Workflow definition in Mistral v2 DSL" + tags = [wtypes.text] + scope = SCOPE_TYPES + "'private' or 'public'" + project_id = wtypes.text + + created_at = wtypes.text + updated_at = wtypes.text + + @classmethod + def sample(cls): + return cls(id='123e4567-e89b-12d3-a456-426655440000', + name='flow', + input='param1, param2', + definition='HERE GOES' + 'WORKFLOW DEFINITION IN MISTRAL DSL v2', + tags=['large', 'expensive'], + scope='private', + project_id='a7eb669e9819420ea4bd1453e672c0a7', + created_at='1970-01-01T00:00:00.000000', + updated_at='1970-01-01T00:00:00.000000') + + @classmethod + def from_dict(cls, d): + e = cls() + input_list = [] + + for key, val in d.items(): + if hasattr(e, key): + setattr(e, key, val) + + if 'spec' in d: + input = d.get('spec', {}).get('input', []) + for param in input: + if isinstance(param, dict): + for k, v in param.items(): + input_list.append("%s=%s" % (k, v)) + else: + input_list.append(param) + + setattr(e, 'input', ", ".join(input_list) if input_list else '') + + return e + + +class Workflows(resource.ResourceList): + """A collection of workflows.""" + + workflows = [Workflow] + + def __init__(self, **kwargs): + self._type = 'workflows' + + super(Workflows, self).__init__(**kwargs) + + @classmethod + def sample(cls): + workflows_sample = cls() + workflows_sample.workflows = [Workflow.sample()] + workflows_sample.next = ("http://localhost:8989/v2/workflows?" + "sort_keys=id,name&" + "sort_dirs=asc,desc&limit=10&" + "marker=123e4567-e89b-12d3-a456-426655440000") + + return workflows_sample + + class Action(resource.Resource): """Action resource. @@ -273,3 +390,153 @@ class ActionExecutions(resource.ResourceList): @classmethod def sample(cls): return cls(action_executions=[ActionExecution.sample()]) + + +class CronTrigger(resource.Resource): + """CronTrigger resource.""" + + id = wtypes.text + name = wtypes.text + workflow_name = wtypes.text + workflow_id = wtypes.text + workflow_input = types.jsontype + workflow_params = types.jsontype + + scope = SCOPE_TYPES + + pattern = wtypes.text + remaining_executions = wtypes.IntegerType(minimum=1) + first_execution_time = wtypes.text + next_execution_time = wtypes.text + + created_at = wtypes.text + updated_at = wtypes.text + + @classmethod + def sample(cls): + return cls( + id='123e4567-e89b-12d3-a456-426655440000', + name='my_trigger', + workflow_name='my_wf', + workflow_id='123e4567-e89b-12d3-a456-426655441111', + workflow_input={}, + workflow_params={}, + scope='private', + pattern='* * * * *', + remaining_executions=42, + created_at='1970-01-01T00:00:00.000000', + updated_at='1970-01-01T00:00:00.000000' + ) + + +class CronTriggers(resource.ResourceList): + """A collection of cron triggers.""" + + cron_triggers = [CronTrigger] + + def __init__(self, **kwargs): + self._type = 'cron_triggers' + + super(CronTriggers, self).__init__(**kwargs) + + @classmethod + def sample(cls): + return cls(cron_triggers=[CronTrigger.sample()]) + + +class Environment(resource.Resource): + """Environment resource.""" + + id = wtypes.text + name = wtypes.text + description = wtypes.text + variables = types.jsontype + scope = SCOPE_TYPES + created_at = wtypes.text + updated_at = wtypes.text + + @classmethod + def sample(cls): + return cls( + id='123e4567-e89b-12d3-a456-426655440000', + name='sample', + description='example environment entry', + variables={ + 'server': 'localhost', + 'database': 'temp', + 'timeout': 600, + 'verbose': True + }, + scope='private', + created_at='1970-01-01T00:00:00.000000', + updated_at='1970-01-01T00:00:00.000000' + ) + + +class Environments(resource.ResourceList): + """A collection of Environment resources.""" + + environments = [Environment] + + def __init__(self, **kwargs): + self._type = 'environments' + + super(Environments, self).__init__(**kwargs) + + @classmethod + def sample(cls): + return cls(environments=[Environment.sample()]) + + +class Member(resource.Resource): + id = types.uuid + resource_id = wtypes.text + resource_type = wtypes.text + project_id = wtypes.text + member_id = wtypes.text + status = wtypes.Enum(str, 'pending', 'accepted', 'rejected') + created_at = wtypes.text + updated_at = wtypes.text + + @classmethod + def sample(cls): + return cls( + id='123e4567-e89b-12d3-a456-426655440000', + resource_id='123e4567-e89b-12d3-a456-426655440011', + resource_type='workflow', + project_id='40a908dbddfe48ad80a87fb30fa70a03', + member_id='a7eb669e9819420ea4bd1453e672c0a7', + status='accepted', + created_at='1970-01-01T00:00:00.000000', + updated_at='1970-01-01T00:00:00.000000' + ) + + +class Members(resource.ResourceList): + members = [Member] + + @classmethod + def sample(cls): + return cls(members=[Member.sample()]) + + +class Service(resource.Resource): + """Service resource.""" + + name = wtypes.text + + type = wtypes.text + + @classmethod + def sample(cls): + return cls(name='host1_1234', type='executor_group') + + +class Services(resource.Resource): + """A collection of Services.""" + + services = [Service] + + @classmethod + def sample(cls): + return cls(services=[Service.sample()]) diff --git a/mistral/api/controllers/v2/service.py b/mistral/api/controllers/v2/service.py index cb09e26b0..d20450ee7 100644 --- a/mistral/api/controllers/v2/service.py +++ b/mistral/api/controllers/v2/service.py @@ -17,11 +17,10 @@ from oslo_log import log as logging from pecan import rest import six import tooz.coordination -from wsme import types as wtypes import wsmeext.pecan as wsme_pecan from mistral.api import access_control as acl -from mistral.api.controllers import resource +from mistral.api.controllers.v2 import resources from mistral.cmd import launch from mistral import context from mistral import coordination @@ -32,35 +31,13 @@ from mistral.utils import rest_utils LOG = logging.getLogger(__name__) -class Service(resource.Resource): - """Service resource.""" - - name = wtypes.text - - type = wtypes.text - - @classmethod - def sample(cls): - return cls(name='host1_1234', type='executor_group') - - -class Services(resource.Resource): - """A collection of Services.""" - - services = [Service] - - @classmethod - def sample(cls): - return cls(services=[Service.sample()]) - - class ServicesController(rest.RestController): @rest_utils.wrap_wsme_controller_exception - @wsme_pecan.wsexpose(Services) + @wsme_pecan.wsexpose(resources.Services) def get_all(self): """Return all services.""" - acl.enforce('services:list', context.ctx()) + LOG.info("Fetch services.") if not cfg.CONF.coordination.backend_url: @@ -80,8 +57,8 @@ class ServicesController(rest.RestController): for group in service_group: members = service_coordinator.get_members(group) services_list.extend( - [Service.from_dict({'type': group, 'name': member}) - for member in members] + [resources.Service.from_dict( + {'type': group, 'name': member}) for member in members] ) except tooz.coordination.ToozError as e: # In the scenario of network interruption or manually shutdown @@ -91,4 +68,4 @@ class ServicesController(rest.RestController): % six.text_type(e) ) - return Services(services=services_list) + return resources.Services(services=services_list) diff --git a/mistral/api/controllers/v2/task.py b/mistral/api/controllers/v2/task.py index 0615b85e4..b59175034 100644 --- a/mistral/api/controllers/v2/task.py +++ b/mistral/api/controllers/v2/task.py @@ -274,6 +274,7 @@ class TasksController(rest.RestController): :param task: Task execution object. """ acl.enforce('tasks:update', context.ctx()) + LOG.info("Update task execution [id=%s, task=%s]" % (id, task)) task_ex = db_api.get_task_execution(id) diff --git a/mistral/api/controllers/v2/workbook.py b/mistral/api/controllers/v2/workbook.py index 53dfc3e08..1260304c7 100644 --- a/mistral/api/controllers/v2/workbook.py +++ b/mistral/api/controllers/v2/workbook.py @@ -21,7 +21,7 @@ from wsme import types as wtypes import wsmeext.pecan as wsme_pecan from mistral.api import access_control as acl -from mistral.api.controllers import resource +from mistral.api.controllers.v2 import resources from mistral.api.controllers.v2 import types from mistral.api.controllers.v2 import validation from mistral.api.hooks import content_type as ct_hook @@ -33,49 +33,6 @@ from mistral.workbook import parser as spec_parser LOG = logging.getLogger(__name__) -SCOPE_TYPES = wtypes.Enum(str, 'private', 'public') - - -class Workbook(resource.Resource): - """Workbook resource.""" - - id = wtypes.text - name = wtypes.text - - definition = wtypes.text - "workbook definition in Mistral v2 DSL" - tags = [wtypes.text] - scope = SCOPE_TYPES - "'private' or 'public'" - - created_at = wtypes.text - updated_at = wtypes.text - - @classmethod - def sample(cls): - return cls(id='123e4567-e89b-12d3-a456-426655440000', - name='book', - definition='HERE GOES' - 'WORKBOOK DEFINITION IN MISTRAL DSL v2', - tags=['large', 'expensive'], - scope='private', - created_at='1970-01-01T00:00:00.000000', - updated_at='1970-01-01T00:00:00.000000') - - -class Workbooks(resource.ResourceList): - """A collection of Workbooks.""" - - workbooks = [Workbook] - - def __init__(self, **kwargs): - self._type = 'workbooks' - - super(Workbooks, self).__init__(**kwargs) - - @classmethod - def sample(cls): - return cls(workbooks=[Workbook.sample()]) class WorkbooksController(rest.RestController, hooks.HookController): @@ -85,54 +42,61 @@ class WorkbooksController(rest.RestController, hooks.HookController): spec_parser.get_workbook_spec_from_yaml) @rest_utils.wrap_wsme_controller_exception - @wsme_pecan.wsexpose(Workbook, wtypes.text) + @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 Workbook.from_dict(db_model.to_dict()) + 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 Workbook.from_dict(wb_db.to_dict()).to_json() + 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 Workbook.from_dict(wb_db.to_dict()).to_json() + 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(Workbooks, types.uuid, int, types.uniquelist, - types.list, types.uniquelist, wtypes.text, - wtypes.text, wtypes.text, SCOPE_TYPES, wtypes.text, - types.uniquelist, wtypes.text) + @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, @@ -190,8 +154,8 @@ class WorkbooksController(rest.RestController, hooks.HookController): sort_keys, sort_dirs, fields, filters) return rest_utils.get_all( - Workbooks, - Workbook, + resources.Workbooks, + resources.Workbook, db_api.get_workbooks, db_api.get_workbook, resource_function=None, diff --git a/mistral/api/controllers/v2/workflow.py b/mistral/api/controllers/v2/workflow.py index 9b8281cbf..0ca4990f4 100644 --- a/mistral/api/controllers/v2/workflow.py +++ b/mistral/api/controllers/v2/workflow.py @@ -23,8 +23,8 @@ from wsme import types as wtypes import wsmeext.pecan as wsme_pecan from mistral.api import access_control as acl -from mistral.api.controllers import resource from mistral.api.controllers.v2 import member +from mistral.api.controllers.v2 import resources from mistral.api.controllers.v2 import types from mistral.api.controllers.v2 import validation from mistral.api.hooks import content_type as ct_hook @@ -37,82 +37,6 @@ from mistral.workbook import parser as spec_parser LOG = logging.getLogger(__name__) -SCOPE_TYPES = wtypes.Enum(str, 'private', 'public') - - -class Workflow(resource.Resource): - """Workflow resource.""" - - id = wtypes.text - name = wtypes.text - input = wtypes.text - - definition = wtypes.text - "Workflow definition in Mistral v2 DSL" - tags = [wtypes.text] - scope = SCOPE_TYPES - "'private' or 'public'" - project_id = wtypes.text - - created_at = wtypes.text - updated_at = wtypes.text - - @classmethod - def sample(cls): - return cls(id='123e4567-e89b-12d3-a456-426655440000', - name='flow', - input='param1, param2', - definition='HERE GOES' - 'WORKFLOW DEFINITION IN MISTRAL DSL v2', - tags=['large', 'expensive'], - scope='private', - project_id='a7eb669e9819420ea4bd1453e672c0a7', - created_at='1970-01-01T00:00:00.000000', - updated_at='1970-01-01T00:00:00.000000') - - @classmethod - def from_dict(cls, d): - e = cls() - input_list = [] - - for key, val in d.items(): - if hasattr(e, key): - setattr(e, key, val) - - if 'spec' in d: - input = d.get('spec', {}).get('input', []) - for param in input: - if isinstance(param, dict): - for k, v in param.items(): - input_list.append("%s=%s" % (k, v)) - else: - input_list.append(param) - - setattr(e, 'input', ", ".join(input_list) if input_list else '') - - return e - - -class Workflows(resource.ResourceList): - """A collection of workflows.""" - - workflows = [Workflow] - - def __init__(self, **kwargs): - self._type = 'workflows' - - super(Workflows, self).__init__(**kwargs) - - @classmethod - def sample(cls): - workflows_sample = cls() - workflows_sample.workflows = [Workflow.sample()] - workflows_sample.next = ("http://localhost:8989/v2/workflows?" - "sort_keys=id,name&" - "sort_dirs=asc,desc&limit=10&" - "marker=123e4567-e89b-12d3-a456-426655440000") - - return workflows_sample class WorkflowsController(rest.RestController, hooks.HookController): @@ -151,15 +75,16 @@ class WorkflowsController(rest.RestController, hooks.HookController): ) @rest_utils.wrap_wsme_controller_exception - @wsme_pecan.wsexpose(Workflow, wtypes.text) + @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 Workflow.from_dict(db_model.to_dict()) + return resources.Workflow.from_dict(db_model.to_dict()) @rest_utils.wrap_pecan_controller_exception @pecan.expose(content_type="text/plain") @@ -173,13 +98,14 @@ class WorkflowsController(rest.RestController, hooks.HookController): 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 SCOPE_TYPES.values: + if scope not in resources.SCOPE_TYPES.values: raise exc.InvalidModelException( "Scope must be one of the following: %s; actual: " - "%s" % (SCOPE_TYPES.values, scope) + "%s" % (resources.SCOPE_TYPES.values, scope) ) LOG.info("Update workflow(s) [definition=%s]" % definition) @@ -191,10 +117,12 @@ class WorkflowsController(rest.RestController, hooks.HookController): ) models_dicts = [db_wf.to_dict() for db_wf in db_wfs] - workflow_list = [Workflow.from_dict(wf) for wf in models_dicts] + workflow_list = [ + resources.Workflow.from_dict(wf) for wf in models_dicts + ] return (workflow_list[0].to_json() if identifier - else Workflows(workflows=workflow_list).to_json()) + else resources.Workflows(workflows=workflow_list).to_json()) @rest_utils.wrap_pecan_controller_exception @pecan.expose(content_type="text/plain") @@ -205,14 +133,15 @@ class WorkflowsController(rest.RestController, hooks.HookController): 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 SCOPE_TYPES.values: + if scope not in resources.SCOPE_TYPES.values: raise exc.InvalidModelException( "Scope must be one of the following: %s; actual: " - "%s" % (SCOPE_TYPES.values, scope) + "%s" % (resources.SCOPE_TYPES.values, scope) ) LOG.info("Create workflow(s) [definition=%s]" % definition) @@ -220,9 +149,11 @@ class WorkflowsController(rest.RestController, hooks.HookController): 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] + workflow_list = [ + resources.Workflow.from_dict(wf) for wf in models_dicts + ] - return Workflows(workflows=workflow_list).to_json() + return resources.Workflows(workflows=workflow_list).to_json() @rest_utils.wrap_wsme_controller_exception @wsme_pecan.wsexpose(None, wtypes.text, status_code=204) @@ -235,10 +166,10 @@ class WorkflowsController(rest.RestController, hooks.HookController): 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, wtypes.text, - wtypes.text, wtypes.text, wtypes.text, - types.uniquelist, SCOPE_TYPES, types.uuid, + @wsme_pecan.wsexpose(resources.Workflows, types.uuid, int, + types.uniquelist, types.list, types.uniquelist, + wtypes.text, wtypes.text, wtypes.text, wtypes.text, + types.uniquelist, resources.SCOPE_TYPES, types.uuid, wtypes.text, wtypes.text) def get_all(self, marker=None, limit=None, sort_keys='created_at', sort_dirs='asc', fields='', name=None, input=None, @@ -299,8 +230,8 @@ class WorkflowsController(rest.RestController, hooks.HookController): sort_keys, sort_dirs, fields, filters) return rest_utils.get_all( - Workflows, - Workflow, + resources.Workflows, + resources.Workflow, db_api.get_workflow_definitions, db_api.get_workflow_definition_by_id, resource_function=None, diff --git a/mistral/tests/unit/api/v2/test_environment.py b/mistral/tests/unit/api/v2/test_environment.py index 28d7d7cb9..d4b49c41d 100644 --- a/mistral/tests/unit/api/v2/test_environment.py +++ b/mistral/tests/unit/api/v2/test_environment.py @@ -20,7 +20,7 @@ import uuid import mock import six -from mistral.api.controllers.v2 import environment as api +from mistral.api.controllers.v2 import resources from mistral.db.v2 import api as db_api from mistral.db.v2.sqlalchemy import models as db from mistral import exceptions as exc @@ -142,7 +142,7 @@ class TestEnvironmentController(base.APITest): self.assertDictEqual(expected, actual) def test_resource(self): - resource = api.Environment(**copy.deepcopy(ENVIRONMENT)) + resource = resources.Environment(**copy.deepcopy(ENVIRONMENT)) self._assert_dict_equal( copy.deepcopy(ENVIRONMENT),