From 00d3677615b0587fc05e3043ee0a3a10d237c780 Mon Sep 17 00:00:00 2001 From: Kanagaraj Manickam Date: Wed, 9 Dec 2015 22:51:54 +0530 Subject: [PATCH] Enable client plugin to support dynamic api version It helps client plugin to create client based on the required api version given at run time instead of hard-coded version. implements blueprint enable-client-plugin-to-use-a-given-service-api-version Change-Id: I1cf3cdf498b7a97b6aa262eb3012edda8ad2aaaf --- heat/common/exception.py | 4 ++ heat/engine/clients/__init__.py | 7 +++- heat/engine/clients/client_plugin.py | 47 ++++++++++++++++------ heat/engine/resource.py | 4 +- heat/tests/clients/test_barbican_client.py | 2 +- heat/tests/clients/test_cinder_client.py | 2 +- heat/tests/clients/test_clients.py | 7 ++-- heat/tests/clients/test_glance_client.py | 2 +- heat/tests/clients/test_manila_client.py | 2 +- heat/tests/clients/test_neutron_client.py | 2 +- heat/tests/clients/test_nova_client.py | 4 +- heat/tests/clients/test_sahara_client.py | 2 +- heat/tests/clients/test_swift_client.py | 2 +- 13 files changed, 58 insertions(+), 29 deletions(-) diff --git a/heat/common/exception.py b/heat/common/exception.py index 8ef78e2d5f..01b62c47bf 100644 --- a/heat/common/exception.py +++ b/heat/common/exception.py @@ -500,3 +500,7 @@ class SIGHUPInterrupt(HeatException): class NoActionRequired(Exception): pass + + +class InvalidServiceVersion(HeatException): + msg_fmt = _("Invalid service %(service)s version %(version)s") diff --git a/heat/engine/clients/__init__.py b/heat/engine/clients/__init__.py index 2730a911cf..fd15ce1fde 100644 --- a/heat/engine/clients/__init__.py +++ b/heat/engine/clients/__init__.py @@ -64,10 +64,13 @@ class OpenStackClients(object): self._client_plugins[name] = client_plugin return client_plugin - def client(self, name): + def client(self, name, version=None): client_plugin = self.client_plugin(name) if client_plugin: - return client_plugin.client() + if version: + return client_plugin.client(version=version) + else: + return client_plugin.client() if name in self._clients: return self._clients[name] diff --git a/heat/engine/clients/client_plugin.py b/heat/engine/clients/client_plugin.py index 8e03471395..fec60fc1c3 100644 --- a/heat/engine/clients/client_plugin.py +++ b/heat/engine/clients/client_plugin.py @@ -26,6 +26,7 @@ import requests import six from heat.common import config +from heat.common import exception as heat_exception from heat.common.i18n import _ cfg.CONF.import_opt('client_retry_limit', 'heat.common.config') @@ -99,10 +100,15 @@ class ClientPlugin(object): # types, so its used in list format service_types = [] + # To make the backward compatibility with existing resource plugins + default_version = None + + supported_versions = [] + def __init__(self, context): self._context = weakref.ref(context) self._clients = weakref.ref(context.clients) - self._client = None + self.invalidate() self._keystone_session_obj = None @property @@ -135,23 +141,38 @@ class ClientPlugin(object): def invalidate(self): """Invalidate/clear any cached client.""" - self._client = None + self._client_instances = {} - def client(self): - if not self._client: - self._client = self._create() - elif (cfg.CONF.reauthentication_auth_method == 'trusts' + def client(self, version=None): + if not version: + version = self.default_version + + if version in self._client_instances: + if (cfg.CONF.reauthentication_auth_method == 'trusts' and self.context.auth_plugin.auth_ref.will_expire_soon( cfg.CONF.stale_token_duration)): - # If the token is near expiry, force creating a new client, - # which will get a new token via another call to auth_token - # We also have to invalidate all other cached clients - self.clients.invalidate_plugins() - self._client = self._create() - return self._client + # If the token is near expiry, force creating a new client, + # which will get a new token via another call to auth_token + # We also have to invalidate all other cached clients + self.clients.invalidate_plugins() + else: + return self._client_instances[version] + + # Back-ward compatibility + if version is None: + self._client_instances[version] = self._create() + else: + if version not in self.supported_versions: + raise heat_exception.InvalidServiceVersion( + version=version, + service=self._get_service_name()) + + self._client_instances[version] = self._create(version=version) + + return self._client_instances[version] @abc.abstractmethod - def _create(self): + def _create(self, version=None): """Return a newly created client.""" pass diff --git a/heat/engine/resource.py b/heat/engine/resource.py index c008697624..accb4f973e 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -609,10 +609,10 @@ class Resource(object): """ return [r.name for r in self.stack.dependencies.required_by(self)] - def client(self, name=None): + def client(self, name=None, version=None): client_name = name or self.default_client_name assert client_name, "Must specify client name" - return self.stack.clients.client(client_name) + return self.stack.clients.client(client_name, version) def client_plugin(self, name=None): client_name = name or self.default_client_name diff --git a/heat/tests/clients/test_barbican_client.py b/heat/tests/clients/test_barbican_client.py index 94509e3b04..6a08149842 100644 --- a/heat/tests/clients/test_barbican_client.py +++ b/heat/tests/clients/test_barbican_client.py @@ -30,7 +30,7 @@ class BarbicanClientPluginTest(common.HeatTestCase): con = utils.dummy_context() c = con.clients self.barbican_plugin = c.client_plugin('barbican') - self.barbican_plugin._client = self.barbican_client + self.barbican_plugin.client = lambda: self.barbican_client def test_create(self): context = utils.dummy_context() diff --git a/heat/tests/clients/test_cinder_client.py b/heat/tests/clients/test_cinder_client.py index 9d90a23769..13a84bde5a 100644 --- a/heat/tests/clients/test_cinder_client.py +++ b/heat/tests/clients/test_cinder_client.py @@ -31,7 +31,7 @@ class CinderClientPluginTest(common.HeatTestCase): con = utils.dummy_context() c = con.clients self.cinder_plugin = c.client_plugin('cinder') - self.cinder_plugin._client = self.cinder_client + self.cinder_plugin.client = lambda: self.cinder_client def test_get_volume(self): """Tests the get_volume function.""" diff --git a/heat/tests/clients/test_clients.py b/heat/tests/clients/test_clients.py index 344c5c7867..e981c86879 100644 --- a/heat/tests/clients/test_clients.py +++ b/heat/tests/clients/test_clients.py @@ -155,7 +155,6 @@ class ClientsTest(common.HeatTestCase): obj.get_heat_url.return_value = None obj.url_for = mock.Mock(name="url_for") obj.url_for.return_value = "url_from_keystone" - obj._client = None heat = obj.client() heat_cached = obj.client() self.assertEqual(heat, heat_cached) @@ -414,7 +413,7 @@ class TestClientPluginsInitialise(common.HeatTestCase): self.assertIsNotNone(plugin) self.assertEqual(c, plugin.clients) self.assertEqual(con, plugin.context) - self.assertIsNone(plugin._client) + self.assertEqual({}, plugin._client_instances) self.assertTrue(clients.has_client(plugin_name)) self.assertIsInstance(plugin.service_types, list) self.assertTrue(len(plugin.service_types) >= 1, @@ -430,7 +429,9 @@ class TestClientPluginsInitialise(common.HeatTestCase): plugin = c.client_plugin(plugin_name) self.assertIsNotNone(plugin) c.invalidate_plugins() - self.assertEqual(len(plugin_types), mock_invalidate.call_count) + # while client plugin is initialized and while client is invoked + # its being invalidated, so the count will be doubled + self.assertEqual(len(plugin_types) * 2, mock_invalidate.call_count) class TestIsNotFound(common.HeatTestCase): diff --git a/heat/tests/clients/test_glance_client.py b/heat/tests/clients/test_glance_client.py index 36443e7fa1..f73083f83f 100644 --- a/heat/tests/clients/test_glance_client.py +++ b/heat/tests/clients/test_glance_client.py @@ -31,7 +31,7 @@ class GlanceUtilsTest(common.HeatTestCase): con = utils.dummy_context() c = con.clients self.glance_plugin = c.client_plugin('glance') - self.glance_plugin._client = self.glance_client + self.glance_plugin.client = lambda: self.glance_client self.my_image = mock.MagicMock() def test_find_image_by_name_or_id(self): diff --git a/heat/tests/clients/test_manila_client.py b/heat/tests/clients/test_manila_client.py index d18179ae01..a1a986091b 100644 --- a/heat/tests/clients/test_manila_client.py +++ b/heat/tests/clients/test_manila_client.py @@ -43,7 +43,7 @@ class ManilaClientPluginTest(common.HeatTestCase): con = utils.dummy_context() c = con.clients self.manila_plugin = c.client_plugin('manila') - self.manila_plugin._client = self.manila_client + self.manila_plugin.client = lambda: self.manila_client # prepare list of items to test search Item = collections.namedtuple('Item', ['id', 'name']) self.item_list = [ diff --git a/heat/tests/clients/test_neutron_client.py b/heat/tests/clients/test_neutron_client.py index 525483947b..5a0c288ebf 100644 --- a/heat/tests/clients/test_neutron_client.py +++ b/heat/tests/clients/test_neutron_client.py @@ -32,7 +32,7 @@ class NeutronClientPluginTestCase(common.HeatTestCase): con = utils.dummy_context() c = con.clients self.neutron_plugin = c.client_plugin('neutron') - self.neutron_plugin._client = self.neutron_client + self.neutron_plugin.client = lambda: self.neutron_client class NeutronClientPluginTest(NeutronClientPluginTestCase): diff --git a/heat/tests/clients/test_nova_client.py b/heat/tests/clients/test_nova_client.py index c3459688fd..42604f1703 100644 --- a/heat/tests/clients/test_nova_client.py +++ b/heat/tests/clients/test_nova_client.py @@ -38,7 +38,7 @@ class NovaClientPluginTestCase(common.HeatTestCase): con = utils.dummy_context() c = con.clients self.nova_plugin = c.client_plugin('nova') - self.nova_plugin._client = self.nova_client + self.nova_plugin.client = lambda: self.nova_client class NovaClientPluginTest(NovaClientPluginTestCase): @@ -628,7 +628,7 @@ class ConsoleUrlsTest(common.HeatTestCase): con = utils.dummy_context() c = con.clients self.nova_plugin = c.client_plugin('nova') - self.nova_plugin._client = self.nova_client + self.nova_plugin.client = lambda: self.nova_client self.server = mock.Mock() self.console_method = getattr(self.server, 'get_%s_console' % self.srv_method) diff --git a/heat/tests/clients/test_sahara_client.py b/heat/tests/clients/test_sahara_client.py index fbde71e2ce..bf8f9f6313 100644 --- a/heat/tests/clients/test_sahara_client.py +++ b/heat/tests/clients/test_sahara_client.py @@ -32,7 +32,7 @@ class SaharaUtilsTest(common.HeatTestCase): con = utils.dummy_context() c = con.clients self.sahara_plugin = c.client_plugin('sahara') - self.sahara_plugin._client = self.sahara_client + self.sahara_plugin.client = lambda: self.sahara_client self.my_image = mock.MagicMock() self.my_plugin = mock.MagicMock() diff --git a/heat/tests/clients/test_swift_client.py b/heat/tests/clients/test_swift_client.py index 2e587e12b0..97ae9dd012 100644 --- a/heat/tests/clients/test_swift_client.py +++ b/heat/tests/clients/test_swift_client.py @@ -30,7 +30,7 @@ class SwiftClientPluginTestCase(common.HeatTestCase): self.context.tenant_id = "demo" c = self.context.clients self.swift_plugin = c.client_plugin('swift') - self.swift_plugin._client = self.swift_client + self.swift_plugin.client = lambda: self.swift_client class SwiftUtilsTest(SwiftClientPluginTestCase):