diff --git a/mistralclient/api/v2/client.py b/mistralclient/api/v2/client.py index 350d94cd..9fcb8b78 100644 --- a/mistralclient/api/v2/client.py +++ b/mistralclient/api/v2/client.py @@ -16,6 +16,7 @@ import six from mistralclient.api import httpclient from mistralclient.api.v2 import actions +from mistralclient.api.v2 import cron_triggers from mistralclient.api.v2 import workbooks from mistralclient.api.v2 import executions from mistralclient.api.v2 import tasks @@ -51,6 +52,7 @@ class Client(object): self.tasks = tasks.TaskManager(self) self.actions = actions.ActionManager(self) self.workflows = workflows.WorkflowManager(self) + self.cron_triggers = cron_triggers.CronTriggerManager(self) def authenticate(self, mistral_url=None, username=None, api_key=None, project_name=None, auth_url=None, project_id=None, diff --git a/mistralclient/api/v2/cron_triggers.py b/mistralclient/api/v2/cron_triggers.py new file mode 100644 index 00000000..8c63f501 --- /dev/null +++ b/mistralclient/api/v2/cron_triggers.py @@ -0,0 +1,55 @@ +# Copyright 2014 - Mirantis, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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 json +from mistralclient.api import base + + +class CronTrigger(base.Resource): + resource_name = 'CronTrigger' + + +class CronTriggerManager(base.ResourceManager): + resource_class = CronTrigger + + def create(self, name, pattern, workflow_name, workflow_input=None): + self._ensure_not_empty( + name=name, + pattern=pattern, + workflow_name=workflow_name + ) + + data = { + 'name': name, + 'pattern': pattern, + 'workflow_name': workflow_name, + } + + if workflow_input: + data.update({'workflow_input': json.dumps(workflow_input)}) + + return self._create('/cron_triggers', data) + + def list(self): + return self._list('/cron_triggers', response_key='cron_triggers') + + def get(self, name): + self._ensure_not_empty(name=name) + + return self._get('/cron_triggers/%s' % name) + + def delete(self, name): + self._ensure_not_empty(name=name) + + self._delete('/cron_triggers/%s' % name) diff --git a/mistralclient/commands/v2/cron_triggers.py b/mistralclient/commands/v2/cron_triggers.py new file mode 100644 index 00000000..1bfa500b --- /dev/null +++ b/mistralclient/commands/v2/cron_triggers.py @@ -0,0 +1,145 @@ +# Copyright 2014 - Mirantis, Inc. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, 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 json +import logging + +from cliff import command +from cliff import show + +from mistralclient.api.v2 import cron_triggers +from mistralclient.commands.v2 import base + +LOG = logging.getLogger(__name__) + + +def format_list(trigger=None): + return format(trigger, lister=True) + + +def format(trigger=None, lister=False): + columns = ( + 'Name', + 'Pattern', + 'Workflow', + 'Workflow input', + 'Next execution time', + 'Created at', + 'Updated at' + ) + + if trigger: + wf_input = trigger.workflow_input if not lister \ + else base.cut(trigger.workflow_input) + + data = ( + trigger.name, + trigger.pattern, + trigger.workflow_name, + wf_input, + trigger.next_execution_time, + trigger.created_at, + ) + + if hasattr(trigger, 'updated_at'): + data += (trigger.updated_at,) + else: + data += (None,) + else: + data = (tuple('' for _ in range(len(columns))),) + + return columns, data + + +class List(base.MistralLister): + """List all cron triggers.""" + + def _get_format_function(self): + return format_list + + def _get_resources(self, parsed_args): + return cron_triggers.CronTriggerManager(self.app.client).list() + + +class Get(show.ShowOne): + """Show specific cron trigger.""" + + def get_parser(self, prog_name): + parser = super(Get, self).get_parser(prog_name) + + parser.add_argument('name', help='Cron trigger name') + + return parser + + def take_action(self, parsed_args): + mgr = cron_triggers.CronTriggerManager(self.app.client) + + return format(mgr.get(parsed_args.name)) + + +class Create(show.ShowOne): + """Create new trigger.""" + + def get_parser(self, prog_name): + parser = super(Create, self).get_parser(prog_name) + + parser.add_argument('name', help='Cron trigger name') + parser.add_argument('pattern', help='Cron trigger pattern') + parser.add_argument('workflow_name', help='Workflow name') + + parser.add_argument( + 'workflow_input', + nargs='?', + help='Workflow input' + ) + + return parser + + def take_action(self, parsed_args): + mgr = cron_triggers.CronTriggerManager(self.app.client) + + if parsed_args.workflow_input: + try: + wf_input = json.loads(parsed_args.workflow_input) + except: + wf_input = json.load(open(parsed_args.workflow_input)) + else: + wf_input = {} + + trigger = mgr.create( + parsed_args.name, + parsed_args.pattern, + parsed_args.workflow_name, + wf_input + ) + + return format(trigger) + + +class Delete(command.Command): + """Delete trigger.""" + + def get_parser(self, prog_name): + parser = super(Delete, self).get_parser(prog_name) + + parser.add_argument('name', help='Cron trigger name') + + return parser + + def take_action(self, parsed_args): + mgr = cron_triggers.CronTriggerManager(self.app.client) + + mgr.delete(parsed_args.name) diff --git a/mistralclient/shell.py b/mistralclient/shell.py index 88f3d7a7..0533a1f9 100644 --- a/mistralclient/shell.py +++ b/mistralclient/shell.py @@ -29,6 +29,7 @@ import mistralclient.commands.v1.workbooks import mistralclient.commands.v1.executions import mistralclient.commands.v1.tasks import mistralclient.commands.v2.actions +import mistralclient.commands.v2.cron_triggers import mistralclient.commands.v2.executions import mistralclient.commands.v2.tasks import mistralclient.commands.v2.workbooks @@ -237,15 +238,22 @@ class MistralShell(app.App): 'workbook-update': mistralclient.commands.v2.workbooks.Update, 'workbook-get-definition': mistralclient.commands.v2.workbooks.GetDefinition, + 'workflow-list': mistralclient.commands.v2.workflows.List, + 'workflow-get': mistralclient.commands.v2.workflows.Get, + 'workflow-create': mistralclient.commands.v2.workflows.Create, + 'workflow-delete': mistralclient.commands.v2.workflows.Delete, + 'workflow-update': mistralclient.commands.v2.workflows.Update, + 'workflow-get-definition': + mistralclient.commands.v2.workflows.GetDefinition, + 'execution-create': mistralclient.commands.v2.executions.Create, + 'execution-delete': mistralclient.commands.v2.executions.Delete, + 'execution-update': mistralclient.commands.v2.executions.Update, 'execution-list': mistralclient.commands.v2.executions.List, 'execution-get': mistralclient.commands.v2.executions.Get, 'execution-get-input': mistralclient.commands.v2.executions.GetInput, 'execution-get-output': mistralclient.commands.v2.executions.GetOutput, - 'execution-create': mistralclient.commands.v2.executions.Create, - 'execution-delete': mistralclient.commands.v2.executions.Delete, - 'execution-update': mistralclient.commands.v2.executions.Update, 'task-list': mistralclient.commands.v2.tasks.List, 'task-get': mistralclient.commands.v2.tasks.Get, 'task-get-input': mistralclient.commands.v2.tasks.GetInput, @@ -259,13 +267,12 @@ class MistralShell(app.App): 'action-update': mistralclient.commands.v2.actions.Update, 'action-get-definition': mistralclient.commands.v2.actions.GetDefinition, - 'workflow-list': mistralclient.commands.v2.workflows.List, - 'workflow-get': mistralclient.commands.v2.workflows.Get, - 'workflow-create': mistralclient.commands.v2.workflows.Create, - 'workflow-delete': mistralclient.commands.v2.workflows.Delete, - 'workflow-update': mistralclient.commands.v2.workflows.Update, - 'workflow-get-definition': - mistralclient.commands.v2.workflows.GetDefinition + 'cron-trigger-list': mistralclient.commands.v2.cron_triggers.List, + 'cron-trigger-get': mistralclient.commands.v2.cron_triggers.Get, + 'cron-trigger-create': + mistralclient.commands.v2.cron_triggers.Create, + 'cron-trigger-delete': + mistralclient.commands.v2.cron_triggers.Delete } diff --git a/mistralclient/tests/unit/v2/test_cli_cron_triggers.py b/mistralclient/tests/unit/v2/test_cli_cron_triggers.py new file mode 100644 index 00000000..7c297bbf --- /dev/null +++ b/mistralclient/tests/unit/v2/test_cli_cron_triggers.py @@ -0,0 +1,82 @@ +# Copyright 2014 Mirantis, Inc. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, 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 mock + +from mistralclient.tests.unit import base + +from mistralclient.commands.v2 import cron_triggers as cron_triggers_cmd +from mistralclient.api.v2 import cron_triggers + + +TRIGGER_DICT = { + 'name': 'my_trigger', + 'pattern': '* * * * *', + 'workflow_name': 'flow1', + 'workflow_input': {}, + 'next_execution_time': '1', + 'created_at': '1', + 'updated_at': '1' +} + + +TRIGGER = cron_triggers.CronTrigger(mock, TRIGGER_DICT) + + +class TestCLIWorkbooksV2(base.BaseCommandTest): + @mock.patch('argparse.open', create=True) + @mock.patch('mistralclient.api.v2.cron_triggers.CronTriggerManager.create') + def test_create(self, mock, mock_open): + mock.return_value = TRIGGER + mock_open.return_value = mock.MagicMock(spec=file) + + result = self.call( + cron_triggers_cmd.Create, + app_args=['my_trigger', '* * * * *', 'flow1'] + ) + + self.assertEqual( + ('my_trigger', '* * * * *', 'flow1', {}, '1', '1', '1'), + result[1] + ) + + @mock.patch('mistralclient.api.v2.cron_triggers.CronTriggerManager.list') + def test_list(self, mock): + mock.return_value = (TRIGGER,) + + result = self.call(cron_triggers_cmd.List) + + self.assertEqual( + [('my_trigger', '* * * * *', 'flow1', {}, '1', '1', '1')], + result[1] + ) + + @mock.patch('mistralclient.api.v2.cron_triggers.CronTriggerManager.get') + def test_get(self, mock): + mock.return_value = TRIGGER + + result = self.call(cron_triggers_cmd.Get, app_args=['name']) + + self.assertEqual( + ('my_trigger', '* * * * *', 'flow1', {}, '1', '1', '1'), + result[1] + ) + + @mock.patch('mistralclient.api.v2.cron_triggers.CronTriggerManager.delete') + def test_delete(self, mock): + self.assertIsNone( + self.call(cron_triggers_cmd.Delete, app_args=['name']) + )