Merge pull request #38647 from gtmanfred/nova

Allow novaclient to use keystoneauth1 sessions for authentication
This commit is contained in:
Mike Place 2017-01-10 10:48:25 -07:00 committed by GitHub
commit 7b850d472d
2 changed files with 133 additions and 1 deletions

View file

@ -48,6 +48,25 @@ examples could be set up in the cloud configuration at
driver: nova
userdata_file: /tmp/userdata.txt
To use keystoneauth1 instead of keystoneclient, include the `use_keystoneauth`
option in the provider config.
.. note:: this is required to use keystone v3 as for authentication.
.. code-block:: yaml
my-openstack-config:
use_keystoneauth: True
identity_url: 'https://controller:5000/v3'
auth_version: 3
compute_name: nova
compute_region: RegionOne
service_type: compute
tenant: admin
user: admin
password: passwordgoeshere
driver: nova
For local installations that only use private IP address ranges, the
following option may be useful. Using the old syntax:
@ -266,6 +285,7 @@ def get_conn():
kwargs['project_id'] = vm_['tenant']
kwargs['auth_url'] = vm_['identity_url']
kwargs['region_name'] = vm_['compute_region']
kwargs['use_keystoneauth'] = vm_['use_keystoneauth']
if 'password' in vm_:
kwargs['password'] = vm_['password']

View file

@ -6,9 +6,9 @@ Nova class
# Import Python libs
from __future__ import absolute_import, with_statement
from distutils.version import LooseVersion
import time
import inspect
import logging
import time
# Import third party libs
import salt.ext.six as six
@ -26,6 +26,14 @@ try:
HAS_NOVA = True
except ImportError:
pass
HAS_KEYSTONEAUTH = False
try:
import keystoneauth1.loading
import keystoneauth1.session
HAS_KEYSTONEAUTH = True
except ImportError:
pass
# pylint: enable=import-error
# Import salt libs
@ -164,6 +172,15 @@ def get_entry(dict_, key, value, raise_error=True):
return {}
def get_entry_multi(dict_, pairs, raise_error=True):
for entry in dict_:
if all([entry[key] == value for key, value in pairs]):
return entry
if raise_error is True:
raise SaltCloudSystemExit('Unable to find {0} in {1}.'.format(pairs, dict_))
return {}
def sanatize_novaclient(kwargs):
variables = (
'username', 'api_key', 'project_id', 'auth_url', 'insecure',
@ -196,11 +213,79 @@ class SaltNova(object):
region_name=None,
password=None,
os_auth_plugin=None,
use_keystoneauth=False,
**kwargs
):
'''
Set up nova credentials
'''
if all([use_keystoneauth, HAS_KEYSTONEAUTH]):
self._new_init(username=username,
project_id=project_id,
auth_url=auth_url,
region_name=region_name,
password=password,
os_auth_plugin=os_auth_plugin,
**kwargs)
else:
self._old_init(username=username,
project_id=project_id,
auth_url=auth_url,
region_name=region_name,
password=password,
os_auth_plugin=os_auth_plugin,
**kwargs)
def _new_init(self, username, project_id, auth_url, region_name, password, os_auth_plugin, auth=None, **kwargs):
if auth is None:
auth = {}
loader = keystoneauth1.loading.get_plugin_loader(os_auth_plugin or 'password')
self.client_kwargs = kwargs.copy()
self.kwargs = auth.copy()
if not self.extensions:
if hasattr(OpenStackComputeShell, '_discover_extensions'):
self.extensions = OpenStackComputeShell()._discover_extensions('2.0')
else:
self.extensions = client.discover_extensions('2.0')
for extension in self.extensions:
extension.run_hooks('__pre_parse_args__')
self.client_kwargs['extensions'] = self.extensions
self.kwargs['username'] = username
self.kwargs['project_name'] = project_id
self.kwargs['auth_url'] = auth_url
self.kwargs['password'] = password
if auth_url.endswith('3'):
self.kwargs['user_domain_name'] = kwargs.get('user_domain_name', 'default')
self.kwargs['project_domain_name'] = kwargs.get('project_domain_name', 'default')
self.client_kwargs['region_name'] = region_name
self.client_kwargs['service_type'] = 'compute'
if hasattr(self, 'extensions'):
# needs an object, not a dictionary
self.kwargstruct = KwargsStruct(**self.client_kwargs)
for extension in self.extensions:
extension.run_hooks('__post_parse_args__', self.kwargstruct)
self.client_kwargs = self.kwargstruct.__dict__
# Requires novaclient version >= 2.6.1
self.version = str(kwargs.get('version', 2))
self.client_kwargs = sanatize_novaclient(self.client_kwargs)
options = loader.load_from_options(**self.kwargs)
self.session = keystoneauth1.session.Session(auth=options)
conn = client.Client(version=self.version, session=self.session, **self.client_kwargs)
self.kwargs['auth_token'] = conn.client.session.get_token()
self.catalog = conn.client.session.get('/auth/catalog', endpoint_filter={'service_type': 'identity'}).json().get('catalog', [])
if conn.client.get_endpoint(service_type='identity').endswith('v3'):
self._v3_setup(region_name)
else:
self._v2_setup(region_name)
def _old_init(self, username, project_id, auth_url, region_name, password, os_auth_plugin, **kwargs):
self.kwargs = kwargs.copy()
if not self.extensions:
if hasattr(OpenStackComputeShell, '_discover_extensions'):
@ -254,6 +339,33 @@ class SaltNova(object):
self.kwargs['auth_token'] = conn.client.auth_token
self.catalog = conn.client.service_catalog.catalog['access']['serviceCatalog']
self._v2_setup(region_name)
def _v3_setup(self, region_name):
if region_name is not None:
servers_endpoints = get_entry(self.catalog, 'type', 'compute')['endpoints']
self.kwargs['bypass_url'] = get_entry_multi(
servers_endpoints,
[('region', region_name), ('interface', 'public')]
)['url']
self.compute_conn = client.Client(version=self.version, session=self.session, **self.client_kwargs)
volume_endpoints = get_entry(self.catalog, 'type', 'volume', raise_error=False).get('endpoints', {})
if volume_endpoints:
if region_name is not None:
self.kwargs['bypass_url'] = get_entry_multi(
volume_endpoints,
[('region', region_name), ('interface', 'public')]
)['url']
self.volume_conn = client.Client(version=self.version, session=self.session, **self.client_kwargs)
if hasattr(self, 'extensions'):
self.expand_extensions()
else:
self.volume_conn = None
def _v2_setup(self, region_name):
if region_name is not None:
servers_endpoints = get_entry(self.catalog, 'type', 'compute')['endpoints']
self.kwargs['bypass_url'] = get_entry(