From 6fe6e884a2cb6f5d7d2624762aa052bb09473586 Mon Sep 17 00:00:00 2001 From: "OTSUKA, Yuanying" Date: Fri, 6 Mar 2015 13:59:42 +0900 Subject: [PATCH] Add Heat tasks To manage and track bay status, this adds heat tasks which include: * Creating stack * Updating stack * Deleting stack Partially-Implements: blueprint magnum-taskflow Change-Id: Icd388ce7ebd3ca35eac392406c28bbad40b1de24 --- magnum/conductor/tasks/__init__.py | 22 +++ magnum/conductor/tasks/heat_tasks.py | 55 +++++++ magnum/tests/conductor/tasks/__init__.py | 0 .../tests/conductor/tasks/test_heat_tasks.py | 143 ++++++++++++++++++ requirements.txt | 1 + 5 files changed, 221 insertions(+) create mode 100644 magnum/conductor/tasks/__init__.py create mode 100644 magnum/conductor/tasks/heat_tasks.py create mode 100644 magnum/tests/conductor/tasks/__init__.py create mode 100644 magnum/tests/conductor/tasks/test_heat_tasks.py diff --git a/magnum/conductor/tasks/__init__.py b/magnum/conductor/tasks/__init__.py new file mode 100644 index 0000000000..3f263ca0fb --- /dev/null +++ b/magnum/conductor/tasks/__init__.py @@ -0,0 +1,22 @@ +# Copyright 2015 NEC Corporation. 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 taskflow.task as task + + +class OSBaseTask(task.Task): + def __init__(self, os_client, name=None, **kwargs): + self.os_client = os_client + + super(OSBaseTask, self).__init__(name=name, **kwargs) diff --git a/magnum/conductor/tasks/heat_tasks.py b/magnum/conductor/tasks/heat_tasks.py new file mode 100644 index 0000000000..01f92d5aff --- /dev/null +++ b/magnum/conductor/tasks/heat_tasks.py @@ -0,0 +1,55 @@ +# Copyright 2015 NEC Corporation. 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. + +from magnum.conductor import tasks + + +class CreateStack(tasks.OSBaseTask): + """CreateStack Task + + This task interfaces with Heat API and creates a stack based on parameters + provided to the Task. + + """ + + def execute(self, stack_name, parameters, template, files): + stack = self.os_client.stacks.create(stack_name=stack_name, + parameters=parameters, + template=template, files=files) + return stack + + +class UpdateStack(tasks.OSBaseTask): + """UpdateStack Task + + This task interfaces with Heat API and update a stack based on parameters + provided to the Task. + + """ + + def execute(self, stack_id, parameters, template, files): + self.os_client.stacks.update(stack_id, parameters=parameters, + template=template, files=files) + + +class DeleteStack(tasks.OSBaseTask): + """DeleteStack Task + + This task interfaces with Heat API and delete a stack based on parameters + provided to the Task. + + """ + + def execute(self, stack_id): + self.os_client.stacks.delete(stack_id) diff --git a/magnum/tests/conductor/tasks/__init__.py b/magnum/tests/conductor/tasks/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/magnum/tests/conductor/tasks/test_heat_tasks.py b/magnum/tests/conductor/tasks/test_heat_tasks.py new file mode 100644 index 0000000000..e667044b7b --- /dev/null +++ b/magnum/tests/conductor/tasks/test_heat_tasks.py @@ -0,0 +1,143 @@ +# Copyright 2015 NEC Corporation. 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. + +from magnum.conductor.tasks import heat_tasks +from magnum.tests import base + +import mock +from taskflow import engines +from taskflow.patterns import linear_flow + + +class HeatTasksTests(base.TestCase): + + def setUp(self): + super(HeatTasksTests, self).setUp() + self.heat_client = mock.MagicMock(name='heat_client') + + def _get_create_stack_flow(self, heat_client): + flow = linear_flow.Flow("create stack flow") + flow.add( + heat_tasks.CreateStack( + os_client=heat_client, + requires=('stack_name', 'parameters', 'template', 'files'), + provides='new_stack', + ), + ) + return flow + + def _get_update_stack_flow(self, heat_client): + flow = linear_flow.Flow("update stack flow") + flow.add( + heat_tasks.UpdateStack( + os_client=heat_client, + requires=('stack_id', 'parameters', 'template', 'files'), + ), + ) + return flow + + def _get_delete_stack_flow(self, heat_client): + flow = linear_flow.Flow("delete stack flow") + flow.add( + heat_tasks.DeleteStack( + os_client=heat_client, + requires=('stack_id'), + ), + ) + return flow + + def test_create_stack(self): + heat_client = mock.MagicMock(name='heat_client') + stack_id = 'stack_id' + stack_name = 'stack_name' + stack = { + 'stack': { + 'id': stack_id + } + } + heat_client.stacks.create.return_value = stack + flow_store = { + 'stack_name': stack_name, + 'parameters': 'parameters', + 'template': 'template', + 'files': 'files' + } + flow = self._get_create_stack_flow(heat_client) + + result = engines.run(flow, store=flow_store) + heat_client.stacks.create.assert_called_once_with(**flow_store) + self.assertEqual(result['new_stack']['stack']['id'], stack_id) + + def test_create_stack_with_error(self): + heat_client = mock.MagicMock(name='heat_client') + heat_client.stacks.create.side_effect = ValueError + stack_name = 'stack_name' + flow_store = { + 'stack_name': stack_name, + 'parameters': 'parameters', + 'template': 'template', + 'files': 'files' + } + flow = self._get_create_stack_flow(heat_client) + + self.assertRaises(ValueError, engines.run, flow, store=flow_store) + + def test_update_stack(self): + heat_client = mock.MagicMock(name='heat_client') + stack_id = 'stack_id' + flow_store = { + 'stack_id': stack_id, + 'parameters': 'parameters', + 'template': 'template', + 'files': 'files' + } + flow = self._get_update_stack_flow(heat_client) + expected_params = dict(flow_store) + del expected_params['stack_id'] + + engines.run(flow, store=flow_store) + heat_client.stacks.update.assert_called_once_with(stack_id, + **expected_params) + + def test_update_stack_with_error(self): + heat_client = mock.MagicMock(name='heat_client') + heat_client.stacks.update.side_effect = ValueError + stack_id = 'stack_id' + flow_store = { + 'stack_id': stack_id, + 'parameters': 'parameters', + 'template': 'template', + 'files': 'files' + } + flow = self._get_update_stack_flow(heat_client) + + self.assertRaises(ValueError, engines.run, flow, store=flow_store) + + def test_delete_stack(self): + heat_client = mock.MagicMock(name='heat_client') + stack_id = 'stack_id' + flow_store = {'stack_id': stack_id} + flow = self._get_delete_stack_flow(heat_client) + + engines.run(flow, store=flow_store) + heat_client.stacks.delete.assert_called_once_with(stack_id) + + def test_delete_stack_with_error(self): + heat_client = mock.MagicMock(name='heat_client') + heat_client.stacks.delete.side_effect = ValueError + stack_id = 'stack_id' + flow_store = {'stack_id': stack_id} + flow = self._get_delete_stack_flow(heat_client) + + self.assertRaises(ValueError, engines.run, flow, store=flow_store) diff --git a/requirements.txt b/requirements.txt index 9e97f2f72b..56e6a4c5ce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,6 +22,7 @@ python-keystoneclient>=1.1.0 # Apache-2.0 python-kubernetes>=0.2 # Apache-2.0 six>=1.7.0 # MIT SQLAlchemy>=0.9.7,<=0.9.99 # MIT +taskflow>=0.6 # Apache-2.0 WSME>=0.6 # MIT docker-py>=0.5.1 # Apache-2.0 jsonpatch>=1.1 # Modified BSD http://tinyurl.com/kghfvjw