Add keystone v3 auth support
This change enables the neutron client to use the keystone v3 API (in addition to v2). This allows user domains and tenant/project domains to be specified. This is necessary because keystone v2 API is deprecated as of Icehouse. The keystone session object (and auth plugin) can now be specified and are required to use the keystone v3 API. The existing HTTPClient is retained for backward compatibility. See changes in shell.py for an example of how to construct the session and auth plugin. Implements: blueprint keystone-api-v3-support Change-Id: I9d0395d405b9fbe4db08ad3727f9413be7b82811
This commit is contained in:
parent
d07c65c7fc
commit
2203b013fb
@ -22,6 +22,7 @@ import logging
|
||||
import os
|
||||
|
||||
from keystoneclient import access
|
||||
from keystoneclient.auth.identity.base import BaseIdentityPlugin
|
||||
import requests
|
||||
|
||||
from neutronclient.common import exceptions
|
||||
@ -41,11 +42,25 @@ else:
|
||||
logging.getLogger("requests").setLevel(_requests_log_level)
|
||||
|
||||
|
||||
class HTTPClient(object):
|
||||
"""Handles the REST calls and responses, include authn."""
|
||||
class NeutronClientMixin(object):
|
||||
|
||||
USER_AGENT = 'python-neutronclient'
|
||||
|
||||
def get_status_code(self, response):
|
||||
"""Returns the integer status code from the response.
|
||||
|
||||
Either a Webob.Response (used in testing) or requests.Response
|
||||
is returned.
|
||||
"""
|
||||
if hasattr(response, 'status_int'):
|
||||
return response.status_int
|
||||
else:
|
||||
return response.status_code
|
||||
|
||||
|
||||
class HTTPClient(NeutronClientMixin):
|
||||
"""Handles the REST calls and responses, include authn."""
|
||||
|
||||
def __init__(self, username=None, user_id=None,
|
||||
tenant_name=None, tenant_id=None,
|
||||
password=None, auth_url=None,
|
||||
@ -265,13 +280,122 @@ class HTTPClient(object):
|
||||
'auth_user_id': self.auth_user_id,
|
||||
'endpoint_url': self.endpoint_url}
|
||||
|
||||
def get_status_code(self, response):
|
||||
"""Returns the integer status code from the response.
|
||||
|
||||
Either a Webob.Response (used in testing) or requests.Response
|
||||
is returned.
|
||||
"""
|
||||
if hasattr(response, 'status_int'):
|
||||
return response.status_int
|
||||
else:
|
||||
return response.status_code
|
||||
class SessionClient(NeutronClientMixin):
|
||||
|
||||
def __init__(self,
|
||||
session,
|
||||
auth,
|
||||
interface=None,
|
||||
service_type=None,
|
||||
region_name=None):
|
||||
|
||||
self.session = session
|
||||
self.auth = auth
|
||||
self.interface = interface
|
||||
self.service_type = service_type
|
||||
self.region_name = region_name
|
||||
self.auth_token = None
|
||||
self.endpoint_url = None
|
||||
|
||||
def request(self, url, method, **kwargs):
|
||||
kwargs.setdefault('user_agent', self.USER_AGENT)
|
||||
kwargs.setdefault('auth', self.auth)
|
||||
kwargs.setdefault('authenticated', False)
|
||||
|
||||
try:
|
||||
kwargs['data'] = kwargs.pop('body')
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
endpoint_filter = kwargs.setdefault('endpoint_filter', {})
|
||||
endpoint_filter.setdefault('interface', self.interface)
|
||||
endpoint_filter.setdefault('service_type', self.service_type)
|
||||
endpoint_filter.setdefault('region_name', self.region_name)
|
||||
|
||||
kwargs = utils.safe_encode_dict(kwargs)
|
||||
resp = self.session.request(url, method, **kwargs)
|
||||
return resp, resp.text
|
||||
|
||||
def do_request(self, url, method, **kwargs):
|
||||
kwargs.setdefault('authenticated', True)
|
||||
return self.request(url, method, **kwargs)
|
||||
|
||||
def authenticate(self):
|
||||
# This method is provided for backward compatibility only.
|
||||
# We only care about setting the service endpoint.
|
||||
self.endpoint_url = self.session.get_endpoint(
|
||||
self.auth,
|
||||
service_type=self.service_type,
|
||||
region_name=self.region_name,
|
||||
interface=self.interface)
|
||||
|
||||
def authenticate_and_fetch_endpoint_url(self):
|
||||
# This method is provided for backward compatibility only.
|
||||
# We only care about setting the service endpoint.
|
||||
self.authenticate()
|
||||
|
||||
def get_auth_info(self):
|
||||
# This method is provided for backward compatibility only.
|
||||
if not isinstance(self.auth, BaseIdentityPlugin):
|
||||
msg = ('Auth info not available. Auth plugin is not an identity '
|
||||
'auth plugin.')
|
||||
raise exceptions.NeutronClientException(message=msg)
|
||||
access_info = self.auth.get_access(self.session)
|
||||
endpoint_url = self.auth.get_endpoint(self.session,
|
||||
service_type=self.service_type,
|
||||
region_name=self.region_name,
|
||||
interface=self.interface)
|
||||
return {'auth_token': access_info.auth_token,
|
||||
'auth_tenant_id': access_info.tenant_id,
|
||||
'auth_user_id': access_info.user_id,
|
||||
'endpoint_url': endpoint_url}
|
||||
|
||||
|
||||
# FIXME(bklei): Should refactor this to use kwargs and only
|
||||
# explicitly list arguments that are not None.
|
||||
def construct_http_client(username=None,
|
||||
user_id=None,
|
||||
tenant_name=None,
|
||||
tenant_id=None,
|
||||
password=None,
|
||||
auth_url=None,
|
||||
token=None,
|
||||
region_name=None,
|
||||
timeout=None,
|
||||
endpoint_url=None,
|
||||
insecure=False,
|
||||
endpoint_type='publicURL',
|
||||
log_credentials=None,
|
||||
auth_strategy='keystone',
|
||||
ca_cert=None,
|
||||
service_type='network',
|
||||
session=None,
|
||||
auth=None):
|
||||
|
||||
if session:
|
||||
return SessionClient(session=session,
|
||||
auth=auth,
|
||||
interface=endpoint_type,
|
||||
service_type=service_type,
|
||||
region_name=region_name)
|
||||
else:
|
||||
# FIXME(bklei): username and password are now optional. Need
|
||||
# to test that they were provided in this mode. Should also
|
||||
# refactor to use kwargs.
|
||||
return HTTPClient(username=username,
|
||||
password=password,
|
||||
tenant_id=tenant_id,
|
||||
tenant_name=tenant_name,
|
||||
user_id=user_id,
|
||||
auth_url=auth_url,
|
||||
token=token,
|
||||
endpoint_url=endpoint_url,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
region_name=region_name,
|
||||
endpoint_type=endpoint_type,
|
||||
service_type=service_type,
|
||||
ca_cert=ca_cert,
|
||||
log_credentials=log_credentials,
|
||||
auth_strategy=auth_strategy)
|
||||
|
@ -66,7 +66,9 @@ class ClientManager(object):
|
||||
service_type=None,
|
||||
timeout=None,
|
||||
retries=0,
|
||||
raise_errors=True
|
||||
raise_errors=True,
|
||||
session=None,
|
||||
auth=None,
|
||||
):
|
||||
self._token = token
|
||||
self._url = url
|
||||
@ -88,10 +90,13 @@ class ClientManager(object):
|
||||
self._timeout = timeout
|
||||
self._retries = retries
|
||||
self._raise_errors = raise_errors
|
||||
self._session = session
|
||||
self._auth = auth
|
||||
return
|
||||
|
||||
def initialize(self):
|
||||
if not self._url:
|
||||
httpclient = client.HTTPClient(
|
||||
httpclient = client.construct_http_client(
|
||||
username=self._username,
|
||||
user_id=self._user_id,
|
||||
tenant_name=self._tenant_name,
|
||||
@ -103,8 +108,10 @@ class ClientManager(object):
|
||||
endpoint_type=self._endpoint_type,
|
||||
insecure=self._insecure,
|
||||
ca_cert=self._ca_cert,
|
||||
log_credentials=self._log_credentials,
|
||||
timeout=self._timeout)
|
||||
timeout=self._timeout,
|
||||
session=self._session,
|
||||
auth=self._auth,
|
||||
log_credentials=self._log_credentials)
|
||||
httpclient.authenticate()
|
||||
# Populate other password flow attributes
|
||||
self._token = httpclient.auth_token
|
||||
|
@ -48,7 +48,9 @@ def make_client(instance):
|
||||
insecure=instance._insecure,
|
||||
ca_cert=instance._ca_cert,
|
||||
retries=instance._retries,
|
||||
raise_errors=instance._raise_errors)
|
||||
raise_errors=instance._raise_errors,
|
||||
session=instance._session,
|
||||
auth=instance._auth)
|
||||
return client
|
||||
else:
|
||||
raise exceptions.UnsupportedVersion(_("API version %s is not "
|
||||
|
@ -25,6 +25,13 @@ import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from keystoneclient.auth.identity import v2 as v2_auth
|
||||
from keystoneclient.auth.identity import v3 as v3_auth
|
||||
from keystoneclient import discover
|
||||
from keystoneclient.openstack.common.apiclient import exceptions as ks_exc
|
||||
from keystoneclient import session
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
from cliff import app
|
||||
from cliff import commandmanager
|
||||
|
||||
@ -386,13 +393,50 @@ class NeutronShell(app.App):
|
||||
default=0,
|
||||
help=_("How many times the request to the Neutron server should "
|
||||
"be retried if it fails."))
|
||||
# Global arguments
|
||||
# FIXME(bklei): this method should come from python-keystoneclient
|
||||
self._append_global_identity_args(parser)
|
||||
|
||||
return parser
|
||||
|
||||
def _append_global_identity_args(self, parser):
|
||||
# FIXME(bklei): these are global identity (Keystone) arguments which
|
||||
# should be consistent and shared by all service clients. Therefore,
|
||||
# they should be provided by python-keystoneclient. We will need to
|
||||
# refactor this code once this functionality is available in
|
||||
# python-keystoneclient.
|
||||
#
|
||||
# Note: At that time we'll need to decide if we can just abandon
|
||||
# the deprecated args (--service-type and --endpoint-type).
|
||||
|
||||
parser.add_argument(
|
||||
'--os-service-type', metavar='<os-service-type>',
|
||||
default=env('OS_NETWORK_SERVICE_TYPE', default='network'),
|
||||
help=_('Defaults to env[OS_NETWORK_SERVICE_TYPE] or network.'))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-endpoint-type', metavar='<os-endpoint-type>',
|
||||
default=env('OS_ENDPOINT_TYPE', default='publicURL'),
|
||||
help=_('Defaults to env[OS_ENDPOINT_TYPE] or publicURL.'))
|
||||
|
||||
# FIXME(bklei): --service-type is deprecated but kept in for
|
||||
# backward compatibility.
|
||||
parser.add_argument(
|
||||
'--service-type', metavar='<service-type>',
|
||||
default=env('OS_NETWORK_SERVICE_TYPE', default='network'),
|
||||
help=_('DEPRECATED! Use --os-service-type.'))
|
||||
|
||||
# FIXME(bklei): --endpoint-type is deprecated but kept in for
|
||||
# backward compatibility.
|
||||
parser.add_argument(
|
||||
'--endpoint-type', metavar='<endpoint-type>',
|
||||
default=env('OS_ENDPOINT_TYPE', default='publicURL'),
|
||||
help=_('DEPRECATED! Use --os-endpoint-type.'))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-auth-strategy', metavar='<auth-strategy>',
|
||||
default=env('OS_AUTH_STRATEGY', default='keystone'),
|
||||
help=_('Authentication strategy, defaults to '
|
||||
'env[OS_AUTH_STRATEGY] or keystone. For now, any '
|
||||
'other value will disable the authentication.'))
|
||||
help=_('DEPRECATED! Only keystone is supported.'))
|
||||
|
||||
parser.add_argument(
|
||||
'--os_auth_strategy',
|
||||
help=argparse.SUPPRESS)
|
||||
@ -405,20 +449,39 @@ class NeutronShell(app.App):
|
||||
'--os_auth_url',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
parser.add_argument(
|
||||
project_name_group = parser.add_mutually_exclusive_group()
|
||||
project_name_group.add_argument(
|
||||
'--os-tenant-name', metavar='<auth-tenant-name>',
|
||||
default=env('OS_TENANT_NAME'),
|
||||
help=_('Authentication tenant name, defaults to '
|
||||
'env[OS_TENANT_NAME].'))
|
||||
project_name_group.add_argument(
|
||||
'--os-project-name',
|
||||
metavar='<auth-project-name>',
|
||||
default=utils.env('OS_PROJECT_NAME'),
|
||||
help='Another way to specify tenant name. '
|
||||
'This option is mutually exclusive with '
|
||||
' --os-tenant-name. '
|
||||
'Defaults to env[OS_PROJECT_NAME].')
|
||||
|
||||
parser.add_argument(
|
||||
'--os_tenant_name',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
parser.add_argument(
|
||||
project_id_group = parser.add_mutually_exclusive_group()
|
||||
project_id_group.add_argument(
|
||||
'--os-tenant-id', metavar='<auth-tenant-id>',
|
||||
default=env('OS_TENANT_ID'),
|
||||
help=_('Authentication tenant ID, defaults to '
|
||||
'env[OS_TENANT_ID].'))
|
||||
project_id_group.add_argument(
|
||||
'--os-project-id',
|
||||
metavar='<auth-project-id>',
|
||||
default=utils.env('OS_PROJECT_ID'),
|
||||
help='Another way to specify tenant ID. '
|
||||
'This option is mutually exclusive with '
|
||||
' --os-tenant-id. '
|
||||
'Defaults to env[OS_PROJECT_ID].')
|
||||
|
||||
parser.add_argument(
|
||||
'--os-username', metavar='<auth-username>',
|
||||
@ -433,6 +496,78 @@ class NeutronShell(app.App):
|
||||
default=env('OS_USER_ID'),
|
||||
help=_('Authentication user ID (Env: OS_USER_ID)'))
|
||||
|
||||
parser.add_argument(
|
||||
'--os_user_id',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-user-domain-id',
|
||||
metavar='<auth-user-domain-id>',
|
||||
default=utils.env('OS_USER_DOMAIN_ID'),
|
||||
help='OpenStack user domain ID. '
|
||||
'Defaults to env[OS_USER_DOMAIN_ID].')
|
||||
|
||||
parser.add_argument(
|
||||
'--os_user_domain_id',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-user-domain-name',
|
||||
metavar='<auth-user-domain-name>',
|
||||
default=utils.env('OS_USER_DOMAIN_NAME'),
|
||||
help='OpenStack user domain name. '
|
||||
'Defaults to env[OS_USER_DOMAIN_NAME].')
|
||||
|
||||
parser.add_argument(
|
||||
'--os_user_domain_name',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
parser.add_argument(
|
||||
'--os_project_id',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
parser.add_argument(
|
||||
'--os_project_name',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-project-domain-id',
|
||||
metavar='<auth-project-domain-id>',
|
||||
default=utils.env('OS_PROJECT_DOMAIN_ID'),
|
||||
help='Defaults to env[OS_PROJECT_DOMAIN_ID].')
|
||||
|
||||
parser.add_argument(
|
||||
'--os-project-domain-name',
|
||||
metavar='<auth-project-domain-name>',
|
||||
default=utils.env('OS_PROJECT_DOMAIN_NAME'),
|
||||
help='Defaults to env[OS_PROJECT_DOMAIN_NAME].')
|
||||
|
||||
parser.add_argument(
|
||||
'--os-cert',
|
||||
metavar='<certificate>',
|
||||
default=utils.env('OS_CERT'),
|
||||
help=_("Path of certificate file to use in SSL "
|
||||
"connection. This file can optionally be "
|
||||
"prepended with the private key. Defaults "
|
||||
"to env[OS_CERT]"))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-cacert',
|
||||
metavar='<ca-certificate>',
|
||||
default=env('OS_CACERT', default=None),
|
||||
help=_("Specify a CA bundle file to use in "
|
||||
"verifying a TLS (https) server certificate. "
|
||||
"Defaults to env[OS_CACERT]"))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-key',
|
||||
metavar='<key>',
|
||||
default=utils.env('OS_KEY'),
|
||||
help=_("Path of client key to use in SSL "
|
||||
"connection. This option is not necessary "
|
||||
"if your key is prepended to your certificate "
|
||||
"file. Defaults to env[OS_KEY]"))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-password', metavar='<auth-password>',
|
||||
default=utils.env('OS_PASSWORD'),
|
||||
@ -458,22 +593,12 @@ class NeutronShell(app.App):
|
||||
'--os_token',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
parser.add_argument(
|
||||
'--service-type', metavar='<service-type>',
|
||||
default=env('OS_NETWORK_SERVICE_TYPE', default='network'),
|
||||
help=_('Defaults to env[OS_NETWORK_SERVICE_TYPE] or network.'))
|
||||
|
||||
parser.add_argument(
|
||||
'--timeout', metavar='<seconds>',
|
||||
default=env('OS_NETWORK_TIMEOUT', default=None), type=float,
|
||||
help=_('Timeout in seconds to wait for an HTTP response. Defaults '
|
||||
'to env[OS_NETWORK_TIMEOUT] or None if not specified.'))
|
||||
|
||||
parser.add_argument(
|
||||
'--endpoint-type', metavar='<endpoint-type>',
|
||||
default=env('OS_ENDPOINT_TYPE', default='publicURL'),
|
||||
help=_('Defaults to env[OS_ENDPOINT_TYPE] or publicURL.'))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-url', metavar='<url>',
|
||||
default=env('OS_URL'),
|
||||
@ -482,14 +607,6 @@ class NeutronShell(app.App):
|
||||
'--os_url',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-cacert',
|
||||
metavar='<ca-certificate>',
|
||||
default=env('OS_CACERT', default=None),
|
||||
help=_("Specify a CA bundle file to use in "
|
||||
"verifying a TLS (https) server certificate. "
|
||||
"Defaults to env[OS_CACERT]."))
|
||||
|
||||
parser.add_argument(
|
||||
'--insecure',
|
||||
action='store_true',
|
||||
@ -499,8 +616,6 @@ class NeutronShell(app.App):
|
||||
"not be verified against any certificate authorities. "
|
||||
"This option should be used with caution."))
|
||||
|
||||
return parser
|
||||
|
||||
def _bash_completion(self):
|
||||
"""Prints all of the commands and options for bash-completion."""
|
||||
commands = set()
|
||||
@ -622,6 +737,13 @@ class NeutronShell(app.App):
|
||||
|
||||
else:
|
||||
# Validate password flow auth
|
||||
project_info = (self.options.os_tenant_name or
|
||||
self.options.os_tenant_id or
|
||||
(self.options.os_project_name and
|
||||
(self.options.project_domain_name or
|
||||
self.options.project_domain_id)) or
|
||||
self.options.os_project_id)
|
||||
|
||||
if (not self.options.os_username
|
||||
and not self.options.os_user_id):
|
||||
raise exc.CommandError(
|
||||
@ -634,12 +756,19 @@ class NeutronShell(app.App):
|
||||
_("You must provide a password via"
|
||||
" either --os-password or env[OS_PASSWORD]"))
|
||||
|
||||
if (not self.options.os_tenant_name
|
||||
and not self.options.os_tenant_id):
|
||||
if (not project_info):
|
||||
# tenent is deprecated in Keystone v3. Use the latest
|
||||
# terminology instead.
|
||||
raise exc.CommandError(
|
||||
_("You must provide a tenant_name or tenant_id via"
|
||||
" --os-tenant-name, env[OS_TENANT_NAME]"
|
||||
" --os-tenant-id, or via env[OS_TENANT_ID]"))
|
||||
_("You must provide a project_id or project_name ("
|
||||
"with project_domain_name or project_domain_id) "
|
||||
"via "
|
||||
" --os-project-id (env[OS_PROJECT_ID])"
|
||||
" --os-project-name (env[OS_PROJECT_NAME]),"
|
||||
" --os-project-domain-id "
|
||||
"(env[OS_PROJECT_DOMAIN_ID])"
|
||||
" --os-project-domain-name "
|
||||
"(env[OS_PROJECT_DOMAIN_NAME])"))
|
||||
|
||||
if not self.options.os_auth_url:
|
||||
raise exc.CommandError(
|
||||
@ -651,6 +780,8 @@ class NeutronShell(app.App):
|
||||
_("You must provide a service URL via"
|
||||
" either --os-url or env[OS_URL]"))
|
||||
|
||||
auth_session = self._get_keystone_session()
|
||||
|
||||
self.client_manager = clientmanager.ClientManager(
|
||||
token=self.options.os_token,
|
||||
url=self.options.os_url,
|
||||
@ -663,13 +794,18 @@ class NeutronShell(app.App):
|
||||
region_name=self.options.os_region_name,
|
||||
api_version=self.api_version,
|
||||
auth_strategy=self.options.os_auth_strategy,
|
||||
service_type=self.options.service_type,
|
||||
endpoint_type=self.options.endpoint_type,
|
||||
# FIXME (bklei) honor deprecated service_type and
|
||||
# endpoint type until they are removed
|
||||
service_type=self.options.os_service_type or
|
||||
self.options.service_type,
|
||||
endpoint_type=self.options.os_endpoint_type or self.endpoint_type,
|
||||
insecure=self.options.insecure,
|
||||
ca_cert=self.options.os_cacert,
|
||||
timeout=self.options.timeout,
|
||||
retries=self.options.retries,
|
||||
raise_errors=False,
|
||||
session=auth_session,
|
||||
auth=auth_session.auth,
|
||||
log_credentials=True)
|
||||
return
|
||||
|
||||
@ -721,6 +857,89 @@ class NeutronShell(app.App):
|
||||
root_logger.addHandler(console)
|
||||
return
|
||||
|
||||
def get_v2_auth(self, v2_auth_url):
|
||||
return v2_auth.Password(
|
||||
v2_auth_url,
|
||||
username=self.options.os_username,
|
||||
password=self.options.os_password,
|
||||
tenant_id=self.options.os_tenant_id,
|
||||
tenant_name=self.options.os_tenant_name)
|
||||
|
||||
def get_v3_auth(self, v3_auth_url):
|
||||
project_id = self.options.os_project_id or self.options.os_tenant_id
|
||||
project_name = (self.options.os_project_name or
|
||||
self.options.os_tenant_name)
|
||||
|
||||
return v3_auth.Password(
|
||||
v3_auth_url,
|
||||
username=self.options.os_username,
|
||||
password=self.options.os_password,
|
||||
user_id=self.options.os_user_id,
|
||||
user_domain_name=self.options.os_user_domain_name,
|
||||
user_domain_id=self.options.os_user_domain_id,
|
||||
project_id=project_id,
|
||||
project_name=project_name,
|
||||
project_domain_name=self.options.os_project_domain_name,
|
||||
project_domain_id=self.options.os_project_domain_id
|
||||
)
|
||||
|
||||
def _discover_auth_versions(self, session, auth_url):
|
||||
# discover the API versions the server is supporting base on the
|
||||
# given URL
|
||||
try:
|
||||
ks_discover = discover.Discover(session=session, auth_url=auth_url)
|
||||
return (ks_discover.url_for('2.0'), ks_discover.url_for('3.0'))
|
||||
except ks_exc.ClientException:
|
||||
# Identity service may not support discover API version.
|
||||
# Lets try to figure out the API version from the original URL.
|
||||
url_parts = urlparse.urlparse(auth_url)
|
||||
(scheme, netloc, path, params, query, fragment) = url_parts
|
||||
path = path.lower()
|
||||
if path.startswith('/v3'):
|
||||
return (None, auth_url)
|
||||
elif path.startswith('/v2'):
|
||||
return (auth_url, None)
|
||||
else:
|
||||
# not enough information to determine the auth version
|
||||
msg = _('Unable to determine the Keystone version '
|
||||
'to authenticate with using the given '
|
||||
'auth_url. Identity service may not support API '
|
||||
'version discovery. Please provide a versioned '
|
||||
'auth_url instead.')
|
||||
raise exc.CommandError(msg)
|
||||
|
||||
def _get_keystone_session(self):
|
||||
# first create a Keystone session
|
||||
cacert = self.options.os_cacert or None
|
||||
cert = self.options.os_cert or None
|
||||
key = self.options.os_key or None
|
||||
insecure = self.options.insecure or False
|
||||
ks_session = session.Session.construct(dict(cacert=cacert,
|
||||
cert=cert,
|
||||
key=key,
|
||||
insecure=insecure))
|
||||
# discover the supported keystone versions using the given url
|
||||
(v2_auth_url, v3_auth_url) = self._discover_auth_versions(
|
||||
session=ks_session,
|
||||
auth_url=self.options.os_auth_url)
|
||||
|
||||
# Determine which authentication plugin to use. First inspect the
|
||||
# auth_url to see the supported version. If both v3 and v2 are
|
||||
# supported, then use the highest version if possible.
|
||||
user_domain_name = self.options.os_user_domain_name or None
|
||||
user_domain_id = self.options.os_user_domain_id or None
|
||||
project_domain_name = self.options.os_project_domain_name or None
|
||||
project_domain_id = self.options.os_project_domain_id or None
|
||||
domain_info = (user_domain_name or user_domain_id or
|
||||
project_domain_name or project_domain_id)
|
||||
|
||||
if (v2_auth_url and not domain_info) or not v3_auth_url:
|
||||
ks_session.auth = self.get_v2_auth(v2_auth_url)
|
||||
else:
|
||||
ks_session.auth = self.get_v3_auth(v3_auth_url)
|
||||
|
||||
return ks_session
|
||||
|
||||
|
||||
def main(argv=sys.argv[1:]):
|
||||
try:
|
||||
|
@ -14,18 +14,25 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import copy
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from keystoneclient import exceptions as k_exceptions
|
||||
import httpretty
|
||||
from mox3 import mox
|
||||
import requests
|
||||
import testtools
|
||||
|
||||
from keystoneclient.auth.identity import v2 as ks_v2_auth
|
||||
from keystoneclient.auth.identity import v3 as ks_v3_auth
|
||||
from keystoneclient import exceptions as ks_exceptions
|
||||
from keystoneclient.fixture import v2 as ks_v2_fixture
|
||||
from keystoneclient.fixture import v3 as ks_v3_fixture
|
||||
from keystoneclient import session
|
||||
|
||||
from neutronclient import client
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient.common import utils
|
||||
from neutronclient.openstack.common import jsonutils
|
||||
|
||||
|
||||
USERNAME = 'testuser'
|
||||
@ -33,11 +40,14 @@ USER_ID = 'testuser_id'
|
||||
TENANT_NAME = 'testtenant'
|
||||
TENANT_ID = 'testtenant_id'
|
||||
PASSWORD = 'password'
|
||||
AUTH_URL = 'authurl'
|
||||
ENDPOINT_URL = 'localurl'
|
||||
PUBLIC_ENDPOINT_URL = 'public_%s' % ENDPOINT_URL
|
||||
ADMIN_ENDPOINT_URL = 'admin_%s' % ENDPOINT_URL
|
||||
INTERNAL_ENDPOINT_URL = 'internal_%s' % ENDPOINT_URL
|
||||
ENDPOINT_OVERRIDE = 'otherurl'
|
||||
TOKEN = 'tokentoken'
|
||||
REGION = 'RegionTest'
|
||||
TOKENID = uuid.uuid4().hex
|
||||
REGION = 'RegionOne'
|
||||
NOAUTH = 'noauth'
|
||||
|
||||
KS_TOKEN_RESULT = {
|
||||
@ -69,6 +79,55 @@ ENDPOINTS_RESULT = {
|
||||
}]
|
||||
}
|
||||
|
||||
BASE_HOST = 'http://keystone.example.com'
|
||||
BASE_URL = "%s:5000/" % BASE_HOST
|
||||
UPDATED = '2013-03-06T00:00:00Z'
|
||||
|
||||
# FIXME (bklei): A future release of keystoneclient will support
|
||||
# a discovery fixture which can replace these constants and clean
|
||||
# this up.
|
||||
V2_URL = "%sv2.0" % BASE_URL
|
||||
V2_DESCRIBED_BY_HTML = {'href': 'http://docs.openstack.org/api/'
|
||||
'openstack-identity-service/2.0/content/',
|
||||
'rel': 'describedby',
|
||||
'type': 'text/html'}
|
||||
|
||||
V2_DESCRIBED_BY_PDF = {'href': 'http://docs.openstack.org/api/openstack-ident'
|
||||
'ity-service/2.0/identity-dev-guide-2.0.pdf',
|
||||
'rel': 'describedby',
|
||||
'type': 'application/pdf'}
|
||||
|
||||
V2_VERSION = {'id': 'v2.0',
|
||||
'links': [{'href': V2_URL, 'rel': 'self'},
|
||||
V2_DESCRIBED_BY_HTML, V2_DESCRIBED_BY_PDF],
|
||||
'status': 'stable',
|
||||
'updated': UPDATED}
|
||||
|
||||
V3_URL = "%sv3" % BASE_URL
|
||||
V3_MEDIA_TYPES = [{'base': 'application/json',
|
||||
'type': 'application/vnd.openstack.identity-v3+json'},
|
||||
{'base': 'application/xml',
|
||||
'type': 'application/vnd.openstack.identity-v3+xml'}]
|
||||
|
||||
V3_VERSION = {'id': 'v3.0',
|
||||
'links': [{'href': V3_URL, 'rel': 'self'}],
|
||||
'media-types': V3_MEDIA_TYPES,
|
||||
'status': 'stable',
|
||||
'updated': UPDATED}
|
||||
|
||||
|
||||
def _create_version_entry(version):
|
||||
return jsonutils.dumps({'version': version})
|
||||
|
||||
|
||||
def _create_version_list(versions):
|
||||
return jsonutils.dumps({'versions': {'values': versions}})
|
||||
|
||||
|
||||
V3_VERSION_LIST = _create_version_list([V3_VERSION, V2_VERSION])
|
||||
V3_VERSION_ENTRY = _create_version_entry(V3_VERSION)
|
||||
V2_VERSION_ENTRY = _create_version_entry(V2_VERSION)
|
||||
|
||||
|
||||
def get_response(status_code, headers=None):
|
||||
response = mox.Mox().CreateMock(requests.Response)
|
||||
@ -77,6 +136,49 @@ def get_response(status_code, headers=None):
|
||||
return response
|
||||
|
||||
|
||||
def setup_keystone_v2():
|
||||
v2_token = ks_v2_fixture.Token(token_id=TOKENID)
|
||||
service = v2_token.add_service('network')
|
||||
service.add_endpoint(PUBLIC_ENDPOINT_URL, region=REGION)
|
||||
|
||||
httpretty.register_uri(httpretty.POST,
|
||||
'%s/tokens' % (V2_URL),
|
||||
body=json.dumps(v2_token))
|
||||
|
||||
auth_session = session.Session()
|
||||
auth_plugin = ks_v2_auth.Password(V2_URL, 'xx', 'xx')
|
||||
return auth_session, auth_plugin
|
||||
|
||||
|
||||
def setup_keystone_v3():
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
V3_URL,
|
||||
body=V3_VERSION_ENTRY)
|
||||
|
||||
v3_token = ks_v3_fixture.Token()
|
||||
service = v3_token.add_service('network')
|
||||
service.add_standard_endpoints(public=PUBLIC_ENDPOINT_URL,
|
||||
admin=ADMIN_ENDPOINT_URL,
|
||||
internal=INTERNAL_ENDPOINT_URL,
|
||||
region=REGION)
|
||||
|
||||
httpretty.register_uri(httpretty.POST,
|
||||
'%s/auth/tokens' % (V3_URL),
|
||||
body=json.dumps(v3_token),
|
||||
adding_headers={'X-Subject-Token': TOKENID})
|
||||
|
||||
auth_session = session.Session()
|
||||
auth_plugin = ks_v3_auth.Password(V3_URL,
|
||||
username='xx',
|
||||
user_id='xx',
|
||||
user_domain_name='xx',
|
||||
user_domain_id='xx')
|
||||
return auth_session, auth_plugin
|
||||
|
||||
|
||||
AUTH_URL = V2_URL
|
||||
|
||||
|
||||
class CLITestAuthNoAuth(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@ -109,20 +211,18 @@ class CLITestAuthNoAuth(testtools.TestCase):
|
||||
|
||||
class CLITestAuthKeystone(testtools.TestCase):
|
||||
|
||||
# Auth Body expected
|
||||
auth_body = ('{"auth": {"tenantName": "testtenant", '
|
||||
'"passwordCredentials": '
|
||||
'{"username": "testuser", "password": "password"}}}')
|
||||
|
||||
def setUp(self):
|
||||
"""Prepare the test environment."""
|
||||
super(CLITestAuthKeystone, self).setUp()
|
||||
self.mox = mox.Mox()
|
||||
self.client = client.HTTPClient(username=USERNAME,
|
||||
tenant_name=TENANT_NAME,
|
||||
password=PASSWORD,
|
||||
auth_url=AUTH_URL,
|
||||
region_name=REGION)
|
||||
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME,
|
||||
tenant_name=TENANT_NAME,
|
||||
password=PASSWORD,
|
||||
auth_url=AUTH_URL,
|
||||
region_name=REGION)
|
||||
|
||||
self.addCleanup(self.mox.VerifyAll)
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
|
||||
@ -142,24 +242,30 @@ class CLITestAuthKeystone(testtools.TestCase):
|
||||
'endpoint_url': self.client.endpoint_url}
|
||||
self.assertEqual(client_.get_auth_info(), expected)
|
||||
|
||||
@httpretty.activate
|
||||
def test_get_token(self):
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
auth_session, auth_plugin = setup_keystone_v2()
|
||||
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME,
|
||||
tenant_name=TENANT_NAME,
|
||||
password=PASSWORD,
|
||||
auth_url=AUTH_URL,
|
||||
region_name=REGION,
|
||||
session=auth_session,
|
||||
auth=auth_plugin)
|
||||
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
res200 = get_response(200)
|
||||
|
||||
self.client.request(
|
||||
AUTH_URL + '/tokens', 'POST',
|
||||
body=self.auth_body, headers=mox.IsA(dict)
|
||||
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
|
||||
self.client.request(
|
||||
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
|
||||
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
|
||||
'/resource', 'GET',
|
||||
authenticated=True
|
||||
).AndReturn((res200, ''))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.client.do_request('/resource', 'GET')
|
||||
self.assertEqual(self.client.endpoint_url, ENDPOINT_URL)
|
||||
self.assertEqual(self.client.auth_token, TOKEN)
|
||||
|
||||
def test_refresh_token(self):
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
@ -292,53 +398,55 @@ class CLITestAuthKeystone(testtools.TestCase):
|
||||
self.mox.ReplayAll()
|
||||
self.client.do_request('/resource', 'GET')
|
||||
|
||||
@httpretty.activate
|
||||
def test_endpoint_type(self):
|
||||
resources = copy.deepcopy(KS_TOKEN_RESULT)
|
||||
endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0]
|
||||
endpoints['internalURL'] = 'internal'
|
||||
endpoints['adminURL'] = 'admin'
|
||||
endpoints['publicURL'] = 'public'
|
||||
auth_session, auth_plugin = setup_keystone_v3()
|
||||
|
||||
# Test default behavior is to choose public.
|
||||
self.client = client.HTTPClient(
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
||||
auth_url=AUTH_URL, region_name=REGION)
|
||||
auth_url=AUTH_URL, region_name=REGION,
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.client._extract_service_catalog(resources)
|
||||
self.assertEqual(self.client.endpoint_url, 'public')
|
||||
self.client.authenticate()
|
||||
self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL)
|
||||
|
||||
# Test admin url
|
||||
self.client = client.HTTPClient(
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='adminURL')
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='adminURL',
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.client._extract_service_catalog(resources)
|
||||
self.assertEqual(self.client.endpoint_url, 'admin')
|
||||
self.client.authenticate()
|
||||
self.assertEqual(self.client.endpoint_url, ADMIN_ENDPOINT_URL)
|
||||
|
||||
# Test public url
|
||||
self.client = client.HTTPClient(
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='publicURL')
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='publicURL',
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.client._extract_service_catalog(resources)
|
||||
self.assertEqual(self.client.endpoint_url, 'public')
|
||||
self.client.authenticate()
|
||||
self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL)
|
||||
|
||||
# Test internal url
|
||||
self.client = client.HTTPClient(
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='internalURL')
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='internalURL',
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.client._extract_service_catalog(resources)
|
||||
self.assertEqual(self.client.endpoint_url, 'internal')
|
||||
self.client.authenticate()
|
||||
self.assertEqual(self.client.endpoint_url, INTERNAL_ENDPOINT_URL)
|
||||
|
||||
# Test url that isn't found in the service catalog
|
||||
self.client = client.HTTPClient(
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='privateURL')
|
||||
auth_url=AUTH_URL, region_name=REGION, endpoint_type='privateURL',
|
||||
session=auth_session, auth=auth_plugin)
|
||||
|
||||
self.assertRaises(k_exceptions.EndpointNotFound,
|
||||
self.client._extract_service_catalog,
|
||||
resources)
|
||||
self.assertRaises(
|
||||
ks_exceptions.EndpointNotFound,
|
||||
self.client.authenticate)
|
||||
|
||||
def test_strip_credentials_from_log(self):
|
||||
def verify_no_credentials(kwargs):
|
||||
@ -370,11 +478,6 @@ class CLITestAuthKeystone(testtools.TestCase):
|
||||
|
||||
class CLITestAuthKeystoneWithId(CLITestAuthKeystone):
|
||||
|
||||
# Auth Body expected
|
||||
auth_body = ('{"auth": {"passwordCredentials": '
|
||||
'{"password": "password", "userId": "testuser_id"}, '
|
||||
'"tenantId": "testtenant_id"}}')
|
||||
|
||||
def setUp(self):
|
||||
"""Prepare the test environment."""
|
||||
super(CLITestAuthKeystoneWithId, self).setUp()
|
||||
@ -387,11 +490,6 @@ class CLITestAuthKeystoneWithId(CLITestAuthKeystone):
|
||||
|
||||
class CLITestAuthKeystoneWithIdandName(CLITestAuthKeystone):
|
||||
|
||||
# Auth Body expected
|
||||
auth_body = ('{"auth": {"passwordCredentials": '
|
||||
'{"password": "password", "userId": "testuser_id"}, '
|
||||
'"tenantId": "testtenant_id"}}')
|
||||
|
||||
def setUp(self):
|
||||
"""Prepare the test environment."""
|
||||
super(CLITestAuthKeystoneWithIdandName, self).setUp()
|
||||
@ -402,3 +500,61 @@ class CLITestAuthKeystoneWithIdandName(CLITestAuthKeystone):
|
||||
password=PASSWORD,
|
||||
auth_url=AUTH_URL,
|
||||
region_name=REGION)
|
||||
|
||||
|
||||
class TestKeystoneClientVersions(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Prepare the test environment."""
|
||||
super(TestKeystoneClientVersions, self).setUp()
|
||||
self.mox = mox.Mox()
|
||||
self.addCleanup(self.mox.VerifyAll)
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
|
||||
@httpretty.activate
|
||||
def test_v2_auth(self):
|
||||
auth_session, auth_plugin = setup_keystone_v2()
|
||||
res200 = get_response(200)
|
||||
|
||||
self.client = client.construct_http_client(
|
||||
username=USERNAME,
|
||||
tenant_name=TENANT_NAME,
|
||||
password=PASSWORD,
|
||||
auth_url=AUTH_URL,
|
||||
region_name=REGION,
|
||||
session=auth_session,
|
||||
auth=auth_plugin)
|
||||
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
|
||||
self.client.request(
|
||||
'/resource', 'GET',
|
||||
authenticated=True
|
||||
).AndReturn((res200, ''))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
self.client.do_request('/resource', 'GET')
|
||||
|
||||
@httpretty.activate
|
||||
def test_v3_auth(self):
|
||||
auth_session, auth_plugin = setup_keystone_v3()
|
||||
res200 = get_response(200)
|
||||
|
||||
self.client = client.construct_http_client(
|
||||
user_id=USER_ID,
|
||||
tenant_id=TENANT_ID,
|
||||
password=PASSWORD,
|
||||
auth_url=V3_URL,
|
||||
region_name=REGION,
|
||||
session=auth_session,
|
||||
auth=auth_plugin)
|
||||
|
||||
self.mox.StubOutWithMock(self.client, "request")
|
||||
|
||||
self.client.request(
|
||||
'/resource', 'GET',
|
||||
authenticated=True
|
||||
).AndReturn((res200, ''))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
self.client.do_request('/resource', 'GET')
|
||||
|
@ -20,13 +20,19 @@ import re
|
||||
import sys
|
||||
|
||||
import fixtures
|
||||
import httpretty
|
||||
from mox3 import mox
|
||||
import six
|
||||
import testtools
|
||||
from testtools import matchers
|
||||
|
||||
from keystoneclient.auth.identity import v2 as v2_auth
|
||||
from keystoneclient.auth.identity import v3 as v3_auth
|
||||
from keystoneclient import session
|
||||
|
||||
from neutronclient.common import clientmanager
|
||||
from neutronclient import shell as openstack_shell
|
||||
from neutronclient.tests.unit import test_auth as auth
|
||||
|
||||
|
||||
DEFAULT_USERNAME = 'username'
|
||||
@ -91,6 +97,14 @@ class ShellTest(testtools.TestCase):
|
||||
matchers.MatchesRegex(required))
|
||||
self.assertFalse(stderr)
|
||||
|
||||
def test_bash_completion(self):
|
||||
required = '.*os_user_domain_id.*'
|
||||
bash_completion, stderr = self.shell('bash-completion')
|
||||
self.assertThat(
|
||||
bash_completion,
|
||||
matchers.MatchesRegex(required))
|
||||
self.assertFalse(stderr)
|
||||
|
||||
def test_help_on_subcommand(self):
|
||||
required = [
|
||||
'.*?^usage: .* quota-list']
|
||||
@ -116,27 +130,287 @@ class ShellTest(testtools.TestCase):
|
||||
self.assertEqual('You must provide a service URL via '
|
||||
'either --os-url or env[OS_URL]', stderr.strip())
|
||||
|
||||
@httpretty.activate
|
||||
def test_auth(self):
|
||||
#import pdb; pdb.set_trace()
|
||||
# emulate Keystone version discovery
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
auth.V3_URL,
|
||||
body=auth.V3_VERSION_ENTRY)
|
||||
|
||||
neutron_shell = openstack_shell.NeutronShell('2.0')
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__')
|
||||
self.mox.StubOutWithMock(neutron_shell, 'run_subcommand')
|
||||
clientmanager.ClientManager.__init__(
|
||||
token='', url='', auth_url='http://127.0.0.1:5000/',
|
||||
token='', url='', auth_url=auth.V3_URL,
|
||||
tenant_name='test', tenant_id='tenant_id',
|
||||
username='test', user_id='',
|
||||
password='test', region_name='', api_version={'network': '2.0'},
|
||||
auth_strategy='keystone', service_type='network',
|
||||
endpoint_type='publicURL', insecure=False, ca_cert=None, retries=0,
|
||||
raise_errors=False, log_credentials=True, timeout=None)
|
||||
endpoint_type='publicURL', insecure=False, ca_cert=None,
|
||||
timeout=None,
|
||||
raise_errors=False,
|
||||
retries=0,
|
||||
auth=mox.IsA(v3_auth.Password),
|
||||
session=mox.IsA(session.Session),
|
||||
log_credentials=True)
|
||||
neutron_shell.run_subcommand(['quota-list'])
|
||||
self.mox.ReplayAll()
|
||||
cmdline = ('--os-username test '
|
||||
'--os-password test '
|
||||
'--os-tenant-name test '
|
||||
'--os-auth-url http://127.0.0.1:5000/ '
|
||||
'--os-auth-strategy keystone quota-list')
|
||||
'--os-auth-url %s '
|
||||
'--os-auth-strategy keystone quota-list'
|
||||
% auth.V3_URL)
|
||||
neutron_shell.run(cmdline.split())
|
||||
self.mox.VerifyAll()
|
||||
|
||||
@httpretty.activate
|
||||
def test_auth_cert_and_key(self):
|
||||
# emulate Keystone version discovery
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
auth.V3_URL,
|
||||
body=auth.V3_VERSION_ENTRY)
|
||||
|
||||
neutron_shell = openstack_shell.NeutronShell('2.0')
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__')
|
||||
self.mox.StubOutWithMock(neutron_shell, 'run_subcommand')
|
||||
clientmanager.ClientManager.__init__(
|
||||
token='', url='', auth_url=auth.V3_URL,
|
||||
tenant_name='test', tenant_id='tenant_id',
|
||||
username='test', user_id='',
|
||||
password='test', region_name='', api_version={'network': '2.0'},
|
||||
auth_strategy='keystone', service_type='network',
|
||||
raise_errors=False,
|
||||
endpoint_type='publicURL', insecure=False, ca_cert=None, retries=0,
|
||||
timeout=None,
|
||||
auth=mox.IsA(v3_auth.Password),
|
||||
session=mox.IsA(session.Session),
|
||||
log_credentials=True)
|
||||
neutron_shell.run_subcommand(['quota-list'])
|
||||
self.mox.ReplayAll()
|
||||
cmdline = ('--os-username test '
|
||||
'--os-password test '
|
||||
'--os-tenant-name test '
|
||||
'--os-cert test '
|
||||
'--os-key test '
|
||||
'--os-auth-url %s '
|
||||
'--os-auth-strategy keystone quota-list'
|
||||
% auth.V3_URL)
|
||||
neutron_shell.run(cmdline.split())
|
||||
self.mox.VerifyAll()
|
||||
|
||||
@httpretty.activate
|
||||
def test_v2_auth(self):
|
||||
# emulate Keystone version discovery
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
auth.V2_URL,
|
||||
body=auth.V2_VERSION_ENTRY)
|
||||
|
||||
neutron_shell = openstack_shell.NeutronShell('2.0')
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__')
|
||||
self.mox.StubOutWithMock(neutron_shell, 'run_subcommand')
|
||||
clientmanager.ClientManager.__init__(
|
||||
token='', url='', auth_url=auth.V2_URL,
|
||||
tenant_name='test', tenant_id='tenant_id',
|
||||
username='test', user_id='',
|
||||
password='test', region_name='', api_version={'network': '2.0'},
|
||||
auth_strategy='keystone', service_type='network',
|
||||
endpoint_type='publicURL', insecure=False, ca_cert=None,
|
||||
timeout=None,
|
||||
raise_errors=False,
|
||||
retries=0,
|
||||
auth=mox.IsA(v2_auth.Password),
|
||||
session=mox.IsA(session.Session),
|
||||
log_credentials=True)
|
||||
neutron_shell.run_subcommand(['quota-list'])
|
||||
self.mox.ReplayAll()
|
||||
cmdline = ('--os-username test '
|
||||
'--os-password test '
|
||||
'--os-tenant-name test '
|
||||
'--os-auth-url %s '
|
||||
'--os-auth-strategy keystone quota-list'
|
||||
% auth.V2_URL)
|
||||
neutron_shell.run(cmdline.split())
|
||||
self.mox.VerifyAll()
|
||||
|
||||
@httpretty.activate
|
||||
def test_failed_auth_version_discovery_v3_auth_url(self):
|
||||
# emulate Keystone version discovery
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
auth.V3_URL,
|
||||
status=405)
|
||||
|
||||
neutron_shell = openstack_shell.NeutronShell('2.0')
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__')
|
||||
self.mox.StubOutWithMock(neutron_shell, 'run_subcommand')
|
||||
clientmanager.ClientManager.__init__(
|
||||
token='', url='', auth_url=auth.V3_URL,
|
||||
tenant_name='test', tenant_id='tenant_id',
|
||||
username='test', user_id='',
|
||||
password='test', region_name='', api_version={'network': '2.0'},
|
||||
auth_strategy='keystone', service_type='network',
|
||||
endpoint_type='publicURL', insecure=False, ca_cert=None,
|
||||
timeout=None,
|
||||
raise_errors=False,
|
||||
retries=0,
|
||||
auth=mox.IsA(v3_auth.Password),
|
||||
session=mox.IsA(session.Session),
|
||||
log_credentials=True)
|
||||
neutron_shell.run_subcommand(['quota-list'])
|
||||
self.mox.ReplayAll()
|
||||
cmdline = ('--os-username test '
|
||||
'--os-password test '
|
||||
'--os-user-domain-name test '
|
||||
'--os-tenant-name test '
|
||||
'--os-auth-url %s '
|
||||
'--os-auth-strategy keystone quota-list'
|
||||
% auth.V3_URL)
|
||||
neutron_shell.run(cmdline.split())
|
||||
self.mox.VerifyAll()
|
||||
|
||||
@httpretty.activate
|
||||
def test_failed_auth_version_discovery_v2_auth_url(self):
|
||||
# emulate Keystone version discovery
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
auth.V2_URL,
|
||||
status=405)
|
||||
|
||||
neutron_shell = openstack_shell.NeutronShell('2.0')
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__')
|
||||
self.mox.StubOutWithMock(neutron_shell, 'run_subcommand')
|
||||
clientmanager.ClientManager.__init__(
|
||||
token='', url='', auth_url=auth.V2_URL,
|
||||
tenant_name='test', tenant_id='tenant_id',
|
||||
username='test', user_id='',
|
||||
password='test', region_name='', api_version={'network': '2.0'},
|
||||
auth_strategy='keystone', service_type='network',
|
||||
endpoint_type='publicURL', insecure=False, ca_cert=None,
|
||||
timeout=None,
|
||||
raise_errors=False,
|
||||
retries=0,
|
||||
auth=mox.IsA(v2_auth.Password),
|
||||
session=mox.IsA(session.Session),
|
||||
log_credentials=True)
|
||||
neutron_shell.run_subcommand(['quota-list'])
|
||||
self.mox.ReplayAll()
|
||||
cmdline = ('--os-username test '
|
||||
'--os-password test '
|
||||
'--os-tenant-name test '
|
||||
'--os-auth-url %s '
|
||||
'--os-auth-strategy keystone quota-list'
|
||||
% auth.V2_URL)
|
||||
neutron_shell.run(cmdline.split())
|
||||
self.mox.VerifyAll()
|
||||
|
||||
@httpretty.activate
|
||||
def test_auth_version_discovery_v3(self):
|
||||
# emulate Keystone version discovery
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
auth.BASE_URL,
|
||||
body=auth.V3_VERSION_LIST)
|
||||
|
||||
neutron_shell = openstack_shell.NeutronShell('2.0')
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__')
|
||||
self.mox.StubOutWithMock(neutron_shell, 'run_subcommand')
|
||||
clientmanager.ClientManager.__init__(
|
||||
token='', url='', auth_url=auth.BASE_URL,
|
||||
tenant_name='test', tenant_id='tenant_id',
|
||||
username='test', user_id='',
|
||||
password='test', region_name='', api_version={'network': '2.0'},
|
||||
auth_strategy='keystone', service_type='network',
|
||||
endpoint_type='publicURL', insecure=False, ca_cert=None,
|
||||
timeout=None,
|
||||
raise_errors=False,
|
||||
retries=0,
|
||||
auth=mox.IsA(v3_auth.Password),
|
||||
session=mox.IsA(session.Session),
|
||||
log_credentials=True)
|
||||
neutron_shell.run_subcommand(['quota-list'])
|
||||
self.mox.ReplayAll()
|
||||
cmdline = ('--os-username test '
|
||||
'--os-password test '
|
||||
'--os-user-domain-name test '
|
||||
'--os-tenant-name test '
|
||||
'--os-auth-url %s '
|
||||
'--os-auth-strategy keystone quota-list'
|
||||
% auth.BASE_URL)
|
||||
neutron_shell.run(cmdline.split())
|
||||
self.mox.VerifyAll()
|
||||
|
||||
@httpretty.activate
|
||||
def test_auth_version_discovery_v2(self):
|
||||
# emulate Keystone version discovery
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
auth.BASE_URL,
|
||||
body=auth.V3_VERSION_LIST)
|
||||
|
||||
neutron_shell = openstack_shell.NeutronShell('2.0')
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__')
|
||||
self.mox.StubOutWithMock(neutron_shell, 'run_subcommand')
|
||||
clientmanager.ClientManager.__init__(
|
||||
token='', url='', auth_url=auth.BASE_URL,
|
||||
tenant_name='test', tenant_id='tenant_id',
|
||||
username='test', user_id='',
|
||||
password='test', region_name='', api_version={'network': '2.0'},
|
||||
auth_strategy='keystone', service_type='network',
|
||||
endpoint_type='publicURL', insecure=False, ca_cert=None,
|
||||
timeout=None,
|
||||
raise_errors=False,
|
||||
retries=0,
|
||||
auth=mox.IsA(v2_auth.Password),
|
||||
session=mox.IsA(session.Session),
|
||||
log_credentials=True)
|
||||
neutron_shell.run_subcommand(['quota-list'])
|
||||
self.mox.ReplayAll()
|
||||
cmdline = ('--os-username test '
|
||||
'--os-password test '
|
||||
'--os-tenant-name test '
|
||||
'--os-auth-url %s '
|
||||
'--os-auth-strategy keystone quota-list'
|
||||
% auth.BASE_URL)
|
||||
neutron_shell.run(cmdline.split())
|
||||
self.mox.VerifyAll()
|
||||
|
||||
@httpretty.activate
|
||||
def test_insecure_auth(self):
|
||||
# emulate Keystone version discovery
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
auth.V2_URL,
|
||||
body=auth.V2_VERSION_ENTRY)
|
||||
|
||||
neutron_shell = openstack_shell.NeutronShell('2.0')
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__')
|
||||
self.mox.StubOutWithMock(neutron_shell, 'run_subcommand')
|
||||
clientmanager.ClientManager.__init__(
|
||||
token='', url='', auth_url=auth.V2_URL,
|
||||
tenant_name='test', tenant_id='tenant_id',
|
||||
username='test', user_id='',
|
||||
password='test', region_name='', api_version={'network': '2.0'},
|
||||
auth_strategy='keystone', service_type='network',
|
||||
endpoint_type='publicURL', insecure=True, ca_cert=None,
|
||||
timeout=None,
|
||||
raise_errors=False,
|
||||
retries=0,
|
||||
auth=mox.IgnoreArg(),
|
||||
session=mox.IgnoreArg(),
|
||||
log_credentials=True)
|
||||
neutron_shell.run_subcommand(['quota-list'])
|
||||
self.mox.ReplayAll()
|
||||
cmdline = ('--os-username test '
|
||||
'--os-password test '
|
||||
'--os-tenant-name test '
|
||||
'--insecure '
|
||||
'--os-auth-url %s '
|
||||
'--os-auth-strategy keystone quota-list'
|
||||
% auth.V2_URL)
|
||||
neutron_shell.run(cmdline.split())
|
||||
self.mox.VerifyAll()
|
||||
|
||||
@ -162,13 +436,13 @@ class ShellTest(testtools.TestCase):
|
||||
shell = openstack_shell.NeutronShell('2.0')
|
||||
parser = shell.build_option_parser('descr', '2.0')
|
||||
|
||||
# Neither $OS_ENDPOINT_TYPE nor --endpoint-type
|
||||
# Neither $OS_ENDPOINT_TYPE nor --os-endpoint-type
|
||||
namespace = parser.parse_args([])
|
||||
self.assertEqual('publicURL', namespace.endpoint_type)
|
||||
self.assertEqual('publicURL', namespace.os_endpoint_type)
|
||||
|
||||
# --endpoint-type but not $OS_ENDPOINT_TYPE
|
||||
namespace = parser.parse_args(['--endpoint-type=admin'])
|
||||
self.assertEqual('admin', namespace.endpoint_type)
|
||||
namespace = parser.parse_args(['--os-endpoint-type=admin'])
|
||||
self.assertEqual('admin', namespace.os_endpoint_type)
|
||||
|
||||
def test_endpoint_environment_variable(self):
|
||||
fixture = fixtures.EnvironmentVariable("OS_ENDPOINT_TYPE",
|
||||
@ -180,7 +454,7 @@ class ShellTest(testtools.TestCase):
|
||||
|
||||
# $OS_ENDPOINT_TYPE but not --endpoint-type
|
||||
namespace = parser.parse_args([])
|
||||
self.assertEqual("public", namespace.endpoint_type)
|
||||
self.assertEqual("public", namespace.os_endpoint_type)
|
||||
|
||||
# --endpoint-type and $OS_ENDPOINT_TYPE
|
||||
namespace = parser.parse_args(['--endpoint-type=admin'])
|
||||
|
@ -14,15 +14,17 @@
|
||||
# under the License.
|
||||
|
||||
import fixtures
|
||||
from mox3 import mox
|
||||
import requests
|
||||
import testtools
|
||||
|
||||
import httpretty
|
||||
from mox3 import mox
|
||||
|
||||
from neutronclient.client import HTTPClient
|
||||
from neutronclient.common.clientmanager import ClientManager
|
||||
from neutronclient.common import exceptions
|
||||
from neutronclient import shell as openstack_shell
|
||||
|
||||
from neutronclient.tests.unit import test_auth as auth
|
||||
|
||||
AUTH_TOKEN = 'test_token'
|
||||
END_URL = 'test_url'
|
||||
@ -41,7 +43,13 @@ class TestSSL(testtools.TestCase):
|
||||
self.mox = mox.Mox()
|
||||
self.addCleanup(self.mox.UnsetStubs)
|
||||
|
||||
@httpretty.activate
|
||||
def test_ca_cert_passed(self):
|
||||
# emulate Keystone version discovery
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
auth.V3_URL,
|
||||
body=auth.V3_VERSION_ENTRY)
|
||||
|
||||
self.mox.StubOutWithMock(ClientManager, '__init__')
|
||||
self.mox.StubOutWithMock(openstack_shell.NeutronShell, 'interact')
|
||||
|
||||
@ -66,14 +74,27 @@ class TestSSL(testtools.TestCase):
|
||||
raise_errors=mox.IgnoreArg(),
|
||||
log_credentials=mox.IgnoreArg(),
|
||||
timeout=mox.IgnoreArg(),
|
||||
auth=mox.IgnoreArg(),
|
||||
session=mox.IgnoreArg()
|
||||
)
|
||||
openstack_shell.NeutronShell.interact().AndReturn(0)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
openstack_shell.NeutronShell('2.0').run(['--os-cacert', CA_CERT])
|
||||
cmdline = (
|
||||
'--os-cacert %s --os-auth-url %s' %
|
||||
(CA_CERT, auth.V3_URL))
|
||||
|
||||
openstack_shell.NeutronShell('2.0').run(cmdline.split())
|
||||
self.mox.VerifyAll()
|
||||
|
||||
@httpretty.activate
|
||||
def test_ca_cert_passed_as_env_var(self):
|
||||
|
||||
# emulate Keystone version discovery
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
auth.V3_URL,
|
||||
body=auth.V3_VERSION_ENTRY)
|
||||
|
||||
self.useFixture(fixtures.EnvironmentVariable('OS_CACERT', CA_CERT))
|
||||
|
||||
self.mox.StubOutWithMock(ClientManager, '__init__')
|
||||
@ -100,11 +121,15 @@ class TestSSL(testtools.TestCase):
|
||||
raise_errors=mox.IgnoreArg(),
|
||||
log_credentials=mox.IgnoreArg(),
|
||||
timeout=mox.IgnoreArg(),
|
||||
auth=mox.IgnoreArg(),
|
||||
session=mox.IgnoreArg()
|
||||
)
|
||||
openstack_shell.NeutronShell.interact().AndReturn(0)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
openstack_shell.NeutronShell('2.0').run([])
|
||||
cmdline = ('--os-auth-url %s' % auth.V3_URL)
|
||||
openstack_shell.NeutronShell('2.0').run(cmdline.split())
|
||||
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_client_manager_properly_creates_httpclient_instance(self):
|
||||
@ -121,8 +146,12 @@ class TestSSL(testtools.TestCase):
|
||||
tenant_name=mox.IgnoreArg(),
|
||||
token=mox.IgnoreArg(),
|
||||
username=mox.IgnoreArg(),
|
||||
retries=mox.IgnoreArg(),
|
||||
raise_errors=mox.IgnoreArg(),
|
||||
user_id=mox.IgnoreArg(),
|
||||
tenant_id=mox.IgnoreArg(),
|
||||
timeout=mox.IgnoreArg(),
|
||||
log_credentials=mox.IgnoreArg(),
|
||||
service_type=mox.IgnoreArg(),
|
||||
endpoint_type=mox.IgnoreArg()
|
||||
)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
|
@ -135,6 +135,8 @@ class Client(object):
|
||||
:param bool raise_errors: If True then exceptions caused by connection
|
||||
failure are propagated to the caller.
|
||||
(default: True)
|
||||
:param session: Keystone client auth session to use. (optional)
|
||||
:param auth: Keystone auth plugin to use. (optional)
|
||||
|
||||
Example::
|
||||
|
||||
@ -1196,12 +1198,12 @@ class Client(object):
|
||||
def __init__(self, **kwargs):
|
||||
"""Initialize a new client for the Neutron v2.0 API."""
|
||||
super(Client, self).__init__()
|
||||
self.httpclient = client.HTTPClient(**kwargs)
|
||||
self.retries = kwargs.pop('retries', 0)
|
||||
self.raise_errors = kwargs.pop('raise_errors', True)
|
||||
self.httpclient = client.construct_http_client(**kwargs)
|
||||
self.version = '2.0'
|
||||
self.format = 'json'
|
||||
self.action_prefix = "/v%s" % (self.version)
|
||||
self.retries = kwargs.get('retries', 0)
|
||||
self.raise_errors = kwargs.get('raise_errors', True)
|
||||
self.retry_interval = 1
|
||||
|
||||
def _handle_fault_response(self, status_code, response_body):
|
||||
|
@ -4,6 +4,7 @@ cliff-tablib>=1.0
|
||||
coverage>=3.6
|
||||
discover
|
||||
fixtures>=0.3.14
|
||||
httpretty>=0.8.0,!=0.8.1,!=0.8.2
|
||||
mox3>=0.7.0
|
||||
oslosphinx
|
||||
oslotest
|
||||
|
Loading…
x
Reference in New Issue
Block a user