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:
@@ -22,6 +22,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from keystoneclient import access
|
from keystoneclient import access
|
||||||
|
from keystoneclient.auth.identity.base import BaseIdentityPlugin
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from neutronclient.common import exceptions
|
from neutronclient.common import exceptions
|
||||||
@@ -41,11 +42,25 @@ else:
|
|||||||
logging.getLogger("requests").setLevel(_requests_log_level)
|
logging.getLogger("requests").setLevel(_requests_log_level)
|
||||||
|
|
||||||
|
|
||||||
class HTTPClient(object):
|
class NeutronClientMixin(object):
|
||||||
"""Handles the REST calls and responses, include authn."""
|
|
||||||
|
|
||||||
USER_AGENT = 'python-neutronclient'
|
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,
|
def __init__(self, username=None, user_id=None,
|
||||||
tenant_name=None, tenant_id=None,
|
tenant_name=None, tenant_id=None,
|
||||||
password=None, auth_url=None,
|
password=None, auth_url=None,
|
||||||
@@ -265,13 +280,122 @@ class HTTPClient(object):
|
|||||||
'auth_user_id': self.auth_user_id,
|
'auth_user_id': self.auth_user_id,
|
||||||
'endpoint_url': self.endpoint_url}
|
'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
|
class SessionClient(NeutronClientMixin):
|
||||||
is returned.
|
|
||||||
"""
|
def __init__(self,
|
||||||
if hasattr(response, 'status_int'):
|
session,
|
||||||
return response.status_int
|
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:
|
else:
|
||||||
return response.status_code
|
# 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,
|
service_type=None,
|
||||||
timeout=None,
|
timeout=None,
|
||||||
retries=0,
|
retries=0,
|
||||||
raise_errors=True
|
raise_errors=True,
|
||||||
|
session=None,
|
||||||
|
auth=None,
|
||||||
):
|
):
|
||||||
self._token = token
|
self._token = token
|
||||||
self._url = url
|
self._url = url
|
||||||
@@ -88,10 +90,13 @@ class ClientManager(object):
|
|||||||
self._timeout = timeout
|
self._timeout = timeout
|
||||||
self._retries = retries
|
self._retries = retries
|
||||||
self._raise_errors = raise_errors
|
self._raise_errors = raise_errors
|
||||||
|
self._session = session
|
||||||
|
self._auth = auth
|
||||||
|
return
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
if not self._url:
|
if not self._url:
|
||||||
httpclient = client.HTTPClient(
|
httpclient = client.construct_http_client(
|
||||||
username=self._username,
|
username=self._username,
|
||||||
user_id=self._user_id,
|
user_id=self._user_id,
|
||||||
tenant_name=self._tenant_name,
|
tenant_name=self._tenant_name,
|
||||||
@@ -103,8 +108,10 @@ class ClientManager(object):
|
|||||||
endpoint_type=self._endpoint_type,
|
endpoint_type=self._endpoint_type,
|
||||||
insecure=self._insecure,
|
insecure=self._insecure,
|
||||||
ca_cert=self._ca_cert,
|
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()
|
httpclient.authenticate()
|
||||||
# Populate other password flow attributes
|
# Populate other password flow attributes
|
||||||
self._token = httpclient.auth_token
|
self._token = httpclient.auth_token
|
||||||
|
@@ -48,7 +48,9 @@ def make_client(instance):
|
|||||||
insecure=instance._insecure,
|
insecure=instance._insecure,
|
||||||
ca_cert=instance._ca_cert,
|
ca_cert=instance._ca_cert,
|
||||||
retries=instance._retries,
|
retries=instance._retries,
|
||||||
raise_errors=instance._raise_errors)
|
raise_errors=instance._raise_errors,
|
||||||
|
session=instance._session,
|
||||||
|
auth=instance._auth)
|
||||||
return client
|
return client
|
||||||
else:
|
else:
|
||||||
raise exceptions.UnsupportedVersion(_("API version %s is not "
|
raise exceptions.UnsupportedVersion(_("API version %s is not "
|
||||||
|
@@ -25,6 +25,13 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import sys
|
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 app
|
||||||
from cliff import commandmanager
|
from cliff import commandmanager
|
||||||
|
|
||||||
@@ -386,13 +393,50 @@ class NeutronShell(app.App):
|
|||||||
default=0,
|
default=0,
|
||||||
help=_("How many times the request to the Neutron server should "
|
help=_("How many times the request to the Neutron server should "
|
||||||
"be retried if it fails."))
|
"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(
|
parser.add_argument(
|
||||||
'--os-auth-strategy', metavar='<auth-strategy>',
|
'--os-auth-strategy', metavar='<auth-strategy>',
|
||||||
default=env('OS_AUTH_STRATEGY', default='keystone'),
|
default=env('OS_AUTH_STRATEGY', default='keystone'),
|
||||||
help=_('Authentication strategy, defaults to '
|
help=_('DEPRECATED! Only keystone is supported.'))
|
||||||
'env[OS_AUTH_STRATEGY] or keystone. For now, any '
|
|
||||||
'other value will disable the authentication.'))
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--os_auth_strategy',
|
'--os_auth_strategy',
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
@@ -405,20 +449,39 @@ class NeutronShell(app.App):
|
|||||||
'--os_auth_url',
|
'--os_auth_url',
|
||||||
help=argparse.SUPPRESS)
|
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>',
|
'--os-tenant-name', metavar='<auth-tenant-name>',
|
||||||
default=env('OS_TENANT_NAME'),
|
default=env('OS_TENANT_NAME'),
|
||||||
help=_('Authentication tenant name, defaults to '
|
help=_('Authentication tenant name, defaults to '
|
||||||
'env[OS_TENANT_NAME].'))
|
'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(
|
parser.add_argument(
|
||||||
'--os_tenant_name',
|
'--os_tenant_name',
|
||||||
help=argparse.SUPPRESS)
|
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>',
|
'--os-tenant-id', metavar='<auth-tenant-id>',
|
||||||
default=env('OS_TENANT_ID'),
|
default=env('OS_TENANT_ID'),
|
||||||
help=_('Authentication tenant ID, defaults to '
|
help=_('Authentication tenant ID, defaults to '
|
||||||
'env[OS_TENANT_ID].'))
|
'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(
|
parser.add_argument(
|
||||||
'--os-username', metavar='<auth-username>',
|
'--os-username', metavar='<auth-username>',
|
||||||
@@ -433,6 +496,78 @@ class NeutronShell(app.App):
|
|||||||
default=env('OS_USER_ID'),
|
default=env('OS_USER_ID'),
|
||||||
help=_('Authentication user ID (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(
|
parser.add_argument(
|
||||||
'--os-password', metavar='<auth-password>',
|
'--os-password', metavar='<auth-password>',
|
||||||
default=utils.env('OS_PASSWORD'),
|
default=utils.env('OS_PASSWORD'),
|
||||||
@@ -458,22 +593,12 @@ class NeutronShell(app.App):
|
|||||||
'--os_token',
|
'--os_token',
|
||||||
help=argparse.SUPPRESS)
|
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(
|
parser.add_argument(
|
||||||
'--timeout', metavar='<seconds>',
|
'--timeout', metavar='<seconds>',
|
||||||
default=env('OS_NETWORK_TIMEOUT', default=None), type=float,
|
default=env('OS_NETWORK_TIMEOUT', default=None), type=float,
|
||||||
help=_('Timeout in seconds to wait for an HTTP response. Defaults '
|
help=_('Timeout in seconds to wait for an HTTP response. Defaults '
|
||||||
'to env[OS_NETWORK_TIMEOUT] or None if not specified.'))
|
'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(
|
parser.add_argument(
|
||||||
'--os-url', metavar='<url>',
|
'--os-url', metavar='<url>',
|
||||||
default=env('OS_URL'),
|
default=env('OS_URL'),
|
||||||
@@ -482,14 +607,6 @@ class NeutronShell(app.App):
|
|||||||
'--os_url',
|
'--os_url',
|
||||||
help=argparse.SUPPRESS)
|
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(
|
parser.add_argument(
|
||||||
'--insecure',
|
'--insecure',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@@ -499,8 +616,6 @@ class NeutronShell(app.App):
|
|||||||
"not be verified against any certificate authorities. "
|
"not be verified against any certificate authorities. "
|
||||||
"This option should be used with caution."))
|
"This option should be used with caution."))
|
||||||
|
|
||||||
return parser
|
|
||||||
|
|
||||||
def _bash_completion(self):
|
def _bash_completion(self):
|
||||||
"""Prints all of the commands and options for bash-completion."""
|
"""Prints all of the commands and options for bash-completion."""
|
||||||
commands = set()
|
commands = set()
|
||||||
@@ -622,6 +737,13 @@ class NeutronShell(app.App):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
# Validate password flow auth
|
# 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
|
if (not self.options.os_username
|
||||||
and not self.options.os_user_id):
|
and not self.options.os_user_id):
|
||||||
raise exc.CommandError(
|
raise exc.CommandError(
|
||||||
@@ -634,12 +756,19 @@ class NeutronShell(app.App):
|
|||||||
_("You must provide a password via"
|
_("You must provide a password via"
|
||||||
" either --os-password or env[OS_PASSWORD]"))
|
" either --os-password or env[OS_PASSWORD]"))
|
||||||
|
|
||||||
if (not self.options.os_tenant_name
|
if (not project_info):
|
||||||
and not self.options.os_tenant_id):
|
# tenent is deprecated in Keystone v3. Use the latest
|
||||||
|
# terminology instead.
|
||||||
raise exc.CommandError(
|
raise exc.CommandError(
|
||||||
_("You must provide a tenant_name or tenant_id via"
|
_("You must provide a project_id or project_name ("
|
||||||
" --os-tenant-name, env[OS_TENANT_NAME]"
|
"with project_domain_name or project_domain_id) "
|
||||||
" --os-tenant-id, or via env[OS_TENANT_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:
|
if not self.options.os_auth_url:
|
||||||
raise exc.CommandError(
|
raise exc.CommandError(
|
||||||
@@ -651,6 +780,8 @@ class NeutronShell(app.App):
|
|||||||
_("You must provide a service URL via"
|
_("You must provide a service URL via"
|
||||||
" either --os-url or env[OS_URL]"))
|
" either --os-url or env[OS_URL]"))
|
||||||
|
|
||||||
|
auth_session = self._get_keystone_session()
|
||||||
|
|
||||||
self.client_manager = clientmanager.ClientManager(
|
self.client_manager = clientmanager.ClientManager(
|
||||||
token=self.options.os_token,
|
token=self.options.os_token,
|
||||||
url=self.options.os_url,
|
url=self.options.os_url,
|
||||||
@@ -663,13 +794,18 @@ class NeutronShell(app.App):
|
|||||||
region_name=self.options.os_region_name,
|
region_name=self.options.os_region_name,
|
||||||
api_version=self.api_version,
|
api_version=self.api_version,
|
||||||
auth_strategy=self.options.os_auth_strategy,
|
auth_strategy=self.options.os_auth_strategy,
|
||||||
service_type=self.options.service_type,
|
# FIXME (bklei) honor deprecated service_type and
|
||||||
endpoint_type=self.options.endpoint_type,
|
# 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,
|
insecure=self.options.insecure,
|
||||||
ca_cert=self.options.os_cacert,
|
ca_cert=self.options.os_cacert,
|
||||||
timeout=self.options.timeout,
|
timeout=self.options.timeout,
|
||||||
retries=self.options.retries,
|
retries=self.options.retries,
|
||||||
raise_errors=False,
|
raise_errors=False,
|
||||||
|
session=auth_session,
|
||||||
|
auth=auth_session.auth,
|
||||||
log_credentials=True)
|
log_credentials=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -721,6 +857,89 @@ class NeutronShell(app.App):
|
|||||||
root_logger.addHandler(console)
|
root_logger.addHandler(console)
|
||||||
return
|
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:]):
|
def main(argv=sys.argv[1:]):
|
||||||
try:
|
try:
|
||||||
|
@@ -14,18 +14,25 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
import copy
|
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from keystoneclient import exceptions as k_exceptions
|
import httpretty
|
||||||
from mox3 import mox
|
from mox3 import mox
|
||||||
import requests
|
import requests
|
||||||
import testtools
|
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 import client
|
||||||
from neutronclient.common import exceptions
|
from neutronclient.common import exceptions
|
||||||
from neutronclient.common import utils
|
from neutronclient.common import utils
|
||||||
|
from neutronclient.openstack.common import jsonutils
|
||||||
|
|
||||||
|
|
||||||
USERNAME = 'testuser'
|
USERNAME = 'testuser'
|
||||||
@@ -33,11 +40,14 @@ USER_ID = 'testuser_id'
|
|||||||
TENANT_NAME = 'testtenant'
|
TENANT_NAME = 'testtenant'
|
||||||
TENANT_ID = 'testtenant_id'
|
TENANT_ID = 'testtenant_id'
|
||||||
PASSWORD = 'password'
|
PASSWORD = 'password'
|
||||||
AUTH_URL = 'authurl'
|
|
||||||
ENDPOINT_URL = 'localurl'
|
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'
|
ENDPOINT_OVERRIDE = 'otherurl'
|
||||||
TOKEN = 'tokentoken'
|
TOKEN = 'tokentoken'
|
||||||
REGION = 'RegionTest'
|
TOKENID = uuid.uuid4().hex
|
||||||
|
REGION = 'RegionOne'
|
||||||
NOAUTH = 'noauth'
|
NOAUTH = 'noauth'
|
||||||
|
|
||||||
KS_TOKEN_RESULT = {
|
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):
|
def get_response(status_code, headers=None):
|
||||||
response = mox.Mox().CreateMock(requests.Response)
|
response = mox.Mox().CreateMock(requests.Response)
|
||||||
@@ -77,6 +136,49 @@ def get_response(status_code, headers=None):
|
|||||||
return response
|
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):
|
class CLITestAuthNoAuth(testtools.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -109,20 +211,18 @@ class CLITestAuthNoAuth(testtools.TestCase):
|
|||||||
|
|
||||||
class CLITestAuthKeystone(testtools.TestCase):
|
class CLITestAuthKeystone(testtools.TestCase):
|
||||||
|
|
||||||
# Auth Body expected
|
|
||||||
auth_body = ('{"auth": {"tenantName": "testtenant", '
|
|
||||||
'"passwordCredentials": '
|
|
||||||
'{"username": "testuser", "password": "password"}}}')
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Prepare the test environment."""
|
"""Prepare the test environment."""
|
||||||
super(CLITestAuthKeystone, self).setUp()
|
super(CLITestAuthKeystone, self).setUp()
|
||||||
self.mox = mox.Mox()
|
self.mox = mox.Mox()
|
||||||
self.client = client.HTTPClient(username=USERNAME,
|
|
||||||
|
self.client = client.construct_http_client(
|
||||||
|
username=USERNAME,
|
||||||
tenant_name=TENANT_NAME,
|
tenant_name=TENANT_NAME,
|
||||||
password=PASSWORD,
|
password=PASSWORD,
|
||||||
auth_url=AUTH_URL,
|
auth_url=AUTH_URL,
|
||||||
region_name=REGION)
|
region_name=REGION)
|
||||||
|
|
||||||
self.addCleanup(self.mox.VerifyAll)
|
self.addCleanup(self.mox.VerifyAll)
|
||||||
self.addCleanup(self.mox.UnsetStubs)
|
self.addCleanup(self.mox.UnsetStubs)
|
||||||
|
|
||||||
@@ -142,24 +242,30 @@ class CLITestAuthKeystone(testtools.TestCase):
|
|||||||
'endpoint_url': self.client.endpoint_url}
|
'endpoint_url': self.client.endpoint_url}
|
||||||
self.assertEqual(client_.get_auth_info(), expected)
|
self.assertEqual(client_.get_auth_info(), expected)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
def test_get_token(self):
|
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)
|
res200 = get_response(200)
|
||||||
|
|
||||||
self.client.request(
|
self.client.request(
|
||||||
AUTH_URL + '/tokens', 'POST',
|
'/resource', 'GET',
|
||||||
body=self.auth_body, headers=mox.IsA(dict)
|
authenticated=True
|
||||||
).AndReturn((res200, json.dumps(KS_TOKEN_RESULT)))
|
|
||||||
self.client.request(
|
|
||||||
mox.StrContains(ENDPOINT_URL + '/resource'), 'GET',
|
|
||||||
headers=mox.ContainsKeyValue('X-Auth-Token', TOKEN)
|
|
||||||
).AndReturn((res200, ''))
|
).AndReturn((res200, ''))
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
self.client.do_request('/resource', 'GET')
|
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):
|
def test_refresh_token(self):
|
||||||
self.mox.StubOutWithMock(self.client, "request")
|
self.mox.StubOutWithMock(self.client, "request")
|
||||||
@@ -292,53 +398,55 @@ class CLITestAuthKeystone(testtools.TestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
self.client.do_request('/resource', 'GET')
|
self.client.do_request('/resource', 'GET')
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
def test_endpoint_type(self):
|
def test_endpoint_type(self):
|
||||||
resources = copy.deepcopy(KS_TOKEN_RESULT)
|
auth_session, auth_plugin = setup_keystone_v3()
|
||||||
endpoints = resources['access']['serviceCatalog'][0]['endpoints'][0]
|
|
||||||
endpoints['internalURL'] = 'internal'
|
|
||||||
endpoints['adminURL'] = 'admin'
|
|
||||||
endpoints['publicURL'] = 'public'
|
|
||||||
|
|
||||||
# Test default behavior is to choose public.
|
# 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,
|
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.client.authenticate()
|
||||||
self.assertEqual(self.client.endpoint_url, 'public')
|
self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL)
|
||||||
|
|
||||||
# Test admin url
|
# Test admin url
|
||||||
self.client = client.HTTPClient(
|
self.client = client.construct_http_client(
|
||||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
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.client.authenticate()
|
||||||
self.assertEqual(self.client.endpoint_url, 'admin')
|
self.assertEqual(self.client.endpoint_url, ADMIN_ENDPOINT_URL)
|
||||||
|
|
||||||
# Test public url
|
# Test public url
|
||||||
self.client = client.HTTPClient(
|
self.client = client.construct_http_client(
|
||||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
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.client.authenticate()
|
||||||
self.assertEqual(self.client.endpoint_url, 'public')
|
self.assertEqual(self.client.endpoint_url, PUBLIC_ENDPOINT_URL)
|
||||||
|
|
||||||
# Test internal url
|
# Test internal url
|
||||||
self.client = client.HTTPClient(
|
self.client = client.construct_http_client(
|
||||||
username=USERNAME, tenant_name=TENANT_NAME, password=PASSWORD,
|
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.client.authenticate()
|
||||||
self.assertEqual(self.client.endpoint_url, 'internal')
|
self.assertEqual(self.client.endpoint_url, INTERNAL_ENDPOINT_URL)
|
||||||
|
|
||||||
# Test url that isn't found in the service catalog
|
# 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,
|
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.assertRaises(
|
||||||
self.client._extract_service_catalog,
|
ks_exceptions.EndpointNotFound,
|
||||||
resources)
|
self.client.authenticate)
|
||||||
|
|
||||||
def test_strip_credentials_from_log(self):
|
def test_strip_credentials_from_log(self):
|
||||||
def verify_no_credentials(kwargs):
|
def verify_no_credentials(kwargs):
|
||||||
@@ -370,11 +478,6 @@ class CLITestAuthKeystone(testtools.TestCase):
|
|||||||
|
|
||||||
class CLITestAuthKeystoneWithId(CLITestAuthKeystone):
|
class CLITestAuthKeystoneWithId(CLITestAuthKeystone):
|
||||||
|
|
||||||
# Auth Body expected
|
|
||||||
auth_body = ('{"auth": {"passwordCredentials": '
|
|
||||||
'{"password": "password", "userId": "testuser_id"}, '
|
|
||||||
'"tenantId": "testtenant_id"}}')
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Prepare the test environment."""
|
"""Prepare the test environment."""
|
||||||
super(CLITestAuthKeystoneWithId, self).setUp()
|
super(CLITestAuthKeystoneWithId, self).setUp()
|
||||||
@@ -387,11 +490,6 @@ class CLITestAuthKeystoneWithId(CLITestAuthKeystone):
|
|||||||
|
|
||||||
class CLITestAuthKeystoneWithIdandName(CLITestAuthKeystone):
|
class CLITestAuthKeystoneWithIdandName(CLITestAuthKeystone):
|
||||||
|
|
||||||
# Auth Body expected
|
|
||||||
auth_body = ('{"auth": {"passwordCredentials": '
|
|
||||||
'{"password": "password", "userId": "testuser_id"}, '
|
|
||||||
'"tenantId": "testtenant_id"}}')
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Prepare the test environment."""
|
"""Prepare the test environment."""
|
||||||
super(CLITestAuthKeystoneWithIdandName, self).setUp()
|
super(CLITestAuthKeystoneWithIdandName, self).setUp()
|
||||||
@@ -402,3 +500,61 @@ class CLITestAuthKeystoneWithIdandName(CLITestAuthKeystone):
|
|||||||
password=PASSWORD,
|
password=PASSWORD,
|
||||||
auth_url=AUTH_URL,
|
auth_url=AUTH_URL,
|
||||||
region_name=REGION)
|
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 sys
|
||||||
|
|
||||||
import fixtures
|
import fixtures
|
||||||
|
import httpretty
|
||||||
from mox3 import mox
|
from mox3 import mox
|
||||||
import six
|
import six
|
||||||
import testtools
|
import testtools
|
||||||
from testtools import matchers
|
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.common import clientmanager
|
||||||
from neutronclient import shell as openstack_shell
|
from neutronclient import shell as openstack_shell
|
||||||
|
from neutronclient.tests.unit import test_auth as auth
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_USERNAME = 'username'
|
DEFAULT_USERNAME = 'username'
|
||||||
@@ -91,6 +97,14 @@ class ShellTest(testtools.TestCase):
|
|||||||
matchers.MatchesRegex(required))
|
matchers.MatchesRegex(required))
|
||||||
self.assertFalse(stderr)
|
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):
|
def test_help_on_subcommand(self):
|
||||||
required = [
|
required = [
|
||||||
'.*?^usage: .* quota-list']
|
'.*?^usage: .* quota-list']
|
||||||
@@ -116,27 +130,287 @@ class ShellTest(testtools.TestCase):
|
|||||||
self.assertEqual('You must provide a service URL via '
|
self.assertEqual('You must provide a service URL via '
|
||||||
'either --os-url or env[OS_URL]', stderr.strip())
|
'either --os-url or env[OS_URL]', stderr.strip())
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
def test_auth(self):
|
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')
|
neutron_shell = openstack_shell.NeutronShell('2.0')
|
||||||
self.addCleanup(self.mox.UnsetStubs)
|
self.addCleanup(self.mox.UnsetStubs)
|
||||||
self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__')
|
self.mox.StubOutWithMock(clientmanager.ClientManager, '__init__')
|
||||||
self.mox.StubOutWithMock(neutron_shell, 'run_subcommand')
|
self.mox.StubOutWithMock(neutron_shell, 'run_subcommand')
|
||||||
clientmanager.ClientManager.__init__(
|
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',
|
tenant_name='test', tenant_id='tenant_id',
|
||||||
username='test', user_id='',
|
username='test', user_id='',
|
||||||
password='test', region_name='', api_version={'network': '2.0'},
|
password='test', region_name='', api_version={'network': '2.0'},
|
||||||
auth_strategy='keystone', service_type='network',
|
auth_strategy='keystone', service_type='network',
|
||||||
endpoint_type='publicURL', insecure=False, ca_cert=None, retries=0,
|
endpoint_type='publicURL', insecure=False, ca_cert=None,
|
||||||
raise_errors=False, log_credentials=True, timeout=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'])
|
neutron_shell.run_subcommand(['quota-list'])
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
cmdline = ('--os-username test '
|
cmdline = ('--os-username test '
|
||||||
'--os-password test '
|
'--os-password test '
|
||||||
'--os-tenant-name test '
|
'--os-tenant-name test '
|
||||||
'--os-auth-url http://127.0.0.1:5000/ '
|
'--os-auth-url %s '
|
||||||
'--os-auth-strategy keystone quota-list')
|
'--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())
|
neutron_shell.run(cmdline.split())
|
||||||
self.mox.VerifyAll()
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
@@ -162,13 +436,13 @@ class ShellTest(testtools.TestCase):
|
|||||||
shell = openstack_shell.NeutronShell('2.0')
|
shell = openstack_shell.NeutronShell('2.0')
|
||||||
parser = shell.build_option_parser('descr', '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([])
|
namespace = parser.parse_args([])
|
||||||
self.assertEqual('publicURL', namespace.endpoint_type)
|
self.assertEqual('publicURL', namespace.os_endpoint_type)
|
||||||
|
|
||||||
# --endpoint-type but not $OS_ENDPOINT_TYPE
|
# --endpoint-type but not $OS_ENDPOINT_TYPE
|
||||||
namespace = parser.parse_args(['--endpoint-type=admin'])
|
namespace = parser.parse_args(['--os-endpoint-type=admin'])
|
||||||
self.assertEqual('admin', namespace.endpoint_type)
|
self.assertEqual('admin', namespace.os_endpoint_type)
|
||||||
|
|
||||||
def test_endpoint_environment_variable(self):
|
def test_endpoint_environment_variable(self):
|
||||||
fixture = fixtures.EnvironmentVariable("OS_ENDPOINT_TYPE",
|
fixture = fixtures.EnvironmentVariable("OS_ENDPOINT_TYPE",
|
||||||
@@ -180,7 +454,7 @@ class ShellTest(testtools.TestCase):
|
|||||||
|
|
||||||
# $OS_ENDPOINT_TYPE but not --endpoint-type
|
# $OS_ENDPOINT_TYPE but not --endpoint-type
|
||||||
namespace = parser.parse_args([])
|
namespace = parser.parse_args([])
|
||||||
self.assertEqual("public", namespace.endpoint_type)
|
self.assertEqual("public", namespace.os_endpoint_type)
|
||||||
|
|
||||||
# --endpoint-type and $OS_ENDPOINT_TYPE
|
# --endpoint-type and $OS_ENDPOINT_TYPE
|
||||||
namespace = parser.parse_args(['--endpoint-type=admin'])
|
namespace = parser.parse_args(['--endpoint-type=admin'])
|
||||||
|
@@ -14,15 +14,17 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import fixtures
|
import fixtures
|
||||||
from mox3 import mox
|
|
||||||
import requests
|
import requests
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
|
import httpretty
|
||||||
|
from mox3 import mox
|
||||||
|
|
||||||
from neutronclient.client import HTTPClient
|
from neutronclient.client import HTTPClient
|
||||||
from neutronclient.common.clientmanager import ClientManager
|
from neutronclient.common.clientmanager import ClientManager
|
||||||
from neutronclient.common import exceptions
|
from neutronclient.common import exceptions
|
||||||
from neutronclient import shell as openstack_shell
|
from neutronclient import shell as openstack_shell
|
||||||
|
from neutronclient.tests.unit import test_auth as auth
|
||||||
|
|
||||||
AUTH_TOKEN = 'test_token'
|
AUTH_TOKEN = 'test_token'
|
||||||
END_URL = 'test_url'
|
END_URL = 'test_url'
|
||||||
@@ -41,7 +43,13 @@ class TestSSL(testtools.TestCase):
|
|||||||
self.mox = mox.Mox()
|
self.mox = mox.Mox()
|
||||||
self.addCleanup(self.mox.UnsetStubs)
|
self.addCleanup(self.mox.UnsetStubs)
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
def test_ca_cert_passed(self):
|
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(ClientManager, '__init__')
|
||||||
self.mox.StubOutWithMock(openstack_shell.NeutronShell, 'interact')
|
self.mox.StubOutWithMock(openstack_shell.NeutronShell, 'interact')
|
||||||
|
|
||||||
@@ -66,14 +74,27 @@ class TestSSL(testtools.TestCase):
|
|||||||
raise_errors=mox.IgnoreArg(),
|
raise_errors=mox.IgnoreArg(),
|
||||||
log_credentials=mox.IgnoreArg(),
|
log_credentials=mox.IgnoreArg(),
|
||||||
timeout=mox.IgnoreArg(),
|
timeout=mox.IgnoreArg(),
|
||||||
|
auth=mox.IgnoreArg(),
|
||||||
|
session=mox.IgnoreArg()
|
||||||
)
|
)
|
||||||
openstack_shell.NeutronShell.interact().AndReturn(0)
|
openstack_shell.NeutronShell.interact().AndReturn(0)
|
||||||
self.mox.ReplayAll()
|
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()
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
@httpretty.activate
|
||||||
def test_ca_cert_passed_as_env_var(self):
|
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.useFixture(fixtures.EnvironmentVariable('OS_CACERT', CA_CERT))
|
||||||
|
|
||||||
self.mox.StubOutWithMock(ClientManager, '__init__')
|
self.mox.StubOutWithMock(ClientManager, '__init__')
|
||||||
@@ -100,11 +121,15 @@ class TestSSL(testtools.TestCase):
|
|||||||
raise_errors=mox.IgnoreArg(),
|
raise_errors=mox.IgnoreArg(),
|
||||||
log_credentials=mox.IgnoreArg(),
|
log_credentials=mox.IgnoreArg(),
|
||||||
timeout=mox.IgnoreArg(),
|
timeout=mox.IgnoreArg(),
|
||||||
|
auth=mox.IgnoreArg(),
|
||||||
|
session=mox.IgnoreArg()
|
||||||
)
|
)
|
||||||
openstack_shell.NeutronShell.interact().AndReturn(0)
|
openstack_shell.NeutronShell.interact().AndReturn(0)
|
||||||
self.mox.ReplayAll()
|
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()
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
def test_client_manager_properly_creates_httpclient_instance(self):
|
def test_client_manager_properly_creates_httpclient_instance(self):
|
||||||
@@ -121,8 +146,12 @@ class TestSSL(testtools.TestCase):
|
|||||||
tenant_name=mox.IgnoreArg(),
|
tenant_name=mox.IgnoreArg(),
|
||||||
token=mox.IgnoreArg(),
|
token=mox.IgnoreArg(),
|
||||||
username=mox.IgnoreArg(),
|
username=mox.IgnoreArg(),
|
||||||
retries=mox.IgnoreArg(),
|
user_id=mox.IgnoreArg(),
|
||||||
raise_errors=mox.IgnoreArg(),
|
tenant_id=mox.IgnoreArg(),
|
||||||
|
timeout=mox.IgnoreArg(),
|
||||||
|
log_credentials=mox.IgnoreArg(),
|
||||||
|
service_type=mox.IgnoreArg(),
|
||||||
|
endpoint_type=mox.IgnoreArg()
|
||||||
)
|
)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@@ -135,6 +135,8 @@ class Client(object):
|
|||||||
:param bool raise_errors: If True then exceptions caused by connection
|
:param bool raise_errors: If True then exceptions caused by connection
|
||||||
failure are propagated to the caller.
|
failure are propagated to the caller.
|
||||||
(default: True)
|
(default: True)
|
||||||
|
:param session: Keystone client auth session to use. (optional)
|
||||||
|
:param auth: Keystone auth plugin to use. (optional)
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
@@ -1196,12 +1198,12 @@ class Client(object):
|
|||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
"""Initialize a new client for the Neutron v2.0 API."""
|
"""Initialize a new client for the Neutron v2.0 API."""
|
||||||
super(Client, self).__init__()
|
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.version = '2.0'
|
||||||
self.format = 'json'
|
self.format = 'json'
|
||||||
self.action_prefix = "/v%s" % (self.version)
|
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
|
self.retry_interval = 1
|
||||||
|
|
||||||
def _handle_fault_response(self, status_code, response_body):
|
def _handle_fault_response(self, status_code, response_body):
|
||||||
|
@@ -4,6 +4,7 @@ cliff-tablib>=1.0
|
|||||||
coverage>=3.6
|
coverage>=3.6
|
||||||
discover
|
discover
|
||||||
fixtures>=0.3.14
|
fixtures>=0.3.14
|
||||||
|
httpretty>=0.8.0,!=0.8.1,!=0.8.2
|
||||||
mox3>=0.7.0
|
mox3>=0.7.0
|
||||||
oslosphinx
|
oslosphinx
|
||||||
oslotest
|
oslotest
|
||||||
|
Reference in New Issue
Block a user