diff --git a/vmware_nsxlib/tests/unit/v3/nsxlib_testcase.py b/vmware_nsxlib/tests/unit/v3/nsxlib_testcase.py index 161dcfdc..e7d1cf4a 100644 --- a/vmware_nsxlib/tests/unit/v3/nsxlib_testcase.py +++ b/vmware_nsxlib/tests/unit/v3/nsxlib_testcase.py @@ -30,6 +30,7 @@ NSX_PASSWORD = 'default' NSX_MANAGER = '1.2.3.4' NSX_INSECURE = False NSX_CERT = '/opt/stack/certs/nsx.pem' +CLIENT_CERT = '/opt/stack/certs/client.pem' NSX_HTTP_RETRIES = 10 NSX_HTTP_TIMEOUT = 10 NSX_HTTP_READ_TIMEOUT = 180 @@ -108,13 +109,37 @@ def get_default_nsxlib_config(): plugin_ver=PLUGIN_VER) +def get_nsxlib_config_with_client_cert(): + return config.NsxLibConfig( + client_cert_file=CLIENT_CERT, + retries=NSX_HTTP_RETRIES, + insecure=NSX_INSECURE, + ca_file=NSX_CERT, + concurrent_connections=NSX_CONCURENT_CONN, + http_timeout=NSX_HTTP_TIMEOUT, + http_read_timeout=NSX_HTTP_READ_TIMEOUT, + conn_idle_timeout=NSX_CONN_IDLE_TIME, + http_provider=None, + nsx_api_managers=[], + plugin_scope=PLUGIN_SCOPE, + plugin_tag=PLUGIN_TAG, + plugin_ver=PLUGIN_VER) + + class NsxLibTestCase(unittest.TestCase): + def use_client_cert_auth(self): + return False + def setUp(self, *args, **kwargs): super(NsxLibTestCase, self).setUp() _mock_nsxlib() - nsxlib_config = get_default_nsxlib_config() + if self.use_client_cert_auth(): + nsxlib_config = get_nsxlib_config_with_client_cert() + else: + nsxlib_config = get_default_nsxlib_config() + self.nsxlib = v3.NsxLib(nsxlib_config) # print diffs when assert comparisons fail diff --git a/vmware_nsxlib/tests/unit/v3/test_cluster.py b/vmware_nsxlib/tests/unit/v3/test_cluster.py index a4a6307d..747c57d3 100644 --- a/vmware_nsxlib/tests/unit/v3/test_cluster.py +++ b/vmware_nsxlib/tests/unit/v3/test_cluster.py @@ -58,6 +58,25 @@ class RequestsHTTPProviderTestCase(unittest.TestCase): self.assertEqual(session.adapters['https://'].max_retries.total, 100) self.assertEqual(session.timeout, 99) + def test_new_connection_with_client_auth(self): + mock_api = mock.Mock() + mock_api.nsxlib_config = mock.Mock() + mock_api.nsxlib_config.retries = 100 + mock_api.nsxlib_config.insecure = True + mock_api.nsxlib_config.ca_file = None + mock_api.nsxlib_config.http_timeout = 99 + mock_api.nsxlib_config.conn_idle_timeout = 39 + provider = cluster.NSXRequestsHTTPProvider() + session = provider.new_connection( + mock_api, cluster.Provider('9.8.7.6', 'https://9.8.7.6', + None, None, None, + '/etc/cert.pem')) + + self.assertEqual(session.auth, None) + self.assertEqual(session.verify, False) + self.assertEqual(session.cert, '/etc/cert.pem') + self.assertEqual(session.timeout, 99) + def test_validate_connection(self): self.skipTest("Revist") mock_conn = mocks.MockRequestSessionApi() @@ -125,6 +144,14 @@ class NsxV3ClusteredAPITestCase(nsxlib_testcase.NsxClientTestCase): self.assertEqual(kwargs['timeout'], (7, 37)) +# Repeat the above tests with client cert present +# in NsxLib initialization +class NsxV3ClusteredAPIWithClientCertTestCase(NsxV3ClusteredAPITestCase): + + def use_client_cert_auth(self): + return True + + class ClusteredAPITestCase(nsxlib_testcase.NsxClientTestCase): def _test_health(self, validate_fn, expected_health): diff --git a/vmware_nsxlib/v3/cluster.py b/vmware_nsxlib/v3/cluster.py index 55bef58f..450e2f0b 100644 --- a/vmware_nsxlib/v3/cluster.py +++ b/vmware_nsxlib/v3/cluster.py @@ -121,7 +121,11 @@ class NSXRequestsHTTPProvider(AbstractHTTPProvider): config = cluster_api.nsxlib_config session = TimeoutSession(config.http_timeout, config.http_read_timeout) - session.auth = (provider.username, provider.password) + if provider.client_cert_file: + session.cert = provider.client_cert_file + else: + session.auth = (provider.username, provider.password) + # NSX v3 doesn't use redirects session.max_redirects = 0 @@ -172,11 +176,13 @@ class Provider(object): a connection URL, and the credential details. """ - def __init__(self, provider_id, provider_url, username, password, ca_file): + def __init__(self, provider_id, provider_url, + username, password, ca_file, client_cert_file=None): self.id = provider_id self.url = provider_url self.username = username self.password = password + self.client_cert_file = client_cert_file self.ca_file = ca_file def __str__(self): @@ -489,5 +495,6 @@ class NSXClusteredAPI(ClusteredAPI): urlparse.urlunparse(conf_url), self.nsxlib_config.username(provider_index), self.nsxlib_config.password(provider_index), - self.nsxlib_config.ca_file(provider_index))) + self.nsxlib_config.ca_file(provider_index), + self.nsxlib_config.client_cert_file(provider_index))) return providers diff --git a/vmware_nsxlib/v3/config.py b/vmware_nsxlib/v3/config.py index dc831f12..b032085f 100644 --- a/vmware_nsxlib/v3/config.py +++ b/vmware_nsxlib/v3/config.py @@ -25,6 +25,10 @@ class NsxLibConfig(object): and port 443 for https. :param username: User name for the NSX manager :param password: Password for the NSX manager + :param client_cert_file: Specify a file containing client certificate and + private key. If specified, nsxlib will use client + cert authentication instead of basic + authentication. :param insecure: If true, the NSX Manager server certificate is not verified. If false the CA bundle specified via "ca_file" will be used or if unsest the default system root CAs @@ -67,6 +71,7 @@ class NsxLibConfig(object): nsx_api_managers=None, username=None, password=None, + client_cert_file=None, insecure=True, ca_file=None, concurrent_connections=10, @@ -86,6 +91,7 @@ class NsxLibConfig(object): self.nsx_api_managers = nsx_api_managers self._username = username self._password = password + self._client_cert_file = client_cert_file self._ca_file = ca_file self.insecure = insecure self.concurrent_connections = concurrent_connections @@ -119,5 +125,8 @@ class NsxLibConfig(object): def password(self, index): return self._attribute_by_index(self._password, index) + def client_cert_file(self, index): + return self._attribute_by_index(self._client_cert_file, index) + def ca_file(self, index): return self._attribute_by_index(self._ca_file, index)