Add heat actions
* Added heat actions and action generator * Modified base action generator * Unit tests Implements blueprint: mistral-openstack-actions Change-Id: I043e8f61e00a3fcf81d096790e3486fe0c052da9
This commit is contained in:
parent
86a93cc3ad
commit
9f78c6f251
@ -48,9 +48,8 @@ def get_registered_namespaces():
|
||||
|
||||
def _register_dynamic_action_classes():
|
||||
all_generators = generator_factory.all_generators()
|
||||
for name in all_generators:
|
||||
ns = _find_or_create_namespace(name)
|
||||
generator = all_generators[name]()
|
||||
for generator in all_generators:
|
||||
ns = _find_or_create_namespace(generator.action_namespace)
|
||||
action_classes = generator.create_action_classes()
|
||||
for action_name, action in action_classes.items():
|
||||
ns.add(action_name, action)
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Copyright 2014 - Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# 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
|
||||
#
|
||||
@ -16,8 +16,9 @@ from mistral.actions.openstack.action_generator import generators
|
||||
|
||||
|
||||
def all_generators():
|
||||
return {
|
||||
"nova": generators.NovaActionGenerator,
|
||||
"glance": generators.GlanceActionGenerator,
|
||||
"keystone": generators.KeystoneActionGenerator
|
||||
}
|
||||
return [
|
||||
generators.NovaActionGenerator,
|
||||
generators.GlanceActionGenerator,
|
||||
generators.KeystoneActionGenerator,
|
||||
generators.HeatActionGenerator
|
||||
]
|
@ -37,27 +37,29 @@ class OpenStackActionGenerator(action_generator.ActionGenerator):
|
||||
specific python-client and sets needed arguments
|
||||
to actions.
|
||||
"""
|
||||
_action_namespace = None
|
||||
action_namespace = None
|
||||
base_action_class = None
|
||||
|
||||
def create_action_class(self, method_name):
|
||||
@classmethod
|
||||
def create_action_class(cls, method_name):
|
||||
if not method_name:
|
||||
return None
|
||||
|
||||
action_class = type(str(method_name), (self.base_action_class,), {})
|
||||
action_class = type(str(method_name), (cls.base_action_class,), {})
|
||||
setattr(action_class, 'client_method', method_name)
|
||||
|
||||
return action_class
|
||||
|
||||
def create_action_classes(self):
|
||||
@classmethod
|
||||
def create_action_classes(cls):
|
||||
mapping = json.loads(open(pkg.resource_filename(
|
||||
version.version_info.package,
|
||||
MAPPING_PATH)).read())
|
||||
method_dict = mapping[self._action_namespace]
|
||||
method_dict = mapping[cls.action_namespace]
|
||||
|
||||
action_classes = {}
|
||||
|
||||
for action_name, method_name in method_dict.items():
|
||||
action_classes[action_name] = self.create_action_class(method_name)
|
||||
action_classes[action_name] = cls.create_action_class(method_name)
|
||||
|
||||
return action_classes
|
||||
|
@ -17,15 +17,20 @@ from mistral.actions.openstack import actions
|
||||
|
||||
|
||||
class NovaActionGenerator(base.OpenStackActionGenerator):
|
||||
_action_namespace = "nova"
|
||||
action_namespace = "nova"
|
||||
base_action_class = actions.NovaAction
|
||||
|
||||
|
||||
class GlanceActionGenerator(base.OpenStackActionGenerator):
|
||||
_action_namespace = "glance"
|
||||
action_namespace = "glance"
|
||||
base_action_class = actions.GlanceAction
|
||||
|
||||
|
||||
class KeystoneActionGenerator(base.OpenStackActionGenerator):
|
||||
_action_namespace = "keystone"
|
||||
action_namespace = "keystone"
|
||||
base_action_class = actions.KeystoneAction
|
||||
|
||||
|
||||
class HeatActionGenerator(base.OpenStackActionGenerator):
|
||||
action_namespace = "heat"
|
||||
base_action_class = actions.HeatAction
|
||||
|
@ -12,13 +12,17 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import inspect
|
||||
|
||||
from glanceclient.v2 import client as glanceclient
|
||||
from heatclient.v1 import client as heatclient
|
||||
from keystoneclient.v3 import client as keystoneclient
|
||||
from novaclient.v1_1 import client as novaclient
|
||||
from oslo.config import cfg
|
||||
|
||||
from mistral.actions.openstack import base
|
||||
from mistral import context
|
||||
from mistral import exceptions as exc
|
||||
from mistral.utils.openstack import keystone as keystone_utils
|
||||
|
||||
|
||||
@ -57,3 +61,27 @@ class KeystoneAction(base.OpenStackAction):
|
||||
|
||||
return self._client_class(token=ctx.auth_token,
|
||||
auth_url=auth_url)
|
||||
|
||||
|
||||
class HeatAction(base.OpenStackAction):
|
||||
_client_class = heatclient.Client
|
||||
|
||||
def _get_client(self):
|
||||
ctx = context.ctx()
|
||||
endpoint_url_tmpl = keystone_utils.get_endpoint_for_project('heat')
|
||||
endpoint_url = endpoint_url_tmpl % {'tenant_id': ctx.project_id}
|
||||
|
||||
return self._client_class(endpoint_url,
|
||||
token=ctx.auth_token,
|
||||
username=ctx.user_name)
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
method = self._get_client_method()
|
||||
result = method(**self._kwargs_for_run)
|
||||
if inspect.isgenerator(result):
|
||||
return [v for v in result]
|
||||
return result
|
||||
except Exception as e:
|
||||
raise exc.ActionException("%s failed: %s"
|
||||
% (self.__class__.__name__, e))
|
@ -306,5 +306,37 @@
|
||||
"users_resource_class": "users.resource_class",
|
||||
"users_update": "users.update",
|
||||
"users_update_password": "users.update_password"
|
||||
},
|
||||
"heat": {
|
||||
"_comment": "It uses heatclient.v2.",
|
||||
"actions_resume": "actions.resume",
|
||||
"actions_suspend": "actions.suspend",
|
||||
"build_info_build_info": "build_info.build_info",
|
||||
"events_get": "events.get",
|
||||
"events_list": "events.list",
|
||||
"resources_generate_template": "resources.generate_template",
|
||||
"resources_get": "resources.get",
|
||||
"resources_list": "resources.list",
|
||||
"resources_metadata": "resources.metadata",
|
||||
"resources_signal": "resources.signal",
|
||||
"resource_types_get": "resource_types.get",
|
||||
"resource_types_list": "resource_types.list",
|
||||
"software_configs_create": "software_configs.create",
|
||||
"software_configs_delete": "software_configs.delete",
|
||||
"software_configs_get": "software_configs.get",
|
||||
"software_deployments_create": "software_deployments.create",
|
||||
"software_deployments_delete": "software_deployments.delete",
|
||||
"software_deployments_get": "software_deployments.get",
|
||||
"software_deployments_list": "software_deployments.list",
|
||||
"software_deployments_metadata": "software_deployments.metadata",
|
||||
"software_deployments_update": "software_deployments.update",
|
||||
"stacks_abandon": "stacks.abandon",
|
||||
"stacks_create": "stacks.create",
|
||||
"stacks_delete": "stacks.delete",
|
||||
"stacks_get": "stacks.get",
|
||||
"stacks_list": "stacks.list",
|
||||
"stacks_template": "stacks.template",
|
||||
"stacks_update": "stacks.update",
|
||||
"stacks_validate": "stacks.validate"
|
||||
}
|
||||
}
|
4
mistral/tests/resources/openstack_tasks/heat.yaml
Normal file
4
mistral/tests/resources/openstack_tasks/heat.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
Workflow:
|
||||
tasks:
|
||||
heat_stack_list:
|
||||
action: heat.stacks_list
|
@ -14,14 +14,14 @@
|
||||
|
||||
from oslotest import base
|
||||
|
||||
from mistral.actions import generator_factory
|
||||
from mistral.actions.openstack.action_generator import generators
|
||||
from mistral.actions.openstack import actions
|
||||
|
||||
|
||||
class GlanceGeneratorTest(base.BaseTestCase):
|
||||
def test_generator(self):
|
||||
action_name = "glance.images_list"
|
||||
generator = generator_factory.all_generators()["glance"]()
|
||||
generator = generators.GlanceActionGenerator
|
||||
action_classes = generator.create_action_classes()
|
||||
short_action_name = action_name.split(".")[1]
|
||||
action_class = action_classes[short_action_name]
|
||||
|
31
mistral/tests/unit/actions/openstack/test_heat_generator.py
Normal file
31
mistral/tests/unit/actions/openstack/test_heat_generator.py
Normal file
@ -0,0 +1,31 @@
|
||||
# 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.
|
||||
|
||||
from oslotest import base
|
||||
|
||||
from mistral.actions.openstack.action_generator import generators
|
||||
from mistral.actions.openstack import actions
|
||||
|
||||
|
||||
class HeatGeneratorTest(base.BaseTestCase):
|
||||
def test_generator(self):
|
||||
action_name = "heat.stacks_list"
|
||||
generator = generators.HeatActionGenerator
|
||||
action_classes = generator.create_action_classes()
|
||||
short_action_name = action_name.split(".")[1]
|
||||
action_class = action_classes[short_action_name]
|
||||
|
||||
self.assertIsNotNone(generator)
|
||||
self.assertIn(short_action_name, action_classes)
|
||||
self.assertTrue(issubclass(action_class, actions.HeatAction))
|
@ -14,14 +14,14 @@
|
||||
|
||||
from oslotest import base
|
||||
|
||||
from mistral.actions import generator_factory
|
||||
from mistral.actions.openstack.action_generator import generators
|
||||
from mistral.actions.openstack import actions
|
||||
|
||||
|
||||
class KeystoneGeneratorTest(base.BaseTestCase):
|
||||
def test_generator(self):
|
||||
action_name = "keystone.users_create"
|
||||
generator = generator_factory.all_generators()["keystone"]()
|
||||
generator = generators.KeystoneActionGenerator
|
||||
action_classes = generator.create_action_classes()
|
||||
short_action_name = action_name.split(".")[1]
|
||||
action_class = action_classes[short_action_name]
|
||||
@ -29,4 +29,4 @@ class KeystoneGeneratorTest(base.BaseTestCase):
|
||||
self.assertIsNotNone(generator)
|
||||
self.assertIn(short_action_name, action_classes)
|
||||
self.assertTrue(issubclass(action_class, actions.KeystoneAction))
|
||||
self.assertEqual("users.create", action_class.client_method)
|
||||
self.assertEqual("users.create", action_class.client_method)
|
||||
|
@ -14,14 +14,14 @@
|
||||
|
||||
from oslotest import base
|
||||
|
||||
from mistral.actions import generator_factory
|
||||
from mistral.actions.openstack.action_generator import generators
|
||||
from mistral.actions.openstack import actions
|
||||
|
||||
|
||||
class NovaGeneratorTest(base.BaseTestCase):
|
||||
def test_generator(self):
|
||||
action_name = "nova.servers_get"
|
||||
generator = generator_factory.all_generators()["nova"]()
|
||||
generator = generators.NovaActionGenerator
|
||||
action_classes = generator.create_action_classes()
|
||||
short_action_name = action_name.split(".")[1]
|
||||
action_class = action_classes[short_action_name]
|
||||
|
@ -54,3 +54,15 @@ class OpenStackActionTest(base.BaseTestCase):
|
||||
|
||||
self.assertTrue(mocked().users.get.called)
|
||||
mocked().users.get.assert_called_once_with(user="1234-abcd")
|
||||
|
||||
@mock.patch.object(actions.HeatAction, '_get_client')
|
||||
def test_heat_action(self, mocked):
|
||||
method_name = "stacks.get"
|
||||
action_class = actions.HeatAction
|
||||
action_class.client_method = method_name
|
||||
params = {'id': '1234-abcd'}
|
||||
action = action_class(**params)
|
||||
action.run()
|
||||
|
||||
self.assertTrue(mocked().stacks.get.called)
|
||||
mocked().stacks.get.assert_called_once_with(id="1234-abcd")
|
||||
|
@ -122,3 +122,28 @@ class OpenStackActionsEngineTest(base.EngineTestCase):
|
||||
|
||||
self.assertEqual(states.SUCCESS, task['state'])
|
||||
self.assertEqual("servers", task['output']['task'][task_name])
|
||||
|
||||
@mock.patch.object(actions.HeatAction, 'run',
|
||||
mock.Mock(return_value="stacks"))
|
||||
def test_heat_action(self):
|
||||
context = {}
|
||||
wb = create_workbook('openstack_tasks/heat.yaml')
|
||||
task_name = 'heat_stack_list'
|
||||
execution = self.engine.start_workflow_execution(wb['name'],
|
||||
task_name,
|
||||
context)
|
||||
|
||||
# We have to reread execution to get its latest version.
|
||||
execution = db_api.execution_get(execution['id'])
|
||||
|
||||
self.assertEqual(states.SUCCESS, execution['state'])
|
||||
|
||||
tasks = db_api.tasks_get(workbook_name=wb['name'],
|
||||
execution_id=execution['id'])
|
||||
|
||||
self.assertEqual(1, len(tasks))
|
||||
|
||||
task = self._assert_single_item(tasks, name=task_name)
|
||||
|
||||
self.assertEqual(states.SUCCESS, task['state'])
|
||||
self.assertEqual("stacks", task['output']['task'][task_name])
|
||||
|
@ -15,6 +15,7 @@ oslo.config>=1.2.1
|
||||
oslo.db>=0.2.0 # Apache-2.0
|
||||
oslo.messaging>=1.3.0
|
||||
paramiko>=1.13.0
|
||||
python-heatclient>=0.2.9
|
||||
python-keystoneclient>=0.9.0
|
||||
python-novaclient>=2.17
|
||||
python-glanceclient>=0.13
|
||||
|
Loading…
Reference in New Issue
Block a user