From 11b38f3188686368412cdbb22ef55570720e3002 Mon Sep 17 00:00:00 2001
From: Valeriy Ponomaryov <vponomaryov@mirantis.com>
Date: Fri, 14 Oct 2016 15:54:23 +0300
Subject: [PATCH] Add support of endpoint_type and region_name to clients
 manila uses

Since change [1] it is impossible to set endpoint_type and region_name
for nova, cinder and neutron clients that are used by manila in some
cases.
So, to fix it, add additional options called 'endpoint_type' and
'region_name' for each of config groups related to these clients.
These options can be defined in appropriate config group as following:

[nova]
endpoint_type = publicURL
region_name = SomeRegionName

[cinder]
endpoint_type = internalURL
region_name = SomeRegionName

[neutron]
endpoint_type = adminURL
region_name = SomeRegionName

[1] Ic211a11308a3295409467efd88bff413482ee58d

Change-Id: I6be0e77bbc0e4b1e9905eba7a7b48ff540e9e377
Closes-Bug: #1633454
---
 manila/compute/nova.py                        |  9 ++-
 manila/network/neutron/api.py                 | 13 +++-
 manila/tests/compute/test_nova.py             | 77 +++++++++++++++++++
 .../tests/network/neutron/test_neutron_api.py | 67 ++++++++++++++++
 manila/tests/volume/test_cinder.py            | 77 +++++++++++++++++++
 manila/volume/cinder.py                       | 17 ++--
 6 files changed, 252 insertions(+), 8 deletions(-)

diff --git a/manila/compute/nova.py b/manila/compute/nova.py
index f273d0ded7..0f59db21d7 100644
--- a/manila/compute/nova.py
+++ b/manila/compute/nova.py
@@ -92,6 +92,11 @@ nova_opts = [
                 deprecated_group="DEFAULT",
                 deprecated_name="nova_api_insecure",
                 help='Allow to perform insecure SSL requests to nova.'),
+    cfg.StrOpt('endpoint_type',
+               default='publicURL',
+               help='Endpoint type to be used with nova client calls.'),
+    cfg.StrOpt('region_name',
+               help='Region name for connecting to nova.'),
     ]
 
 CONF = cfg.CONF
@@ -123,7 +128,9 @@ def novaclient(context):
     return AUTH_OBJ.get_client(context,
                                version=CONF[NOVA_GROUP].api_microversion,
                                insecure=CONF[NOVA_GROUP].api_insecure,
-                               cacert=CONF[NOVA_GROUP].ca_certificates_file)
+                               cacert=CONF[NOVA_GROUP].ca_certificates_file,
+                               endpoint_type=CONF[NOVA_GROUP].endpoint_type,
+                               region_name=CONF[NOVA_GROUP].region_name)
 
 
 def _untranslate_server_summary_view(server):
diff --git a/manila/network/neutron/api.py b/manila/network/neutron/api.py
index b3d49547af..a61d55f1c3 100644
--- a/manila/network/neutron/api.py
+++ b/manila/network/neutron/api.py
@@ -93,9 +93,13 @@ neutron_opts = [
         deprecated_group="DEFAULT",
         help='Location of CA certificates file to use for '
              'neutron client requests.'),
+    cfg.StrOpt(
+        'endpoint_type',
+        default='publicURL',
+        help='Endpoint type to be used with neutron client calls.'),
     cfg.StrOpt(
         'region_name',
-        help='Region name for connecting to neutron in admin context')
+        help='Region name for connecting to neutron in admin context.'),
 ]
 
 CONF = cfg.CONF
@@ -145,7 +149,12 @@ class API(object):
                 cfg_group=NEUTRON_GROUP,
                 deprecated_opts_for_v2=v2_deprecated_opts)
 
-        return self.auth_obj.get_client(self, context)
+        return self.auth_obj.get_client(
+            self,
+            context,
+            endpoint_type=CONF[NEUTRON_GROUP].endpoint_type,
+            region_name=CONF[NEUTRON_GROUP].region_name,
+        )
 
     @property
     def admin_project_id(self):
diff --git a/manila/tests/compute/test_nova.py b/manila/tests/compute/test_nova.py
index 42f1b905b1..09ef9d9b59 100644
--- a/manila/tests/compute/test_nova.py
+++ b/manila/tests/compute/test_nova.py
@@ -22,6 +22,7 @@ from manila.compute import nova
 from manila import context
 from manila import exception
 from manila import test
+from manila.tests import utils as test_utils
 from manila.volume import cinder
 
 
@@ -131,6 +132,82 @@ class TranslateServerExceptionTestCase(test.TestCase):
             'foo_self', 'foo_ctxt', 'foo_instance_id', exc)
 
 
+def get_fake_auth_obj():
+    return type('FakeAuthObj', (object, ), {'get_client': mock.Mock()})
+
+
+class NovaclientTestCase(test.TestCase):
+
+    @mock.patch('manila.compute.nova.AUTH_OBJ', None)
+    def test_no_auth_obj(self):
+        mock_client_loader = self.mock_object(
+            nova.client_auth, 'AuthClientLoader')
+        fake_context = 'fake_context'
+        data = {
+            'DEFAULT': {
+                'nova_admin_username': 'foo_username',
+                'nova_admin_password': 'foo_password',
+                'nova_admin_tenant_name': 'foo_tenant_name',
+                'nova_admin_auth_url': 'foo_auth_url',
+            },
+            'nova': {
+                'api_microversion': 'foo_api_microversion',
+                'api_insecure': True,
+                'ca_certificates_file': 'foo_ca_certificates_file',
+                'endpoint_type': 'foo_endpoint_type',
+                'region_name': 'foo_region_name',
+            }
+        }
+
+        with test_utils.create_temp_config_with_opts(data):
+            nova.novaclient(fake_context)
+
+        mock_client_loader.assert_called_once_with(
+            client_class=nova.nova_client.Client,
+            exception_module=nova.nova_exception,
+            cfg_group=nova.NOVA_GROUP,
+            deprecated_opts_for_v2={
+                'username': data['DEFAULT']['nova_admin_username'],
+                'password': data['DEFAULT']['nova_admin_password'],
+                'tenant_name': data['DEFAULT']['nova_admin_tenant_name'],
+                'auth_url': data['DEFAULT']['nova_admin_auth_url'],
+            },
+        )
+        mock_client_loader.return_value.get_client.assert_called_once_with(
+            fake_context,
+            version=data['nova']['api_microversion'],
+            insecure=data['nova']['api_insecure'],
+            cacert=data['nova']['ca_certificates_file'],
+            endpoint_type=data['nova']['endpoint_type'],
+            region_name=data['nova']['region_name'],
+        )
+
+    @mock.patch('manila.compute.nova.AUTH_OBJ', get_fake_auth_obj())
+    def test_with_auth_obj(self):
+        fake_context = 'fake_context'
+        data = {
+            'nova': {
+                'api_microversion': 'foo_api_microversion',
+                'api_insecure': True,
+                'ca_certificates_file': 'foo_ca_certificates_file',
+                'endpoint_type': 'foo_endpoint_type',
+                'region_name': 'foo_region_name',
+            }
+        }
+
+        with test_utils.create_temp_config_with_opts(data):
+            nova.novaclient(fake_context)
+
+        nova.AUTH_OBJ.get_client.assert_called_once_with(
+            fake_context,
+            version=data['nova']['api_microversion'],
+            insecure=data['nova']['api_insecure'],
+            cacert=data['nova']['ca_certificates_file'],
+            endpoint_type=data['nova']['endpoint_type'],
+            region_name=data['nova']['region_name'],
+        )
+
+
 @ddt.ddt
 class NovaApiTestCase(test.TestCase):
     def setUp(self):
diff --git a/manila/tests/network/neutron/test_neutron_api.py b/manila/tests/network/neutron/test_neutron_api.py
index 0214280265..5374aba25d 100644
--- a/manila/tests/network/neutron/test_neutron_api.py
+++ b/manila/tests/network/neutron/test_neutron_api.py
@@ -26,6 +26,7 @@ from manila.network.neutron import api as neutron_api
 from manila.network.neutron import constants as neutron_constants
 from manila import test
 from manila.tests.db import fakes
+from manila.tests import utils as test_utils
 
 CONF = cfg.CONF
 
@@ -81,6 +82,72 @@ class FakeNeutronClient(object):
         pass
 
 
+class NeutronclientTestCase(test.TestCase):
+
+    def test_no_auth_obj(self):
+        mock_client_loader = self.mock_object(
+            neutron_api.client_auth, 'AuthClientLoader')
+        fake_context = 'fake_context'
+        data = {
+            'DEFAULT': {
+                'neutron_admin_username': 'foo_username',
+                'neutron_admin_password': 'foo_password',
+                'neutron_admin_tenant_name': 'foo_tenant_name',
+                'neutron_admin_auth_url': 'foo_auth_url',
+            },
+            'neutron': {
+                'endpoint_type': 'foo_endpoint_type',
+                'region_name': 'foo_region_name',
+            }
+        }
+
+        self.client = None
+        with test_utils.create_temp_config_with_opts(data):
+            self.client = neutron_api.API()
+            self.client.get_client(fake_context)
+
+        mock_client_loader.assert_called_once_with(
+            client_class=neutron_api.clientv20.Client,
+            exception_module=neutron_api.neutron_client_exc,
+            cfg_group=neutron_api.NEUTRON_GROUP,
+            deprecated_opts_for_v2={
+                'username': data['DEFAULT']['neutron_admin_username'],
+                'password': data['DEFAULT']['neutron_admin_password'],
+                'tenant_name': data['DEFAULT']['neutron_admin_tenant_name'],
+                'auth_url': data['DEFAULT']['neutron_admin_auth_url'],
+            },
+        )
+        mock_client_loader.return_value.get_client.assert_called_once_with(
+            self.client,
+            fake_context,
+            endpoint_type=data['neutron']['endpoint_type'],
+            region_name=data['neutron']['region_name'],
+        )
+
+    def test_with_auth_obj(self):
+        fake_context = 'fake_context'
+        data = {
+            'neutron': {
+                'endpoint_type': 'foo_endpoint_type',
+                'region_name': 'foo_region_name',
+            }
+        }
+
+        self.client = None
+        with test_utils.create_temp_config_with_opts(data):
+            self.client = neutron_api.API()
+            self.client.auth_obj = type(
+                'FakeAuthObj', (object, ), {'get_client': mock.Mock()})
+            self.client.get_client(fake_context)
+
+        self.client.auth_obj.get_client.assert_called_once_with(
+            self.client,
+            fake_context,
+            endpoint_type=data['neutron']['endpoint_type'],
+            region_name=data['neutron']['region_name'],
+        )
+
+
 class NeutronApiTest(test.TestCase):
 
     def setUp(self):
diff --git a/manila/tests/volume/test_cinder.py b/manila/tests/volume/test_cinder.py
index d6709ccc41..5082993e34 100644
--- a/manila/tests/volume/test_cinder.py
+++ b/manila/tests/volume/test_cinder.py
@@ -19,6 +19,7 @@ import mock
 from manila import context
 from manila import exception
 from manila import test
+from manila.tests import utils as test_utils
 from manila.volume import cinder
 
 
@@ -41,6 +42,82 @@ class FakeCinderClient(object):
         self.volume_snapshots = self.volumes
 
 
+def get_fake_auth_obj():
+    return type('FakeAuthObj', (object, ), {'get_client': mock.Mock()})
+
+
+class CinderclientTestCase(test.TestCase):
+
+    @mock.patch('manila.volume.cinder.AUTH_OBJ', None)
+    def test_no_auth_obj(self):
+        mock_client_loader = self.mock_object(
+            cinder.client_auth, 'AuthClientLoader')
+        fake_context = 'fake_context'
+        data = {
+            'DEFAULT': {
+                'cinder_admin_username': 'foo_username',
+                'cinder_admin_password': 'foo_password',
+                'cinder_admin_tenant_name': 'foo_tenant_name',
+                'cinder_admin_auth_url': 'foo_auth_url',
+            },
+            'cinder': {
+                'api_insecure': True,
+                'ca_certificates_file': 'foo_ca_certificates_file',
+                'http_retries': 3,
+                'endpoint_type': 'foo_endpoint_type',
+                'region_name': 'foo_region_name',
+            }
+        }
+
+        with test_utils.create_temp_config_with_opts(data):
+            cinder.cinderclient(fake_context)
+
+        mock_client_loader.assert_called_once_with(
+            client_class=cinder.cinder_client.Client,
+            exception_module=cinder.cinder_exception,
+            cfg_group=cinder.CINDER_GROUP,
+            deprecated_opts_for_v2={
+                'username': data['DEFAULT']['cinder_admin_username'],
+                'password': data['DEFAULT']['cinder_admin_password'],
+                'tenant_name': data['DEFAULT']['cinder_admin_tenant_name'],
+                'auth_url': data['DEFAULT']['cinder_admin_auth_url'],
+            },
+        )
+        mock_client_loader.return_value.get_client.assert_called_once_with(
+            fake_context,
+            insecure=data['cinder']['api_insecure'],
+            cacert=data['cinder']['ca_certificates_file'],
+            retries=data['cinder']['http_retries'],
+            endpoint_type=data['cinder']['endpoint_type'],
+            region_name=data['cinder']['region_name'],
+        )
+
+    @mock.patch('manila.volume.cinder.AUTH_OBJ', get_fake_auth_obj())
+    def test_with_auth_obj(self):
+        fake_context = 'fake_context'
+        data = {
+            'cinder': {
+                'api_insecure': True,
+                'ca_certificates_file': 'foo_ca_certificates_file',
+                'http_retries': 3,
+                'endpoint_type': 'foo_endpoint_type',
+                'region_name': 'foo_region_name',
+            }
+        }
+
+        with test_utils.create_temp_config_with_opts(data):
+            cinder.cinderclient(fake_context)
+
+        cinder.AUTH_OBJ.get_client.assert_called_once_with(
+            fake_context,
+            insecure=data['cinder']['api_insecure'],
+            cacert=data['cinder']['ca_certificates_file'],
+            retries=data['cinder']['http_retries'],
+            endpoint_type=data['cinder']['endpoint_type'],
+            region_name=data['cinder']['region_name'],
+        )
+
+
 @ddt.ddt
 class CinderApiTestCase(test.TestCase):
     def setUp(self):
diff --git a/manila/volume/cinder.py b/manila/volume/cinder.py
index adca896c2d..39f78255aa 100644
--- a/manila/volume/cinder.py
+++ b/manila/volume/cinder.py
@@ -96,6 +96,11 @@ cinder_opts = [
                 help='Allow to perform insecure SSL requests to cinder.',
                 deprecated_group='DEFAULT',
                 deprecated_name="cinder_api_insecure"),
+    cfg.StrOpt('endpoint_type',
+               default='publicURL',
+               help='Endpoint type to be used with cinder client calls.'),
+    cfg.StrOpt('region_name',
+               help='Region name for connecting to cinder.'),
     ]
 
 CONF = cfg.CONF
@@ -114,10 +119,10 @@ def cinderclient(context):
     global AUTH_OBJ
     if not AUTH_OBJ:
         deprecated_opts_for_v2 = {
-            'username': CONF.nova_admin_username,
-            'password': CONF.nova_admin_password,
-            'tenant_name': CONF.nova_admin_tenant_name,
-            'auth_url': CONF.nova_admin_auth_url,
+            'username': CONF.cinder_admin_username,
+            'password': CONF.cinder_admin_password,
+            'tenant_name': CONF.cinder_admin_tenant_name,
+            'auth_url': CONF.cinder_admin_auth_url,
         }
         AUTH_OBJ = client_auth.AuthClientLoader(
             client_class=cinder_client.Client,
@@ -127,7 +132,9 @@ def cinderclient(context):
     return AUTH_OBJ.get_client(context,
                                insecure=CONF[CINDER_GROUP].api_insecure,
                                cacert=CONF[CINDER_GROUP].ca_certificates_file,
-                               retries=CONF[CINDER_GROUP].http_retries)
+                               retries=CONF[CINDER_GROUP].http_retries,
+                               endpoint_type=CONF[CINDER_GROUP].endpoint_type,
+                               region_name=CONF[CINDER_GROUP].region_name)
 
 
 def _untranslate_volume_summary_view(context, vol):