From 56e62a5beee014b9add4c7580732a9746ee0bcf2 Mon Sep 17 00:00:00 2001 From: Bhaskar Duvvuri Date: Thu, 19 Nov 2015 23:23:14 +0530 Subject: [PATCH] Add Trove to mistral actions Implement Trove actions in mistral, useful for Trove automated backups Change-Id: I638a7cad1d12d5e08a8a1741e72a75fd19d62233 Implements: blueprint trove-api-actions --- mistral/actions/generator_factory.py | 3 +- .../openstack/action_generator/generators.py | 5 ++ mistral/actions/openstack/actions.py | 37 ++++++++++ mistral/actions/openstack/mapping.json | 72 ++++++++++++++++++- .../openstack/test_openstack_actions.py | 12 ++++ .../actions/openstack/test_trove_generator.py | 32 +++++++++ requirements.txt | 1 + tools/get_action_list.py | 9 ++- 8 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 mistral/tests/unit/actions/openstack/test_trove_generator.py diff --git a/mistral/actions/generator_factory.py b/mistral/actions/generator_factory.py index eafc6bf5a..090ad0971 100644 --- a/mistral/actions/generator_factory.py +++ b/mistral/actions/generator_factory.py @@ -23,5 +23,6 @@ def all_generators(): generators.HeatActionGenerator, generators.NeutronActionGenerator, generators.CinderActionGenerator, - generators.CeilometerActionGenerator + generators.CeilometerActionGenerator, + generators.TroveActionGenerator ] diff --git a/mistral/actions/openstack/action_generator/generators.py b/mistral/actions/openstack/action_generator/generators.py index 6ca60f3ed..60a2cc278 100644 --- a/mistral/actions/openstack/action_generator/generators.py +++ b/mistral/actions/openstack/action_generator/generators.py @@ -49,3 +49,8 @@ class CinderActionGenerator(base.OpenStackActionGenerator): class CeilometerActionGenerator(base.OpenStackActionGenerator): action_namespace = "ceilometer" base_action_class = actions.CeilometerAction + + +class TroveActionGenerator(base.OpenStackActionGenerator): + action_namespace = "trove" + base_action_class = actions.TroveAction diff --git a/mistral/actions/openstack/actions.py b/mistral/actions/openstack/actions.py index af58d9ef3..57ece0e62 100644 --- a/mistral/actions/openstack/actions.py +++ b/mistral/actions/openstack/actions.py @@ -20,6 +20,8 @@ from keystoneclient import httpclient from keystoneclient.v3 import client as keystoneclient from neutronclient.v2_0 import client as neutronclient from novaclient import client as novaclient +from troveclient import client as troveclient + from oslo_config import cfg from oslo_log import log @@ -231,3 +233,38 @@ class CinderAction(base.OpenStackAction): @classmethod def _get_fake_client(cls): return cls._client_class() + + +class TroveAction(base.OpenStackAction): + _client_class = troveclient.Client + + def _get_client(self): + ctx = context.ctx() + + LOG.debug("Trove action security context: %s" % ctx) + + trove_endpoint = keystone_utils.get_endpoint_for_project( + service_type='database' + ) + + trove_url = keystone_utils.format_url( + trove_endpoint.url, + {'tenant_id': ctx.project_id} + ) + + client = self._client_class( + ctx.user_name, + ctx.auth_token, + project_id=ctx.project_id, + auth_url=trove_url, + region_name=trove_endpoint.region + ) + + client.client.auth_token = ctx.auth_token + client.client.management_url = trove_url + + return client + + @classmethod + def _get_fake_client(cls): + return cls._client_class() diff --git a/mistral/actions/openstack/mapping.json b/mistral/actions/openstack/mapping.json index c613a39fa..627f17523 100644 --- a/mistral/actions/openstack/mapping.json +++ b/mistral/actions/openstack/mapping.json @@ -795,5 +795,75 @@ "volumes_update_all_metadata": "volumes.update_all_metadata", "volumes_update_readonly_flag": "volumes.update_readonly_flag", "volumes_upload_to_image": "volumes.upload_to_image" + }, + "trove": { + "_comment": "It uses troveclient.v1.", + "flavors_list": "flavors.list", + "flavors_list_datastore_version_associated_flavors": "flavors.list_datastore_version_associated_flavors", + "flavors_get": "flavors.get", + "users_create": "users.create", + "users_delete": "users.delete", + "users_list": "users.list", + "users_get": "users.get", + "users_update_attributes": "users.update_attributes", + "users_list_access": "users.list_access", + "users_grant": "users.grant", + "users_revoke": "users.revoke", + "users_change_passwords": "users.change_passwords", + "databases_create": "databases.create", + "databases_delete": "databases.delete", + "databases_list": "databases.list", + "backups_list": "backups.list", + "backups_create": "backups.create", + "backups_get": "backups.get", + "backups_delete": "backups.delete", + "clusters_create": "clusters.create", + "clusters_get": "clusters.get", + "clusters_list": "clusters.list", + "clusters_delete": "clusters.delete", + "clusters_add_shard": "clusters.add_shard", + "clusters_grow": "clusters.grow", + "clusters_shrink": "clusters.shrink", + "instances_create": "instances.create", + "instances_modify": "instances.modify", + "instances_edit": "instances.edit", + "instances_list": "instances.list", + "instances_get": "instances.get", + "instances_backups": "instances.backups", + "instances_delete": "instances.delete", + "instances_resize_volume": "instances.resize_volume", + "instances_resize_instance": "instances.resize_instance", + "instances_restart": "instances.restart", + "instances_configuration": "instances.configuration", + "instances_promote_to_replica_source": "instances.promote_to_replica_source", + "instances_eject_replica_source": "instances.eject_replica_source", + "limits_list": "limits.list", + "root_create": "root.create", + "root_create_instance_root": "root.create_instance_root", + "root_create_cluster_root": "root.create_cluster_root", + "root_is_root_enabled": "root.is_root_enabled", + "root_is_instance_root_enabled": "root.is_instance_root_enabled", + "root_is_cluster_root_enabled": "root.is_cluster_root_enabled", + "security_group_rules_create": "security_group_rules.create", + "security_group_rules_delete": "security_group_rules.delete", + "security_groups_list": "security_groups.list", + "security_groups_get": "security_groups.get", + "datastores_list": "datastores.list", + "datastores_get": "datastores.get", + "datastore_versions_list": "datastore_versions.list", + "datastore_versions_get": "datastore_versions.get", + "datastore_versions_get_by_uuid": "datastore_versions.get_by_uuid", + "datastore_versions_update": "datastore_versions.update", + "configurations_get": "configurations.get", + "configurations_instances": "configurations.instances", + "configurations_list": "configurations.list", + "configurations_create": "configurations.create", + "configurations_update": "configurations.update", + "configurations_edit": "configurations.edit", + "configurations_delete": "configurations.delete", + "configuration_parameters_parameters": "configuration_parameters.parameters", + "configuration_parameters_get_parameter": "configuration_parameters.get_parameter", + "configuration_parameters_parameters_by_version": "configuration_parameters.parameters_by_version", + "configuration_parameters_get_parameter_by_version": "configuration_parameters.get_parameter_by_version" } -} \ No newline at end of file +} diff --git a/mistral/tests/unit/actions/openstack/test_openstack_actions.py b/mistral/tests/unit/actions/openstack/test_openstack_actions.py index 1ce25dd92..7348900dc 100644 --- a/mistral/tests/unit/actions/openstack/test_openstack_actions.py +++ b/mistral/tests/unit/actions/openstack/test_openstack_actions.py @@ -102,3 +102,15 @@ class OpenStackActionTest(base.BaseTestCase): self.assertTrue(mocked().alarms.get.called) mocked().alarms.get.assert_called_once_with(alarm_id="1234-abcd") + + @mock.patch.object(actions.TroveAction, '_get_client') + def test_trove_action(self, mocked): + method_name = "instances.get" + action_class = actions.TroveAction + action_class.client_method_name = method_name + params = {'instance': '1234-abcd'} + action = action_class(**params) + action.run() + + self.assertTrue(mocked().instances.get.called) + mocked().instances.get.assert_called_once_with(instance="1234-abcd") diff --git a/mistral/tests/unit/actions/openstack/test_trove_generator.py b/mistral/tests/unit/actions/openstack/test_trove_generator.py new file mode 100644 index 000000000..ef88005e1 --- /dev/null +++ b/mistral/tests/unit/actions/openstack/test_trove_generator.py @@ -0,0 +1,32 @@ +# Copyright 2015 - AT&T Services, 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 +from mistral.actions.openstack import actions +from mistral.tests.unit import base + + +class TroveGeneratorTest(base.BaseTest): + def test_generator(self): + action_name = "trove.instances_list" + generator = generators.TroveActionGenerator + action_classes = generator.create_actions() + action = self._assert_single_item( + action_classes, + name=action_name + ) + + self.assertIsNotNone(generator) + self.assertTrue(issubclass(action['class'], actions.TroveAction)) + self.assertEqual("instances.list", action['class'].client_method_name) diff --git a/requirements.txt b/requirements.txt index d33932ee9..d9acd7c8e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,6 +28,7 @@ python-heatclient>=0.6.0 python-keystoneclient!=1.8.0,>=1.6.0 python-neutronclient>=2.6.0 python-novaclient!=2.33.0,>=2.29.0 +python-troveclient>=1.2.0 PyYAML>=3.1.0 requests!=2.8.0,>=2.5.2 retrying!=1.3.0,>=1.2.3 # Apache-2.0 diff --git a/tools/get_action_list.py b/tools/get_action_list.py index ca1c6c83b..b89168029 100644 --- a/tools/get_action_list.py +++ b/tools/get_action_list.py @@ -28,7 +28,8 @@ from keystoneclient import base as keystone_base from keystoneclient.v3 import client as keystoneclient from novaclient import client as novaclient from novaclient.openstack.common.apiclient import base as nova_base - +from troveclient import base as trove_base +from troveclient.v1 import client as troveclient # TODO(nmakhotkin): Find a rational way to do it for neutron. # TODO(nmakhotkin): Implement recursive way of searching for managers @@ -60,7 +61,7 @@ BASE_HEAT_MANAGER = heat_base.HookableMixin BASE_NOVA_MANAGER = nova_base.HookableMixin BASE_KEYSTONE_MANAGER = keystone_base.Manager BASE_CINDER_MANAGER = cinder_base.HookableMixin - +BASE_TROVE_MANAGER = trove_base.HookableMixin def get_parser(): parser = argparse.ArgumentParser( @@ -134,6 +135,8 @@ def get_ceilometer_client(**kwargs): def get_cinder_client(**kwargs): return cinderclient.Client() +def get_trove_client(**kwargs): + return troveclient.Client() CLIENTS = { 'nova': get_nova_client, @@ -142,6 +145,7 @@ CLIENTS = { 'cinder': get_cinder_client, 'keystone': get_keystone_client, 'glance': get_glance_client, + 'trove' : get_trove_client, # 'neutron': get_nova_client } BASE_MANAGERS = { @@ -151,6 +155,7 @@ BASE_MANAGERS = { 'cinder': BASE_CINDER_MANAGER, 'keystone': BASE_KEYSTONE_MANAGER, 'glance': None, + 'trove': BASE_TROVE_MANAGER, # 'neutron': BASE_NOVA_MANAGER } NAMESPACES = {