NSX|V3 support different credentials for the NSX manages

In case of multiple NSX managers in the nsx_api_managers configuration,
it is now possible to configure a different username/password/ca_file for each
of the managers.
The nsxv3 configuration parameters ca_file, nsx_api_user & nsx_api_password are
now lists.
If they contain only 1 value, it will be used for all the managers.
Else, the order of of the values is expected to match the order of the
nsx_api_managers.

Change-Id: I31b955c9ee449126acde96de48a1887b94c38e29
This commit is contained in:
Adit Sarfaty 2016-09-13 12:14:51 +03:00
parent cdf7ab939c
commit 367d511068
4 changed files with 66 additions and 25 deletions

View File

@ -0,0 +1,7 @@
---
prelude: >
The NSX-v3 plugin supports different credentials for the NSX managers.
features:
The nsxv3 configuration parameters ca_file, nsx_api_user & nsx_api_password
are now lists, in order to support different credentials for each of the
NSX managers.

View File

@ -246,15 +246,15 @@ nsx_common_opts = [
]
nsx_v3_opts = [
cfg.StrOpt('nsx_api_user',
cfg.ListOpt('nsx_api_user',
deprecated_name='nsx_user',
default='admin',
help=_('User name for the NSX manager')),
cfg.StrOpt('nsx_api_password',
default=['admin'],
help=_('User names for the NSX managers')),
cfg.ListOpt('nsx_api_password',
deprecated_name='nsx_password',
default='default',
default=['default'],
secret=True,
help=_('Password for the NSX manager')),
help=_('Passwords for the NSX managers')),
cfg.ListOpt('nsx_api_managers',
default=[],
deprecated_name='nsx_manager',
@ -291,10 +291,10 @@ nsx_v3_opts = [
default=10,
help=_('Maximum number of times to retry API requests upon '
'stale revision errors.')),
cfg.StrOpt('ca_file',
help=_('Specify a CA bundle file to use in verifying the NSX '
'Manager server certificate. This option is ignored if '
'"insecure" is set to True. If "insecure" is set to '
cfg.ListOpt('ca_file',
help=_('Specify a CA bundle files to use in verifying the NSX '
'Managers server certificate. This option is ignored '
'if "insecure" is set to True. If "insecure" is set to '
'False and ca_file is unset, the system root CAs will '
'be used to verify the server certificate.')),
cfg.BoolOpt('insecure',

View File

@ -124,14 +124,14 @@ class NSXRequestsHTTPProvider(AbstractHTTPProvider):
def new_connection(self, cluster_api, provider):
session = TimeoutSession(cluster_api.http_timeout,
cluster_api.http_read_timeout)
session.auth = (cluster_api.username, cluster_api.password)
session.auth = (provider.username, provider.password)
# NSX v3 doesn't use redirects
session.max_redirects = 0
session.verify = not cluster_api.insecure
if session.verify and cluster_api.ca_file:
if session.verify and provider.ca_file:
# verify using the said ca bundle path
session.verify = cluster_api.ca_file
session.verify = provider.ca_file
# we are pooling with eventlet in the cluster class
adapter = adapters.HTTPAdapter(
@ -172,12 +172,15 @@ class EndpointState(object):
class Provider(object):
"""Data holder for a provider which has a unique id
and a connection URL.
a connection URL, and the credential details.
"""
def __init__(self, provider_id, provider_url):
def __init__(self, provider_id, provider_url, username, password, ca_file):
self.id = provider_id
self.url = provider_url
self.username = username
self.password = password
self.ca_file = ca_file
def __str__(self):
return str(self.url)
@ -459,11 +462,15 @@ class NSXClusteredAPI(ClusteredAPI):
http_read_timeout=None,
conn_idle_timeout=None,
http_provider=None):
self.username = username or cfg.CONF.nsx_v3.nsx_api_user
self.password = password or cfg.CONF.nsx_v3.nsx_api_password
self.retries = retries or cfg.CONF.nsx_v3.http_retries
self.insecure = insecure or cfg.CONF.nsx_v3.insecure
self.ca_file = ca_file or cfg.CONF.nsx_v3.ca_file
# username, password & ca_file may be lists, in order to support
# different credentials per nsx manager
self._username = username or cfg.CONF.nsx_v3.nsx_api_user
self._password = password or cfg.CONF.nsx_v3.nsx_api_password
self._ca_file = ca_file or cfg.CONF.nsx_v3.ca_file
self.conns_per_pool = (concurrent_connections or
cfg.CONF.nsx_v3.concurrent_connections)
self.http_timeout = http_timeout or cfg.CONF.nsx_v3.http_timeout
@ -494,14 +501,40 @@ class NSXClusteredAPI(ClusteredAPI):
conf_urls = cfg.CONF.nsx_v3.nsx_api_managers[:]
urls = []
providers = []
provider_index = -1
for conf_url in conf_urls:
provider_index += 1
conf_url = _schemed_url(conf_url)
if conf_url in urls:
LOG.warning(_LW("'%s' already defined in configuration file. "
"Skipping."), urlparse.urlunparse(conf_url))
continue
urls.append(conf_url)
providers.append(Provider(
conf_url.netloc, urlparse.urlunparse(conf_url)))
providers.append(
Provider(
conf_url.netloc,
urlparse.urlunparse(conf_url),
self.username(provider_index),
self.password(provider_index),
self.ca_file(provider_index)))
return providers
def _attribute_by_index(self, scalar_or_list, index):
if isinstance(scalar_or_list, list):
if not len(scalar_or_list):
return None
if len(scalar_or_list) > index:
return scalar_or_list[index]
# if not long enough - use the first one as default
return scalar_or_list[0]
# this is a scalar
return scalar_or_list
def username(self, index):
return self._attribute_by_index(self._username, index)
def password(self, index):
return self._attribute_by_index(self._password, index)
def ca_file(self, index):
return self._attribute_by_index(self._ca_file, index)

View File

@ -39,16 +39,17 @@ class RequestsHTTPProviderTestCase(unittest.TestCase):
def test_new_connection(self):
mock_api = mock.Mock()
mock_api.username = 'nsxuser'
mock_api.password = 'nsxpassword'
mock_api._username = 'nsxuser'
mock_api._password = 'nsxpassword'
mock_api.retries = 100
mock_api.insecure = True
mock_api.ca_file = None
mock_api._ca_file = None
mock_api.http_timeout = 99
mock_api.conn_idle_timeout = 39
provider = cluster.NSXRequestsHTTPProvider()
session = provider.new_connection(
mock_api, cluster.Provider('9.8.7.6', 'https://9.8.7.6'))
mock_api, cluster.Provider('9.8.7.6', 'https://9.8.7.6',
'nsxuser', 'nsxpassword', None))
self.assertEqual(session.auth, ('nsxuser', 'nsxpassword'))
self.assertEqual(session.verify, False)