Add openstack actions
* Added Generator for OpenStack Actions * Added Nova actions * Added Keystone actions * Added Glance actions * Init all needed namespaces in action factory * Unit tests of generators and engine Implements blueprint: mistral-openstack-actions Change-Id: I2ac24843e6434c513ebc813cbe42f91000309918
This commit is contained in:
parent
92075fa5bb
commit
86a93cc3ad
@ -1,5 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2014 - Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -15,9 +13,11 @@
|
||||
# limitations under the License.
|
||||
|
||||
import inspect
|
||||
|
||||
from stevedore import extension
|
||||
|
||||
from mistral.actions import base
|
||||
from mistral.actions import generator_factory
|
||||
from mistral.actions import std_actions
|
||||
from mistral import exceptions as exc
|
||||
from mistral import expressions as expr
|
||||
@ -25,6 +25,7 @@ from mistral.openstack.common import log as logging
|
||||
from mistral.workbook import actions
|
||||
from mistral.workbook import tasks
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
_ACTION_CTX_PARAM = 'action_context'
|
||||
@ -45,6 +46,16 @@ def get_registered_namespaces():
|
||||
return _NAMESPACES.copy()
|
||||
|
||||
|
||||
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]()
|
||||
action_classes = generator.create_action_classes()
|
||||
for action_name, action in action_classes.items():
|
||||
ns.add(action_name, action)
|
||||
|
||||
|
||||
def _register_action_classes():
|
||||
mgr = extension.ExtensionManager(
|
||||
namespace='mistral.actions',
|
||||
@ -57,6 +68,8 @@ def _register_action_classes():
|
||||
for ns in _NAMESPACES:
|
||||
_NAMESPACES[ns].log()
|
||||
|
||||
_register_dynamic_action_classes()
|
||||
|
||||
|
||||
def get_action_class(action_full_name):
|
||||
"""Finds action class by full action name (i.e. 'namespace.action_name').
|
||||
|
30
mistral/actions/action_generator.py
Normal file
30
mistral/actions/action_generator.py
Normal file
@ -0,0 +1,30 @@
|
||||
# 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 abc
|
||||
|
||||
|
||||
class ActionGenerator(object):
|
||||
"""Action generator.
|
||||
|
||||
Action generator uses some data to build Action class
|
||||
dynamically.
|
||||
"""
|
||||
@abc.abstractmethod
|
||||
def create_action_classes(self, *args, **kwargs):
|
||||
"""Constructs classes of needed action.
|
||||
|
||||
return: dict of action names and Action classes.
|
||||
"""
|
||||
pass
|
23
mistral/actions/generator_factory.py
Normal file
23
mistral/actions/generator_factory.py
Normal file
@ -0,0 +1,23 @@
|
||||
# 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 mistral.actions.openstack.action_generator import generators
|
||||
|
||||
|
||||
def all_generators():
|
||||
return {
|
||||
"nova": generators.NovaActionGenerator,
|
||||
"glance": generators.GlanceActionGenerator,
|
||||
"keystone": generators.KeystoneActionGenerator
|
||||
}
|
0
mistral/actions/openstack/__init__.py
Normal file
0
mistral/actions/openstack/__init__.py
Normal file
63
mistral/actions/openstack/action_generator/base.py
Normal file
63
mistral/actions/openstack/action_generator/base.py
Normal file
@ -0,0 +1,63 @@
|
||||
# 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 oslo.config import cfg
|
||||
import pkg_resources as pkg
|
||||
|
||||
from mistral.actions import action_generator
|
||||
from mistral import version
|
||||
|
||||
os_actions_mapping_path = cfg.StrOpt('openstack_actions_mapping_path',
|
||||
default='actions/openstack/mapping.json')
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opt(os_actions_mapping_path)
|
||||
MAPPING_PATH = CONF.openstack_actions_mapping_path
|
||||
|
||||
|
||||
class OpenStackActionGenerator(action_generator.ActionGenerator):
|
||||
"""OpenStackActionGenerator.
|
||||
|
||||
Base generator for all OpenStack actions,
|
||||
creates a client method declaration using
|
||||
specific python-client and sets needed arguments
|
||||
to actions.
|
||||
"""
|
||||
_action_namespace = None
|
||||
base_action_class = None
|
||||
|
||||
def create_action_class(self, method_name):
|
||||
if not method_name:
|
||||
return None
|
||||
|
||||
action_class = type(str(method_name), (self.base_action_class,), {})
|
||||
setattr(action_class, 'client_method', method_name)
|
||||
|
||||
return action_class
|
||||
|
||||
def create_action_classes(self):
|
||||
mapping = json.loads(open(pkg.resource_filename(
|
||||
version.version_info.package,
|
||||
MAPPING_PATH)).read())
|
||||
method_dict = mapping[self._action_namespace]
|
||||
|
||||
action_classes = {}
|
||||
|
||||
for action_name, method_name in method_dict.items():
|
||||
action_classes[action_name] = self.create_action_class(method_name)
|
||||
|
||||
return action_classes
|
31
mistral/actions/openstack/action_generator/generators.py
Normal file
31
mistral/actions/openstack/action_generator/generators.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 mistral.actions.openstack.action_generator import base
|
||||
from mistral.actions.openstack import actions
|
||||
|
||||
|
||||
class NovaActionGenerator(base.OpenStackActionGenerator):
|
||||
_action_namespace = "nova"
|
||||
base_action_class = actions.NovaAction
|
||||
|
||||
|
||||
class GlanceActionGenerator(base.OpenStackActionGenerator):
|
||||
_action_namespace = "glance"
|
||||
base_action_class = actions.GlanceAction
|
||||
|
||||
|
||||
class KeystoneActionGenerator(base.OpenStackActionGenerator):
|
||||
_action_namespace = "keystone"
|
||||
base_action_class = actions.KeystoneAction
|
59
mistral/actions/openstack/actions.py
Normal file
59
mistral/actions/openstack/actions.py
Normal file
@ -0,0 +1,59 @@
|
||||
# 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 glanceclient.v2 import client as glanceclient
|
||||
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.utils.openstack import keystone as keystone_utils
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class NovaAction(base.OpenStackAction):
|
||||
_client_class = novaclient.Client
|
||||
|
||||
def _get_client(self):
|
||||
ctx = context.ctx()
|
||||
auth_url = keystone_utils.get_keystone_url_v2()
|
||||
|
||||
return self._client_class(username=ctx.user_name,
|
||||
auth_token=ctx.auth_token,
|
||||
tenant_id=ctx.project_id,
|
||||
auth_url=auth_url)
|
||||
|
||||
|
||||
class GlanceAction(base.OpenStackAction):
|
||||
_client_class = glanceclient.Client
|
||||
|
||||
def _get_client(self):
|
||||
ctx = context.ctx()
|
||||
endpoint_url = keystone_utils.get_endpoint_for_project('glance')
|
||||
|
||||
return self._client_class(endpoint_url, token=ctx.auth_token)
|
||||
|
||||
|
||||
class KeystoneAction(base.OpenStackAction):
|
||||
_client_class = keystoneclient.Client
|
||||
|
||||
def _get_client(self):
|
||||
ctx = context.ctx()
|
||||
auth_url = CONF.keystone_authtoken.auth_uri
|
||||
|
||||
return self._client_class(token=ctx.auth_token,
|
||||
auth_url=auth_url)
|
61
mistral/actions/openstack/base.py
Normal file
61
mistral/actions/openstack/base.py
Normal file
@ -0,0 +1,61 @@
|
||||
# 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 abc
|
||||
|
||||
from mistral.actions import base
|
||||
from mistral import exceptions as exc
|
||||
|
||||
|
||||
class OpenStackAction(base.Action):
|
||||
"""OpenStack Action.
|
||||
|
||||
OpenStack Action is the basis of all OpenStack-specific actions,
|
||||
which are constructed via OpenStack Action generators.
|
||||
"""
|
||||
_kwargs_for_run = {}
|
||||
_client_class = None
|
||||
client_method = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._kwargs_for_run = kwargs
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_client(self):
|
||||
"""Returns python-client instance
|
||||
|
||||
Gets client instance according to specific OpenStack Service
|
||||
(e.g. Nova, Glance, Heat, Keystone etc)
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def _get_client_method(self):
|
||||
hierarchy_list = self.client_method.split('.')
|
||||
attribute = self._get_client()
|
||||
for attr in hierarchy_list:
|
||||
attribute = getattr(attribute, attr)
|
||||
return attribute
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
method = self._get_client_method()
|
||||
return method(**self._kwargs_for_run)
|
||||
except Exception as e:
|
||||
raise exc.ActionException("%s failed: %s"
|
||||
% (self.__class__.__name__, e))
|
||||
|
||||
def test(self):
|
||||
return dict(zip(self._kwargs_for_run,
|
||||
['test'] * len(self._kwargs_for_run)))
|
310
mistral/actions/openstack/mapping.json
Normal file
310
mistral/actions/openstack/mapping.json
Normal file
@ -0,0 +1,310 @@
|
||||
{
|
||||
"_comment": "Mapping OpenStack action namespaces to all its actions. Each action name is mapped to python-client method name in this namespace.",
|
||||
"nova": {
|
||||
"_comment": "It uses novaclient.v1_1.",
|
||||
"agents_create": "agents.create",
|
||||
"agents_delete": "agents.delete",
|
||||
"agents_find": "agents.find",
|
||||
"agents_findall": "agents.findall",
|
||||
"agents_list": "agents.list",
|
||||
"agents_update": "agents.update",
|
||||
"availability_zones_find": "availability_zones.find",
|
||||
"availability_zones_findall": "availability_zones.findall",
|
||||
"availability_zones_list": "availability_zones.list",
|
||||
"availability_zones_return_parameter_name": "availability_zones.return_parameter_name",
|
||||
"certs_create": "certs.create",
|
||||
"certs_get": "certs.get",
|
||||
"dns_domains_create_private": "dns_domains.create_private",
|
||||
"dns_domains_create_public": "dns_domains.create_public",
|
||||
"dns_domains_delete": "dns_domains.delete",
|
||||
"dns_domains_domains": "dns_domains.domains",
|
||||
"dns_entries_create": "dns_entries.create",
|
||||
"dns_entries_delete": "dns_entries.delete",
|
||||
"dns_entries_get": "dns_entries.get",
|
||||
"dns_entries_get_for_ip": "dns_entries.get_for_ip",
|
||||
"dns_entries_modify_ip": "dns_entries.modify_ip",
|
||||
"fixed_ips_get": "fixed_ips.get",
|
||||
"fixed_ips_reserve": "fixed_ips.reserve",
|
||||
"fixed_ips_unreserve": "fixed_ips.unreserve",
|
||||
"flavor_access_add_tenant_access": "flavor_access.add_tenant_access",
|
||||
"flavor_access_find": "flavor_access.find",
|
||||
"flavor_access_findall": "flavor_access.findall",
|
||||
"flavor_access_list": "flavor_access.list",
|
||||
"flavor_access_remove_tenant_access": "flavor_access.remove_tenant_access",
|
||||
"flavors_create": "flavors.create",
|
||||
"flavors_delete": "flavors.delete",
|
||||
"flavors_find": "flavors.find",
|
||||
"flavors_findall": "flavors.findall",
|
||||
"flavors_get": "flavors.get",
|
||||
"flavors_is_alphanum_id_allowed": "flavors.is_alphanum_id_allowed",
|
||||
"flavors_list": "flavors.list",
|
||||
"floating_ip_pools_find": "floating_ip_pools.find",
|
||||
"floating_ip_pools_findall": "floating_ip_pools.findall",
|
||||
"floating_ip_pools_list": "floating_ip_pools.list",
|
||||
"floating_ips_create": "floating_ips.create",
|
||||
"floating_ips_delete": "floating_ips.delete",
|
||||
"floating_ips_find": "floating_ips.find",
|
||||
"floating_ips_findall": "floating_ips.findall",
|
||||
"floating_ips_get": "floating_ips.get",
|
||||
"floating_ips_list": "floating_ips.list",
|
||||
"hosts_find": "hosts.find",
|
||||
"hosts_findall": "hosts.findall",
|
||||
"hosts_get": "hosts.get",
|
||||
"hosts_host_action": "hosts.host_action",
|
||||
"hosts_list": "hosts.list",
|
||||
"hosts_list_all": "hosts.list_all",
|
||||
"hosts_update": "hosts.update",
|
||||
"hypervisors_find": "hypervisors.find",
|
||||
"hypervisors_findall": "hypervisors.findall",
|
||||
"hypervisors_get": "hypervisors.get",
|
||||
"hypervisors_list": "hypervisors.list",
|
||||
"hypervisors_search": "hypervisors.search",
|
||||
"hypervisors_statistics": "hypervisors.statistics",
|
||||
"hypervisors_uptime": "hypervisors.uptime",
|
||||
"images_delete": "images.delete",
|
||||
"images_delete_meta": "images.delete_meta",
|
||||
"images_find": "images.find",
|
||||
"images_findall": "images.findall",
|
||||
"images_get": "images.get",
|
||||
"images_list": "images.list",
|
||||
"images_set_meta": "images.set_meta",
|
||||
"keypairs_create": "keypairs.create",
|
||||
"keypairs_delete": "keypairs.delete",
|
||||
"keypairs_find": "keypairs.find",
|
||||
"keypairs_findall": "keypairs.findall",
|
||||
"keypairs_get": "keypairs.get",
|
||||
"keypairs_is_alphanum_id_allowed": "keypairs.is_alphanum_id_allowed",
|
||||
"keypairs_keypair_prefix": "keypairs.keypair_prefix",
|
||||
"keypairs_list": "keypairs.list",
|
||||
"networks_add": "networks.add",
|
||||
"networks_associate_host": "networks.associate_host",
|
||||
"networks_associate_project": "networks.associate_project",
|
||||
"networks_create": "networks.create",
|
||||
"networks_delete": "networks.delete",
|
||||
"networks_disassociate": "networks.disassociate",
|
||||
"networks_find": "networks.find",
|
||||
"networks_findall": "networks.findall",
|
||||
"networks_get": "networks.get",
|
||||
"networks_list": "networks.list",
|
||||
"quotas_defaults": "quotas.defaults",
|
||||
"quotas_delete": "quotas.delete",
|
||||
"quotas_get": "quotas.get",
|
||||
"quotas_update": "quotas.update",
|
||||
"security_groups_create": "security_groups.create",
|
||||
"security_groups_delete": "security_groups.delete",
|
||||
"security_groups_find": "security_groups.find",
|
||||
"security_groups_findall": "security_groups.findall",
|
||||
"security_groups_get": "security_groups.get",
|
||||
"security_groups_list": "security_groups.list",
|
||||
"security_groups_update": "security_groups.update",
|
||||
"servers_add_fixed_ip": "servers.add_fixed_ip",
|
||||
"servers_add_floating_ip": "servers.add_floating_ip",
|
||||
"servers_add_security_group": "servers.add_security_group",
|
||||
"servers_backup": "servers.backup",
|
||||
"servers_change_password": "servers.change_password",
|
||||
"servers_clear_password": "servers.clear_password",
|
||||
"servers_confirm_resize": "servers.confirm_resize",
|
||||
"servers_create": "servers.create",
|
||||
"servers_create_image": "servers.create_image",
|
||||
"servers_delete": "servers.delete",
|
||||
"servers_delete_meta": "servers.delete_meta",
|
||||
"servers_diagnostics": "servers.diagnostics",
|
||||
"servers_evacuate": "servers.evacuate",
|
||||
"servers_find": "servers.find",
|
||||
"servers_findall": "servers.findall",
|
||||
"servers_force_delete": "servers.force_delete",
|
||||
"servers_get": "servers.get",
|
||||
"servers_get_console_output": "servers.get_console_output",
|
||||
"servers_get_password": "servers.get_password",
|
||||
"servers_get_rdp_console": "servers.get_rdp_console",
|
||||
"servers_get_spice_console": "servers.get_spice_console",
|
||||
"servers_get_vnc_console": "servers.get_vnc_console",
|
||||
"servers_interface_attach": "servers.interface_attach",
|
||||
"servers_interface_detach": "servers.interface_detach",
|
||||
"servers_interface_list": "servers.interface_list",
|
||||
"servers_list": "servers.list",
|
||||
"servers_list_security_group": "servers.list_security_group",
|
||||
"servers_live_migrate": "servers.live_migrate",
|
||||
"servers_lock": "servers.lock",
|
||||
"servers_migrate": "servers.migrate",
|
||||
"servers_pause": "servers.pause",
|
||||
"servers_reboot": "servers.reboot",
|
||||
"servers_rebuild": "servers.rebuild",
|
||||
"servers_remove_fixed_ip": "servers.remove_fixed_ip",
|
||||
"servers_remove_floating_ip": "servers.remove_floating_ip",
|
||||
"servers_remove_security_group": "servers.remove_security_group",
|
||||
"servers_rescue": "servers.rescue",
|
||||
"servers_reset_network": "servers.reset_network",
|
||||
"servers_reset_state": "servers.reset_state",
|
||||
"servers_resize": "servers.resize",
|
||||
"servers_restore": "servers.restore",
|
||||
"servers_resume": "servers.resume",
|
||||
"servers_revert_resize": "servers.revert_resize",
|
||||
"servers_set_meta": "servers.set_meta",
|
||||
"servers_set_meta_item": "servers.set_meta_item",
|
||||
"servers_shelve": "servers.shelve",
|
||||
"servers_shelve_offload": "servers.shelve_offload",
|
||||
"servers_start": "servers.start",
|
||||
"servers_stop": "servers.stop",
|
||||
"servers_suspend": "servers.suspend",
|
||||
"servers_unlock": "servers.unlock",
|
||||
"servers_unpause": "servers.unpause",
|
||||
"servers_unrescue": "servers.unrescue",
|
||||
"servers_unshelve": "servers.unshelve",
|
||||
"servers_update": "servers.update",
|
||||
"services_delete": "services.delete",
|
||||
"services_disable": "services.disable",
|
||||
"services_disable_log_reason": "services.disable_log_reason",
|
||||
"services_enable": "services.enable",
|
||||
"services_find": "services.find",
|
||||
"services_findall": "services.findall",
|
||||
"services_list": "services.list",
|
||||
"usage_find": "usage.find",
|
||||
"usage_findall": "usage.findall",
|
||||
"usage_get": "usage.get",
|
||||
"usage_list": "usage.list",
|
||||
"virtual_interfaces_find": "virtual_interfaces.find",
|
||||
"virtual_interfaces_findall": "virtual_interfaces.findall",
|
||||
"virtual_interfaces_list": "virtual_interfaces.list",
|
||||
"volume_snapshots_create": "volume_snapshots.create",
|
||||
"volume_snapshots_delete": "volume_snapshots.delete",
|
||||
"volume_snapshots_find": "volume_snapshots.find",
|
||||
"volume_snapshots_findall": "volume_snapshots.findall",
|
||||
"volume_snapshots_get": "volume_snapshots.get",
|
||||
"volume_snapshots_list": "volume_snapshots.list",
|
||||
"volume_types_create": "volume_types.create",
|
||||
"volume_types_delete": "volume_types.delete",
|
||||
"volume_types_find": "volume_types.find",
|
||||
"volume_types_findall": "volume_types.findall",
|
||||
"volume_types_get": "volume_types.get",
|
||||
"volume_types_list": "volume_types.list",
|
||||
"volumes_create": "volumes.create",
|
||||
"volumes_create_server_volume": "volumes.create_server_volume",
|
||||
"volumes_delete": "volumes.delete",
|
||||
"volumes_delete_server_volume": "volumes.delete_server_volume",
|
||||
"volumes_find": "volumes.find",
|
||||
"volumes_findall": "volumes.findall",
|
||||
"volumes_get": "volumes.get",
|
||||
"volumes_get_server_volume": "volumes.get_server_volume",
|
||||
"volumes_get_server_volumes": "volumes.get_server_volumes",
|
||||
"volumes_list": "volumes.list",
|
||||
"volumes_update_server_volume": "volumes.update_server_volume"
|
||||
},
|
||||
"glance": {
|
||||
"_comment": "It uses glanceclient.v2.",
|
||||
"image_members_create": "image_members.create",
|
||||
"image_members_delete": "image_members.delete",
|
||||
"image_members_list": "image_members.list",
|
||||
"image_members_model": "image_members.model",
|
||||
"image_members_update": "image_members.update",
|
||||
"image_tags_delete": "image_tags.delete",
|
||||
"image_tags_model": "image_tags.model",
|
||||
"image_tags_update": "image_tags.update",
|
||||
"images_add_location": "images.add_location",
|
||||
"images_create": "images.create",
|
||||
"images_data": "images.data",
|
||||
"images_delete": "images.delete",
|
||||
"images_delete_locations": "images.delete_locations",
|
||||
"images_get": "images.get",
|
||||
"images_list": "images.list",
|
||||
"images_model": "images.model",
|
||||
"images_update": "images.update",
|
||||
"images_update_location": "images.update_location",
|
||||
"images_upload": "images.upload",
|
||||
"schemas_get": "schemas.get"
|
||||
},
|
||||
"keystone": {
|
||||
"_comment": "It uses keystoneclient.v3.",
|
||||
"credentials_create": "credentials.create",
|
||||
"credentials_delete": "credentials.delete",
|
||||
"credentials_find": "credentials.find",
|
||||
"credentials_get": "credentials.get",
|
||||
"credentials_list": "credentials.list",
|
||||
"credentials_update": "credentials.update",
|
||||
"domains_create": "domains.create",
|
||||
"domains_delete": "domains.delete",
|
||||
"domains_find": "domains.find",
|
||||
"domains_get": "domains.get",
|
||||
"domains_list": "domains.list",
|
||||
"domains_update": "domains.update",
|
||||
"endpoints_create": "endpoints.create",
|
||||
"endpoints_delete": "endpoints.delete",
|
||||
"endpoints_find": "endpoints.find",
|
||||
"endpoints_get": "endpoints.get",
|
||||
"endpoints_list": "endpoints.list",
|
||||
"endpoints_update": "endpoints.update",
|
||||
"federation_identity_providers": "federation.identity_providers",
|
||||
"federation_mappings": "federation.mappings",
|
||||
"federation_protocols": "federation.protocols",
|
||||
"groups_create": "groups.create",
|
||||
"groups_delete": "groups.delete",
|
||||
"groups_find": "groups.find",
|
||||
"groups_get": "groups.get",
|
||||
"groups_list": "groups.list",
|
||||
"groups_update": "groups.update",
|
||||
"oauth1_access_tokens": "oauth1.access_tokens",
|
||||
"oauth1_consumers": "oauth1.consumers",
|
||||
"oauth1_request_tokens": "oauth1.request_tokens",
|
||||
"policies_create": "policies.create",
|
||||
"policies_delete": "policies.delete",
|
||||
"policies_find": "policies.find",
|
||||
"policies_get": "policies.get",
|
||||
"policies_list": "policies.list",
|
||||
"policies_update": "policies.update",
|
||||
"projects_create": "projects.create",
|
||||
"projects_delete": "projects.delete",
|
||||
"projects_find": "projects.find",
|
||||
"projects_get": "projects.get",
|
||||
"projects_list": "projects.list",
|
||||
"projects_update": "projects.update",
|
||||
"regions_create": "regions.create",
|
||||
"regions_delete": "regions.delete",
|
||||
"regions_find": "regions.find",
|
||||
"regions_get": "regions.get",
|
||||
"regions_list": "regions.list",
|
||||
"regions_update": "regions.update",
|
||||
"roles_check": "roles.check",
|
||||
"roles_client": "roles.client",
|
||||
"roles_create": "roles.create",
|
||||
"roles_delete": "roles.delete",
|
||||
"roles_find": "roles.find",
|
||||
"roles_get": "roles.get",
|
||||
"roles_grant": "roles.grant",
|
||||
"roles_list": "roles.list",
|
||||
"roles_revoke": "roles.revoke",
|
||||
"roles_update": "roles.update",
|
||||
"service_catalog_catalog": "service_catalog.catalog",
|
||||
"service_catalog_factory": "service_catalog.factory",
|
||||
"service_catalog_get_data": "service_catalog.get_data",
|
||||
"service_catalog_get_endpoints": "service_catalog.get_endpoints",
|
||||
"service_catalog_get_token": "service_catalog.get_token",
|
||||
"service_catalog_get_urls": "service_catalog.get_urls",
|
||||
"service_catalog_is_valid": "service_catalog.is_valid",
|
||||
"service_catalog_region_name": "service_catalog.region_name",
|
||||
"service_catalog_url_for": "service_catalog.url_for",
|
||||
"services_create": "services.create",
|
||||
"services_delete": "services.delete",
|
||||
"services_find": "services.find",
|
||||
"services_get": "services.get",
|
||||
"services_list": "services.list",
|
||||
"services_update": "services.update",
|
||||
"trusts_create": "trusts.create",
|
||||
"trusts_delete": "trusts.delete",
|
||||
"trusts_find": "trusts.find",
|
||||
"trusts_get": "trusts.get",
|
||||
"trusts_list": "trusts.list",
|
||||
"trusts_update": "trusts.update",
|
||||
"users_add_to_group": "users.add_to_group",
|
||||
"users_check_in_group": "users.check_in_group",
|
||||
"users_create": "users.create",
|
||||
"users_delete": "users.delete",
|
||||
"users_find": "users.find",
|
||||
"users_get": "users.get",
|
||||
"users_list": "users.list",
|
||||
"users_put": "users.put",
|
||||
"users_remove_from_group": "users.remove_from_group",
|
||||
"users_resource_class": "users.resource_class",
|
||||
"users_update": "users.update",
|
||||
"users_update_password": "users.update_password"
|
||||
}
|
||||
}
|
4
mistral/tests/resources/openstack_tasks/glance.yaml
Normal file
4
mistral/tests/resources/openstack_tasks/glance.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
Workflow:
|
||||
tasks:
|
||||
glance_image_list:
|
||||
action: glance.images_list
|
4
mistral/tests/resources/openstack_tasks/keystone.yaml
Normal file
4
mistral/tests/resources/openstack_tasks/keystone.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
Workflow:
|
||||
tasks:
|
||||
keystone_user_list:
|
||||
action: keystone.users_list
|
4
mistral/tests/resources/openstack_tasks/nova.yaml
Normal file
4
mistral/tests/resources/openstack_tasks/nova.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
Workflow:
|
||||
tasks:
|
||||
nova_server_list:
|
||||
action: nova.servers_list
|
0
mistral/tests/unit/actions/openstack/__init__.py
Normal file
0
mistral/tests/unit/actions/openstack/__init__.py
Normal file
@ -0,0 +1,32 @@
|
||||
# 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 import generator_factory
|
||||
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"]()
|
||||
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.GlanceAction))
|
||||
self.assertEqual("images.list", action_class.client_method)
|
@ -0,0 +1,32 @@
|
||||
# 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 import generator_factory
|
||||
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"]()
|
||||
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.KeystoneAction))
|
||||
self.assertEqual("users.create", action_class.client_method)
|
32
mistral/tests/unit/actions/openstack/test_nova_generator.py
Normal file
32
mistral/tests/unit/actions/openstack/test_nova_generator.py
Normal file
@ -0,0 +1,32 @@
|
||||
# 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 import generator_factory
|
||||
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"]()
|
||||
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.NovaAction))
|
||||
self.assertEqual("servers.get", action_class.client_method)
|
@ -0,0 +1,56 @@
|
||||
# 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 mock
|
||||
from oslotest import base
|
||||
|
||||
from mistral.actions.openstack import actions
|
||||
|
||||
|
||||
class OpenStackActionTest(base.BaseTestCase):
|
||||
@mock.patch.object(actions.NovaAction, '_get_client')
|
||||
def test_nova_action(self, mocked):
|
||||
method_name = "servers.get"
|
||||
action_class = actions.NovaAction
|
||||
action_class.client_method = method_name
|
||||
params = {'server': '1234-abcd'}
|
||||
action = action_class(**params)
|
||||
action.run()
|
||||
|
||||
self.assertTrue(mocked().servers.get.called)
|
||||
mocked().servers.get.assert_called_once_with(server="1234-abcd")
|
||||
|
||||
@mock.patch.object(actions.GlanceAction, '_get_client')
|
||||
def test_glance_action(self, mocked):
|
||||
method_name = "images.delete"
|
||||
action_class = actions.GlanceAction
|
||||
action_class.client_method = method_name
|
||||
params = {'image': '1234-abcd'}
|
||||
action = action_class(**params)
|
||||
action.run()
|
||||
|
||||
self.assertTrue(mocked().images.delete.called)
|
||||
mocked().images.delete.assert_called_once_with(image="1234-abcd")
|
||||
|
||||
@mock.patch.object(actions.KeystoneAction, '_get_client')
|
||||
def test_keystone_action(self, mocked):
|
||||
method_name = "users.get"
|
||||
action_class = actions.KeystoneAction
|
||||
action_class.client_method = method_name
|
||||
params = {'user': '1234-abcd'}
|
||||
action = action_class(**params)
|
||||
action.run()
|
||||
|
||||
self.assertTrue(mocked().users.get.called)
|
||||
mocked().users.get.assert_called_once_with(user="1234-abcd")
|
@ -93,13 +93,27 @@ class ActionFactoryTest(base.BaseTest):
|
||||
def test_register_standard_actions(self):
|
||||
namespaces = a_f.get_registered_namespaces()
|
||||
|
||||
self.assertEqual(1, len(namespaces))
|
||||
self.assertIn("nova", namespaces)
|
||||
self.assertIn("glance", namespaces)
|
||||
self.assertIn("keystone", namespaces)
|
||||
self.assertIn("std", namespaces)
|
||||
|
||||
std_ns = namespaces["std"]
|
||||
nova_ns = namespaces["nova"]
|
||||
keystone_ns = namespaces["keystone"]
|
||||
glance_ns = namespaces["glance"]
|
||||
|
||||
self.assertEqual(5, len(std_ns))
|
||||
|
||||
self.assertTrue(nova_ns.contains_action_name("servers_get"))
|
||||
self.assertTrue(nova_ns.contains_action_name("volumes_delete"))
|
||||
|
||||
self.assertTrue(keystone_ns.contains_action_name("users_list"))
|
||||
self.assertTrue(keystone_ns.contains_action_name("trusts_create"))
|
||||
|
||||
self.assertTrue(glance_ns.contains_action_name("images_list"))
|
||||
self.assertTrue(glance_ns.contains_action_name("images_delete"))
|
||||
|
||||
self.assertTrue(std_ns.contains_action_name("echo"))
|
||||
self.assertTrue(std_ns.contains_action_name("http"))
|
||||
self.assertTrue(std_ns.contains_action_name("mistral_http"))
|
||||
|
124
mistral/tests/unit/engine/test_openstack_actions.py
Normal file
124
mistral/tests/unit/engine/test_openstack_actions.py
Normal file
@ -0,0 +1,124 @@
|
||||
# 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 mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from mistral.actions.openstack import actions
|
||||
from mistral.db import api as db_api
|
||||
from mistral import engine
|
||||
from mistral.engine.drivers.default import engine as concrete_engine
|
||||
from mistral.engine import states
|
||||
from mistral.openstack.common import log as logging
|
||||
from mistral.tests import base
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
# Use the set_default method to set value otherwise in certain test cases
|
||||
# the change in value is not permanent.
|
||||
cfg.CONF.set_default('auth_enable', False, group='pecan')
|
||||
|
||||
|
||||
def create_workbook(definition_path):
|
||||
return db_api.workbook_create({
|
||||
'name': 'my_workbook',
|
||||
'definition': base.get_resource(definition_path)
|
||||
})
|
||||
|
||||
|
||||
@mock.patch.object(
|
||||
engine.EngineClient, 'start_workflow_execution',
|
||||
mock.MagicMock(side_effect=base.EngineTestCase.mock_start_workflow))
|
||||
@mock.patch.object(
|
||||
engine.EngineClient, 'convey_task_result',
|
||||
mock.MagicMock(side_effect=base.EngineTestCase.mock_task_result))
|
||||
@mock.patch.object(
|
||||
concrete_engine.DefaultEngine, '_run_task',
|
||||
mock.MagicMock(side_effect=base.EngineTestCase.mock_run_task))
|
||||
class OpenStackActionsEngineTest(base.EngineTestCase):
|
||||
@mock.patch.object(actions.GlanceAction, 'run',
|
||||
mock.Mock(return_value="images"))
|
||||
def test_glance_action(self):
|
||||
context = {}
|
||||
wb = create_workbook('openstack_tasks/glance.yaml')
|
||||
task_name = 'glance_image_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("images", task['output']['task'][task_name])
|
||||
|
||||
@mock.patch.object(actions.KeystoneAction, 'run',
|
||||
mock.Mock(return_value="users"))
|
||||
def test_keystone_action(self):
|
||||
context = {}
|
||||
wb = create_workbook('openstack_tasks/keystone.yaml')
|
||||
task_name = 'keystone_user_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("users", task['output']['task'][task_name])
|
||||
|
||||
@mock.patch.object(actions.NovaAction, 'run',
|
||||
mock.Mock(return_value="servers"))
|
||||
def test_nova_action(self):
|
||||
context = {}
|
||||
wb = create_workbook('openstack_tasks/nova.yaml')
|
||||
task_name = 'nova_server_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("servers", task['output']['task'][task_name])
|
@ -53,3 +53,17 @@ def client_for_admin(project_name):
|
||||
|
||||
def client_for_trusts(trust_id):
|
||||
return _admin_client(trust_id=trust_id)
|
||||
|
||||
|
||||
def get_endpoint_for_project(service_name):
|
||||
admin_project_name = CONF.keystone_authtoken.admin_tenant_name
|
||||
keystone_client = _admin_client(project_name=admin_project_name)
|
||||
service_list = keystone_client.services.list()
|
||||
service_id = [s.id for s in service_list if s.name == service_name][0]
|
||||
|
||||
return keystone_client.endpoints.find(service_id=service_id,
|
||||
interface='public').url
|
||||
|
||||
|
||||
def get_keystone_url_v2():
|
||||
return get_endpoint_for_project('keystone')
|
||||
|
@ -16,6 +16,8 @@ oslo.db>=0.2.0 # Apache-2.0
|
||||
oslo.messaging>=1.3.0
|
||||
paramiko>=1.13.0
|
||||
python-keystoneclient>=0.9.0
|
||||
python-novaclient>=2.17
|
||||
python-glanceclient>=0.13
|
||||
networkx>=1.8
|
||||
six>=1.7.0
|
||||
SQLAlchemy>=0.7.8,!=0.9.5,<=0.9.99
|
||||
|
Loading…
Reference in New Issue
Block a user