From 9c63a24797890acab1f6be1d74871b950258297a Mon Sep 17 00:00:00 2001 From: Jeff Peeler Date: Wed, 29 Jun 2016 14:24:21 -0400 Subject: [PATCH] Add configuration option for endpoint type This adds a new option in the default group called os-actions-endpoint-type. Doing so allows removing the hard coding of the parameter passed into novaclient. Although novaclient on the CLI will append the URL suffix to the option [1], it seems passing in the full text (e.g., publicURL) is most appropriate. The environment variable OS_ACTIONS_ENDPOINT_TYPE may also be specified instead of using the option from the configuration file. [1] https://github.com/openstack/python-novaclient/blob/d6ca9e2d7473f6d15a3c76bcd5a7c3cc65b49296/novaclient/shell.py#L809 Change-Id: I1ac52113193f286b6f2ee53f6221c39de91d20f1 Closes-bug: #1526300 --- mistral/actions/openstack/actions.py | 2 +- mistral/config.py | 13 ++- .../openstack/test_openstack_actions.py | 88 ++++++++++++++++++- 3 files changed, 100 insertions(+), 3 deletions(-) diff --git a/mistral/actions/openstack/actions.py b/mistral/actions/openstack/actions.py index 62808e66..39e340ab 100644 --- a/mistral/actions/openstack/actions.py +++ b/mistral/actions/openstack/actions.py @@ -64,7 +64,7 @@ class NovaAction(base.OpenStackAction): 2, username=None, api_key=None, - endpoint_type='publicURL', + endpoint_type=CONF.os_actions_endpoint_type, service_type='compute', auth_token=ctx.auth_token, tenant_id=ctx.project_id, diff --git a/mistral/config.py b/mistral/config.py index 9d70ecd0..166c18f5 100644 --- a/mistral/config.py +++ b/mistral/config.py @@ -18,6 +18,7 @@ Configuration options registration and useful routines. """ import itertools +import os from oslo_config import cfg from oslo_log import log @@ -54,6 +55,14 @@ rpc_impl_opt = cfg.StrOpt( 'kombu driver is experimental.' ) +os_endpoint_type = cfg.StrOpt( + 'os-actions-endpoint-type', + default=os.environ.get('OS_ACTIONS_ENDPOINT_TYPE', 'publicURL'), + choices=['publicURL', 'adminURL', 'internalURL'], + help='Type of endpoint in identity service catalog to use for' + ' communication with OpenStack services.' +) + pecan_opts = [ cfg.StrOpt( 'root', @@ -197,6 +206,7 @@ CONF.register_opt(wf_trace_log_name_opt) CONF.register_opts(coordination_opts, group=COORDINATION_GROUP) CONF.register_opts(profiler_opts, group=PROFILER_GROUP) CONF.register_opt(rpc_impl_opt) +CONF.register_opt(os_endpoint_type) CLI_OPTS = [ @@ -233,7 +243,8 @@ def list_opts(): CLI_OPTS, [ wf_trace_log_name_opt, - rpc_impl_opt + rpc_impl_opt, + os_endpoint_type, ] )) ] diff --git a/mistral/tests/unit/actions/openstack/test_openstack_actions.py b/mistral/tests/unit/actions/openstack/test_openstack_actions.py index 45f33b81..b861144f 100644 --- a/mistral/tests/unit/actions/openstack/test_openstack_actions.py +++ b/mistral/tests/unit/actions/openstack/test_openstack_actions.py @@ -13,9 +13,16 @@ # limitations under the License. import mock -from oslotest import base from mistral.actions.openstack import actions +from mistral.config import CONF +from mistral import context as ctx +from oslotest import base + + +class FakeEndpoint(object): + def __init__(self, **kwargs): + self.__dict__.update(kwargs) class OpenStackActionTest(base.BaseTestCase): @@ -31,6 +38,85 @@ class OpenStackActionTest(base.BaseTestCase): self.assertTrue(mocked().servers.get.called) mocked().servers.get.assert_called_once_with(server="1234-abcd") + @mock.patch('mistral.actions.openstack.actions.keystone_utils.get_' + 'keystone_endpoint_v2') + @mock.patch('mistral.actions.openstack.actions.keystone_utils.get_' + 'endpoint_for_project') + @mock.patch('mistral.actions.openstack.actions.novaclient') + def test_nova_action_config_endpoint(self, mock_novaclient, + mock_nova_endpoint, + mock_ks_endpoint_v2): + + # this is the default, but be explicit + CONF.set_default('os_actions_endpoint_type', 'publicURL') + + test_ctx = ctx.MistralContext( + user_id=None, + project_id='1234', + project_name='admin', + auth_token=None, + is_admin=False + ) + ctx.set_ctx(test_ctx) + + # attributes mirror keystone Endpoint object exactly + # (with endpoint type publicURL) + keystone_attrs = { + 'url': 'http://192.0.2.1:5000/v2.0', + 'enabled': True, + 'id': 'b1ddf133fa6e491c8ee13701be97db2d', + 'interface': 'public', + 'links': { + u'self': u'http://192.0.2.1:5000/v3/endpoints/' + 'b1ddf133fa6e491c8ee13701be97db2d' + }, + 'region': 'regionOne', + 'region_id': 'regionOne', + 'service_id': '8f4afc75cd584d5cb381f68a9db80147', + } + keystone_endpoint = FakeEndpoint(**keystone_attrs) + + nova_attrs = { + 'url': 'http://192.0.2.1:8774/v2/%(tenant_id)s', + 'enabled': True, + 'id': '5bb51b33c9984513b52b6a3e85154305', + 'interface': 'public', + 'links': { + u'self': u'http://192.0.2.1:5000/v3/endpoints/' + '5bb51b33c9984513b52b6a3e85154305' + }, + 'region': 'regionOne', + 'region_id': 'regionOne', + 'service_id': '1af46173f37848edb65bd4962ed2d09d', + } + nova_endpoint = FakeEndpoint(**nova_attrs) + + mock_ks_endpoint_v2.return_value(keystone_endpoint) + mock_nova_endpoint.return_value(nova_endpoint) + + method_name = "servers.get" + action_class = actions.NovaAction + action_class.client_method_name = method_name + params = {'server': '1234-abcd'} + action = action_class(**params) + action.run() + + mock_novaclient.Client.assert_called_once_with( + 2, + username=None, + api_key=None, + endpoint_type='publicURL', + service_type='compute', + auth_token=test_ctx.auth_token, + tenant_id=test_ctx.project_id, + region_name=mock_ks_endpoint_v2().region, + auth_url=mock_ks_endpoint_v2().url + ) + + self.assertTrue(mock_novaclient.Client().servers.get.called) + mock_novaclient.Client().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"