diff --git a/openstack/auth/__init__.py b/openstack/auth/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/openstack/auth/access.py b/openstack/auth/access.py deleted file mode 100644 index 5e781eb3c..000000000 --- a/openstack/auth/access.py +++ /dev/null @@ -1,520 +0,0 @@ -# Copyright 2012 Nebula, Inc. -# -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import datetime -import logging - -from oslo_utils import timeutils - -from openstack.auth import service_catalog as catalog - - -logger = logging.getLogger(__name__) - -# Do not use token before expiration -BEST_BEFORE_SECONDS = 30 - - -class AccessInfo(object): - """Encapsulates a raw authentication token from keystone. - - Provides helper methods for extracting useful values from that token. - - """ - def __init__(self, **kwargs): - """Construct access info.""" - self._info = kwargs - - @classmethod - def factory(cls, resp=None, body=None, **kwargs): - """AccessInfo factory. - - Create AccessInfo object given a successful auth response & body - or a user-provided dict. - """ - - if body is not None or len(kwargs): - if AccessInfoV3.is_valid(body, **kwargs): - token = None - if resp: - token = resp.headers['X-Subject-Token'] - if body: - return AccessInfoV3(token, **body['token']) - else: - return AccessInfoV3(token, **kwargs) - elif AccessInfoV2.is_valid(body, **kwargs): - if body: - return AccessInfoV2(**body['access']) - else: - return AccessInfoV2(**kwargs) - else: - raise NotImplementedError('Unrecognized auth response') - else: - return AccessInfoV2(**kwargs) - - def will_expire_soon(self, best_before=BEST_BEFORE_SECONDS): - """Determines if expiration is about to occur. - - :return: boolean : true if expiration is within the given duration - - """ - norm_expires = timeutils.normalize_time(self.expires) - soon = (timeutils.utcnow() + datetime.timedelta(seconds=best_before)) - expiring = norm_expires < soon - - if expiring: - logger.debug("Token expiring at %s", norm_expires) - - return expiring - - @classmethod - def is_valid(cls, body, **kwargs): - """Valid v2 or v3 token - - Determines if processing v2 or v3 token given a successful - auth body or a user-provided dict. - - :return: boolean : true if auth body matches implementing class - """ - raise NotImplementedError() - - def has_service_catalog(self): - """Returns true if the authorization token has a service catalog. - - :returns: boolean - """ - raise NotImplementedError() - - @property - def auth_token(self): - """Authorize token - - Returns the token_id associated with the auth request, to be used - in headers for authenticating OpenStack API requests. - - :returns: str - """ - raise NotImplementedError() - - @property - def expires(self): - """Returns the token expiration (as datetime object) - - :returns: datetime - """ - raise NotImplementedError() - - @property - def username(self): - """User name - - Returns the username associated with the authentication request. - Follows the pattern defined in the V2 API of first looking for 'name', - returning that if available, and falling back to 'username' if name - is unavailable. - - :returns: str - """ - raise NotImplementedError() - - @property - def user_id(self): - """Returns the user id associated with the authentication request. - - :returns: str - """ - raise NotImplementedError() - - @property - def user_domain_id(self): - """Users domain id - - Returns the domain id of the user associated with the authentication - request. For v2, it always returns 'default' which may be different - from the Keystone configuration. - - :returns: str - """ - raise NotImplementedError() - - @property - def user_domain_name(self): - """Users domain name - - Returns the domain name of the user associated with the authentication - request. For v2, it always returns 'Default' which may be different - from the Keystone configuration. - - :returns: str - """ - raise NotImplementedError() - - @property - def role_names(self): - """Role names - - Returns a list of role names of the user associated with the - authentication request. - - :returns: a list of strings of role names - """ - raise NotImplementedError() - - @property - def domain_name(self): - """Returns the domain name associated with the authentication token. - - :returns: str or None (if no domain associated with the token) - """ - raise NotImplementedError() - - @property - def domain_id(self): - """Returns the domain id associated with the authentication token. - - :returns: str or None (if no domain associated with the token) - """ - raise NotImplementedError() - - @property - def project_name(self): - """Returns the project name associated with the authentication request. - - :returns: str or None (if no project associated with the token) - """ - raise NotImplementedError() - - @property - def tenant_name(self): - """Synonym for project_name.""" - return self.project_name - - @property - def project_scoped(self): - """Returns true if the authorization token was scoped to a project. - - :returns: bool - """ - raise NotImplementedError() - - @property - def domain_scoped(self): - """Returns true if the authorization token was scoped to a domain. - - :returns: bool - """ - raise NotImplementedError() - - @property - def trust_id(self): - """Returns the trust id associated with the authentication token. - - :returns: str or None (if no trust associated with the token) - """ - raise NotImplementedError() - - @property - def trust_scoped(self): - """Delegated to a trust. - - Returns true if the authorization token was scoped as delegated in a - trust, via the OS-TRUST v3 extension. - - :returns: bool - """ - raise NotImplementedError() - - @property - def project_id(self): - """Project id. - - Returns the project ID associated with the authentication request, - or None if the authentication request wasn't scoped to a project. - - :returns: str or None (if no project associated with the token) - """ - raise NotImplementedError() - - @property - def tenant_id(self): - """Synonym for project_id.""" - return self.project_id - - @property - def project_domain_id(self): - """Project domain. - - Returns the domain id of the project associated with the authentication - request. For v2, it returns 'default' if a project is scoped or None - which may be different from the keystone configuration. - - :returns: str - """ - raise NotImplementedError() - - @property - def project_domain_name(self): - """Project domain name. - - Returns the domain name of the project associated with the - authentication request. For v2, it returns 'Default' if a project is - scoped or None which may be different from the keystone configuration. - - :returns: str - """ - raise NotImplementedError() - - @property - def version(self): - """Returns the version of the auth token from identity service. - - :returns: str - """ - return self._info.get('version') - - def __repr__(self): - return str(self._info) - - -class AccessInfoV2(AccessInfo): - """Object for encapsulating a raw v2 auth token from identity service.""" - - def __init__(self, **kwargs): - super(AccessInfoV2, self).__init__(**kwargs) - self._info.update(version='v2.0') - service_catalog = self._info['serviceCatalog'] - self.service_catalog = catalog.ServiceCatalogV2(service_catalog) - - @classmethod - def is_valid(cls, body, **kwargs): - if body: - return 'access' in body - elif kwargs: - return kwargs.get('version') == 'v2.0' - else: - return False - - def has_service_catalog(self): - return 'serviceCatalog' in self._info - - @property - def auth_token(self): - return self._info['token']['id'] - - @property - def expires(self): - return timeutils.parse_isotime(self._info['token']['expires']) - - @property - def username(self): - user = self._info['user'] - return user.get('name', user.get('username')) - - @property - def user_id(self): - return self._info['user']['id'] - - @property - def user_domain_id(self): - return 'default' - - @property - def user_domain_name(self): - return 'Default' - - @property - def role_names(self): - return [r['name'] for r in self._info['user'].get('roles', [])] - - @property - def domain_name(self): - return None - - @property - def domain_id(self): - return None - - @property - def project_name(self): - try: - tenant_dict = self._info['token']['tenant'] - except KeyError: - pass - else: - return tenant_dict.get('name') - - # pre grizzly - try: - return self._info['user']['tenantName'] - except KeyError: - pass - - # pre diablo, keystone only provided a tenantId - try: - return self._info['token']['tenantId'] - except KeyError: - pass - - @property - def project_scoped(self): - return 'tenant' in self._info['token'] - - @property - def domain_scoped(self): - return False - - @property - def trust_id(self): - return self._info.get('trust', {}).get('id') - - @property - def trust_scoped(self): - return 'trust' in self._info - - @property - def project_id(self): - try: - tenant_dict = self._info['token']['tenant'] - except KeyError: - pass - else: - return tenant_dict.get('id') - - # pre grizzly - try: - return self._info['user']['tenantId'] - except KeyError: - pass - - # pre diablo - try: - return self._info['token']['tenantId'] - except KeyError: - pass - - @property - def project_domain_id(self): - if self.project_id: - return 'default' - - @property - def project_domain_name(self): - if self.project_id: - return 'Default' - - -class AccessInfoV3(AccessInfo): - """Object for encapsulating a raw v3 auth token from identity service.""" - - def __init__(self, token, **kwargs): - super(AccessInfoV3, self).__init__(**kwargs) - self._info.update(version='v3') - self.service_catalog = catalog.ServiceCatalog( - self._info.get('catalog')) - if token: - self._info.update(auth_token=token) - - @classmethod - def is_valid(cls, body, **kwargs): - if body: - return 'token' in body - elif kwargs: - return kwargs.get('version') == 'v3' - else: - return False - - def has_service_catalog(self): - return 'catalog' in self._info - - @property - def auth_token(self): - return self._info['auth_token'] - - @property - def expires(self): - return timeutils.parse_isotime(self._info['expires_at']) - - @property - def user_id(self): - return self._info['user']['id'] - - @property - def user_domain_id(self): - return self._info['user']['domain']['id'] - - @property - def user_domain_name(self): - return self._info['user']['domain']['name'] - - @property - def role_names(self): - return [r['name'] for r in self._info.get('roles', [])] - - @property - def username(self): - return self._info['user']['name'] - - @property - def domain_name(self): - domain = self._info.get('domain') - if domain: - return domain['name'] - - @property - def domain_id(self): - domain = self._info.get('domain') - if domain: - return domain['id'] - - @property - def project_id(self): - project = self._info.get('project') - if project: - return project['id'] - - @property - def project_domain_id(self): - project = self._info.get('project') - if project: - return project['domain']['id'] - - @property - def project_domain_name(self): - project = self._info.get('project') - if project: - return project['domain']['name'] - - @property - def project_name(self): - project = self._info.get('project') - if project: - return project['name'] - - @property - def project_scoped(self): - return 'project' in self._info - - @property - def domain_scoped(self): - return 'domain' in self._info - - @property - def trust_id(self): - return self._info.get('OS-TRUST:trust', {}).get('id') - - @property - def trust_scoped(self): - return 'OS-TRUST:trust' in self._info diff --git a/openstack/auth/base.py b/openstack/auth/base.py deleted file mode 100644 index 2eac41706..000000000 --- a/openstack/auth/base.py +++ /dev/null @@ -1,105 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -The base class for an authenticator. A plugin must define the get_token, -get_endpoint, and get_versions methods. The simpliest example would be -something that is just given such as:: - - class SimpleAuthenticator(base.BaseAuthPlugin): - def __init__(self, token, endpoint, versions): - super(SimpleAuthenticator, self).__init__() - self.token = token - self.endpoint = endpoint - self.versions = versions - - def get_token(self, transport, **kwargs): - return self.token - - def get_endpoint(self, transport, service, **kwargs): - return self.endpoint - - def get_versions(self, transport, service, **kwargs): - return self.versions -""" - -import abc - -import six - - -@six.add_metaclass(abc.ABCMeta) -class BaseAuthPlugin(object): - - @abc.abstractmethod - def get_token(self, transport, **kwargs): - """Obtain a token. - - How the token is obtained is up to the authenticator. If it is still - valid it may be re-used, retrieved from cache or invoke an - authentication request against a server. - - There are no required kwargs. They are implementation specific to - an authenticator. - - An authenticator may raise an exception if it fails to retrieve a - token. - - :param transport: A transport object so the authenticator can make - HTTP calls. - :type transport: :class:`~openstack.transport.Transport` - :return string: A token to use. - """ - - @abc.abstractmethod - def get_endpoint(self, transport, service, **kwargs): - """Return an endpoint for the client. - - There are no required keyword arguments to ``get_endpoint`` as an - authenticator should use best effort with the information available to - determine the endpoint. - - :param transport: Authenticator may need to make HTTP calls. - :type transport: :class:`~openstack.transport.Transport` - :param service: Filter to identify the desired service. - :type service: :class:`~openstack.service_filter.ServiceFilter` - - :returns string: The base URL that will be used to talk to the - required service or None if not available. - """ - - @abc.abstractmethod - def get_versions(self, transport, service, **kwargs): - """Return the valid versions for the given service. - - :param transport: Authenticator may need to make HTTP calls. - :type transport: :class:`~openstack.transport.Transport` - :param service: Filter to identify the desired service. - :type service: :class:`~openstack.service_filter.ServiceFilter` - - :returns list: Returns list of versions that match the filter. - """ - - def invalidate(self): - """Invalidate the current authentication data. - - This should result in fetching a new token on next call. - - A plugin may be invalidated if an Unauthorized HTTP response is - returned to indicate that the token may have been revoked or is - otherwise now invalid. - - :returns bool: True if there was something that the plugin did to - invalidate. This means that it makes sense to try again. - If nothing happens returns False to indicate give up. - """ - return False diff --git a/openstack/auth/identity/__init__.py b/openstack/auth/identity/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/openstack/auth/identity/base.py b/openstack/auth/identity/base.py deleted file mode 100644 index 3e37c820c..000000000 --- a/openstack/auth/identity/base.py +++ /dev/null @@ -1,164 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -The base identity plugin. Identity plugins must define the authorize method. -For examples of this class, see the v2 and v3 authentication plugins. -""" - -import logging - -import abc - -import six - -from openstack.auth import base - - -logger = logging.getLogger(__name__) - - -@six.add_metaclass(abc.ABCMeta) -class BaseIdentityPlugin(base.BaseAuthPlugin): - - #: Consider a token valid if it does not expire for this many seconds - BEST_BEFORE_SECONDS = 1 - - def __init__(self, auth_url=None, reauthenticate=True): - """Create an identity authorization plugin. - - :param string auth_url: Authorization URL - :param bool reauthenticate: Should the plugin attempt reauthorization. - """ - super(BaseIdentityPlugin, self).__init__() - self.auth_url = auth_url - self.access_info = None - self.reauthenticate = reauthenticate - - @abc.abstractmethod - def authorize(self, transport, **kwargs): - """Obtain access information from an OpenStack Identity Service. - - Thus method will authenticate and fetch a new AccessInfo when - invoked. - - :param transport: A transport object for the authenticator. - :type transport: :class:`~openstack.transport.Transport` - - :raises InvalidResponse: The response returned wasn't appropriate. - :raises HttpError: An error from an invalid HTTP response. - - :returns AccessInfo: Token access information. - """ - - def get_token(self, transport, **kwargs): - """Return a valid auth token. - - If a valid token is not present then a new one will be fetched. - - :param transport: A transport object for the authenticator. - :type transport: :class:`~openstack.transport.Transport` - - :raises HttpError: An error from an invalid HTTP response. - - :return string: A valid token. - """ - return self.get_access(transport).auth_token - - def _needs_reauthenticate(self): - """Return if the existing token needs to be re-authenticated. - - The token should be refreshed if it is about to expire. - - :returns: True if the plugin should fetch a new token. False otherwise. - """ - if not self.access_info: - # authentication was never fetched. - return True - - if not self.reauthenticate: - # don't re-authenticate if it has been disallowed. - return False - - if self.access_info.will_expire_soon(self.BEST_BEFORE_SECONDS): - # if it's about to expire we should re-authenticate now. - self.invalidate() - return True - - # otherwise it's fine and use the existing one. - return False - - def get_access(self, transport): - """Fetch or return a current AccessInfo object. - - If a valid AccessInfo is present then it is returned otherwise a new - one will be fetched. - - :param transport: A transport object for the authenticator. - :type transport: :class:`~openstack.transport.Transport` - - :raises HttpError: An error from an invalid HTTP response. - - :returns AccessInfo: Valid AccessInfo - """ - if self._needs_reauthenticate(): - logger.debug("Re-authentication required") - self.access_info = self.authorize(transport) - - return self.access_info - - def invalidate(self): - """Invalidate the current authentication data. - - This should result in fetching a new token on next call. - - A plugin may be invalidated if an Unauthorized HTTP response is - returned to indicate that the token may have been revoked or is - otherwise now invalid. - - :returns bool: True if there was something that the plugin did to - invalidate. This means that it makes sense to try again. - If nothing happens returns False to indicate give up. - """ - self.access_info = None - return True - - def get_endpoint(self, transport, service, **kwargs): - """Return a valid endpoint for a service. - - If a valid token is not present then a new one will be fetched using - the transport. - - :param transport: A transport object for the authenticator. - :type transport: :class:`~openstack.transport.Transport` - :param service: The filter to identify the desired service. - :type service: :class:`~openstack.service_filter.ServiceFilter` - - :raises HttpError: An error from an invalid HTTP response. - - :return string or None: A valid endpoint URL or None if not available. - """ - service_catalog = self.get_access(transport).service_catalog - return service_catalog.get_url(service) - - def get_versions(self, transport, service, **kwargs): - """Return the valid versions for the given service. - - :param Transport transport: Authenticator may need to make HTTP calls. - :type transport: :class:`~openstack.transport.Transport` - :param ServiceFilter service: Filter to identify the desired service. - :type service: :class:`~openstack.service_filter.ServiceFilter` - - :returns list: Returns list of versions that match the filter. - """ - service_catalog = self.get_access(transport).service_catalog - return service_catalog.get_versions(service) diff --git a/openstack/auth/identity/discoverable.py b/openstack/auth/identity/discoverable.py deleted file mode 100644 index 699e9be04..000000000 --- a/openstack/auth/identity/discoverable.py +++ /dev/null @@ -1,87 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Identity discoverable authorization plugin must be constructed with an -auhorization URL and a user id, user name or token. A user id or user name -would also require a password. The arguments that apply to the selected v2 -or v3 plugin will be used. The rest of the arguments will be ignored. For -example:: - - from openstack.auth.identity import discoverable - from openstack import transport - - args = { - 'password': 'openSesame', - 'auth_url': 'https://10.1.1.1:5000/v3/', - 'username': 'alibaba', - } - auth = discoverable.Auth(**args) - xport = transport.Transport() - accessInfo = auth.authorize(xport) -""" - -from openstack.auth.identity import base -from openstack.auth.identity import v2 -from openstack.auth.identity import v3 -from openstack import exceptions - - -class Auth(base.BaseIdentityPlugin): - - #: Valid options for this plugin - valid_options = set(list(v3.Password.valid_options) - + list(v3.Token.valid_options) - + list(v2.Password.valid_options) - + list(v2.Token.valid_options)) - - def __init__(self, auth_url=None, **auth_args): - """Construct an Identity Authentication Plugin. - - This authorization plugin should be constructed with an auth_url - and everything needed by either a v2 or v3 identity plugin. - - :param string auth_url: Identity service endpoint for authentication. - - :raises TypeError: if a user_id, username or token is not provided. - """ - - super(Auth, self).__init__(auth_url=auth_url) - - if not auth_url: - msg = ("The authorization URL auth_url was not provided.") - raise exceptions.AuthorizationFailure(msg) - endpoint_version = auth_url.split('v')[-1][0] - if endpoint_version == '2': - if auth_args.get('token'): - plugin = v2.Token - else: - plugin = v2.Password - else: - if auth_args.get('token'): - plugin = v3.Token - else: - plugin = v3.Password - valid_list = plugin.valid_options - args = dict((n, auth_args[n]) for n in valid_list if n in auth_args) - self.auth_plugin = plugin(auth_url, **args) - - @property - def token_url(self): - """The full URL where we will send authentication data.""" - return self.auth_plugin.token_url - - def authorize(self, transport, **kwargs): - return self.auth_plugin.authorize(transport, **kwargs) - - def invalidate(self): - return self.auth_plugin.invalidate() diff --git a/openstack/auth/identity/v2.py b/openstack/auth/identity/v2.py deleted file mode 100644 index c9ccda767..000000000 --- a/openstack/auth/identity/v2.py +++ /dev/null @@ -1,194 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Identity v2 authorization plugins. The plugin must be constructed with an -auhorization URL and a user id, user name or token. A user id or user name -would also require a password. For example:: - - from openstack.auth.identity import v2 - from openstack import transport - - args = { - 'password': 'openSesame', - 'auth_url': 'https://10.1.1.1:5000/v2.0/', - 'username': 'alibaba', - } - auth = v2.Auth(**args) - xport = transport.Transport() - accessInfo = auth.authorize(xport) -""" - -import abc -import logging - -import six - -from openstack.auth import access -from openstack.auth.identity import base -from openstack import exceptions - -_logger = logging.getLogger(__name__) - - -@six.add_metaclass(abc.ABCMeta) -class Auth(base.BaseIdentityPlugin): - - def __init__(self, auth_url, - trust_id=None, - project_id=None, - project_name=None, - tenant_id=None, - tenant_name=None, - reauthenticate=True): - """Construct an Identity V2 Authentication Plugin. - - :param string auth_url: Identity service endpoint for authorization. - :param string trust_id: Trust ID for trust scoping. - :param string project_id: Project ID for scoping. - :param string project_name: Project name for scoping. - :param string tenant_id: Tenant ID for project scoping. - :param string tenant_name: Tenant name for project scoping. - :param bool reauthenticate: Allow fetching a new token if the current - one is going to expire. - (optional) default True - """ - super(Auth, self).__init__(auth_url=auth_url, - reauthenticate=reauthenticate) - - self.trust_id = trust_id - self.tenant_id = project_id - if not self.tenant_id: - self.tenant_id = tenant_id - self.tenant_name = project_name - if not self.tenant_name: - self.tenant_name = tenant_name - - def authorize(self, transport, **kwargs): - """Obtain access information from an OpenStack Identity Service.""" - headers = {'Accept': 'application/json'} - url = self.auth_url.rstrip('/') + '/tokens' - params = {'auth': self.get_auth_data(headers)} - - if self.tenant_id: - params['auth']['tenantId'] = self.tenant_id - elif self.tenant_name: - params['auth']['tenantName'] = self.tenant_name - if self.trust_id: - params['auth']['trust_id'] = self.trust_id - - _logger.debug('Making authentication request to %s', url) - resp = transport.post(url, json=params, headers=headers) - - try: - resp_data = resp.json()['access'] - except (KeyError, ValueError): - raise exceptions.InvalidResponse(response=resp) - - return access.AccessInfoV2(**resp_data) - - @abc.abstractmethod - def get_auth_data(self, headers=None): - """Return the authentication section of an auth plugin. - - :param dict headers: The headers that will be sent with the auth - request if a plugin needs to add to them. - :return dict: A dict of authentication data for the auth type. - """ - - -_NOT_PASSED = object() - - -class Password(Auth): - - #: Valid options for Password plugin - valid_options = { - 'access_info', - 'auth_url', - 'username', - 'user_id', - 'password', - 'project_id', - 'project_name', - 'tenant_name', - 'tenant_id', - 'reauthenticate', - 'trust_id', - } - - def __init__(self, auth_url, username=_NOT_PASSED, password=None, - user_id=_NOT_PASSED, **kwargs): - """A plugin for authenticating with a username and password. - - A username or user_id must be provided. - - :param string auth_url: Identity service endpoint for authorization. - :param string username: Username for authentication. - :param string password: Password for authentication. - :param string user_id: User ID for authentication. - - :raises TypeError: if a user_id or username is not provided. - """ - super(Password, self).__init__(auth_url, **kwargs) - - if username is _NOT_PASSED and user_id is _NOT_PASSED: - msg = 'You need to specify either a username or user_id' - raise TypeError(msg) - if username is _NOT_PASSED: - username = None - if user_id is _NOT_PASSED: - user_id = None - - self.user_id = user_id - self.username = username - self.password = password - - def get_auth_data(self, headers=None): - auth = {'password': self.password} - - if self.username: - auth['username'] = self.username - elif self.user_id: - auth['userId'] = self.user_id - - return {'passwordCredentials': auth} - - -class Token(Auth): - - #: Valid options for this plugin - valid_options = { - 'access_info', - 'auth_url', - 'project_id', - 'project_name', - 'tenant_name', - 'tenant_id', - 'reauthenticate', - 'token', - 'trust_id', - } - - def __init__(self, auth_url, token, **kwargs): - """A plugin for authenticating with an existing token. - - :param string auth_url: Identity service endpoint for authorization. - :param string token: Existing token for authentication. - """ - super(Token, self).__init__(auth_url, **kwargs) - self.token = token - - def get_auth_data(self, headers=None): - if headers is not None: - headers['X-Auth-Token'] = self.token - return {'token': {'id': self.token}} diff --git a/openstack/auth/identity/v3.py b/openstack/auth/identity/v3.py deleted file mode 100644 index 5b91f5b2b..000000000 --- a/openstack/auth/identity/v3.py +++ /dev/null @@ -1,314 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Identity v3 authorization plugins. The plugin must be constructed with an -auhorization URL and a user id, user name or token. A user id or user name -would also require a password. For example:: - - from openstack.auth.identity import v3 - from openstack import transport - - args = { - 'password': 'openSesame', - 'auth_url': 'https://10.1.1.1:5000/v3/', - 'username': 'alibaba', - } - auth = v3.Auth(**args) - xport = transport.Transport() - accessInfo = auth.authorize(xport) -""" - -import abc -import logging - -import six - -from openstack.auth import access -from openstack.auth.identity import base -from openstack import exceptions - -_logger = logging.getLogger(__name__) - - -class Auth(base.BaseIdentityPlugin): - - def __init__(self, auth_url, auth_methods, - trust_id=None, - domain_id=None, - domain_name=None, - project_id=None, - project_name=None, - project_domain_id=None, - project_domain_name=None, - reauthenticate=True, - include_catalog=True): - """Construct an Identity V3 Authentication Plugin. - - :param string auth_url: Identity service endpoint for authentication. - :param list auth_methods: A collection of methods to authenticate with. - :param string trust_id: Trust ID for trust scoping. - :param string domain_id: Domain ID for domain scoping. - :param string domain_name: Domain name for domain scoping. - :param string project_id: Project ID for project scoping. - :param string project_name: Project name for project scoping. - :param string project_domain_id: Project's domain ID for project. - :param string project_domain_name: Project's domain name for project. - :param bool reauthenticate: Allow fetching a new token if the current - one is going to expire. - (optional) default True - :param bool include_catalog: Include the service catalog in the - returned token. (optional) default True. - """ - - super(Auth, self).__init__(auth_url=auth_url, - reauthenticate=reauthenticate) - - self.auth_methods = auth_methods - self.trust_id = trust_id - self.domain_id = domain_id - self.domain_name = domain_name - self.project_id = project_id - self.project_name = project_name - self.project_domain_id = project_domain_id - self.project_domain_name = project_domain_name - self.include_catalog = include_catalog - - @property - def token_url(self): - """The full URL where we will send authentication data.""" - return '%s/auth/tokens' % self.auth_url.rstrip('/') - - def authorize(self, transport, **kwargs): - """Obtain access information from an OpenStack Identity Service.""" - headers = {'Accept': 'application/json'} - body = {'auth': {'identity': {}}} - ident = body['auth']['identity'] - - for method in self.auth_methods: - name, auth_data = method.get_auth_data(transport, self, headers) - ident.setdefault('methods', []).append(name) - ident[name] = auth_data - - if not ident: - raise exceptions.AuthorizationFailure('Authentication method ' - 'required (e.g. password)') - - mutual_exclusion = [bool(self.domain_id or self.domain_name), - bool(self.project_id or self.project_name), - bool(self.trust_id)] - - if sum(mutual_exclusion) > 1: - raise exceptions.AuthorizationFailure('Authentication cannot be ' - 'scoped to multiple ' - 'targets. Pick one of: ' - 'project, domain or trust') - - if self.domain_id: - body['auth']['scope'] = {'domain': {'id': self.domain_id}} - elif self.domain_name: - body['auth']['scope'] = {'domain': {'name': self.domain_name}} - elif self.project_id: - body['auth']['scope'] = {'project': {'id': self.project_id}} - elif self.project_name: - scope = body['auth']['scope'] = {'project': {}} - scope['project']['name'] = self.project_name - - if self.project_domain_id: - scope['project']['domain'] = {'id': self.project_domain_id} - elif self.project_domain_name: - scope['project']['domain'] = {'name': self.project_domain_name} - elif self.trust_id: - body['auth']['scope'] = {'OS-TRUST:trust': {'id': self.trust_id}} - - # NOTE(jamielennox): we add nocatalog here rather than in token_url - # directly as some federation plugins require the base token_url - token_url = self.token_url - if not self.include_catalog: - token_url += '?nocatalog' - - _logger.debug('Making authentication request to %s', token_url) - resp = transport.post(token_url, json=body, headers=headers) - - try: - resp_data = resp.json()['token'] - except (KeyError, ValueError): - raise exceptions.InvalidResponse(response=resp) - - return access.AccessInfoV3(resp.headers['X-Subject-Token'], - **resp_data) - - -@six.add_metaclass(abc.ABCMeta) -class AuthMethod(object): - """One part of a V3 Authentication strategy. - - V3 Tokens allow multiple methods to be presented when authentication - against the server. Each one of these methods is implemented by an - AuthMethod. - - Note: When implementing an AuthMethod use the method_parameters - and do not use positional arguments. Otherwise they can't be picked up by - the factory method and don't work as well with AuthConstructors. - """ - - _method_parameters = [] - - def __init__(self, **kwargs): - for param in self._method_parameters: - setattr(self, param, kwargs.pop(param, None)) - - if kwargs: - msg = "Unexpected Attributes: %s" % ", ".join(kwargs.keys()) - raise AttributeError(msg) - - @classmethod - def _extract_kwargs(cls, kwargs): - """Remove parameters related to this method from other kwargs.""" - return dict([(p, kwargs.pop(p, None)) - for p in cls._method_parameters]) - - @abc.abstractmethod - def get_auth_data(self, transport, auth, headers, **kwargs): - """Return the authentication section of an auth plugin. - - :param Transport transport: The communication transport. - :param Auth auth: The auth plugin calling the method. - :param dict headers: The headers that will be sent with the auth - request if a plugin needs to add to them. - :return tuple(string, dict): The identifier of this plugin and a dict - of authentication data for the auth type. - """ - - -@six.add_metaclass(abc.ABCMeta) -class AuthConstructor(Auth): - """AuthConstructor creates an authentication plugin with one method. - - AuthConstructor is a means of creating an authentication plugin that - contains only one authentication method. This is generally the required - usage. - - An AuthConstructor creates an AuthMethod based on the method's - arguments and the auth_method_class defined by the plugin. It then - creates the auth plugin with only that authentication method. - """ - - _auth_method_class = None - - def __init__(self, auth_url, *args, **kwargs): - method_kwargs = self._auth_method_class._extract_kwargs(kwargs) - method = self._auth_method_class(*args, **method_kwargs) - super(AuthConstructor, self).__init__(auth_url, [method], **kwargs) - - -class PasswordMethod(AuthMethod): - - _method_parameters = ['user_id', - 'username', - 'user_domain_id', - 'user_domain_name', - 'password'] - - def __init__(self, **kwargs): - """Construct a User/Password based authentication method. - - :param string password: Password for authentication. - :param string username: Username for authentication. - :param string user_id: User ID for authentication. - :param string user_domain_id: User's domain ID for authentication. - :param string user_domain_name: User's domain name for authentication. - """ - super(PasswordMethod, self).__init__(**kwargs) - - def get_auth_data(self, transport, auth, headers, **kwargs): - """Identity v3 password authentication data.""" - user = {'password': self.password} - - if self.user_id: - user['id'] = self.user_id - elif self.username: - user['name'] = self.username - - if self.user_domain_id: - user['domain'] = {'id': self.user_domain_id} - elif self.user_domain_name: - user['domain'] = {'name': self.user_domain_name} - - return 'password', {'user': user} - - -class Password(AuthConstructor): - - #: Valid options for this plugin - valid_options = { - 'access_info', - 'auth_url', - 'domain_id', - 'domain_name', - 'password', - 'project_domain_id', - 'project_domain_name', - 'project_id', - 'project_name', - 'reauthenticate', - 'trust_id', - 'user_domain_id', - 'user_domain_name', - 'user_id', - 'username', - } - - _auth_method_class = PasswordMethod - - -class TokenMethod(AuthMethod): - - _method_parameters = ['token', - 'user_domain_id', - 'user_domain_name'] - - def __init__(self, **kwargs): - """Construct an Auth plugin to fetch a token from a token. - - :param string token: Token for authentication. - """ - super(TokenMethod, self).__init__(**kwargs) - - def get_auth_data(self, transport, auth, headers, **kwargs): - headers['X-Auth-Token'] = self.token - return 'token', {'id': self.token} - - -class Token(AuthConstructor): - - #: Valid options for this plugin - valid_options = { - 'access_info', - 'auth_url', - 'domain_id', - 'domain_name', - 'project_domain_id', - 'project_domain_name', - 'project_id', - 'project_name', - 'reauthenticate', - 'token', - 'trust_id', - 'user_domain_id', - 'user_domain_name', - } - - _auth_method_class = TokenMethod - - def __init__(self, auth_url, token, **kwargs): - super(Token, self).__init__(auth_url, token=token, **kwargs) diff --git a/openstack/auth/service_catalog.py b/openstack/auth/service_catalog.py deleted file mode 100644 index 63872a7df..000000000 --- a/openstack/auth/service_catalog.py +++ /dev/null @@ -1,165 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# Copyright 2011, Piston Cloud Computing, Inc. -# Copyright 2011 Nebula, Inc. -# -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import copy -import re - -import six -from six.moves.urllib import parse - -from openstack import exceptions - - -class ServiceCatalog(object): - """Helper methods for dealing with a Keystone Service Catalog.""" - - def __init__(self, catalog): - if catalog is None: - self.catalog = [] - raise exceptions.EmptyCatalog('The service catalog is missing') - self.catalog = copy.deepcopy(catalog) - self._normalize() - self._parse_endpoints() - - def _normalize(self): - return - - def _parse_endpoints(self): - pattern = re.compile('/v\d[\d.]*') - for service in self.catalog: - for endpoint in service.get('endpoints', []): - url = endpoint.get('url', '') - if not url: - continue - split = parse.urlsplit(url) - split = list(split) - path = split[2] - vstr = pattern.search(path) - if not vstr: - endpoint['url'] = (endpoint['url'].rstrip('/') + - '/%(version)s') - continue - start, end = vstr.span() - endpoint['version'] = path[start + 1:end] - path = path[:start] + '/%(version)s' + path[end:] - path = path.rstrip('/') - split[2] = path - endpoint['url'] = parse.urlunsplit(split) - - def _get_endpoints(self, filtration): - """Fetch and filter urls and version tuples for the specified service. - - Returns a tuple containting the url and version for the specified - service (or all) containing the specified type, name, region and - interface. - """ - eps = [] - for service in self.catalog: - if not filtration.match_service_type(service.get('type')): - continue - if not filtration.match_service_name(service.get('name')): - continue - for endpoint in service.get('endpoints', []): - if not filtration.match_region(endpoint.get('region', None)): - continue - if not filtration.match_interface(endpoint.get('interface')): - continue - url = endpoint.get('url', None) - if not url: - continue - version = endpoint.get('version', None) - eps.append((url, version)) - return eps - - def get_urls(self, filtration): - """Fetch the urls based on the given service filter. - - Returns a list of urls based on the service filter. If not endpoints - are found that match the service filter, an empty list is returned. - The filter may specify type, name, region, version and interface. - """ - urls = [] - for url, version in self._get_endpoints(filtration): - version = filtration.get_version_path(version) - url = url % {'version': version} - urls.append(url) - return urls - - def get_versions(self, filtration): - """Fetch the versions based on the given service filter. - - Returns a list of versions based on the service filter. If there is - no endpoint matching the filter, None will be returned. An empty - list of versions means the service is supported, but no version is - specified in the service catalog. The filter may specify type, name, - region, version and interface. - """ - vers = None - for url, version in self._get_endpoints(filtration): - vers = vers or [] - if not version: - continue - if filtration.version and version != filtration.version: - continue - vers.append(version) - return vers - - def get_url(self, service): - """Fetch an endpoint from the service catalog. - - Get the first endpoint that matches the service filter. - - :param ServiceFilter service: The filter to identify the desired - service. - """ - urls = self.get_urls(service) - if len(urls) < 1: - message = "Endpoint not found for %s" % six.text_type(service) - raise exceptions.EndpointNotFound(message) - return urls[0] - - -class ServiceCatalogV2(ServiceCatalog): - """The V2 service catalog from Keystone.""" - - def _extract_details(self, endpoint, interface): - value = { - 'interface': interface, - 'url': endpoint['%sURL' % interface] - } - region = endpoint.get('region', None) - if region: - value['region'] = region - - return value - - def _normalize(self): - """Handle differences in the way v2 and v3 catalogs specify endpoints. - - Normallize the v2 service catalog to the endpoint types used in v3. - """ - for service in self.catalog: - eps = [] - for endpoint in service['endpoints']: - if 'publicURL' in endpoint: - eps += [self._extract_details(endpoint, "public")] - if 'internalURL' in endpoint: - eps += [self._extract_details(endpoint, "internal")] - if 'adminURL' in endpoint: - eps += [self._extract_details(endpoint, "admin")] - service['endpoints'] = eps diff --git a/openstack/auth/token_endpoint.py b/openstack/auth/token_endpoint.py deleted file mode 100644 index f48521a3d..000000000 --- a/openstack/auth/token_endpoint.py +++ /dev/null @@ -1,31 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -from openstack.auth import base - - -class Token(base.BaseAuthPlugin): - """A provider that will always use the given token and endpoint. - - This is really only useful for testing and in certain CLI cases where you - have a known endpoint and admin token that you want to use. - """ - - def __init__(self, endpoint, token): - # NOTE(jamielennox): endpoint is reserved for when plugins - # can be used to provide that information - self.endpoint = endpoint - self.token = token - - def get_token(self, session): - return self.token diff --git a/openstack/cluster/v1/cluster.py b/openstack/cluster/v1/cluster.py index 9597e7e49..1ebf25113 100644 --- a/openstack/cluster/v1/cluster.py +++ b/openstack/cluster/v1/cluster.py @@ -73,8 +73,8 @@ class Cluster(resource.Resource): def action(self, session, body): url = utils.urljoin(self.base_path, self.id, 'action') - resp = session.put(url, service=self.service, json=body).body - return resp + resp = session.put(url, endpoint_filter=self.service, json=body) + return resp.json() def add_nodes(self, session, nodes): body = { diff --git a/openstack/cluster/v1/node.py b/openstack/cluster/v1/node.py index 197bcc947..7aef4d49e 100644 --- a/openstack/cluster/v1/node.py +++ b/openstack/cluster/v1/node.py @@ -76,8 +76,8 @@ class Node(resource.Resource): :param body: The body of action to be sent. """ url = utils.urljoin(self.base_path, self.id, 'action') - resp = session.put(url, service=self.service, json=body).body - return resp + resp = session.put(url, endpoint_filter=self.service, json=body) + return resp.json() def join(self, session, cluster_id): """An action procedure for the node to join a cluster. diff --git a/openstack/compute/v2/server.py b/openstack/compute/v2/server.py index 47b81de28..6d84f9654 100644 --- a/openstack/compute/v2/server.py +++ b/openstack/compute/v2/server.py @@ -90,11 +90,12 @@ class Server(resource.Resource): """Preform server actions given the message body.""" url = utils.urljoin(self.base_path, self.id, 'action') if has_response: - resp = session.post(url, service=self.service, json=body) + resp = session.post(url, endpoint_filter=self.service, json=body) else: + headers = {'Accept': ''} resp = session.post( - url, service=self.service, json=body, accept=None) - return resp + url, endpoint_filter=self.service, json=body, headers=headers) + return resp.json() def change_password(self, session, new_password): """Change the administrator password to the given password.""" diff --git a/openstack/compute/v2/server_ip.py b/openstack/compute/v2/server_ip.py index 960bc8582..5d3c508f5 100644 --- a/openstack/compute/v2/server_ip.py +++ b/openstack/compute/v2/server_ip.py @@ -39,9 +39,10 @@ class ServerIP(resource.Resource): @classmethod def list(cls, session, path_args=None, **params): url = cls._get_url(path_args) - resp = session.get(url, service=cls.service, params=params) + resp = session.get(url, endpoint_filter=cls.service, params=params) + resp = resp.json() ray = [] - for network_label, addresses in six.iteritems(resp.body['addresses']): + for network_label, addresses in six.iteritems(resp['addresses']): for address in addresses: record = { 'server_id': path_args['server_id'], diff --git a/openstack/compute/v2/server_meta.py b/openstack/compute/v2/server_meta.py index 6df48ab81..0377beb69 100644 --- a/openstack/compute/v2/server_meta.py +++ b/openstack/compute/v2/server_meta.py @@ -41,7 +41,8 @@ class ServerMeta(resource.Resource): def create_by_id(cls, session, attrs, resource_id=None, path_args=None): url = cls._get_url(path_args, resource_id) body = {cls.resource_key: {attrs['key']: attrs['value']}} - resp = session.put(url, service=cls.service, json=body).body + resp = session.put(url, endpoint_filter=cls.service, json=body) + resp = resp.json() return {'key': resource_id, 'value': resp[cls.resource_key][resource_id]} @@ -49,7 +50,8 @@ class ServerMeta(resource.Resource): def get_data_by_id(cls, session, resource_id, path_args=None, include_headers=False): url = cls._get_url(path_args, resource_id) - resp = session.get(url, service=cls.service).body + resp = session.get(url, endpoint_filter=cls.service) + resp = resp.json() return {'key': resource_id, 'value': resp[cls.resource_key][resource_id]} @@ -60,12 +62,14 @@ class ServerMeta(resource.Resource): @classmethod def delete_by_id(cls, session, resource_id, path_args=None): url = cls._get_url(path_args, resource_id) - session.delete(url, service=cls.service, accept=None) + headers = {'Accept': ''} + session.delete(url, endpoint_filter=cls.service, headers=headers) @classmethod def list(cls, session, path_args=None, **params): url = '/servers/%(server_id)s/metadata' % path_args - resp = session.get(url, service=cls.service, params=params).body + resp = session.get(url, endpoint_filter=cls.service, params=params) + resp = resp.json() resp = resp['metadata'] return [cls.existing(server_id=path_args['server_id'], key=key, value=value) diff --git a/openstack/compute/v2/server_metadata.py b/openstack/compute/v2/server_metadata.py index 9f39d6556..bc75a5c1c 100644 --- a/openstack/compute/v2/server_metadata.py +++ b/openstack/compute/v2/server_metadata.py @@ -35,7 +35,8 @@ class ServerMetadata(resource.Resource): no_id.pop('server_id') body = {"metadata": no_id} url = cls._get_url(path_args) - resp = session.put(url, service=cls.service, json=body).body + resp = session.put(url, endpoint_filter=cls.service, json=body) + resp = resp.json() attrs = resp["metadata"].copy() attrs['server_id'] = resource_id return attrs @@ -44,8 +45,8 @@ class ServerMetadata(resource.Resource): def get_data_by_id(cls, session, resource_id, path_args=None, include_headers=False): url = cls._get_url(path_args) - resp = session.get(url, service=cls.service).body - return resp[cls.resource_key] + resp = session.get(url, endpoint_filter=cls.service) + return resp.json()[cls.resource_key] @classmethod def update_by_id(cls, session, resource_id, attrs, path_args=None): diff --git a/openstack/connection.py b/openstack/connection.py index 7613c2185..5b25919f5 100644 --- a/openstack/connection.py +++ b/openstack/connection.py @@ -60,13 +60,12 @@ try to find it and if that fails, you would create it:: import logging import sys +from keystoneauth1.loading import base as ksa_loader import os_client_config -from openstack import module_loader -from openstack import profile +from openstack import profile as _profile from openstack import proxy -from openstack import session -from openstack import transport as xport +from openstack import session as _session from openstack import utils _logger = logging.getLogger(__name__) @@ -95,7 +94,7 @@ def from_config(cloud_name=None, cloud_config=None, options=None): """ # TODO(thowe): I proposed that service name defaults to None in OCC defaults = {} - prof = profile.Profile() + prof = _profile.Profile() services = [service.service_type for service in prof.get_services()] for service in services: defaults[service + '_service_name'] = None @@ -119,16 +118,25 @@ def from_config(cloud_name=None, cloud_config=None, options=None): version = str(version) if not version.startswith("v"): version = "v" + version - prof.set_version(service, version) - prof.set_name(service, cloud_config.get_service_name(service)) - prof.set_interface( - service, cloud_config.get_interface(service)) - prof.set_region(service, cloud_config.get_region_name(service)) + prof.set_version(service, version) + name = cloud_config.get_service_name(service) + if name: + prof.set_name(service, name) + interface = cloud_config.get_interface(service) + if interface: + prof.set_interface(service, interface) + + region = cloud_config.get_region_name(service) + if region: + for service in services: + prof.set_region(service, region) # Auth auth = cloud_config.config['auth'] # TODO(thowe) We should be using auth_type auth['auth_plugin'] = cloud_config.config['auth_type'] + if 'cacert' in auth: + auth['verify'] = auth.pop('cacert') if 'cacert' in cloud_config.config: auth['verify'] = cloud_config.config['cacert'] if 'insecure' in cloud_config.config: @@ -139,22 +147,20 @@ def from_config(cloud_name=None, cloud_config=None, options=None): class Connection(object): - def __init__(self, transport=None, authenticator=None, profile=None, - verify=True, user_agent=None, - auth_plugin=None, **auth_args): + def __init__(self, session=None, authenticator=None, profile=None, + verify=True, user_agent=None, auth_plugin=None, **auth_args): """Create a context for a connection to a cloud provider. A connection needs a transport and an authenticator. The user may pass in a transport and authenticator they want to use or they may pass in the parameters to create a transport and authenticator. The connection creates a - :class:`~openstack.session.Session` which uses the transport + :class:`~openstack.session.Session` which uses the profile and authenticator to perform HTTP requests. - :param transport: A transport object such as that was previously - created. If this parameter is not passed in, the connection will - create a transport. - :type transport: :class:`~openstack.transport.Transport` + :param session: A session object compatible with + :class:`~openstack.session.Session`. + :type session: :class:`~openstack.session.Session` :param authenticator: An authenticator derived from the base authenticator plugin that was previously created. Two common authentication identity plugins are @@ -187,33 +193,33 @@ class Connection(object): authentication arguments that are used by the authentication plugin. """ - self.transport = self._create_transport(transport, verify, user_agent) self.authenticator = self._create_authenticator(authenticator, auth_plugin, **auth_args) - self.session = session.Session(self.transport, self.authenticator, - profile) + self.profile = profile if profile else _profile.Profile() + self.session = session if session else _session.Session( + self.profile, auth=self.authenticator, verify=verify, + user_agent=user_agent) self._open() - def _create_transport(self, transport, verify, user_agent): - if transport: - return transport - return xport.Transport(verify=verify, user_agent=user_agent) - - def _create_authenticator(self, authenticator, auth_plugin, **auth_args): + def _create_authenticator(self, authenticator, auth_plugin, **args): if authenticator: return authenticator - plugin = module_loader.ModuleLoader().get_auth_plugin(auth_plugin) - valid_list = plugin.valid_options - args = dict((n, auth_args[n]) for n in valid_list if n in auth_args) - return plugin(**args) + # TODO(thowe): Jamie was suggesting we should support other + # ways of loading the plugin + loader = ksa_loader.get_plugin_loader(auth_plugin) + load_args = {} + for opt in loader.get_options(): + if args.get(opt.dest): + load_args[opt.dest] = args[opt.dest] + return loader.load_from_options(**load_args) def _open(self): """Open the connection. NOTE(thowe): Have this set up some lazy loader instead. """ - for service in self.session.get_services(): + for service in self.profile.get_services(): self._load(service) def _load(self, service): diff --git a/openstack/database/v1/instance.py b/openstack/database/v1/instance.py index 33e88b6c7..18865eae8 100644 --- a/openstack/database/v1/instance.py +++ b/openstack/database/v1/instance.py @@ -52,8 +52,8 @@ class Instance(resource.Resource): the login credentials. """ url = utils.urljoin(self.base_path, self.id, 'root') - resp = session.post(url, service=self.service).body - return resp['user'] + resp = session.post(url, endpoint_filter=self.service) + return resp.json()['user'] def is_root_enabled(self, session): """Determine if root is enabled on an instance. @@ -66,8 +66,8 @@ class Instance(resource.Resource): instance or ``False`` otherwise. """ url = utils.urljoin(self.base_path, self.id, 'root') - resp = session.get(url, service=self.service).body - return resp['rootEnabled'] + resp = session.get(url, endpoint_filter=self.service) + return resp.json()['rootEnabled'] def restart(self, session): """Restart the database instance @@ -76,7 +76,7 @@ class Instance(resource.Resource): """ body = {'restart': {}} url = utils.urljoin(self.base_path, self.id, 'action') - session.post(url, service=self.service, json=body) + session.post(url, endpoint_filter=self.service, json=body) def resize(self, session, flavor_reference): """Resize the database instance @@ -85,7 +85,7 @@ class Instance(resource.Resource): """ body = {'resize': {'flavorRef': flavor_reference}} url = utils.urljoin(self.base_path, self.id, 'action') - session.post(url, service=self.service, json=body) + session.post(url, endpoint_filter=self.service, json=body) def resize_volume(self, session, volume_size): """Resize the volume attached to the instance @@ -94,4 +94,4 @@ class Instance(resource.Resource): """ body = {'resize': {'volume': volume_size}} url = utils.urljoin(self.base_path, self.id, 'action') - session.post(url, service=self.service, json=body) + session.post(url, endpoint_filter=self.service, json=body) diff --git a/openstack/database/v1/user.py b/openstack/database/v1/user.py index 8745107a1..2abbfec4f 100644 --- a/openstack/database/v1/user.py +++ b/openstack/database/v1/user.py @@ -42,5 +42,5 @@ class User(resource.Resource): url = cls._get_url(path_args) # Create expects an array of users body = {'users': [attrs]} - resp = session.post(url, service=cls.service, json=body).body - return resp + resp = session.post(url, endpoint_filter=cls.service, json=body) + return resp.json() diff --git a/openstack/identity/v2/extension.py b/openstack/identity/v2/extension.py index 9647025a5..cfeb3204c 100644 --- a/openstack/identity/v2/extension.py +++ b/openstack/identity/v2/extension.py @@ -46,6 +46,8 @@ class Extension(resource.Resource): @classmethod def list(cls, session, **params): - resp = session.get(cls.base_path, service=cls.service, params=params) - for data in resp.body[cls.resources_key]['values']: + resp = session.get(cls.base_path, endpoint_filter=cls.service, + params=params) + resp = resp.json() + for data in resp[cls.resources_key]['values']: yield cls.existing(**data) diff --git a/openstack/identity/version.py b/openstack/identity/version.py index eae5965e2..bfea2a3a2 100644 --- a/openstack/identity/version.py +++ b/openstack/identity/version.py @@ -32,6 +32,8 @@ class Version(resource.Resource): @classmethod def list(cls, session, **params): - resp = session.get(cls.base_path, service=cls.service, params=params) - for data in resp.body[cls.resources_key]['values']: + resp = session.get(cls.base_path, endpoint_filter=cls.service, + params=params) + resp = resp.json() + for data in resp[cls.resources_key]['values']: yield cls.existing(**data) diff --git a/openstack/image/v2/image.py b/openstack/image/v2/image.py index b605a8ebf..179ffbf2b 100644 --- a/openstack/image/v2/image.py +++ b/openstack/image/v2/image.py @@ -83,6 +83,6 @@ class Image(resource.Resource): headers = self.get_headers() headers['Content-Type'] = 'application/octet-stream' - - session.put(url, service=self.service, data=self.data, accept=None, + headers['Accept'] = '' + session.put(url, endpoint_filter=self.service, data=self.data, headers=headers) diff --git a/openstack/image/v2/tag.py b/openstack/image/v2/tag.py index 2a9b3b21e..594b1b087 100644 --- a/openstack/image/v2/tag.py +++ b/openstack/image/v2/tag.py @@ -37,7 +37,8 @@ class Tag(resource.Resource): :return: ``None`` """ url = self._get_url({"image": self.image.id}, tag) - session.put(url, service=self.service, accept=None) + headers = {'Accept': ''} + session.put(url, endpoint_filter=self.service, headers=headers) def delete(self, session, tag): """Delete a tag on the image @@ -49,4 +50,5 @@ class Tag(resource.Resource): :return: ``None`` """ url = self._get_url({"image": self.image.id}, tag) - session.delete(url, service=self.service, accept=None) + headers = {'Accept': ''} + session.delete(url, endpoint_filter=self.service, headers=headers) diff --git a/openstack/message/v1/claim.py b/openstack/message/v1/claim.py index e7501f080..24c0f4642 100644 --- a/openstack/message/v1/claim.py +++ b/openstack/message/v1/claim.py @@ -60,10 +60,11 @@ class Claim(resource.Resource): body = [] try: - resp = session.post(url, service=cls.service, headers=headers, + resp = session.post(url, endpoint_filter=cls.service, + headers=headers, data=json.dumps(claim, cls=ClaimEncoder), params=params) - body = resp.body + body = resp.json() except exceptions.InvalidResponse as e: # The Message Service will respond with a 204 and no content in # the body when there are no messages to claim. The transport diff --git a/openstack/message/v1/message.py b/openstack/message/v1/message.py index 1af8aebcc..c552323be 100644 --- a/openstack/message/v1/message.py +++ b/openstack/message/v1/message.py @@ -69,11 +69,12 @@ class Message(resource.Resource): url = cls._get_url({'queue_name': messages[0].queue}) headers = {'Client-ID': messages[0].client} - resp = session.post(url, service=cls.service, headers=headers, + resp = session.post(url, endpoint_filter=cls.service, headers=headers, data=json.dumps(messages, cls=MessageEncoder)) + resp = resp.json() messages_created = [] - hrefs = resp.body['resources'] + hrefs = resp['resources'] for i, href in enumerate(hrefs): message = Message.existing(**messages[i]) @@ -94,10 +95,11 @@ class Message(resource.Resource): @classmethod def delete_by_id(cls, session, message, path_args=None): url = cls._strip_version(message.href) - headers = {'Client-ID': message.client} - - session.delete(url, service=cls.service, - headers=headers, accept=None) + headers = { + 'Client-ID': message.client, + 'Accept': '', + } + session.delete(url, endpoint_filter=cls.service, headers=headers) class MessageEncoder(json.JSONEncoder): diff --git a/openstack/message/v1/queue.py b/openstack/message/v1/queue.py index e88e3930c..dd10420fa 100644 --- a/openstack/message/v1/queue.py +++ b/openstack/message/v1/queue.py @@ -29,5 +29,6 @@ class Queue(resource.Resource): @classmethod def create_by_id(cls, session, attrs, resource_id=None, path_args=None): url = cls._get_url(path_args, resource_id) - session.put(url, service=cls.service, accept=None) + headers = {'Accept': ''} + session.put(url, endpoint_filter=cls.service, headers=headers) return {cls.id_attribute: resource_id} diff --git a/openstack/module_loader.py b/openstack/module_loader.py index 62bc5e6ab..faf271acd 100644 --- a/openstack/module_loader.py +++ b/openstack/module_loader.py @@ -15,8 +15,6 @@ Load various modules for authorization and eventually services. """ from stevedore import extension -from openstack import exceptions - def load_service_plugins(namespace): service_plugins = extension.ExtensionManager( @@ -26,30 +24,6 @@ def load_service_plugins(namespace): services = {} for service in service_plugins: service = service.obj - service.set_interface(None) + service.interface = None services[service.service_type] = service return services - - -class ModuleLoader(object): - - def __init__(self): - """Create a module loader.""" - self.auth_mgr = extension.ExtensionManager( - namespace="openstack.auth.plugin", - invoke_on_load=False, - ) - - def get_auth_plugin(self, plugin_name): - """Get an authentication plugin by name.""" - if not plugin_name: - plugin_name = 'password' - try: - return self.auth_mgr[plugin_name].plugin - except KeyError: - msg = ('Could not find authorization plugin <%s>' % plugin_name) - raise exceptions.NoMatchingPlugin(msg) - - def list_auth_plugins(self): - """Get a list of all the authentication plugins.""" - return self.auth_mgr.names() diff --git a/openstack/network/v2/router.py b/openstack/network/v2/router.py index b387be354..3b93938a9 100644 --- a/openstack/network/v2/router.py +++ b/openstack/network/v2/router.py @@ -52,8 +52,8 @@ class Router(resource.Resource): """ body = {'subnet_id': subnet_id} url = utils.urljoin(self.base_path, self.id, 'add_router_interface') - resp = session.put(url, service=self.service, json=body).body - return resp + resp = session.put(url, endpoint_filter=self.service, json=body) + return resp.json() def remove_interface(self, session, subnet_id): """Remove an internal interface from a logical router. @@ -66,5 +66,5 @@ class Router(resource.Resource): """ body = {'subnet_id': subnet_id} url = utils.urljoin(self.base_path, self.id, 'remove_router_interface') - resp = session.put(url, service=self.service, json=body).body - return resp + resp = session.put(url, endpoint_filter=self.service, json=body) + return resp.json() diff --git a/openstack/object_store/v1/_base.py b/openstack/object_store/v1/_base.py index 9459929d0..c870e01b2 100644 --- a/openstack/object_store/v1/_base.py +++ b/openstack/object_store/v1/_base.py @@ -35,5 +35,6 @@ class BaseResource(resource.Resource): """ url = cls._get_url(None, resource_id) headers = attrs.get(resource.HEADERS, dict()) - return session.post(url, service=cls.service, accept=None, + headers['Accept'] = '' + return session.post(url, endpoint_filter=cls.service, headers=headers).headers diff --git a/openstack/object_store/v1/container.py b/openstack/object_store/v1/container.py index 3e4310161..a34bcb920 100644 --- a/openstack/object_store/v1/container.py +++ b/openstack/object_store/v1/container.py @@ -95,7 +95,8 @@ class Container(_base.BaseResource): """ url = cls._get_url(None, resource_id) headers = attrs.get(resource.HEADERS, dict()) - return session.put(url, service=cls.service, accept=None, + headers['Accept'] = '' + return session.put(url, endpoint_filter=cls.service, headers=headers).headers def create(self, session): diff --git a/openstack/object_store/v1/obj.py b/openstack/object_store/v1/obj.py index 77f2e11d9..635edbd29 100644 --- a/openstack/object_store/v1/obj.py +++ b/openstack/object_store/v1/obj.py @@ -149,7 +149,9 @@ class Object(resource.Resource): def get(self, session): url = self._get_url(self, self.id) # TODO(thowe): Add filter header support bug #1488269 - resp = session.get(url, service=self.service, accept="bytes").content + headers = {'Accept': 'bytes'} + resp = session.get(url, endpoint_filter=self.service, headers=headers) + resp = resp.content return resp def create(self, session): @@ -157,11 +159,13 @@ class Object(resource.Resource): url = self._get_url(self, self.id) headers = self.get_headers() + headers['Accept'] = '' if self.data is not None: - resp = session.put(url, service=self.service, data=self.data, - accept="bytes", headers=headers).headers + resp = session.put(url, endpoint_filter=self.service, + data=self.data, + headers=headers).headers else: - resp = session.post(url, service=self.service, data=None, - accept=None, headers=headers).headers + resp = session.post(url, endpoint_filter=self.service, data=None, + headers=headers).headers self.set_headers(resp) return self diff --git a/openstack/orchestration/v1/stack.py b/openstack/orchestration/v1/stack.py index 6f821eb98..a9aa47615 100644 --- a/openstack/orchestration/v1/stack.py +++ b/openstack/orchestration/v1/stack.py @@ -68,8 +68,8 @@ class Stack(resource.Resource): def _action(self, session, body): """Perform stack actions""" url = utils.urljoin(self.base_path, self.id, 'actions') - resp = session.post(url, service=self.service, json=body).body - return resp + resp = session.post(url, endpoint_filter=self.service, json=body) + return resp.json() def check(self, session): return self._action(session, {'check': ''}) @@ -80,7 +80,8 @@ class Stack(resource.Resource): body.pop('id', None) body.pop('name', None) url = cls.base_path - resp = session.post(url, service=cls.service, json=body).body + resp = session.post(url, endpoint_filter=cls.service, json=body) + resp = resp.json() return resp[cls.resource_key] @classmethod @@ -91,5 +92,5 @@ class Stack(resource.Resource): body.pop('id', None) body.pop('name', None) url = cls._get_url(path_args, resource_id) - session.put(url, service=cls.service, json=body) + session.put(url, endpoint_filter=cls.service, json=body) return cls.get_by_id(session, resource_id) diff --git a/openstack/profile.py b/openstack/profile.py index 3151cd0da..e6cbdd015 100644 --- a/openstack/profile.py +++ b/openstack/profile.py @@ -37,7 +37,7 @@ normally be something like 'compute', 'identity', 'object-store', etc.:: prof.set_version('identity', 'v3') prof.set_interface('object-store', 'internal') for service in prof.get_services(): - print str(prof.get_preference(service.service_type)) + print(prof.get_filter(service.service_type) The resulting preference print out would look something like:: @@ -51,6 +51,7 @@ The resulting preference print out would look something like:: service_type=identity,region=zion,version=v3 """ +import copy import logging import six @@ -88,7 +89,6 @@ class Profile(object): Services are identified by their service type, e.g.: 'identity', 'compute', etc. """ - self._preferences = {} self._services = {} self._add_service(cluster_service.ClusterService()) self._add_service(compute_service.ComputeService()) @@ -107,13 +107,13 @@ class Profile(object): if plugins: for plugin in plugins: self._load_plugin(plugin) - self.service_names = sorted(self._services.keys()) + self.service_keys = sorted(self._services.keys()) def __repr__(self): - return repr(self._preferences) + return repr(self._services) def _add_service(self, serv): - serv.set_interface(None) + serv.interface = None self._services[serv.service_type] = serv def _load_plugin(self, namespace): @@ -128,12 +128,24 @@ class Profile(object): services[service_type]) self._add_service(services[service_type]) - def get_preference(self, service): + def get_filter(self, service): """Get a service preference. :param str service: Desired service type. """ - return self._preferences.get(service, None) + return copy.copy(self._get_filter(service)) + + def _get_filter(self, service): + """Get a service preference. + + :param str service: Desired service type. + """ + serv = self._services.get(service, None) + if serv is not None: + return serv + msg = ("Service %s not in list of valid services: %s" % + (service, self.service_keys)) + raise exceptions.SDKException(msg) def get_services(self): """Get a list of all the known services.""" @@ -142,16 +154,6 @@ class Profile(object): services.append(service) return services - def _get_service(self, service): - """Get a valid service filter.""" - serv = self._services.get(service, None) - if serv is not None: - self._preferences[service] = serv - return serv - msg = ("Service %s not in list of valid services: %s" % - (service, self.service_names)) - raise exceptions.SDKException(msg) - def set_name(self, service, name): """Set the desired name for the specified service. @@ -159,11 +161,11 @@ class Profile(object): :param str name: Desired service name. """ if service == self.ALL: - services = self.service_names + services = self.service_keys else: services = [service] for service in services: - self._get_service(service).service_name = name + self._get_filter(service).service_name = name def set_region(self, service, region): """Set the desired region for the specified service. @@ -172,11 +174,11 @@ class Profile(object): :param str region: Desired service region. """ if service == self.ALL: - services = self.service_names + services = self.service_keys else: services = [service] for service in services: - self._get_service(service).region = region + self._get_filter(service).region = region def set_version(self, service, version): """Set the desired version for the specified service. @@ -184,7 +186,7 @@ class Profile(object): :param str service: Service type. :param str version: Desired service version. """ - self._get_service(service).version = version + self._get_filter(service).version = version def set_interface(self, service, interface): """Set the desired interface for the specified service. @@ -193,8 +195,8 @@ class Profile(object): :param str interface: Desired service interface. """ if service == self.ALL: - services = self.service_names + services = self.service_keys else: services = [service] for service in services: - self._get_service(service).set_interface(interface) + self._get_filter(service).interface = interface diff --git a/openstack/resource.py b/openstack/resource.py index de2354641..76ef3ab5c 100644 --- a/openstack/resource.py +++ b/openstack/resource.py @@ -35,6 +35,7 @@ import copy import itertools import time +from keystoneauth1 import exceptions as ksa_exceptions import six from openstack import exceptions @@ -540,9 +541,10 @@ class Resource(collections.MutableMapping): if headers: args[HEADERS] = headers if resource_id: - resp = session.put(url, service=cls.service, **args).body + resp = session.put(url, endpoint_filter=cls.service, **args) else: - resp = session.post(url, service=cls.service, **args).body + resp = session.post(url, endpoint_filter=cls.service, **args) + resp = resp.json() if cls.resource_key: resp = resp[cls.resource_key] @@ -588,8 +590,8 @@ class Resource(collections.MutableMapping): raise exceptions.MethodNotSupported(cls, 'retrieve') url = cls._get_url(path_args, resource_id) - response = session.get(url, service=cls.service) - body = response.body + response = session.get(url, endpoint_filter=cls.service) + body = response.json() if cls.resource_key: body = body[cls.resource_key] @@ -664,9 +666,10 @@ class Resource(collections.MutableMapping): url = cls._get_url(path_args, resource_id) - data = session.head(url, service=cls.service, accept=None).headers + headers = {'Accept': ''} + resp = session.head(url, endpoint_filter=cls.service, headers=headers) - return {HEADERS: data} + return {HEADERS: resp.headers} @classmethod def head_by_id(cls, session, resource_id, path_args=None): @@ -739,9 +742,10 @@ class Resource(collections.MutableMapping): if headers: args[HEADERS] = headers if cls.patch_update: - resp = session.patch(url, service=cls.service, **args).body + resp = session.patch(url, endpoint_filter=cls.service, **args) else: - resp = session.put(url, service=cls.service, **args).body + resp = session.put(url, endpoint_filter=cls.service, **args) + resp = resp.json() if cls.resource_key and cls.resource_key in resp.keys(): resp = resp[cls.resource_key] @@ -794,7 +798,8 @@ class Resource(collections.MutableMapping): raise exceptions.MethodNotSupported(cls, 'delete') url = cls._get_url(path_args, resource_id) - session.delete(url, service=cls.service, accept=None) + headers = {'Accept': ''} + session.delete(url, endpoint_filter=cls.service, headers=headers) def delete(self, session): """Delete the remote resource associated with this instance. @@ -841,8 +846,11 @@ class Resource(collections.MutableMapping): more_data = True params = {} if params is None else params url = cls._get_url(path_args) + headers = {'Accept': 'application/json'} while more_data: - resp = session.get(url, service=cls.service, params=params).body + resp = session.get(url, endpoint_filter=cls.service, + headers=headers, params=params) + resp = resp.json() if cls.resources_key: resp = resp[cls.resources_key] @@ -919,7 +927,7 @@ class Resource(collections.MutableMapping): try: if cls.allow_retrieve: return cls.get_by_id(session, name_or_id, path_args=path_args) - except exceptions.HttpException: + except ksa_exceptions.http.NotFound: pass data = cls.list(session, path_args=path_args) @@ -995,7 +1003,7 @@ def wait_for_delete(session, resource, interval, wait): while total_sleep < wait: try: resource.get(session) - except exceptions.NotFoundException: + except ksa_exceptions.http.NotFound: return resource time.sleep(interval) total_sleep += interval diff --git a/openstack/service_filter.py b/openstack/service_filter.py index 1d9a09776..47c8620b3 100644 --- a/openstack/service_filter.py +++ b/openstack/service_filter.py @@ -51,8 +51,6 @@ The resulting output from the code:: matches=True """ -from openstack import exceptions - class ValidVersion(object): @@ -66,16 +64,14 @@ class ValidVersion(object): self.path = path or module -class ServiceFilter(object): +class ServiceFilter(dict): UNVERSIONED = '' - ANY = 'any' PUBLIC = 'public' INTERNAL = 'internal' ADMIN = 'admin' - INTERFACE = [PUBLIC, INTERNAL, ADMIN] valid_versions = [] - def __init__(self, service_type=ANY, interface=PUBLIC, region=None, + def __init__(self, service_type, interface=PUBLIC, region=None, service_name=None, version=None): """Create a service identifier. @@ -86,95 +82,72 @@ class ServiceFilter(object): :param string service_name: Name of the service :param string version: Version of service to use. """ - self.service_type = service_type.lower() - self.set_interface(interface) - self.region = region - self.service_name = service_name - self.version = version + self['service_type'] = service_type.lower() + self['interface'] = interface + self['region_name'] = region + self['service_name'] = service_name + self['version'] = version - def __repr__(self): - ret = "service_type=%s" % self.service_type - if self.interface is not None: - ret += ",interface=%s" % self.interface - if self.region is not None: - ret += ",region=%s" % self.region - if self.service_name: - ret += ",service_name=%s" % self.service_name - if self.version: - ret += ",version=%s" % self.version - return ret + @property + def service_type(self): + return self['service_type'] - def join(self, default): - """Create a new service filter by joining filters. + @property + def interface(self): + return self['interface'] - Create a new service filter by joining this service preference with - the default service identifier. + @interface.setter + def interface(self, value): + self['interface'] = value - :param default: Default service identifier from the resource. - :type default: :class:`~openstack.service_filter.ServiceFilter` - """ - if default.version == self.UNVERSIONED: - version = default.version - else: - version = self.version - response = ServiceFilter() - response.service_type = default.service_type - response.service_name = self.service_name - response.valid_versions = default.valid_versions - response.interface = default.interface - if self.interface: - response.interface = self.interface - if self.region: - response.region = self.region - response.version = version - return response + @property + def region(self): + return self['region_name'] - def match_service_type(self, service_type): - """Service types are equavilent.""" - if self.service_type == self.ANY: - return True - return self.service_type == service_type + @region.setter + def region(self, value): + self['region_name'] = value - def match_service_name(self, service_name): - """Service names are equavilent.""" - if not self.service_name: - return True - if self.service_name == service_name: - return True - return False + @property + def service_name(self): + return self['service_name'] - def match_region(self, region): - """Service regions are equavilent.""" - if not self.region: - return True - if self.region == region: - return True - return False + @service_name.setter + def service_name(self, value): + self['service_name'] = value - def match_interface(self, interface): - """Service interfaces are equavilent.""" - if not self.interface: - return True - return self.interface == interface + @property + def version(self): + return self['version'] - def set_interface(self, interface): - """Set the interface of the service filter.""" - if not interface: - self.interface = None - return - interface = interface.replace('URL', '') - interface = interface.lower() - if interface not in self.INTERFACE: - msg = "Interface <%s> not in %s" % (interface, self.INTERFACE) - raise exceptions.SDKException(msg) - self.interface = interface + @version.setter + def version(self, value): + self['version'] = value + + @property + def path(self): + return self['path'] + + @path.setter + def path(self, value): + self['path'] = value + + def get_path(self, version=None): + if not self.version: + self.version = version + return self.get('path', self._get_valid_version().path) + + def get_filter(self): + filter = dict(self) + del filter['version'] + return filter def _get_valid_version(self): if self.valid_versions: if self.version: for valid in self.valid_versions: # NOTE(thowe): should support fuzzy match e.g: v2.1==v2 - if self.version == valid.module: + if self.version.startswith(valid.module): return valid return self.valid_versions[0] return ValidVersion('') @@ -194,17 +167,3 @@ class ServiceFilter(object): is `object_store`. """ return self.__class__.__module__.split('.')[1] - - def get_version_path(self, version): - """Get the desired version path. - - If the service does not have a version, use the suggested version. - """ - if self.version is not None: - return self.version - valid = self._get_valid_version() - if valid.path: - return valid.path - if version: - return version - return '' diff --git a/openstack/session.py b/openstack/session.py index c22145bbd..40f647632 100644 --- a/openstack/session.py +++ b/openstack/session.py @@ -11,152 +11,53 @@ # under the License. """ -The :class:`~openstack.session.Session` is the class that maintains session -layer similar to the OSI model session layer. The session has a transport -and an authenticator. The transport is used by the session and authenticator -for HTTP layer transport. The authenticator is responsible for providing an -authentication token and an endpoint to communicate with. +The :class:`~openstack.session.Session` overrides +:class:`~keystoneauth1.session.Session` to provide end point filtering. -Examples --------- - -The following examples use the example authenticator which takes the token -and endpoint as arguments. - -Create a session -~~~~~~~~~~~~~~~~ - -Constructor:: - - from examples import authenticator - from openstack import session - from openstack import transport - xport = transport.Transport() - token = 'SecretToken' - endpoint = 'http://cloud.example.com:3333' - auther = authenticator.TestAuthenticator(token, endpoint) - sess = session.Session(xport, auther) - -HTTP GET -~~~~~~~~ - -Making a basic HTTP GET call:: - - containers = sess.get('/').json() - -The containers variable will contain a list of dict describing the containers. - -HTTP PUT -~~~~~~~~ - -Creating a new object:: - - objay_data = 'roland garros' - objay_len = len(objay_data) - headers = {"Content-Length": objay_len, "Content-Type": "text/plain"} - resp = sess.put('/pilots/french.txt', headers=headers, data=objay_data) """ +import re +from six.moves.urllib import parse -import logging - -from openstack import profile as _profile -from openstack import utils +from keystoneauth1 import session as _session -_logger = logging.getLogger(__name__) +VERSION_PATTERN = re.compile('/v\d[\d.]*') -class Session(object): +def parse_url(filt, url): + result = parse.urlparse(url) + path = result.path + vstr = VERSION_PATTERN.search(path) + if not vstr: + return result.scheme + "://" + result.netloc + "/" + filt.get_path() + start, end = vstr.span() + prefix = path[:start] + version = '/' + filt.get_path(path[start + 1:end]) + postfix = path[end:].rstrip('/') if path[end:] else '' + url = result.scheme + "://" + result.netloc + prefix + version + postfix + return url - def __init__(self, transport, authenticator, profile=None): - """Create a new object with a transport and authenticator. - Session layer which uses the transport for communication. The - authenticator also uses the transport to keep authenticated. +class Session(_session.Session): + + def __init__(self, profile, **kwargs): + """Create a new Keystone auth session with a profile. - :param transport: A transport that provides an HTTP request method. - The transport is also to be used by the authenticator, if needed. - :type transport: :class:`~openstack.transport.Transport` - :param authenticator: An authenticator that provides get_token and - get_endpoint methods for the session. - :type authenticator: :class:`~openstack.auth.base.BaseAuthPlugin` :param profile: If the user has any special profiles such as the service name, region, version or interface, they may be provided in the profile object. If no profiles are provided, the services that appear first in the service catalog will be used. :type profile: :class:`~openstack.profile.Profile` - - All the other methods of the session accept the following parameters: - - :param str path: Path relative to service base url. - :param service: a service filter for the authenticator to determine - the correct endpoint to use. - :type service: :class:`~openstack.service_filter.ServiceFilter` - :param bool authenticate: A flag that indicates if a token should be - attached to the request. This parameter defaults to true. - :param kwargs: The remaining arguments are passed to the transport - request method. """ - self.transport = transport - self.authenticator = authenticator - self.profile = profile or _profile.Profile() + super(Session, self).__init__(**kwargs) + self.profile = profile - def _request(self, path, method, service=None, authenticate=True, - **kwargs): - """Send an HTTP request with the specified characteristics. + def get_endpoint(self, auth=None, interface=None, **kwargs): + """Override get endpoint to automate endpoint filering""" - Handle a session level request. - - :param string path: Path relative to authentictor base url. - :param string method: The http method to use. (eg. 'GET', 'POST'). - :param service: Object that filters service to the authenticator. - :type service: :class:`~openstack.service_filter.ServiceFilter` - :param bool authenticate: True if a token should be attached - :param kwargs: any other parameter that can be passed to transport - and authenticator. - - :returns: The response to the request. - """ - - headers = kwargs.setdefault('headers', dict()) - if authenticate: - token = self.authenticator.get_token(self.transport) - if token: - headers['X-Auth-Token'] = token - if service: - profile = self.profile.get_preference(service.service_type) - if profile: - service = profile.join(service) - - endpoint = self.authenticator.get_endpoint(self.transport, service) - url = utils.urljoin(endpoint, path) - - return self.transport.request(method, url, **kwargs) - - def head(self, path, **kwargs): - """Perform an HTTP HEAD request.""" - return self._request(path, 'HEAD', **kwargs) - - def get(self, path, **kwargs): - """Perform an HTTP GET request.""" - return self._request(path, 'GET', **kwargs) - - def post(self, path, **kwargs): - """Perform an HTTP POST request.""" - return self._request(path, 'POST', **kwargs) - - def put(self, path, **kwargs): - """Perform an HTTP PUT request.""" - return self._request(path, 'PUT', **kwargs) - - def delete(self, path, **kwargs): - """Perform an HTTP DELETE request.""" - return self._request(path, 'DELETE', **kwargs) - - def patch(self, path, **kwargs): - """Perform an HTTP PATCH request.""" - return self._request(path, 'PATCH', **kwargs) - - def get_services(self): - """Get list of services from profiles.""" - return self.profile.get_services() + service_type = kwargs.get('service_type') + filt = self.profile.get_filter(service_type) + if filt.interface is None: + filt.interface = interface + url = super(Session, self).get_endpoint(auth, **filt.get_filter()) + return parse_url(filt, url) diff --git a/openstack/telemetry/v2/alarm.py b/openstack/telemetry/v2/alarm.py index 52d7caae3..04f2f23c8 100644 --- a/openstack/telemetry/v2/alarm.py +++ b/openstack/telemetry/v2/alarm.py @@ -71,8 +71,8 @@ class Alarm(resource.Resource): The next_state may be one of: 'ok' 'insufficient data' 'alarm' """ url = utils.urljoin(self.base_path, self.id, 'state') - resp = session.put(url, service=self.service, json=next_state).body - return resp + resp = session.put(url, endpoint_filter=self.service, json=next_state) + return resp.json() def check_state(self, session): """Retrieve the current state of an alarm from the service. @@ -80,6 +80,7 @@ class Alarm(resource.Resource): The properties of the alarm are not modified. """ url = utils.urljoin(self.base_path, self.id, 'state') - resp = session.get(url, service=self.service).body + resp = session.get(url, endpoint_filter=self.service) + resp = resp.json() current_state = resp.replace('\"', '') return current_state diff --git a/openstack/telemetry/v2/alarm_change.py b/openstack/telemetry/v2/alarm_change.py index 45e09844a..c06d86ae6 100644 --- a/openstack/telemetry/v2/alarm_change.py +++ b/openstack/telemetry/v2/alarm_change.py @@ -45,5 +45,6 @@ class AlarmChange(resource.Resource): def list(cls, session, limit=None, marker=None, path_args=None, paginated=False, **params): url = cls._get_url(path_args) - for item in session.get(url, service=cls.service, params=params).body: + resp = session.get(url, endpoint_filter=cls.service, params=params) + for item in resp.json(): yield cls.existing(**item) diff --git a/openstack/telemetry/v2/capability.py b/openstack/telemetry/v2/capability.py index 83713c664..a76f844d4 100644 --- a/openstack/telemetry/v2/capability.py +++ b/openstack/telemetry/v2/capability.py @@ -31,6 +31,8 @@ class Capability(resource.Resource): @classmethod def list(cls, session, limit=None, marker=None, path_args=None, paginated=False, **params): - resp = session.get(cls.base_path, service=cls.service, params=params) - for key, value in six.iteritems(resp.body['api']): + resp = session.get(cls.base_path, endpoint_filter=cls.service, + params=params) + resp = resp.json() + for key, value in six.iteritems(resp['api']): yield cls.existing(id=key, enabled=value) diff --git a/openstack/telemetry/v2/sample.py b/openstack/telemetry/v2/sample.py index ae6a50312..bc6f09561 100644 --- a/openstack/telemetry/v2/sample.py +++ b/openstack/telemetry/v2/sample.py @@ -52,7 +52,8 @@ class Sample(resource.Resource): def list(cls, session, limit=None, marker=None, path_args=None, paginated=False, **params): url = cls._get_url(path_args) - for item in session.get(url, service=cls.service, params=params).body: + resp = session.get(url, endpoint_filter=cls.service, params=params) + for item in resp.json(): yield cls.existing(**item) def create(self, session): @@ -60,7 +61,9 @@ class Sample(resource.Resource): # telemetry expects a list of samples attrs = self._attrs.copy() attrs.pop('meter', None) - resp = session.post(url, service=self.service, json=[attrs]) - self.update_attrs(**resp.body.pop()) + resp = session.post(url, endpoint_filter=self.service, + json=[attrs]) + resp = resp.json() + self.update_attrs(**resp.pop()) self._reset_dirty() return self diff --git a/openstack/telemetry/v2/statistics.py b/openstack/telemetry/v2/statistics.py index b1c52be36..be4672f14 100644 --- a/openstack/telemetry/v2/statistics.py +++ b/openstack/telemetry/v2/statistics.py @@ -60,5 +60,6 @@ class Statistics(resource.Resource): def list(cls, session, limit=None, marker=None, path_args=None, paginated=False, **params): url = cls._get_url(path_args) - for stat in session.get(url, service=cls.service, params=params).body: + resp = session.get(url, endpoint_filter=cls.service, params=params) + for stat in resp.json(): yield cls.existing(**stat) diff --git a/openstack/tests/functional/object_store/v1/test_obj.py b/openstack/tests/functional/object_store/v1/test_obj.py index d191a456e..1ed04472b 100644 --- a/openstack/tests/functional/object_store/v1/test_obj.py +++ b/openstack/tests/functional/object_store/v1/test_obj.py @@ -25,7 +25,7 @@ class TestObject(base.BaseFunctionalTest): def setUpClass(cls): super(TestObject, cls).setUpClass() cls.conn.object_store.create_container(name=cls.FOLDER) - cls.sot = cls.conn.object_store.create_object( + cls.sot = cls.conn.object_store.upload_object( container=cls.FOLDER, name=cls.FILE, data=cls.DATA) @classmethod diff --git a/openstack/tests/functional/telemetry/v2/test_meter.py b/openstack/tests/functional/telemetry/v2/test_meter.py index 718aa7094..0a6e10fe6 100644 --- a/openstack/tests/functional/telemetry/v2/test_meter.py +++ b/openstack/tests/functional/telemetry/v2/test_meter.py @@ -26,4 +26,4 @@ class TestMeter(base.BaseFunctionalTest): self.conn.object_store.delete_container(tainer) names = set([o.name for o in self.conn.telemetry.meters()]) - self.assertIn('storage.objects', names) + self.assertIn('storage.objects.incoming.bytes', names) diff --git a/openstack/tests/unit/auth/__init__.py b/openstack/tests/unit/auth/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/openstack/tests/unit/auth/common.py b/openstack/tests/unit/auth/common.py deleted file mode 100644 index 984a34780..000000000 --- a/openstack/tests/unit/auth/common.py +++ /dev/null @@ -1,333 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -TEST_ADMIN_URL = 'http://identity.region1.admin/v1.1/123123' -TEST_DOMAIN_ID = '1' -TEST_DOMAIN_NAME = 'aDomain' -TEST_EXPIRES = '2020-01-01 00:00:10.000123+00:00' -TEST_PASS = 'wasspord' -TEST_PROJECT_ID = 'pid' -TEST_PROJECT_NAME = 'pname' -TEST_SUBJECT = 'subjay' -TEST_TOKEN = 'atoken' -TEST_TENANT_ID = 'tid' -TEST_TENANT_NAME = 'tname' -TEST_TRUST_ID = 'trusty' -TEST_USER = 'youzer' -TEST_USER_ID = 'youid' - -TEST_SERVICE_CATALOG_V2 = [ - { - "endpoints": [{ - "adminURL": "http://compute.region0.admin/v1.1/", - "internalURL": "http://compute.region0.internal/v1.1/", - "publicURL": "http://compute.region0.public/v1.1/", - }], - "type": "compute", - "name": "nova0" - }, { - "endpoints": [{ - "adminURL": "http://compute.region2.admin/v1/", - "region": "RegionTwo", - "internalURL": "http://compute.region2.internal/v1/", - "publicURL": "http://compute.region2.public/v1/", - }], - "type": "compute", - "name": "nova2" - }, { - "endpoints": [{ - "adminURL": "http://compute.region1.admin/v2.0/", - "region": "RegionOne", - "internalURL": "http://compute.region1.internal/v2.0/", - "publicURL": "http://compute.region1.public/v2.0/", - }], - "type": "compute", - "name": "nova" - }, { - "endpoints": [{ - "adminURL": "http://image.region1.admin/v2", - "region": "RegionOne", - "internalURL": "http://image.region1.internal/v2", - "publicURL": "http://image.region1.public/v2", - }], - "type": "image", - "name": "glance" - }, { - "endpoints": [{ - "adminURL": TEST_ADMIN_URL, - "region": "RegionOne", - "internalURL": "http://identity.region1.internal/v1.1/123123", - "publicURL": "http://identity.region1.public/v1.1/123123", - }], - "type": "identity", - "name": "keystone" - }, { - "endpoints": [{ - "adminURL": "http://object-store.region1.admin/", - "region": "RegionOne", - "internalURL": "http://object-store.region1.internal/", - "publicURL": "http://object-store.region1.public/", - }], - "type": "object-store", - "name": "swift" - }] -TEST_SERVICE_CATALOG_NORMALIZED = [ - { - "endpoints": [{ - "interface": "public", - "url": "http://compute.region0.public/%(version)s", - 'version': 'v1.1', - }, { - "interface": "internal", - "url": "http://compute.region0.internal/%(version)s", - 'version': 'v1.1', - }, { - "interface": "admin", - "url": "http://compute.region0.admin/%(version)s", - 'version': 'v1.1', - }], - "type": "compute", - "name": "nova0" - }, { - "endpoints": [{ - "interface": "public", - "region": "RegionTwo", - "url": "http://compute.region2.public/%(version)s", - 'version': 'v1', - }, { - "interface": "internal", - "region": "RegionTwo", - "url": "http://compute.region2.internal/%(version)s", - 'version': 'v1', - }, { - "interface": "admin", - "region": "RegionTwo", - "url": "http://compute.region2.admin/%(version)s", - 'version': 'v1', - }], - "type": "compute", - "name": "nova2" - }, { - "endpoints": [{ - "interface": "public", - "region": "RegionOne", - "url": "http://compute.region1.public/%(version)s", - 'version': 'v2.0', - }, { - "interface": "internal", - "region": "RegionOne", - "url": "http://compute.region1.internal/%(version)s", - 'version': 'v2.0', - }, { - "interface": "admin", - "region": "RegionOne", - "url": "http://compute.region1.admin/%(version)s", - 'version': 'v2.0', - }], - "type": "compute", - "name": "nova" - }, { - "endpoints": [{ - "interface": "public", - "region": "RegionOne", - "url": "http://image.region1.public/%(version)s", - 'version': 'v2', - }, { - "interface": "internal", - "region": "RegionOne", - "url": "http://image.region1.internal/%(version)s", - 'version': 'v2', - }, { - "interface": "admin", - "region": "RegionOne", - "url": "http://image.region1.admin/%(version)s", - 'version': 'v2', - }], - "type": "image", - "name": "glance", - }, { - "endpoints": [{ - "interface": "public", - "region": "RegionOne", - "url": "http://identity.region1.public/%(version)s/123123", - 'version': 'v1.1', - }, { - "interface": "internal", - "region": "RegionOne", - "url": "http://identity.region1.internal/%(version)s/123123", - 'version': 'v1.1', - }, { - "interface": "admin", - "region": "RegionOne", - "url": "http://identity.region1.admin/%(version)s/123123", - 'version': 'v1.1', - }], - "type": "identity", - "name": "keystone", - }, { - "endpoints": [{ - "interface": "public", - "region": "RegionOne", - "url": "http://object-store.region1.public/%(version)s", - }, { - "interface": "internal", - "region": "RegionOne", - "url": "http://object-store.region1.internal/%(version)s", - }, { - "interface": "admin", - "region": "RegionOne", - "url": "http://object-store.region1.admin/%(version)s", - }], - "type": "object-store", - "name": "swift", - }] -TEST_RESPONSE_DICT_V2 = { - "access": { - "token": { - "expires": TEST_EXPIRES, - "id": TEST_TOKEN, - "tenant": { - "id": TEST_TENANT_ID - }, - }, - "user": { - "id": TEST_USER_ID - }, - "serviceCatalog": TEST_SERVICE_CATALOG_V2, - }, -} - - -TEST_SERVICE_CATALOG_V3 = [ - { - "endpoints": [{ - "url": "http://compute.region0.public/v1.1/", - "interface": "public" - }, { - "url": "http://compute.region0.internal/v1.1/", - "interface": "internal" - }, { - "url": "http://compute.region0.admin/v1.1/", - "interface": "admin" - }], - "type": "compute", - "name": "nova0", - }, { - "endpoints": [{ - "url": "http://compute.region2.public/v1/", - "region": "RegionTwo", - "interface": "public" - }, { - "url": "http://compute.region2.internal/v1/", - "region": "RegionTwo", - "interface": "internal" - }, { - "url": "http://compute.region2.admin/v1/", - "region": "RegionTwo", - "interface": "admin" - }], - "type": "compute", - "name": "nova2", - }, { - "endpoints": [{ - "url": "http://compute.region1.public/v2.0/", - "region": "RegionOne", - "interface": "public" - }, { - "url": "http://compute.region1.internal/v2.0/", - "region": "RegionOne", - "interface": "internal" - }, { - "url": "http://compute.region1.admin/v2.0/", - "region": "RegionOne", - "interface": "admin" - }], - "type": "compute", - "name": "nova", - }, { - "endpoints": [{ - "url": "http://image.region1.public/v2", - "region": "RegionOne", - "interface": "public" - }, { - "url": "http://image.region1.internal/v2", - "region": "RegionOne", - "interface": "internal" - }, { - "url": "http://image.region1.admin/v2", - "region": "RegionOne", - "interface": "admin" - }], - "type": "image", - "name": "glance", - }, { - "endpoints": [{ - "url": "http://identity.region1.public/v1.1/123123", - "region": "RegionOne", - "interface": "public" - }, { - "url": "http://identity.region1.internal/v1.1/123123", - "region": "RegionOne", - "interface": "internal" - }, { - "url": "http://identity.region1.admin/v1.1/123123", - "region": "RegionOne", - "interface": "admin" - }], - "type": "identity", - "name": "keystone", - }, { - "endpoints": [{ - "url": "http://object-store.region1.public/", - "region": "RegionOne", - "interface": "public" - }, { - "url": "http://object-store.region1.internal/", - "region": "RegionOne", - "interface": "internal" - }, { - "url": "http://object-store.region1.admin/", - "region": "RegionOne", - "interface": "admin" - }], - "type": "object-store", - "name": "swift", - }] - -TEST_RESPONSE_DICT_V3 = { - "token": { - "methods": [ - "token", - "password" - ], - - "expires_at": TEST_EXPIRES, - "project": { - "domain": { - "id": TEST_DOMAIN_ID, - "name": TEST_DOMAIN_NAME - }, - "id": TEST_PROJECT_ID, - "name": TEST_PROJECT_NAME - }, - "user": { - "domain": { - "id": TEST_DOMAIN_ID, - "name": TEST_DOMAIN_NAME - }, - "id": TEST_USER_ID, - "name": TEST_USER - }, - "issued_at": "2013-05-29T16:55:21.468960Z", - "catalog": TEST_SERVICE_CATALOG_V3 - }, -} diff --git a/openstack/tests/unit/auth/identity/__init__.py b/openstack/tests/unit/auth/identity/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/openstack/tests/unit/auth/identity/test_discoverable.py b/openstack/tests/unit/auth/identity/test_discoverable.py deleted file mode 100644 index 9d56f14d1..000000000 --- a/openstack/tests/unit/auth/identity/test_discoverable.py +++ /dev/null @@ -1,99 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -import testtools - -from openstack.auth.identity import discoverable -from openstack import exceptions -from openstack.tests.unit.auth import common - - -class TestDiscoverableAuth(testtools.TestCase): - def test_valid_options(self): - expected = { - 'access_info', - 'auth_url', - 'domain_id', - 'domain_name', - 'password', - 'project_domain_id', - 'project_domain_name', - 'project_id', - 'project_name', - 'reauthenticate', - 'tenant_id', - 'tenant_name', - 'token', - 'trust_id', - 'user_domain_id', - 'user_domain_name', - 'user_id', - 'username', - } - self.assertEqual(expected, discoverable.Auth.valid_options) - - def test_create2(self): - auth_args = { - 'auth_url': 'http://localhost/v2', - 'username': '1', - 'password': '2', - } - auth = discoverable.Auth(**auth_args) - self.assertEqual('openstack.auth.identity.v2', - auth.auth_plugin.__class__.__module__) - - def test_create3(self): - auth_args = { - 'auth_url': 'http://localhost/v3', - 'username': '1', - 'password': '2', - } - auth = discoverable.Auth(**auth_args) - self.assertEqual('openstack.auth.identity.v3', - auth.auth_plugin.__class__.__module__) - - def test_create_who_knows(self): - auth_args = { - 'auth_url': 'http://localhost:5000/', - 'username': '1', - 'password': '2', - } - auth = discoverable.Auth(**auth_args) - self.assertEqual('openstack.auth.identity.v3', - auth.auth_plugin.__class__.__module__) - - def test_create_authenticator_no_nothing(self): - self.assertRaises( - exceptions.AuthorizationFailure, - discoverable.Auth, - ) - - def test_methods(self): - auth_args = { - 'auth_url': 'http://localhost:5000/', - 'username': '1', - 'password': '2', - } - auth = discoverable.Auth(**auth_args) - self.assertEqual('http://localhost:5000/auth/tokens', auth.token_url) - xport = mock.MagicMock() - xport.post = mock.Mock() - response = mock.Mock() - response.json = mock.Mock() - response.json.return_value = common.TEST_RESPONSE_DICT_V3 - response.headers = {'X-Subject-Token': common.TEST_SUBJECT} - xport.post.return_value = response - - result = auth.authorize(xport) - self.assertEqual(common.TEST_SUBJECT, result.auth_token) - self.assertEqual(True, auth.invalidate()) diff --git a/openstack/tests/unit/auth/identity/test_v2.py b/openstack/tests/unit/auth/identity/test_v2.py deleted file mode 100644 index b272106d9..000000000 --- a/openstack/tests/unit/auth/identity/test_v2.py +++ /dev/null @@ -1,142 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -import testtools - -from openstack.auth.identity import v2 -from openstack import exceptions -from openstack.tests.unit.auth import common - -TEST_URL = 'http://127.0.0.1:5000/v2.0' - -TEST_SERVICE_CATALOG = common.TEST_SERVICE_CATALOG_V2 -TEST_RESPONSE_DICT = common.TEST_RESPONSE_DICT_V2 - - -class TestV2Auth(testtools.TestCase): - def test_password(self): - kargs = {'trust_id': common.TEST_TRUST_ID, - 'project_id': common.TEST_TENANT_ID, - 'project_name': common.TEST_TENANT_NAME} - - sot = v2.Password(TEST_URL, common.TEST_USER, common.TEST_PASS, - **kargs) - - self.assertEqual(common.TEST_USER, sot.username) - self.assertEqual(common.TEST_PASS, sot.password) - self.assertEqual(common.TEST_TRUST_ID, sot.trust_id) - self.assertEqual(common.TEST_TENANT_ID, sot.tenant_id) - self.assertEqual(common.TEST_TENANT_NAME, sot.tenant_name) - expected = {'passwordCredentials': {'password': common.TEST_PASS, - 'username': common.TEST_USER}} - self.assertEqual(expected, sot.get_auth_data()) - - def test_password_no_user(self): - kargs = {'trust_id': common.TEST_TRUST_ID, - 'project_id': common.TEST_TENANT_ID, - 'project_name': common.TEST_TENANT_NAME} - - sot = v2.Password(TEST_URL, None, common.TEST_PASS, - **kargs) - - self.assertEqual(None, sot.username) - self.assertEqual(common.TEST_PASS, sot.password) - self.assertEqual(common.TEST_TRUST_ID, sot.trust_id) - self.assertEqual(common.TEST_TENANT_ID, sot.tenant_id) - self.assertEqual(common.TEST_TENANT_NAME, sot.tenant_name) - expected = {'passwordCredentials': {'password': common.TEST_PASS}} - self.assertEqual(expected, sot.get_auth_data()) - - def test_password_no_nothing(self): - self.assertRaises(TypeError, v2.Password, TEST_URL) - - def test_token(self): - kargs = {'trust_id': common.TEST_TRUST_ID, - 'tenant_id': common.TEST_TENANT_ID, - 'tenant_name': common.TEST_TENANT_NAME} - - sot = v2.Token(TEST_URL, common.TEST_TOKEN, **kargs) - - self.assertEqual(common.TEST_TOKEN, sot.token) - self.assertEqual(common.TEST_TRUST_ID, sot.trust_id) - self.assertEqual(common.TEST_TENANT_ID, sot.tenant_id) - self.assertEqual(common.TEST_TENANT_NAME, sot.tenant_name) - expected = {'token': {'id': common.TEST_TOKEN}} - self.assertEqual(expected, sot.get_auth_data()) - - def create_mock_transport(self, xresp): - transport = mock.Mock() - transport.post = mock.Mock() - response = mock.Mock() - response.json = mock.Mock() - response.json.return_value = xresp - transport.post.return_value = response - return transport - - def test_authorize_tenant_id(self): - kargs = {'trust_id': common.TEST_TRUST_ID, - 'tenant_id': common.TEST_TENANT_ID, - 'tenant_name': common.TEST_TENANT_NAME} - sot = v2.Token(TEST_URL, common.TEST_TOKEN, **kargs) - xport = self.create_mock_transport(TEST_RESPONSE_DICT) - - resp = sot.authorize(xport) - - eurl = TEST_URL + '/tokens' - eheaders = {'Accept': 'application/json', - 'X-Auth-Token': common.TEST_TOKEN} - ejson = {'auth': {'token': {'id': common.TEST_TOKEN}, - 'trust_id': common.TEST_TRUST_ID, - 'tenantId': common.TEST_TENANT_ID}} - xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) - ecatalog = TEST_RESPONSE_DICT['access'].copy() - ecatalog['version'] = 'v2.0' - self.assertEqual(ecatalog, resp._info) - - def test_authorize_tenant_name(self): - kargs = {'tenant_name': common.TEST_TENANT_NAME} - sot = v2.Token(TEST_URL, common.TEST_TOKEN, **kargs) - xport = self.create_mock_transport(TEST_RESPONSE_DICT) - - resp = sot.authorize(xport) - - eurl = TEST_URL + '/tokens' - eheaders = {'Accept': 'application/json', - 'X-Auth-Token': common.TEST_TOKEN} - ejson = {'auth': {'token': {'id': common.TEST_TOKEN}, - 'tenantName': common.TEST_TENANT_NAME}} - xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) - ecatalog = TEST_RESPONSE_DICT['access'].copy() - ecatalog['version'] = 'v2.0' - self.assertEqual(ecatalog, resp._info) - - def test_authorize_token_only(self): - sot = v2.Token(TEST_URL, common.TEST_TOKEN) - xport = self.create_mock_transport(TEST_RESPONSE_DICT) - - resp = sot.authorize(xport) - - eurl = TEST_URL + '/tokens' - eheaders = {'Accept': 'application/json', - 'X-Auth-Token': common.TEST_TOKEN} - ejson = {'auth': {'token': {'id': common.TEST_TOKEN}}} - xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) - ecatalog = TEST_RESPONSE_DICT['access'].copy() - ecatalog['version'] = 'v2.0' - self.assertEqual(ecatalog, resp._info) - - def test_authorize_bad_response(self): - sot = v2.Token(TEST_URL, common.TEST_TOKEN) - xport = self.create_mock_transport({}) - - self.assertRaises(exceptions.InvalidResponse, sot.authorize, xport) diff --git a/openstack/tests/unit/auth/identity/test_v3.py b/openstack/tests/unit/auth/identity/test_v3.py deleted file mode 100644 index 8050a00cd..000000000 --- a/openstack/tests/unit/auth/identity/test_v3.py +++ /dev/null @@ -1,341 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -import testtools - -from openstack.auth.identity import v3 -from openstack import exceptions -from openstack.tests.unit.auth import common - -TEST_URL = 'http://127.0.0.1:5000/v3.0' - - -class TestV3Auth(testtools.TestCase): - def test_password_user_domain(self): - kargs = {'trust_id': common.TEST_TRUST_ID, - 'project_id': common.TEST_PROJECT_ID, - 'project_name': common.TEST_PROJECT_NAME} - - method = v3.PasswordMethod(username=common.TEST_USER, - user_id=common.TEST_USER_ID, - user_domain_id=common.TEST_DOMAIN_ID, - user_domain_name=common.TEST_DOMAIN_NAME, - password=common.TEST_PASS) - sot = v3.Auth(TEST_URL, [method], **kargs) - - self.assertEqual(1, len(sot.auth_methods)) - auther = sot.auth_methods[0] - self.assertEqual(common.TEST_USER_ID, auther.user_id) - self.assertEqual(common.TEST_USER, auther.username) - self.assertEqual(common.TEST_DOMAIN_ID, auther.user_domain_id) - self.assertEqual(common.TEST_DOMAIN_NAME, auther.user_domain_name) - self.assertEqual(common.TEST_PASS, auther.password) - expected = ('password', {'user': {'id': common.TEST_USER_ID, - 'password': common.TEST_PASS}}) - self.assertEqual(expected, auther.get_auth_data(None, None, {})) - self.assertEqual(common.TEST_TRUST_ID, sot.trust_id) - self.assertEqual(None, sot.domain_id) - self.assertEqual(None, sot.domain_name) - self.assertEqual(common.TEST_PROJECT_ID, sot.project_id) - self.assertEqual(common.TEST_PROJECT_NAME, sot.project_name) - self.assertEqual(None, sot.project_domain_id) - self.assertEqual(None, sot.project_domain_name) - self.assertEqual(TEST_URL + '/auth/tokens', sot.token_url) - self.assertTrue(sot.include_catalog) - - def test_password_domain(self): - kargs = {'domain_id': common.TEST_DOMAIN_ID, - 'domain_name': common.TEST_DOMAIN_NAME, - 'trust_id': common.TEST_TRUST_ID, - 'project_id': common.TEST_PROJECT_ID, - 'project_name': common.TEST_PROJECT_NAME} - - methods = [v3.PasswordMethod(username=common.TEST_USER, - user_id=common.TEST_USER_ID, - password=common.TEST_PASS)] - sot = v3.Auth(TEST_URL, methods, **kargs) - - self.assertEqual(1, len(sot.auth_methods)) - auther = sot.auth_methods[0] - self.assertEqual(common.TEST_USER_ID, auther.user_id) - self.assertEqual(common.TEST_USER, auther.username) - self.assertEqual(None, auther.user_domain_id) - self.assertEqual(None, auther.user_domain_name) - self.assertEqual(common.TEST_PASS, auther.password) - expected = ('password', {'user': {'id': common.TEST_USER_ID, - 'password': common.TEST_PASS}}) - self.assertEqual(expected, auther.get_auth_data(None, None, {})) - self.assertEqual(common.TEST_TRUST_ID, sot.trust_id) - self.assertEqual(common.TEST_DOMAIN_ID, sot.domain_id) - self.assertEqual(common.TEST_DOMAIN_NAME, sot.domain_name) - self.assertEqual(common.TEST_PROJECT_ID, sot.project_id) - self.assertEqual(common.TEST_PROJECT_NAME, sot.project_name) - self.assertEqual(None, sot.project_domain_id) - self.assertEqual(None, sot.project_domain_name) - self.assertEqual(TEST_URL + '/auth/tokens', sot.token_url) - self.assertTrue(sot.include_catalog) - - def test_token_project_domain(self): - kargs = {'project_domain_id': common.TEST_DOMAIN_ID, - 'project_domain_name': common.TEST_DOMAIN_NAME, - 'trust_id': common.TEST_TRUST_ID, - 'project_id': common.TEST_PROJECT_ID, - 'project_name': common.TEST_PROJECT_NAME} - - methods = [v3.TokenMethod(token=common.TEST_TOKEN)] - sot = v3.Auth(TEST_URL, methods, **kargs) - - self.assertEqual(1, len(sot.auth_methods)) - auther = sot.auth_methods[0] - self.assertEqual(common.TEST_TOKEN, auther.token) - expected = ('token', {'id': common.TEST_TOKEN}) - self.assertEqual(expected, auther.get_auth_data(None, None, {})) - self.assertEqual(common.TEST_TRUST_ID, sot.trust_id) - self.assertEqual(None, sot.domain_id) - self.assertEqual(None, sot.domain_name) - self.assertEqual(common.TEST_PROJECT_ID, sot.project_id) - self.assertEqual(common.TEST_PROJECT_NAME, sot.project_name) - self.assertEqual(common.TEST_DOMAIN_ID, sot.project_domain_id) - self.assertEqual(common.TEST_DOMAIN_NAME, sot.project_domain_name) - self.assertEqual(TEST_URL + '/auth/tokens', sot.token_url) - - def create_mock_transport(self, xresp): - transport = mock.Mock() - transport.post = mock.Mock() - response = mock.Mock() - response.json = mock.Mock() - response.json.return_value = xresp - response.headers = {'X-Subject-Token': common.TEST_SUBJECT} - transport.post.return_value = response - return transport - - def test_authorize_token(self): - methods = [v3.TokenMethod(token=common.TEST_TOKEN)] - sot = v3.Auth(TEST_URL, methods) - xport = self.create_mock_transport(common.TEST_RESPONSE_DICT_V3) - - resp = sot.authorize(xport) - - eurl = TEST_URL + '/auth/tokens' - eheaders = {'Accept': 'application/json', - 'X-Auth-Token': common.TEST_TOKEN} - ejson = {'auth': {'identity': {'token': {'id': common.TEST_TOKEN}, - 'methods': ['token']}}} - xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) - ecatalog = common.TEST_RESPONSE_DICT_V3['token'].copy() - ecatalog['auth_token'] = common.TEST_SUBJECT - ecatalog['version'] = 'v3' - self.assertEqual(ecatalog, resp._info) - - def test_authorize_token_include_catalog(self): - kargs = {'include_catalog': False} - methods = [v3.TokenMethod(token=common.TEST_TOKEN)] - sot = v3.Auth(TEST_URL, methods, **kargs) - xport = self.create_mock_transport(common.TEST_RESPONSE_DICT_V3) - - resp = sot.authorize(xport) - - eurl = TEST_URL + '/auth/tokens?nocatalog' - eheaders = {'Accept': 'application/json', - 'X-Auth-Token': common.TEST_TOKEN} - ejson = {'auth': {'identity': {'token': {'id': common.TEST_TOKEN}, - 'methods': ['token']}}} - xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) - ecatalog = common.TEST_RESPONSE_DICT_V3['token'].copy() - ecatalog['auth_token'] = common.TEST_SUBJECT - ecatalog['version'] = 'v3' - self.assertEqual(ecatalog, resp._info) - - def test_authorize_token_domain_id(self): - kargs = {'domain_id': common.TEST_DOMAIN_ID} - methods = [v3.TokenMethod(token=common.TEST_TOKEN)] - sot = v3.Auth(TEST_URL, methods, **kargs) - xport = self.create_mock_transport(common.TEST_RESPONSE_DICT_V3) - - resp = sot.authorize(xport) - - eurl = TEST_URL + '/auth/tokens' - eheaders = {'Accept': 'application/json', - 'X-Auth-Token': common.TEST_TOKEN} - ejson = {'auth': {'identity': {'token': {'id': common.TEST_TOKEN}, - 'methods': ['token']}, - 'scope': {'domain': {'id': common.TEST_DOMAIN_ID}}}} - xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) - ecatalog = common.TEST_RESPONSE_DICT_V3['token'].copy() - ecatalog['auth_token'] = common.TEST_SUBJECT - ecatalog['version'] = 'v3' - self.assertEqual(ecatalog, resp._info) - - def test_authorize_token_domain_name(self): - kargs = {'domain_name': common.TEST_DOMAIN_NAME} - methods = [v3.TokenMethod(token=common.TEST_TOKEN)] - sot = v3.Auth(TEST_URL, methods, **kargs) - xport = self.create_mock_transport(common.TEST_RESPONSE_DICT_V3) - - resp = sot.authorize(xport) - - eurl = TEST_URL + '/auth/tokens' - eheaders = {'Accept': 'application/json', - 'X-Auth-Token': common.TEST_TOKEN} - scope = {'domain': {'name': common.TEST_DOMAIN_NAME}} - ejson = {'auth': {'identity': {'token': {'id': common.TEST_TOKEN}, - 'methods': ['token']}, - 'scope': scope}} - xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) - ecatalog = common.TEST_RESPONSE_DICT_V3['token'].copy() - ecatalog['auth_token'] = common.TEST_SUBJECT - ecatalog['version'] = 'v3' - self.assertEqual(ecatalog, resp._info) - - def test_authorize_token_project_id(self): - kargs = {'project_id': common.TEST_PROJECT_ID} - methods = [v3.TokenMethod(token=common.TEST_TOKEN)] - sot = v3.Auth(TEST_URL, methods, **kargs) - xport = self.create_mock_transport(common.TEST_RESPONSE_DICT_V3) - - resp = sot.authorize(xport) - - eurl = TEST_URL + '/auth/tokens' - eheaders = {'Accept': 'application/json', - 'X-Auth-Token': common.TEST_TOKEN} - scope = {'project': {'id': common.TEST_PROJECT_ID}} - ejson = {'auth': {'identity': {'token': {'id': common.TEST_TOKEN}, - 'methods': ['token']}, - 'scope': scope}} - xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) - ecatalog = common.TEST_RESPONSE_DICT_V3['token'].copy() - ecatalog['auth_token'] = common.TEST_SUBJECT - ecatalog['version'] = 'v3' - self.assertEqual(ecatalog, resp._info) - - def test_authorize_token_project_name(self): - kargs = {'project_name': common.TEST_PROJECT_NAME, - 'project_domain_id': common.TEST_DOMAIN_ID} - methods = [v3.TokenMethod(token=common.TEST_TOKEN)] - sot = v3.Auth(TEST_URL, methods, **kargs) - xport = self.create_mock_transport(common.TEST_RESPONSE_DICT_V3) - - resp = sot.authorize(xport) - - eurl = TEST_URL + '/auth/tokens' - eheaders = {'Accept': 'application/json', - 'X-Auth-Token': common.TEST_TOKEN} - domain = {'domain': {'id': common.TEST_DOMAIN_ID}, - 'name': common.TEST_PROJECT_NAME} - scope = {'project': domain} - ejson = {'auth': {'identity': {'methods': ['token'], - 'token': {'id': common.TEST_TOKEN}}, - 'scope': scope}} - xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) - ecatalog = common.TEST_RESPONSE_DICT_V3['token'].copy() - ecatalog['auth_token'] = common.TEST_SUBJECT - ecatalog['version'] = 'v3' - self.assertEqual(ecatalog, resp._info) - - def test_authorize_token_project_name_domain_name(self): - kargs = {'project_name': common.TEST_PROJECT_NAME, - 'project_domain_name': common.TEST_DOMAIN_NAME} - methods = [v3.TokenMethod(token=common.TEST_TOKEN)] - sot = v3.Auth(TEST_URL, methods, **kargs) - xport = self.create_mock_transport(common.TEST_RESPONSE_DICT_V3) - - resp = sot.authorize(xport) - - eurl = TEST_URL + '/auth/tokens' - eheaders = {'Accept': 'application/json', - 'X-Auth-Token': common.TEST_TOKEN} - domain = {'domain': {'name': common.TEST_DOMAIN_NAME}, - 'name': common.TEST_PROJECT_NAME} - scope = {'project': domain} - ejson = {'auth': {'identity': {'methods': ['token'], - 'token': {'id': common.TEST_TOKEN}}, - 'scope': scope}} - xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) - ecatalog = common.TEST_RESPONSE_DICT_V3['token'].copy() - ecatalog['auth_token'] = common.TEST_SUBJECT - ecatalog['version'] = 'v3' - self.assertEqual(ecatalog, resp._info) - - def test_authorize_token_trust_id(self): - kargs = {'trust_id': common.TEST_TRUST_ID} - methods = [v3.TokenMethod(token=common.TEST_TOKEN)] - sot = v3.Auth(TEST_URL, methods, **kargs) - xport = self.create_mock_transport(common.TEST_RESPONSE_DICT_V3) - - resp = sot.authorize(xport) - - eurl = TEST_URL + '/auth/tokens' - eheaders = {'Accept': 'application/json', - 'X-Auth-Token': common.TEST_TOKEN} - scope = {'OS-TRUST:trust': {'id': common.TEST_TRUST_ID}} - ejson = {'auth': {'identity': {'token': {'id': common.TEST_TOKEN}, - 'methods': ['token']}, - 'scope': scope}} - xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) - ecatalog = common.TEST_RESPONSE_DICT_V3['token'].copy() - ecatalog['auth_token'] = common.TEST_SUBJECT - ecatalog['version'] = 'v3' - self.assertEqual(ecatalog, resp._info) - - def test_authorize_multi_method(self): - methods = [v3.PasswordMethod(username=common.TEST_USER, - password=common.TEST_PASS), - v3.TokenMethod(token=common.TEST_TOKEN)] - sot = v3.Auth(TEST_URL, methods) - xport = self.create_mock_transport(common.TEST_RESPONSE_DICT_V3) - - resp = sot.authorize(xport) - - eurl = TEST_URL + '/auth/tokens' - eheaders = {'Accept': 'application/json', - 'X-Auth-Token': common.TEST_TOKEN} - up = {'password': common.TEST_PASS, 'name': common.TEST_USER} - ejson = {'auth': {'identity': {'token': {'id': common.TEST_TOKEN}, - 'password': {'user': up}, - 'methods': ['password', 'token']}}} - xport.post.assert_called_with(eurl, headers=eheaders, json=ejson) - ecatalog = common.TEST_RESPONSE_DICT_V3['token'].copy() - ecatalog['auth_token'] = common.TEST_SUBJECT - ecatalog['version'] = 'v3' - self.assertEqual(ecatalog, resp._info) - - def test_authorize_mutually_exclusive(self): - x = self.create_mock_transport(common.TEST_RESPONSE_DICT_V3) - methods = [v3.TokenMethod(token=common.TEST_TOKEN)] - - kargs = {'domain_id': 'a', - 'project_id': 'b'} - sot = v3.Auth(TEST_URL, methods, **kargs) - self.assertRaises(exceptions.AuthorizationFailure, sot.authorize, x) - - kargs = {'domain_name': 'a', - 'project_name': 'b'} - sot = v3.Auth(TEST_URL, methods, **kargs) - self.assertRaises(exceptions.AuthorizationFailure, sot.authorize, x) - - kargs = {'domain_name': 'a', - 'trust_id': 'b'} - sot = v3.Auth(TEST_URL, methods, **kargs) - self.assertRaises(exceptions.AuthorizationFailure, sot.authorize, x) - - kargs = {'project_id': 'a', - 'trust_id': 'b'} - sot = v3.Auth(TEST_URL, methods, **kargs) - self.assertRaises(exceptions.AuthorizationFailure, sot.authorize, x) - - def test_authorize_bad_response(self): - methods = [v3.TokenMethod(token=common.TEST_TOKEN)] - sot = v3.Auth(TEST_URL, methods) - xport = self.create_mock_transport({}) - - self.assertRaises(exceptions.InvalidResponse, sot.authorize, xport) diff --git a/openstack/tests/unit/auth/test_access.py b/openstack/tests/unit/auth/test_access.py deleted file mode 100644 index fea104827..000000000 --- a/openstack/tests/unit/auth/test_access.py +++ /dev/null @@ -1,88 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -import testtools - -from openstack.auth import access -from openstack.tests.unit.auth import common - - -class TestAccessInfo(testtools.TestCase): - def test_is_valid(self): - v2body = common.TEST_RESPONSE_DICT_V2 - v3body = common.TEST_RESPONSE_DICT_V3 - self.assertTrue(access.AccessInfoV2.is_valid(v2body)) - self.assertFalse(access.AccessInfoV2.is_valid(v3body)) - self.assertFalse(access.AccessInfoV3.is_valid(v2body)) - self.assertTrue(access.AccessInfoV3.is_valid(v3body)) - - def test_factory_v2(self): - sot = access.AccessInfo.factory(body=common.TEST_RESPONSE_DICT_V2) - - self.assertTrue(isinstance(sot, access.AccessInfoV2)) - self.assertFalse(sot.will_expire_soon()) - self.assertTrue(sot.has_service_catalog()) - self.assertEqual(common.TEST_TOKEN, sot.auth_token) - self.assertEqual(common.TEST_EXPIRES, str(sot.expires)) - self.assertEqual(None, sot.username) - self.assertEqual(common.TEST_USER_ID, sot.user_id) - self.assertEqual('default', sot.user_domain_id) - self.assertEqual('Default', sot.user_domain_name) - self.assertEqual([], sot.role_names) - self.assertEqual(None, sot.domain_id) - self.assertEqual(None, sot.domain_name) - self.assertEqual(None, sot.project_name) - self.assertEqual(None, sot.tenant_name) - self.assertTrue(sot.project_scoped) - self.assertFalse(sot.domain_scoped) - self.assertEqual(None, sot.trust_id) - self.assertFalse(sot.trust_scoped) - self.assertEqual(common.TEST_TENANT_ID, sot.project_id) - self.assertEqual(common.TEST_TENANT_ID, sot.tenant_id) - self.assertEqual('default', sot.project_domain_id) - self.assertEqual('Default', sot.project_domain_name) - self.assertEqual('v2.0', sot.version) - - def test_factory_v3(self): - response = mock.Mock() - response.headers = {'X-Subject-Token': common.TEST_TOKEN} - sot = access.AccessInfo.factory(body=common.TEST_RESPONSE_DICT_V3, - resp=response) - - self.assertTrue(isinstance(sot, access.AccessInfoV3)) - self.assertFalse(sot.will_expire_soon()) - self.assertTrue(sot.has_service_catalog()) - self.assertEqual(common.TEST_TOKEN, sot.auth_token) - self.assertEqual(common.TEST_EXPIRES, str(sot.expires)) - self.assertEqual(common.TEST_USER, sot.username) - self.assertEqual(common.TEST_USER_ID, sot.user_id) - self.assertEqual(common.TEST_DOMAIN_ID, sot.user_domain_id) - self.assertEqual(common.TEST_DOMAIN_NAME, sot.user_domain_name) - self.assertEqual([], sot.role_names) - self.assertEqual(None, sot.domain_id) - self.assertEqual(None, sot.domain_name) - self.assertEqual(common.TEST_PROJECT_NAME, sot.project_name) - self.assertEqual(common.TEST_PROJECT_NAME, sot.tenant_name) - self.assertTrue(sot.project_scoped) - self.assertFalse(sot.domain_scoped) - self.assertEqual(None, sot.trust_id) - self.assertFalse(sot.trust_scoped) - self.assertEqual(common.TEST_PROJECT_ID, sot.project_id) - self.assertEqual(common.TEST_PROJECT_ID, sot.tenant_id) - self.assertEqual(common.TEST_DOMAIN_ID, sot.project_domain_id) - self.assertEqual(common.TEST_DOMAIN_NAME, sot.project_domain_name) - self.assertEqual('v3', sot.version) - - def test_factory_raises(self): - self.assertRaises(NotImplementedError, access.AccessInfo.factory, - body={}) diff --git a/openstack/tests/unit/auth/test_service_catalog.py b/openstack/tests/unit/auth/test_service_catalog.py deleted file mode 100644 index 15d710200..000000000 --- a/openstack/tests/unit/auth/test_service_catalog.py +++ /dev/null @@ -1,151 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import testtools - -from openstack.auth import service_catalog as catalog -from openstack.compute import compute_service -from openstack import exceptions as exc -from openstack.identity import identity_service -from openstack.image import image_service -from openstack.network import network_service -from openstack.object_store import object_store_service -from openstack import service_filter -from openstack.tests.unit.auth import common - - -class TestServiceCatalog(testtools.TestCase): - def get_urls(self, sot): - sf = service_filter.ServiceFilter(service_type='compute') - exp = ["http://compute.region0.public/v1.1", - "http://compute.region2.public/v1", - "http://compute.region1.public/v2.0"] - self.assertEqual(exp, sot.get_urls(sf)) - sf = service_filter.ServiceFilter(service_type='image') - self.assertEqual(["http://image.region1.public/v2"], - sot.get_urls(sf)) - sf = service_filter.ServiceFilter(service_type='identity') - self.assertEqual(["http://identity.region1.public/v1.1/123123"], - sot.get_urls(sf)) - sf = service_filter.ServiceFilter(service_type='object-store') - self.assertEqual(["http://object-store.region1.public/"], - sot.get_urls(sf)) - sf = service_filter.ServiceFilter(service_type='object-store', - version='v9') - self.assertEqual(["http://object-store.region1.public/v9"], - sot.get_urls(sf)) - sf = service_filter.ServiceFilter(service_type='object-store') - osf = object_store_service.ObjectStoreService() - sf = sf.join(osf) - self.assertEqual(["http://object-store.region1.public/v1"], - sot.get_urls(sf)) - - def get_urls_name(self, sot): - sf = service_filter.ServiceFilter(service_type='compute', - service_name='nova') - self.assertEqual(["http://compute.region1.public/v2.0"], - sot.get_urls(sf)) - sf = service_filter.ServiceFilter(service_type='compute', - service_name='nova2') - self.assertEqual(["http://compute.region2.public/v1"], - sot.get_urls(sf)) - - def get_urls_region(self, sot): - sf = service_filter.ServiceFilter(service_type='compute', - region='RegionTwo') - self.assertEqual(["http://compute.region2.public/v1"], - sot.get_urls(sf)) - sf = service_filter.ServiceFilter(service_type='compute', - region='RegionOne') - self.assertEqual(["http://compute.region1.public/v2.0"], - sot.get_urls(sf)) - - def get_urls_interface(self, sot): - sf = service_filter.ServiceFilter(service_type='identity', - interface='admin') - self.assertEqual(["http://identity.region1.admin/v1.1/123123"], - sot.get_urls(sf)) - sf = service_filter.ServiceFilter(service_type='identity', - interface='internal') - self.assertEqual( - ["http://identity.region1.internal/v1.1/123123"], - sot.get_urls(sf) - ) - sf = service_filter.ServiceFilter(service_type='identity', - interface='public') - self.assertEqual(["http://identity.region1.public/v1.1/123123"], - sot.get_urls(sf)) - - -class TestServiceCatalogV2(TestServiceCatalog): - def test_catalog(self): - sot = catalog.ServiceCatalogV2(common.TEST_SERVICE_CATALOG_V2) - self.assertEqual(common.TEST_SERVICE_CATALOG_NORMALIZED, - sot.catalog) - - def test_catalog_empty(self): - self.assertRaises(exc.EmptyCatalog, catalog.ServiceCatalogV2, None) - - def test_get_urls(self): - sot = catalog.ServiceCatalogV2(common.TEST_SERVICE_CATALOG_V2) - self.get_urls(sot) - - def test_get_urls_name(self): - sot = catalog.ServiceCatalogV2(common.TEST_SERVICE_CATALOG_V2) - self.get_urls_name(sot) - - def test_get_urls_region(self): - sot = catalog.ServiceCatalogV2(common.TEST_SERVICE_CATALOG_V2) - self.get_urls_region(sot) - - def test_get_urls_interface(self): - sot = catalog.ServiceCatalogV2(common.TEST_SERVICE_CATALOG_V2) - self.get_urls_interface(sot) - - -class TestServiceCatalogV3(TestServiceCatalog): - def test_catalog(self): - sot = catalog.ServiceCatalog(common.TEST_SERVICE_CATALOG_V3) - self.assertEqual(common.TEST_SERVICE_CATALOG_NORMALIZED, - sot.catalog) - - def test_catalog_empty(self): - self.assertRaises(exc.EmptyCatalog, catalog.ServiceCatalog, None) - - def test_get_urls(self): - sot = catalog.ServiceCatalog(common.TEST_SERVICE_CATALOG_V3) - self.get_urls(sot) - - def test_get_urls_name(self): - sot = catalog.ServiceCatalog(common.TEST_SERVICE_CATALOG_V3) - self.get_urls_name(sot) - - def test_get_urls_region(self): - sot = catalog.ServiceCatalog(common.TEST_SERVICE_CATALOG_V3) - self.get_urls_region(sot) - - def test_get_urls_interface(self): - sot = catalog.ServiceCatalog(common.TEST_SERVICE_CATALOG_V3) - self.get_urls_interface(sot) - - def test_get_versions(self): - sot = catalog.ServiceCatalog(common.TEST_SERVICE_CATALOG_V3) - service = compute_service.ComputeService() - self.assertEqual(['v1.1', 'v1', 'v2.0'], sot.get_versions(service)) - service = identity_service.IdentityService() - self.assertEqual(['v1.1'], sot.get_versions(service)) - service = image_service.ImageService() - self.assertEqual(['v2'], sot.get_versions(service)) - service = network_service.NetworkService() - self.assertEqual(None, sot.get_versions(service)) - service = object_store_service.ObjectStoreService() - self.assertEqual([], sot.get_versions(service)) diff --git a/openstack/tests/unit/cluster/v1/test_cluster.py b/openstack/tests/unit/cluster/v1/test_cluster.py index 14e5c5439..13d2c014f 100644 --- a/openstack/tests/unit/cluster/v1/test_cluster.py +++ b/openstack/tests/unit/cluster/v1/test_cluster.py @@ -95,33 +95,35 @@ class TestCluster(testtools.TestCase): sot['id'] = 'IDENTIFIER' resp = mock.Mock() - resp.body = '' + resp.json = mock.Mock(return_value='') sess = mock.Mock() sess.put = mock.MagicMock(return_value=resp) self.assertEqual('', sot.add_nodes(sess, ['node-33'])) url = 'clusters/%s/action' % sot.id body = {'add_nodes': {'nodes': ['node-33']}} - sess.put.assert_called_once_with(url, service=sot.service, json=body) + sess.put.assert_called_once_with(url, endpoint_filter=sot.service, + json=body) def test_del_nodes(self): sot = cluster.Cluster(FAKE) sot['id'] = 'IDENTIFIER' resp = mock.Mock() - resp.body = '' + resp.json = mock.Mock(return_value='') sess = mock.Mock() sess.put = mock.MagicMock(return_value=resp) self.assertEqual('', sot.delete_nodes(sess, ['node-11'])) url = 'clusters/%s/action' % sot.id body = {'del_nodes': {'nodes': ['node-11']}} - sess.put.assert_called_once_with(url, service=sot.service, json=body) + sess.put.assert_called_once_with(url, endpoint_filter=sot.service, + json=body) def test_policy_attach(self): sot = cluster.Cluster(FAKE) sot['id'] = 'IDENTIFIER' resp = mock.Mock() - resp.body = '' + resp.json = mock.Mock(return_value='') sess = mock.Mock() sess.put = mock.MagicMock(return_value=resp) self.assertEqual('', sot.policy_attach(sess, 'POLICY', 1, 2, 0, True)) @@ -136,28 +138,30 @@ class TestCluster(testtools.TestCase): 'enabled': True, } } - sess.put.assert_called_once_with(url, service=sot.service, json=body) + sess.put.assert_called_once_with(url, endpoint_filter=sot.service, + json=body) def test_policy_detach(self): sot = cluster.Cluster(FAKE) sot['id'] = 'IDENTIFIER' resp = mock.Mock() - resp.body = '' + resp.json = mock.Mock(return_value='') sess = mock.Mock() sess.put = mock.MagicMock(return_value=resp) self.assertEqual('', sot.policy_detach(sess, 'POLICY')) url = 'clusters/%s/action' % sot.id body = {'policy_detach': {'policy_id': 'POLICY'}} - sess.put.assert_called_once_with(url, service=sot.service, json=body) + sess.put.assert_called_once_with(url, endpoint_filter=sot.service, + json=body) def test_policy_update(self): sot = cluster.Cluster(FAKE) sot['id'] = 'IDENTIFIER' resp = mock.Mock() - resp.body = '' + resp.json = mock.Mock(return_value='') sess = mock.Mock() sess.put = mock.MagicMock(return_value=resp) self.assertEqual('', sot.policy_update(sess, 'POLICY', 3, 4, 5, False)) @@ -172,14 +176,15 @@ class TestCluster(testtools.TestCase): 'enabled': False } } - sess.put.assert_called_once_with(url, service=sot.service, json=body) + sess.put.assert_called_once_with(url, endpoint_filter=sot.service, + json=body) def test_policy_enable(self): sot = cluster.Cluster(FAKE) sot['id'] = 'IDENTIFIER' resp = mock.Mock() - resp.body = '' + resp.json = mock.Mock(return_value='') sess = mock.Mock() sess.put = mock.MagicMock(return_value=resp) self.assertEqual('', sot.policy_enable(sess, 'POLICY')) @@ -191,14 +196,15 @@ class TestCluster(testtools.TestCase): 'enabled': True, } } - sess.put.assert_called_once_with(url, service=sot.service, json=body) + sess.put.assert_called_once_with(url, endpoint_filter=sot.service, + json=body) def test_policy_disable(self): sot = cluster.Cluster(FAKE) sot['id'] = 'IDENTIFIER' resp = mock.Mock() - resp.body = '' + resp.json = mock.Mock(return_value='') sess = mock.Mock() sess.put = mock.MagicMock(return_value=resp) self.assertEqual('', sot.policy_disable(sess, 'POLICY')) @@ -210,4 +216,5 @@ class TestCluster(testtools.TestCase): 'enabled': False, } } - sess.put.assert_called_once_with(url, service=sot.service, json=body) + sess.put.assert_called_once_with(url, endpoint_filter=sot.service, + json=body) diff --git a/openstack/tests/unit/cluster/v1/test_node.py b/openstack/tests/unit/cluster/v1/test_node.py index 66ecd396a..a8cd835cf 100644 --- a/openstack/tests/unit/cluster/v1/test_node.py +++ b/openstack/tests/unit/cluster/v1/test_node.py @@ -87,12 +87,14 @@ class TestNode(testtools.TestCase): resp = mock.Mock() resp.body = {'action': '1234-5678-abcd'} + resp.json = mock.Mock(return_value=resp.body) sess = mock.Mock() sess.put = mock.MagicMock(return_value=resp) self.assertEqual(resp.body, sot.join(sess, 'cluster-b')) url = 'nodes/%s/action' % sot.id body = {'join': {'cluster_id': 'cluster-b'}} - sess.put.assert_called_once_with(url, service=sot.service, json=body) + sess.put.assert_called_once_with(url, endpoint_filter=sot.service, + json=body) def test_leave(self): sot = node.Node(FAKE) @@ -100,9 +102,11 @@ class TestNode(testtools.TestCase): resp = mock.Mock() resp.body = {'action': '2345-6789-bbbb'} + resp.json = mock.Mock(return_value=resp.body) sess = mock.Mock() sess.put = mock.MagicMock(return_value=resp) self.assertEqual(resp.body, sot.leave(sess)) url = 'nodes/%s/action' % sot.id body = {'leave': {}} - sess.put.assert_called_once_with(url, service=sot.service, json=body) + sess.put.assert_called_once_with(url, endpoint_filter=sot.service, + json=body) diff --git a/openstack/tests/unit/compute/v2/test_server.py b/openstack/tests/unit/compute/v2/test_server.py index 9387aed0c..de7c1e5b9 100644 --- a/openstack/tests/unit/compute/v2/test_server.py +++ b/openstack/tests/unit/compute/v2/test_server.py @@ -45,7 +45,8 @@ class TestServer(testtools.TestCase): def setUp(self): super(TestServer, self).setUp() self.resp = mock.Mock() - self.resp = '' + self.resp.body = '' + self.resp.json = mock.Mock(return_value=self.resp.body) self.sess = mock.Mock() self.sess.post = mock.MagicMock() self.sess.post.return_value = self.resp @@ -105,28 +106,30 @@ class TestServer(testtools.TestCase): def test_change_passowrd(self): sot = server.Server(EXAMPLE) - self.assertEqual(self.resp, sot.change_password(self.sess, 'a')) + self.assertEqual(self.resp.body, sot.change_password(self.sess, 'a')) url = 'servers/IDENTIFIER/action' body = {"changePassword": {"adminPass": "a"}} + headers = {'Accept': ''} self.sess.post.assert_called_with( - url, service=sot.service, json=body, accept=None) + url, endpoint_filter=sot.service, json=body, headers=headers) def test_reboot(self): sot = server.Server(EXAMPLE) - self.assertEqual(self.resp, sot.reboot(self.sess, 'HARD')) + self.assertEqual(self.resp.body, sot.reboot(self.sess, 'HARD')) url = 'servers/IDENTIFIER/action' body = {"reboot": {"type": "HARD"}} + headers = {'Accept': ''} self.sess.post.assert_called_with( - url, service=sot.service, json=body, accept=None) + url, endpoint_filter=sot.service, json=body, headers=headers) def test_rebuild(self): sot = server.Server(EXAMPLE) self.assertEqual( - self.resp, + self.resp.body, sot.rebuild( self.sess, name='noo', @@ -151,14 +154,15 @@ class TestServer(testtools.TestCase): "personality": [{"path": "/etc/motd", "contents": "foo"}], } } + headers = {'Accept': ''} self.sess.post.assert_called_with( - url, service=sot.service, json=body, accept=None) + url, endpoint_filter=sot.service, json=body, headers=headers) def test_rebuild_minimal(self): sot = server.Server(EXAMPLE) self.assertEqual( - self.resp, + self.resp.body, sot.rebuild( self.sess, name='nootoo', @@ -175,38 +179,42 @@ class TestServer(testtools.TestCase): "adminPass": "seekr3two", } } + headers = {'Accept': ''} self.sess.post.assert_called_with( - url, service=sot.service, json=body, accept=None) + url, endpoint_filter=sot.service, json=body, headers=headers) def test_resize(self): sot = server.Server(EXAMPLE) - self.assertEqual(self.resp, sot.resize(self.sess, '2')) + self.assertEqual(self.resp.body, sot.resize(self.sess, '2')) url = 'servers/IDENTIFIER/action' body = {"resize": {"flavorRef": "2"}} + headers = {'Accept': ''} self.sess.post.assert_called_with( - url, service=sot.service, json=body, accept=None) + url, endpoint_filter=sot.service, json=body, headers=headers) def test_confirm_resize(self): sot = server.Server(EXAMPLE) - self.assertEqual(self.resp, sot.confirm_resize(self.sess)) + self.assertEqual(self.resp.body, sot.confirm_resize(self.sess)) url = 'servers/IDENTIFIER/action' body = {"confirmResize": None} + headers = {'Accept': ''} self.sess.post.assert_called_with( - url, service=sot.service, json=body, accept=None) + url, endpoint_filter=sot.service, json=body, headers=headers) def test_revert_resize(self): sot = server.Server(EXAMPLE) - self.assertEqual(self.resp, sot.revert_resize(self.sess)) + self.assertEqual(self.resp.body, sot.revert_resize(self.sess)) url = 'servers/IDENTIFIER/action' body = {"revertResize": None} + headers = {'Accept': ''} self.sess.post.assert_called_with( - url, service=sot.service, json=body, accept=None) + url, endpoint_filter=sot.service, json=body, headers=headers) def test_create_image(self): sot = server.Server(EXAMPLE) @@ -214,28 +222,30 @@ class TestServer(testtools.TestCase): metadata = {'nu': 'image', 'created': 'today'} self.assertEqual( - self.resp, + self.resp.body, sot.create_image(self.sess, name, metadata) ) url = 'servers/IDENTIFIER/action' body = {"createImage": {'name': name, 'metadata': metadata}} + headers = {'Accept': ''} self.sess.post.assert_called_with( - url, service=sot.service, json=body, accept=None) + url, endpoint_filter=sot.service, json=body, headers=headers) def test_create_image_minimal(self): sot = server.Server(EXAMPLE) name = 'noo' self.assertEqual( - self.resp, + self.resp.body, sot.create_image(self.sess, name) ) url = 'servers/IDENTIFIER/action' body = {"createImage": {'name': name}} + headers = {'Accept': ''} self.sess.post.assert_called_with( - url, service=sot.service, json=body, accept=None) + url, endpoint_filter=dict(sot.service), json=body, headers=headers) def test_get_ips(self): name = "jenkins" diff --git a/openstack/tests/unit/compute/v2/test_server_ip.py b/openstack/tests/unit/compute/v2/test_server_ip.py index 07022917c..010bcd4d5 100644 --- a/openstack/tests/unit/compute/v2/test_server_ip.py +++ b/openstack/tests/unit/compute/v2/test_server_ip.py @@ -81,7 +81,7 @@ class TestServerIP(testtools.TestCase): def test_list(self): sess = mock.Mock() resp = mock.Mock() - resp.body = BODY + resp.json = mock.Mock(return_value=BODY) sess.get = mock.Mock(return_value=resp) path_args = {'server_id': IDENTIFIER} diff --git a/openstack/tests/unit/compute/v2/test_server_meta.py b/openstack/tests/unit/compute/v2/test_server_meta.py index 2fe2ded22..41d98ea18 100644 --- a/openstack/tests/unit/compute/v2/test_server_meta.py +++ b/openstack/tests/unit/compute/v2/test_server_meta.py @@ -50,6 +50,7 @@ class TestServerMeta(testtools.TestCase): def test_create(self): resp = mock.Mock() resp.body = FAKE_RESPONSE + resp.json = mock.Mock(return_value=resp.body) sess = mock.Mock() sess.put = mock.MagicMock() sess.put.return_value = resp @@ -59,7 +60,8 @@ class TestServerMeta(testtools.TestCase): url = 'servers/' + FAKE_SERVER_ID + '/metadata/' + FAKE_KEY body = {"meta": {FAKE_KEY: FAKE_VALUE}} - sess.put.assert_called_with(url, service=sot.service, json=body) + sess.put.assert_called_with(url, endpoint_filter=sot.service, + json=body) self.assertEqual(FAKE_VALUE, sot.value) self.assertEqual(FAKE_KEY, sot.key) self.assertEqual(FAKE_SERVER_ID, sot.server_id) @@ -67,6 +69,7 @@ class TestServerMeta(testtools.TestCase): def test_get(self): resp = mock.Mock() resp.body = FAKE_RESPONSES + resp.json = mock.Mock(return_value=resp.body) sess = mock.Mock() sess.get = mock.MagicMock() sess.get.return_value = resp @@ -76,7 +79,8 @@ class TestServerMeta(testtools.TestCase): resp = sot.list(sess, path_args=path_args) url = '/servers/' + FAKE_SERVER_ID + '/metadata' - sess.get.assert_called_with(url, service=sot.service, params={}) + sess.get.assert_called_with(url, endpoint_filter=sot.service, + params={}) self.assertEqual(1, len(resp)) self.assertEqual(FAKE_SERVER_ID, resp[0].server_id) self.assertEqual(FAKE_KEY, resp[0].key) @@ -85,6 +89,7 @@ class TestServerMeta(testtools.TestCase): def test_update(self): resp = mock.Mock() resp.body = FAKE_RESPONSE + resp.json = mock.Mock(return_value=resp.body) sess = mock.Mock() sess.put = mock.MagicMock() sess.put.return_value = resp @@ -94,7 +99,8 @@ class TestServerMeta(testtools.TestCase): url = 'servers/' + FAKE_SERVER_ID + '/metadata/' + FAKE_KEY body = {"meta": {FAKE_KEY: FAKE_VALUE}} - sess.put.assert_called_with(url, service=sot.service, json=body) + sess.put.assert_called_with(url, endpoint_filter=sot.service, + json=body) self.assertEqual(FAKE_VALUE, sot.value) self.assertEqual(FAKE_KEY, sot.key) self.assertEqual(FAKE_SERVER_ID, sot.server_id) @@ -102,6 +108,7 @@ class TestServerMeta(testtools.TestCase): def test_delete(self): resp = mock.Mock() resp.body = FAKE_RESPONSES + resp.json = mock.Mock(return_value=resp.body) sess = mock.Mock() sess.delete = mock.MagicMock() sess.delete.return_value = resp @@ -110,11 +117,14 @@ class TestServerMeta(testtools.TestCase): sot.delete(sess) url = 'servers/' + FAKE_SERVER_ID + '/metadata/' + FAKE_KEY - sess.delete.assert_called_with(url, service=sot.service, accept=None) + headers = {'Accept': ''} + sess.delete.assert_called_with(url, endpoint_filter=sot.service, + headers=headers) def test_list(self): resp = mock.Mock() resp.body = FAKE_RESPONSES + resp.json = mock.Mock(return_value=resp.body) sess = mock.Mock() sess.get = mock.MagicMock() sess.get.return_value = resp @@ -124,7 +134,8 @@ class TestServerMeta(testtools.TestCase): resp = sot.list(sess, path_args=path_args) url = '/servers/' + FAKE_SERVER_ID + '/metadata' - sess.get.assert_called_with(url, service=sot.service, params={}) + sess.get.assert_called_with(url, endpoint_filter=sot.service, + params={}) self.assertEqual(1, len(resp)) self.assertEqual(FAKE_SERVER_ID, resp[0].server_id) self.assertEqual(FAKE_KEY, resp[0].key) diff --git a/openstack/tests/unit/compute/v2/test_server_metadata.py b/openstack/tests/unit/compute/v2/test_server_metadata.py index a7bdb529a..6643a0f33 100644 --- a/openstack/tests/unit/compute/v2/test_server_metadata.py +++ b/openstack/tests/unit/compute/v2/test_server_metadata.py @@ -49,6 +49,7 @@ class TestServerMetadata(testtools.TestCase): def test_create(self): resp = mock.Mock() resp.body = FAKE_RESPONSE + resp.json = mock.Mock(return_value=resp.body) sess = mock.Mock() sess.put = mock.MagicMock() sess.put.return_value = resp @@ -58,13 +59,15 @@ class TestServerMetadata(testtools.TestCase): url = '/servers/' + FAKE_SERVER_ID + '/metadata' body = {"metadata": {FAKE_KEY: FAKE_VALUE}} - sess.put.assert_called_with(url, service=sot.service, json=body) + sess.put.assert_called_with(url, endpoint_filter=sot.service, + json=body) self.assertEqual(FAKE_SERVER_ID, sot.server_id) self.assertEqual(FAKE_VALUE, sot[FAKE_KEY]) def test_get(self): resp = mock.Mock() resp.body = FAKE_RESPONSE + resp.json = mock.Mock(return_value=resp.body) sess = mock.Mock() sess.get = mock.MagicMock() sess.get.return_value = resp @@ -73,7 +76,7 @@ class TestServerMetadata(testtools.TestCase): sot.get(sess) url = '/servers/' + FAKE_SERVER_ID + '/metadata' - sess.get.assert_called_with(url, service=sot.service) + sess.get.assert_called_with(url, endpoint_filter=sot.service) self.assertEqual(FAKE_SERVER_ID, sot.server_id) self.assertEqual(FAKE_VALUE, sot[FAKE_KEY]) self.assertEqual(FAKE_VALUE2, sot[FAKE_KEY2]) @@ -81,6 +84,7 @@ class TestServerMetadata(testtools.TestCase): def test_update(self): resp = mock.Mock() resp.body = FAKE_RESPONSE + resp.json = mock.Mock(return_value=resp.body) sess = mock.Mock() sess.put = mock.MagicMock() sess.put.return_value = resp @@ -90,6 +94,7 @@ class TestServerMetadata(testtools.TestCase): url = '/servers/' + FAKE_SERVER_ID + '/metadata' body = {"metadata": {FAKE_KEY: FAKE_VALUE}} - sess.put.assert_called_with(url, service=sot.service, json=body) + sess.put.assert_called_with(url, endpoint_filter=sot.service, + json=body) self.assertEqual(FAKE_SERVER_ID, sot.server_id) self.assertEqual(FAKE_VALUE, sot[FAKE_KEY]) diff --git a/openstack/tests/unit/database/v1/test_instance.py b/openstack/tests/unit/database/v1/test_instance.py index 956478517..1ec67145f 100644 --- a/openstack/tests/unit/database/v1/test_instance.py +++ b/openstack/tests/unit/database/v1/test_instance.py @@ -53,6 +53,7 @@ class TestInstance(testtools.TestCase): sot = instance.Instance(EXAMPLE) response = mock.Mock() response.body = {'user': {'name': 'root', 'password': 'foo'}} + response.json = mock.Mock(return_value=response.body) sess = mock.Mock() sess.post = mock.MagicMock() sess.post.return_value = response @@ -60,12 +61,13 @@ class TestInstance(testtools.TestCase): self.assertEqual(response.body['user'], sot.enable_root_user(sess)) url = ("instances/%s/root" % IDENTIFIER) - sess.post.assert_called_with(url, service=sot.service) + sess.post.assert_called_with(url, endpoint_filter=sot.service) def test_is_root_enabled(self): sot = instance.Instance(EXAMPLE) response = mock.Mock() response.body = {'rootEnabled': True} + response.json = mock.Mock(return_value=response.body) sess = mock.Mock() sess.get = mock.MagicMock() sess.get.return_value = response @@ -73,12 +75,12 @@ class TestInstance(testtools.TestCase): self.assertEqual(True, sot.is_root_enabled(sess)) url = ("instances/%s/root" % IDENTIFIER) - sess.get.assert_called_with(url, service=sot.service) + sess.get.assert_called_with(url, endpoint_filter=sot.service) def test_action_restart(self): sot = instance.Instance(EXAMPLE) response = mock.Mock() - response.body = '' + response.json = mock.Mock(return_value='') sess = mock.Mock() sess.post = mock.MagicMock() sess.post.return_value = response @@ -87,12 +89,13 @@ class TestInstance(testtools.TestCase): url = ("instances/%s/action" % IDENTIFIER) body = {'restart': {}} - sess.post.assert_called_with(url, service=sot.service, json=body) + sess.post.assert_called_with(url, endpoint_filter=sot.service, + json=body) def test_action_resize(self): sot = instance.Instance(EXAMPLE) response = mock.Mock() - response.body = '' + response.json = mock.Mock(return_value='') sess = mock.Mock() sess.post = mock.MagicMock() sess.post.return_value = response @@ -102,12 +105,13 @@ class TestInstance(testtools.TestCase): url = ("instances/%s/action" % IDENTIFIER) body = {'resize': {'flavorRef': flavor}} - sess.post.assert_called_with(url, service=sot.service, json=body) + sess.post.assert_called_with(url, endpoint_filter=sot.service, + json=body) def test_action_resize_volume(self): sot = instance.Instance(EXAMPLE) response = mock.Mock() - response.body = '' + response.json = mock.Mock(return_value='') sess = mock.Mock() sess.post = mock.MagicMock() sess.post.return_value = response @@ -117,4 +121,5 @@ class TestInstance(testtools.TestCase): url = ("instances/%s/action" % IDENTIFIER) body = {'resize': {'volume': size}} - sess.post.assert_called_with(url, service=sot.service, json=body) + sess.post.assert_called_with(url, endpoint_filter=sot.service, + json=body) diff --git a/openstack/tests/unit/database/v1/test_user.py b/openstack/tests/unit/database/v1/test_user.py index 7a5a5b009..85e05bf23 100644 --- a/openstack/tests/unit/database/v1/test_user.py +++ b/openstack/tests/unit/database/v1/test_user.py @@ -65,5 +65,5 @@ class TestUser(testtools.TestCase): payload = {'users': [CREATING]} user.User.create_by_id(sess, CREATING, path_args=path_args) - sess.post.assert_called_with(url, service=user.User.service, + sess.post.assert_called_with(url, endpoint_filter=user.User.service, json=payload) diff --git a/openstack/tests/unit/identity/test_version.py b/openstack/tests/unit/identity/test_version.py index 36e6b3327..b8b333655 100644 --- a/openstack/tests/unit/identity/test_version.py +++ b/openstack/tests/unit/identity/test_version.py @@ -55,6 +55,7 @@ class TestVersion(testtools.TestCase): ] } } + resp.json = mock.Mock(return_value=resp.body) session = mock.MagicMock() session.get = mock.MagicMock() session.get.return_value = resp diff --git a/openstack/tests/unit/identity/v2/test_extension.py b/openstack/tests/unit/identity/v2/test_extension.py index 142c2a91e..91a835acd 100644 --- a/openstack/tests/unit/identity/v2/test_extension.py +++ b/openstack/tests/unit/identity/v2/test_extension.py @@ -59,6 +59,7 @@ class TestExtension(testtools.TestCase): ] } } + resp.json = mock.Mock(return_value=resp.body) session = mock.MagicMock() session.get = mock.MagicMock() session.get.return_value = resp diff --git a/openstack/tests/unit/image/v2/test_image.py b/openstack/tests/unit/image/v2/test_image.py index 62323ab73..d8eb99b73 100644 --- a/openstack/tests/unit/image/v2/test_image.py +++ b/openstack/tests/unit/image/v2/test_image.py @@ -77,8 +77,11 @@ class TestImage(testtools.TestCase): sot = image.Image(EXAMPLE) sot.upload_image(self.sess) - headers = {'Content-Type': 'application/octet-stream'} + headers = { + 'Content-Type': 'application/octet-stream', + 'Accept': '', + } self.sess.put.assert_called_with( - 'images/IDENTIFIER/file', service=sot.service, - data=EXAMPLE['data'], accept=None, headers=headers) + 'images/IDENTIFIER/file', endpoint_filter=sot.service, + data=EXAMPLE['data'], headers=headers) diff --git a/openstack/tests/unit/image/v2/test_tag.py b/openstack/tests/unit/image/v2/test_tag.py index fbc4db564..173fa33bc 100644 --- a/openstack/tests/unit/image/v2/test_tag.py +++ b/openstack/tests/unit/image/v2/test_tag.py @@ -53,8 +53,9 @@ class TestTag(testtools.TestCase): url = 'images/%(image)s/tags/%(tag)s' % { "image": self.img.get_id(self.img), "tag": test_tag} self.assertIsNone(rv) - session_method.assert_called_with(url, service=sot.service, - accept=None) + headers = {'Accept': ''} + session_method.assert_called_with(url, endpoint_filter=sot.service, + headers=headers) def test_create(self): self._test_action("create", self.session.put) diff --git a/openstack/tests/unit/message/v1/test_claim.py b/openstack/tests/unit/message/v1/test_claim.py index d414b89e5..c1a201c0b 100644 --- a/openstack/tests/unit/message/v1/test_claim.py +++ b/openstack/tests/unit/message/v1/test_claim.py @@ -58,7 +58,7 @@ class TestClaim(testtools.TestCase): url = '/queues/%s/claims' % QUEUE sess.post.assert_called_with( - url, service=sot.service, + url, endpoint_filter=sot.service, headers={'Client-ID': CLIENT}, params=None, data=json.dumps(FAKE, cls=claim.ClaimEncoder)) diff --git a/openstack/tests/unit/message/v1/test_message.py b/openstack/tests/unit/message/v1/test_message.py index 57209b803..0103aa1d4 100644 --- a/openstack/tests/unit/message/v1/test_message.py +++ b/openstack/tests/unit/message/v1/test_message.py @@ -59,7 +59,7 @@ class TestMessage(testtools.TestCase): url = '/queues/%s/messages' % QUEUE sess.post.assert_called_with( - url, service=sot.service, + url, endpoint_filter=sot.service, headers={'Client-ID': CLIENT}, data=mock.ANY) @@ -78,5 +78,5 @@ class TestMessage(testtools.TestCase): url = '/queues/%s/messages/1234' % QUEUE sess.delete.assert_called_with( - url, service=sot.service, accept=None, - headers={'Client-ID': CLIENT}) + url, endpoint_filter=sot.service, + headers={'Client-ID': CLIENT, 'Accept': ''}) diff --git a/openstack/tests/unit/message/v1/test_queue.py b/openstack/tests/unit/message/v1/test_queue.py index a1ff30221..81b07df7f 100644 --- a/openstack/tests/unit/message/v1/test_queue.py +++ b/openstack/tests/unit/message/v1/test_queue.py @@ -48,6 +48,8 @@ class TestQueue(testtools.TestCase): sot.create(sess) url = 'queues/%s' % FAKE_NAME - sess.put.assert_called_with(url, service=sot.service, accept=None) + headers = {'Accept': ''} + sess.put.assert_called_with(url, endpoint_filter=sot.service, + headers=headers) self.assertEqual(FAKE_NAME, sot.id) self.assertEqual(FAKE_NAME, sot.name) diff --git a/openstack/tests/unit/network/v2/test_floating_ip.py b/openstack/tests/unit/network/v2/test_floating_ip.py index 494f66c8f..8d8b8d7a0 100644 --- a/openstack/tests/unit/network/v2/test_floating_ip.py +++ b/openstack/tests/unit/network/v2/test_floating_ip.py @@ -57,25 +57,30 @@ class TestFloatingIP(testtools.TestCase): mock_session = mock.Mock() mock_get = mock.Mock() mock_session.get = mock_get + mock_session.get_filter = mock.Mock(return_value={}) data = {'id': 'one', 'floating_ip_address': '10.0.0.1'} fake_response = mock.Mock() - fake_response.body = {floating_ip.FloatingIP.resources_key: [data]} + body = {floating_ip.FloatingIP.resources_key: [data]} + fake_response.json = mock.Mock(return_value=body) mock_get.return_value = fake_response result = floating_ip.FloatingIP.find_available(mock_session) self.assertEqual('one', result.id) p = {'fields': 'id', 'port_id': ''} - mock_get.assert_called_with(floating_ip.FloatingIP.base_path, - params=p, - service=floating_ip.FloatingIP.service) + mock_get.assert_called_with( + floating_ip.FloatingIP.base_path, + endpoint_filter=floating_ip.FloatingIP.service, + headers={'Accept': 'application/json'}, + params=p) def test_find_available_nada(self): mock_session = mock.Mock() mock_get = mock.Mock() mock_session.get = mock_get fake_response = mock.Mock() - fake_response.body = {floating_ip.FloatingIP.resources_key: []} + body = {floating_ip.FloatingIP.resources_key: []} + fake_response.json = mock.Mock(return_value=body) mock_get.return_value = fake_response self.assertEqual(None, diff --git a/openstack/tests/unit/network/v2/test_router.py b/openstack/tests/unit/network/v2/test_router.py index 0dcc38544..78418aa90 100644 --- a/openstack/tests/unit/network/v2/test_router.py +++ b/openstack/tests/unit/network/v2/test_router.py @@ -54,6 +54,7 @@ class TestRouter(testtools.TestCase): sot = router.Router(EXAMPLE) response = mock.Mock() response.body = {"subnet_id": "3", "port_id": "2"} + response.json = mock.Mock(return_value=response.body) sess = mock.Mock() sess.put = mock.MagicMock() sess.put.return_value = response @@ -62,12 +63,14 @@ class TestRouter(testtools.TestCase): url = 'routers/IDENTIFIER/add_router_interface' body = {"subnet_id": "3"} - sess.put.assert_called_with(url, service=sot.service, json=body) + sess.put.assert_called_with(url, endpoint_filter=sot.service, + json=body) def test_remove_interface(self): sot = router.Router(EXAMPLE) response = mock.Mock() response.body = {"subnet_id": "3", "port_id": "2"} + response.json = mock.Mock(return_value=response.body) sess = mock.Mock() sess.put = mock.MagicMock() sess.put.return_value = response @@ -76,4 +79,5 @@ class TestRouter(testtools.TestCase): url = 'routers/IDENTIFIER/remove_router_interface' body = {"subnet_id": "3"} - sess.put.assert_called_with(url, service=sot.service, json=body) + sess.put.assert_called_with(url, endpoint_filter=sot.service, + json=body) diff --git a/openstack/tests/unit/object_store/v1/test_container.py b/openstack/tests/unit/object_store/v1/test_container.py index bd60072ec..bce374642 100644 --- a/openstack/tests/unit/object_store/v1/test_container.py +++ b/openstack/tests/unit/object_store/v1/test_container.py @@ -59,6 +59,7 @@ class TestContainer(testtools.TestCase): super(TestContainer, self).setUp() self.resp = mock.Mock() self.resp.body = {} + self.resp.json = mock.Mock(return_value=self.resp.body) self.resp.headers = {"X-Trans-Id": "abcdef"} self.sess = mock.Mock() self.sess.put = mock.MagicMock() @@ -136,12 +137,13 @@ class TestContainer(testtools.TestCase): headers = { "x-container-read": "some ACL", "x-container-write": "another ACL", - "x-detect-content-type": True + "x-detect-content-type": True, + "Accept": "", } sot_call(self.sess) url = "/%s" % CONTAINER_NAME - sess_method.assert_called_with(url, service=sot.service, accept=None, + sess_method.assert_called_with(url, endpoint_filter=sot.service, headers=headers) def test_create(self): @@ -156,8 +158,9 @@ class TestContainer(testtools.TestCase): sot = container.Container.new(name=CONTAINER_NAME) sot.create(self.sess) url = "/%s" % CONTAINER_NAME - self.sess.put.assert_called_with(url, service=sot.service, - accept=None, headers=dict()) + headers = {'Accept': ''} + self.sess.put.assert_called_with(url, endpoint_filter=sot.service, + headers=headers) def test_create_no_headers(self): sot = container.Container.new(name=CONTAINER_NAME) diff --git a/openstack/tests/unit/object_store/v1/test_obj.py b/openstack/tests/unit/object_store/v1/test_obj.py index 8a9423b58..ac0f86e3d 100644 --- a/openstack/tests/unit/object_store/v1/test_obj.py +++ b/openstack/tests/unit/object_store/v1/test_obj.py @@ -114,21 +114,22 @@ class TestObject(testtools.TestCase): # "x-newest": True, # "if-match": {"who": "what"} # } - self.sess.get.assert_called_with(url, service=sot.service, - accept="bytes") + headers = {'Accept': 'bytes'} + self.sess.get.assert_called_with(url, endpoint_filter=sot.service, + headers=headers) self.assertEqual(self.resp.content, rv) def _test_create(self, method, data, accept): sot = obj.Object.new(container=CONTAINER_NAME, name=OBJECT_NAME, data=data) sot.newest = True - headers = {"x-newest": True} + headers = {"x-newest": True, "Accept": ""} rv = sot.create(self.sess) url = "%s/%s" % (CONTAINER_NAME, OBJECT_NAME) - method.assert_called_with(url, service=sot.service, data=data, - accept=accept, headers=headers) + method.assert_called_with(url, endpoint_filter=sot.service, data=data, + headers=headers) self.assertEqual(self.resp.headers, rv.get_headers()) def test_create_data(self): diff --git a/openstack/tests/unit/object_store/v1/test_proxy.py b/openstack/tests/unit/object_store/v1/test_proxy.py index 67856b152..67f1d31ae 100644 --- a/openstack/tests/unit/object_store/v1/test_proxy.py +++ b/openstack/tests/unit/object_store/v1/test_proxy.py @@ -17,10 +17,7 @@ from openstack.object_store.v1 import _proxy from openstack.object_store.v1 import account from openstack.object_store.v1 import container from openstack.object_store.v1 import obj -from openstack import session -from openstack.tests.unit import fakes from openstack.tests.unit import test_proxy_base -from openstack import transport class TestObjectStoreProxy(test_proxy_base.TestProxyBase): @@ -95,10 +92,6 @@ class Test_containers(TestObjectStoreProxy): def setUp(self): super(Test_containers, self).setUp() - self.transport = transport.Transport(accept=transport.JSON) - self.auth = fakes.FakeAuthenticator() - self.session = session.Session(self.transport, self.auth) - self.proxy = _proxy.Proxy(self.session) self.containers_body = [] @@ -174,10 +167,6 @@ class Test_objects(TestObjectStoreProxy): def setUp(self): super(Test_objects, self).setUp() - self.transport = transport.Transport(accept=transport.JSON) - self.auth = fakes.FakeAuthenticator() - self.session = session.Session(self.transport, self.auth) - self.proxy = _proxy.Proxy(self.session) self.container_name = six.text_type("my_container") diff --git a/openstack/tests/unit/orchestration/v1/test_stack.py b/openstack/tests/unit/orchestration/v1/test_stack.py index 58fb72fa9..331d27f71 100644 --- a/openstack/tests/unit/orchestration/v1/test_stack.py +++ b/openstack/tests/unit/orchestration/v1/test_stack.py @@ -84,7 +84,7 @@ class TestStack(testtools.TestCase): def test_create(self): resp = mock.MagicMock() - resp.body = FAKE_CREATE_RESPONSE + resp.json = mock.Mock(return_value=FAKE_CREATE_RESPONSE) sess = mock.Mock() sess.post = mock.MagicMock() sess.post.return_value = resp @@ -96,7 +96,8 @@ class TestStack(testtools.TestCase): body = sot._attrs.copy() body.pop('id', None) body.pop('name', None) - sess.post.assert_called_with(url, service=sot.service, json=body) + sess.post.assert_called_with(url, endpoint_filter=sot.service, + json=body) self.assertEqual(FAKE_ID, sot.id) self.assertEqual(FAKE_NAME, sot.name) @@ -105,10 +106,11 @@ class TestStack(testtools.TestCase): resp_update = mock.Mock() resp_update.status_code = 202 sess = mock.Mock() - sess.put = mock.Mock(return_value=resp_update) + sess.patch = mock.Mock(return_value=resp_update) resp_get = mock.Mock() resp_get.body = {'stack': FAKE_CREATE_RESPONSE} + resp_get.json = mock.Mock(return_value=resp_get.body) sess.get = mock.Mock(return_value=resp_get) # create a stack for update @@ -122,9 +124,9 @@ class TestStack(testtools.TestCase): url = 'stacks/%s' % sot.id new_body['timeout_mins'] = 119 - sess.put.assert_called_once_with(url, service=sot.service, + sess.put.assert_called_once_with(url, endpoint_filter=sot.service, json=new_body) - sess.get.assert_called_once_with(url, service=sot.service) + sess.get.assert_called_once_with(url, endpoint_filter=sot.service) self.assertEqual(sot, resp) def test_check(self): diff --git a/openstack/tests/unit/telemetry/v2/test_alarm.py b/openstack/tests/unit/telemetry/v2/test_alarm.py index 505f5a35b..2eed2c1f3 100644 --- a/openstack/tests/unit/telemetry/v2/test_alarm.py +++ b/openstack/tests/unit/telemetry/v2/test_alarm.py @@ -50,6 +50,7 @@ class TestAlarm(testtools.TestCase): super(TestAlarm, self).setUp() self.resp = mock.Mock() self.resp.body = '' + self.resp.json = mock.Mock(return_value=self.resp.body) self.sess = mock.Mock() self.sess.put = mock.MagicMock() self.sess.put.return_value = self.resp @@ -94,12 +95,12 @@ class TestAlarm(testtools.TestCase): sot.check_state(self.sess) url = 'alarms/IDENTIFIER/state' - self.sess.get.assert_called_with(url, service=sot.service) + self.sess.get.assert_called_with(url, endpoint_filter=sot.service) def test_change_status(self): sot = alarm.Alarm(EXAMPLE) self.assertEqual(self.resp.body, sot.change_state(self.sess, 'alarm')) url = 'alarms/IDENTIFIER/state' - self.sess.put.assert_called_with(url, service=sot.service, + self.sess.put.assert_called_with(url, endpoint_filter=sot.service, json='alarm') diff --git a/openstack/tests/unit/telemetry/v2/test_alarm_change.py b/openstack/tests/unit/telemetry/v2/test_alarm_change.py index e44ec33ed..4250b2b90 100644 --- a/openstack/tests/unit/telemetry/v2/test_alarm_change.py +++ b/openstack/tests/unit/telemetry/v2/test_alarm_change.py @@ -57,7 +57,7 @@ class TestAlarmChange(testtools.TestCase): def test_list(self): sess = mock.Mock() resp = mock.Mock() - resp.body = [EXAMPLE, EXAMPLE] + resp.json = mock.Mock(return_value=[EXAMPLE, EXAMPLE]) sess.get = mock.Mock(return_value=resp) path_args = {'alarm_id': IDENTIFIER} diff --git a/openstack/tests/unit/telemetry/v2/test_capability.py b/openstack/tests/unit/telemetry/v2/test_capability.py index d72273529..69af9ff49 100644 --- a/openstack/tests/unit/telemetry/v2/test_capability.py +++ b/openstack/tests/unit/telemetry/v2/test_capability.py @@ -51,7 +51,7 @@ class TestCapability(testtools.TestCase): def test_list(self): sess = mock.Mock() resp = mock.Mock() - resp.body = BODY + resp.json = mock.Mock(return_value=BODY) sess.get = mock.Mock(return_value=resp) caps = capability.Capability.list(sess) diff --git a/openstack/tests/unit/telemetry/v2/test_sample.py b/openstack/tests/unit/telemetry/v2/test_sample.py index e11910744..115bd8d71 100644 --- a/openstack/tests/unit/telemetry/v2/test_sample.py +++ b/openstack/tests/unit/telemetry/v2/test_sample.py @@ -95,7 +95,7 @@ class TestSample(testtools.TestCase): def test_list(self): sess = mock.Mock() resp = mock.Mock() - resp.body = [SAMPLE, OLD_SAMPLE] + resp.json = mock.Mock(return_value=[SAMPLE, OLD_SAMPLE]) sess.get = mock.Mock(return_value=resp) path_args = {'counter_name': 'name_of_meter'} @@ -120,7 +120,7 @@ class TestSample(testtools.TestCase): def test_create(self): sess = mock.Mock() resp = mock.Mock() - resp.body = [SAMPLE] + resp.json = mock.Mock(return_value=[SAMPLE]) sess.post = mock.Mock(return_value=resp) data = {'id': None, @@ -134,6 +134,6 @@ class TestSample(testtools.TestCase): new_sample.create(sess) url = '/meters/temperature' - sess.post.assert_called_with(url, service=new_sample.service, + sess.post.assert_called_with(url, endpoint_filter=new_sample.service, json=[data]) self.assertIsNone(new_sample.id) diff --git a/openstack/tests/unit/telemetry/v2/test_statistics.py b/openstack/tests/unit/telemetry/v2/test_statistics.py index 2f140130c..f869ff6b4 100644 --- a/openstack/tests/unit/telemetry/v2/test_statistics.py +++ b/openstack/tests/unit/telemetry/v2/test_statistics.py @@ -68,7 +68,7 @@ class TestStatistics(testtools.TestCase): def test_list(self): sess = mock.Mock() resp = mock.Mock() - resp.body = [EXAMPLE] + resp.json = mock.Mock(return_value=[EXAMPLE]) sess.get = mock.Mock(return_value=resp) args = {'meter_name': 'example'} @@ -76,6 +76,7 @@ class TestStatistics(testtools.TestCase): url = '/meters/example/statistics' stat = next(reply) - sess.get.assert_called_with(url, service=stat.service, params={}) + sess.get.assert_called_with(url, endpoint_filter=stat.service, + params={}) self.assertEqual(EXAMPLE, stat) self.assertRaises(StopIteration, next, reply) diff --git a/openstack/tests/unit/test_connection.py b/openstack/tests/unit/test_connection.py index a3eb0abc7..dcdfa8028 100644 --- a/openstack/tests/unit/test_connection.py +++ b/openstack/tests/unit/test_connection.py @@ -16,11 +16,9 @@ import tempfile import mock import os_client_config -from openstack.auth.identity import v2 from openstack import connection from openstack import profile from openstack.tests.unit import base -from openstack import transport CONFIG_AUTH_URL = "http://127.0.0.1:5000/v2.0" @@ -42,47 +40,53 @@ clouds: class TestConnection(base.TestCase): - def setUp(self): - super(TestConnection, self).setUp() - self.xport = transport.Transport() - self.auth = v2.Token(auth_url='http://127.0.0.1/v2', token='b') - self.prof = profile.Profile() - self.conn = connection.Connection(authenticator=mock.MagicMock(), - transport=mock.MagicMock()) - self.conn.session = mock.MagicMock() + @mock.patch("openstack.session.Session") + def test_other_parameters(self, mock_session_init): + mock_session_init.return_value = mock_session_init + mock_profile = mock.Mock() + mock_profile.get_services = mock.Mock(return_value=[]) + conn = connection.Connection(profile=mock_profile, authenticator='2', + verify=True, user_agent='1') + args = {'auth': '2', 'user_agent': '1', 'verify': True} + mock_session_init.assert_called_with(mock_profile, **args) + self.assertEqual(mock_session_init, conn.session) - def test_create_transport(self): - conn = connection.Connection(authenticator='2', verify=True, - user_agent='1') - self.assertTrue(conn.transport.verify) - self.assertIn('1', conn.transport._user_agent) - - def test_create_authenticator(self): + @mock.patch("keystoneauth1.loading.base.get_plugin_loader") + def test_create_authenticator(self, mock_get_plugin): + mock_plugin = mock.Mock() + mock_loader = mock.Mock() + mock_options = [ + mock.Mock(dest="auth_url"), + mock.Mock(dest="password"), + mock.Mock(dest="username"), + ] + mock_loader.get_options = mock.Mock(return_value=mock_options) + mock_loader.load_from_options = mock.Mock(return_value=mock_plugin) + mock_get_plugin.return_value = mock_loader auth_args = { 'auth_url': '0', 'username': '1', 'password': '2', } - conn = connection.Connection(transport='0', auth_plugin='password', - **auth_args) - self.assertEqual('0', conn.authenticator.auth_url) - self.assertEqual( - '1', - conn.authenticator.auth_plugin.auth_methods[0].username) - self.assertEqual( - '2', - conn.authenticator.auth_plugin.auth_methods[0].password) + conn = connection.Connection(auth_plugin='v2password', **auth_args) + mock_get_plugin.assert_called_with('v2password') + mock_loader.load_from_options.assert_called_with(**auth_args) + self.assertEqual(mock_plugin, conn.authenticator) + + @mock.patch("keystoneauth1.loading.base.get_plugin_loader") + def test_pass_authenticator(self, mock_get_plugin): + mock_plugin = mock.Mock() + mock_get_plugin.return_value = None + conn = connection.Connection(authenticator=mock_plugin) + self.assertFalse(mock_get_plugin.called) + self.assertEqual(mock_plugin, conn.authenticator) def test_create_session(self): - args = { - 'transport': self.xport, - 'authenticator': self.auth, - 'profile': self.prof, - } - conn = connection.Connection(**args) - self.assertEqual(self.xport, conn.session.transport) - self.assertEqual(self.auth, conn.session.authenticator) - self.assertEqual(self.prof, conn.session.profile) + auth = mock.Mock() + prof = profile.Profile() + conn = connection.Connection(authenticator=auth, profile=prof) + self.assertEqual(auth, conn.authenticator) + self.assertEqual(prof, conn.profile) self.assertEqual('openstack.cluster.v1._proxy', conn.cluster.__class__.__module__) self.assertEqual('openstack.compute.v2._proxy', @@ -102,12 +106,6 @@ class TestConnection(base.TestCase): self.assertEqual('openstack.telemetry.v2._proxy', conn.telemetry.__class__.__module__) - def test_custom_user_agent(self): - user_agent = "MyProgram/1.0" - conn = connection.Connection(authenticator=self.auth, - user_agent=user_agent) - self.assertTrue(conn.transport._user_agent.startswith(user_agent)) - def _prepare_test_config(self): # Create a temporary directory where our test config will live # and insert it into the search path via OS_CLIENT_CONFIG_FILE. @@ -130,13 +128,13 @@ class TestConnection(base.TestCase): sot = connection.from_config(cloud_config=data) self.assertEqual(CONFIG_USERNAME, - sot.authenticator.auth_plugin.username) + sot.authenticator._username) self.assertEqual(CONFIG_PASSWORD, - sot.authenticator.auth_plugin.password) + sot.authenticator._password) self.assertEqual(CONFIG_AUTH_URL, - sot.authenticator.auth_plugin.auth_url) + sot.authenticator.auth_url) self.assertEqual(CONFIG_PROJECT, - sot.authenticator.auth_plugin.tenant_name) + sot.authenticator._project_name) def test_from_config_given_name(self): self._prepare_test_config() @@ -144,13 +142,13 @@ class TestConnection(base.TestCase): sot = connection.from_config(cloud_name="sample") self.assertEqual(CONFIG_USERNAME, - sot.authenticator.auth_plugin.username) + sot.authenticator._username) self.assertEqual(CONFIG_PASSWORD, - sot.authenticator.auth_plugin.password) + sot.authenticator._password) self.assertEqual(CONFIG_AUTH_URL, - sot.authenticator.auth_plugin.auth_url) + sot.authenticator.auth_url) self.assertEqual(CONFIG_PROJECT, - sot.authenticator.auth_plugin.tenant_name) + sot.authenticator._project_name) def test_from_config_given_options(self): self._prepare_test_config() @@ -162,7 +160,7 @@ class TestConnection(base.TestCase): sot = connection.from_config(cloud_name="sample", options=Opts) - pref = sot.session.profile.get_preference("compute") + pref = sot.session.profile.get_filter("compute") # NOTE: Along the way, the `v` prefix gets added so we can build # up URLs with it. diff --git a/openstack/tests/unit/test_module_loader.py b/openstack/tests/unit/test_module_loader.py deleted file mode 100644 index 710151528..000000000 --- a/openstack/tests/unit/test_module_loader.py +++ /dev/null @@ -1,56 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from openstack import exceptions -from openstack import module_loader -from openstack.tests.unit import base - - -class TestModuleLoader(base.TestCase): - def test_load_identity_v2(self): - loader = module_loader.ModuleLoader() - plugin = loader.get_auth_plugin('v2password') - self.assertEqual('openstack.auth.identity.v2', str(plugin.__module__)) - plugin = loader.get_auth_plugin('v2token') - self.assertEqual('openstack.auth.identity.v2', str(plugin.__module__)) - - def test_load_identity_v3(self): - loader = module_loader.ModuleLoader() - plugin = loader.get_auth_plugin('v3password') - self.assertEqual('openstack.auth.identity.v3', str(plugin.__module__)) - plugin = loader.get_auth_plugin('v3token') - self.assertEqual('openstack.auth.identity.v3', str(plugin.__module__)) - - def test_load_identity_discoverable(self): - plugin = module_loader.ModuleLoader().get_auth_plugin('password') - self.assertEqual('openstack.auth.identity.discoverable', - str(plugin.__module__)) - - def test_load_none(self): - plugin = module_loader.ModuleLoader().get_auth_plugin(None) - self.assertEqual('openstack.auth.identity.discoverable', - str(plugin.__module__)) - - def test_load_empty(self): - plugin = module_loader.ModuleLoader().get_auth_plugin('') - self.assertEqual('openstack.auth.identity.discoverable', - str(plugin.__module__)) - - def test_load_bogus(self): - self.assertRaises(exceptions.NoMatchingPlugin, - module_loader.ModuleLoader().get_auth_plugin, 'wot') - - def test_list_auth_plugins(self): - plugins = sorted(module_loader.ModuleLoader().list_auth_plugins()) - expected = ['password', 'token', 'v2password', 'v2token', - 'v3password', 'v3token'] - self.assertEqual(expected, plugins) diff --git a/openstack/tests/unit/test_profile.py b/openstack/tests/unit/test_profile.py index 5caf353c4..9230ade50 100644 --- a/openstack/tests/unit/test_profile.py +++ b/openstack/tests/unit/test_profile.py @@ -33,40 +33,30 @@ class TestProfile(base.TestCase): 'orchestration', 'volume', ] - self.assertEqual(expected, prof.service_names) + self.assertEqual(expected, prof.service_keys) def test_set(self): prof = profile.Profile() - self.assertEqual(None, prof.get_preference('compute')) prof.set_version('compute', 'v2') - self.assertEqual('v2', prof.get_preference('compute').version) - self.assertEqual(None, prof.get_preference('clustering')) + self.assertEqual('v2', prof.get_filter('compute').version) prof.set_version('clustering', 'v1') - self.assertEqual('v1', prof.get_preference('clustering').version) - self.assertEqual(None, prof.get_preference('database')) + self.assertEqual('v1', prof.get_filter('clustering').version) prof.set_version('database', 'v3') - self.assertEqual('v3', prof.get_preference('database').version) - self.assertEqual(None, prof.get_preference('identity')) + self.assertEqual('v3', prof.get_filter('database').version) prof.set_version('identity', 'v4') - self.assertEqual('v4', prof.get_preference('identity').version) - self.assertEqual(None, prof.get_preference('image')) + self.assertEqual('v4', prof.get_filter('identity').version) prof.set_version('image', 'v5') - self.assertEqual('v5', prof.get_preference('image').version) - self.assertEqual(None, prof.get_preference('metering')) + self.assertEqual('v5', prof.get_filter('image').version) prof.set_version('metering', 'v6') - self.assertEqual('v6', prof.get_preference('metering').version) - self.assertEqual(None, prof.get_preference('metric')) + self.assertEqual('v6', prof.get_filter('metering').version) prof.set_version('metric', 'v9') - self.assertEqual('v9', prof.get_preference('metric').version) - self.assertEqual(None, prof.get_preference('network')) + self.assertEqual('v9', prof.get_filter('metric').version) prof.set_version('network', 'v7') - self.assertEqual('v7', prof.get_preference('network').version) - self.assertEqual(None, prof.get_preference('object-store')) + self.assertEqual('v7', prof.get_filter('network').version) prof.set_version('object-store', 'v8') - self.assertEqual('v8', prof.get_preference('object-store').version) - self.assertEqual(None, prof.get_preference('orchestration')) + self.assertEqual('v8', prof.get_filter('object-store').version) prof.set_version('orchestration', 'v9') - self.assertEqual('v9', prof.get_preference('orchestration').version) + self.assertEqual('v9', prof.get_filter('orchestration').version) def test_set_version_bad_service(self): prof = profile.Profile() @@ -78,7 +68,7 @@ class TestProfile(base.TestCase): prof.set_name(prof.ALL, 'fee') prof.set_region(prof.ALL, 'fie') prof.set_interface(prof.ALL, 'public') - for service in prof.service_names: - self.assertEqual('fee', prof.get_preference(service).service_name) - self.assertEqual('fie', prof.get_preference(service).region) - self.assertEqual('public', prof.get_preference(service).interface) + for service in prof.service_keys: + self.assertEqual('fee', prof.get_filter(service).service_name) + self.assertEqual('fie', prof.get_filter(service).region) + self.assertEqual('public', prof.get_filter(service).interface) diff --git a/openstack/tests/unit/test_resource.py b/openstack/tests/unit/test_resource.py index 0fe13b551..8b30c6932 100644 --- a/openstack/tests/unit/test_resource.py +++ b/openstack/tests/unit/test_resource.py @@ -14,6 +14,8 @@ import copy import json import os +from keystoneauth1 import exceptions as ksa_exceptions +from keystoneauth1 import session import mock import requests from testtools import matchers @@ -21,7 +23,6 @@ from testtools import matchers from openstack import exceptions from openstack import format from openstack import resource -from openstack import session from openstack.tests.unit import base from openstack import utils @@ -249,7 +250,8 @@ class HeaderTests(base.TestCase): sot.ho = "johnny" sot.letsgo = "deedee" response = mock.MagicMock() - response.body = {'id': 1} + response_body = {'id': 1} + response.json = mock.Mock(return_value=response_body) sess = mock.MagicMock() sess.post = mock.MagicMock(return_value=response) sess.put = mock.MagicMock(return_value=response) @@ -257,7 +259,7 @@ class HeaderTests(base.TestCase): sot.create(sess) headers = {'guitar': 'johnny', 'bass': 'deedee'} sess.post.assert_called_with(HeaderTests.Test.base_path, - service=HeaderTests.Test.service, + endpoint_filter=HeaderTests.Test.service, headers=headers, json={}) @@ -266,7 +268,7 @@ class HeaderTests(base.TestCase): headers = {'guitar': 'johnny', 'bass': 'cj'} sot.update(sess) sess.put.assert_called_with('ramones/1', - service=HeaderTests.Test.service, + endpoint_filter=HeaderTests.Test.service, headers=headers, json={}) @@ -276,6 +278,7 @@ class ResourceTests(base.TestCase): def setUp(self): super(ResourceTests, self).setUp() self.session = mock.Mock(spec=session.Session) + self.session.get_filter = mock.Mock(return_value={}) def assertCalledURL(self, method, url): # call_args gives a tuple of *args and tuple of **kwargs. @@ -283,7 +286,9 @@ class ResourceTests(base.TestCase): self.assertEqual(method.call_args[0][0], url) def test_empty_id(self): - self.session.get.return_value = mock.Mock(body=fake_body) + resp = mock.Mock() + resp.json = mock.Mock(return_value=fake_body) + self.session.get.return_value = resp obj = FakeResource.new(**fake_arguments) self.assertEqual(obj, obj.get(self.session)) @@ -344,7 +349,7 @@ class ResourceTests(base.TestCase): service = "my_service" response = mock.MagicMock() - response.body = response_body + response.json = mock.Mock(return_value=response_body) sess = mock.MagicMock() sess.put = mock.MagicMock(return_value=response) @@ -353,7 +358,7 @@ class ResourceTests(base.TestCase): resp = FakeResource2.create_by_id(sess, attrs) self.assertEqual(response_value, resp) sess.post.assert_called_with(FakeResource2.base_path, - service=FakeResource2.service, + endpoint_filter=FakeResource2.service, json=json_body) r_id = "my_id" @@ -361,14 +366,14 @@ class ResourceTests(base.TestCase): self.assertEqual(response_value, resp) sess.put.assert_called_with( utils.urljoin(FakeResource2.base_path, r_id), - service=FakeResource2.service, + endpoint_filter=FakeResource2.service, json=json_body) path_args = {"parent_name": "my_name"} resp = FakeResource2.create_by_id(sess, attrs, path_args=path_args) self.assertEqual(response_value, resp) sess.post.assert_called_with(FakeResource2.base_path % path_args, - service=FakeResource2.service, + endpoint_filter=FakeResource2.service, json=json_body) resp = FakeResource2.create_by_id(sess, attrs, resource_id=r_id, @@ -376,7 +381,7 @@ class ResourceTests(base.TestCase): self.assertEqual(response_value, resp) sess.put.assert_called_with( utils.urljoin(FakeResource2.base_path % path_args, r_id), - service=FakeResource2.service, + endpoint_filter=FakeResource2.service, json=json_body) def test_create_without_resource_key(self): @@ -403,7 +408,7 @@ class ResourceTests(base.TestCase): service = "my_service" response = mock.MagicMock() - response.body = response_body + response.json = mock.Mock(return_value=response_body) sess = mock.MagicMock() sess.get = mock.MagicMock(return_value=response) @@ -413,7 +418,7 @@ class ResourceTests(base.TestCase): self.assertEqual(response_value, resp) sess.get.assert_called_with( utils.urljoin(FakeResource2.base_path, r_id), - service=FakeResource2.service) + endpoint_filter=FakeResource2.service) path_args = {"parent_name": "my_name"} resp = FakeResource2.get_data_by_id(sess, resource_id=r_id, @@ -421,7 +426,7 @@ class ResourceTests(base.TestCase): self.assertEqual(response_value, resp) sess.get.assert_called_with( utils.urljoin(FakeResource2.base_path % path_args, r_id), - service=FakeResource2.service) + endpoint_filter=FakeResource2.service) def test_get_data_without_resource_key(self): key = None @@ -449,19 +454,21 @@ class ResourceTests(base.TestCase): r_id = "my_id" resp = FakeResource2.head_data_by_id(sess, resource_id=r_id) self.assertEqual({'headers': response_value}, resp) + headers = {'Accept': ''} sess.head.assert_called_with( utils.urljoin(FakeResource2.base_path, r_id), - service=FakeResource2.service, - accept=None) + endpoint_filter=FakeResource2.service, + headers=headers) path_args = {"parent_name": "my_name"} resp = FakeResource2.head_data_by_id(sess, resource_id=r_id, path_args=path_args) self.assertEqual({'headers': response_value}, resp) + headers = {'Accept': ''} sess.head.assert_called_with( utils.urljoin(FakeResource2.base_path % path_args, r_id), - service=FakeResource2.service, - accept=None) + endpoint_filter=FakeResource2.service, + headers=headers) def test_head_data_without_resource_key(self): key = None @@ -482,7 +489,7 @@ class ResourceTests(base.TestCase): service = "my_service" response = mock.MagicMock() - response.body = response_body + response.json = mock.Mock(return_value=response_body) sess = mock.MagicMock() sess.patch = mock.MagicMock(return_value=response) @@ -492,7 +499,7 @@ class ResourceTests(base.TestCase): self.assertEqual(response_value, resp) sess.patch.assert_called_with( utils.urljoin(FakeResource2.base_path, r_id), - service=FakeResource2.service, + endpoint_filter=FakeResource2.service, json=json_body) path_args = {"parent_name": "my_name"} @@ -501,7 +508,7 @@ class ResourceTests(base.TestCase): self.assertEqual(response_value, resp) sess.patch.assert_called_with( utils.urljoin(FakeResource2.base_path % path_args, r_id), - service=FakeResource2.service, + endpoint_filter=FakeResource2.service, json=json_body) def test_update_without_resource_key(self): @@ -532,21 +539,24 @@ class ResourceTests(base.TestCase): r_id = "my_id" resp = FakeResource2.delete_by_id(sess, r_id) self.assertIsNone(resp) + headers = {'Accept': ''} sess.delete.assert_called_with( utils.urljoin(FakeResource2.base_path, r_id), - service=FakeResource2.service, - accept=None) + endpoint_filter=FakeResource2.service, + headers=headers) path_args = {"parent_name": "my_name"} resp = FakeResource2.delete_by_id(sess, r_id, path_args=path_args) self.assertIsNone(resp) + headers = {'Accept': ''} sess.delete.assert_called_with( utils.urljoin(FakeResource2.base_path % path_args, r_id), - service=FakeResource2.service, - accept=None) + endpoint_filter=FakeResource2.service, + headers=headers) def test_create(self): - resp = mock.Mock(body=fake_body) + resp = mock.Mock() + resp.json = mock.Mock(return_value=fake_body) self.session.post = mock.Mock(return_value=resp) obj = FakeResource.new(parent_name=fake_parent, @@ -580,7 +590,8 @@ class ResourceTests(base.TestCase): self.assertEqual(fake_attr2, obj.second) def test_get(self): - resp = mock.Mock(body=fake_body) + resp = mock.Mock() + resp.json = mock.Mock(return_value=fake_body) self.session.get = mock.Mock(return_value=resp) obj = FakeResource.get_by_id(self.session, fake_id, @@ -606,7 +617,8 @@ class ResourceTests(base.TestCase): headers = {"header1": header1, "header2": header2} - resp = mock.Mock(body=fake_body, headers=headers) + resp = mock.Mock(headers=headers) + resp.json = mock.Mock(return_value=fake_body) self.session.get = mock.Mock(return_value=resp) class FakeResource2(FakeResource): @@ -659,7 +671,8 @@ class ResourceTests(base.TestCase): class FakeResourcePatch(FakeResource): patch_update = True - resp = mock.Mock(body=fake_body) + resp = mock.Mock() + resp.json = mock.Mock(return_value=fake_body) self.session.patch = mock.Mock(return_value=resp) obj = FakeResourcePatch.new(id=fake_id, parent_name=fake_parent, @@ -693,7 +706,8 @@ class ResourceTests(base.TestCase): # This is False by default, but explicit for this test. patch_update = False - resp = mock.Mock(body=fake_body) + resp = mock.Mock() + resp.json = mock.Mock(return_value=fake_body) self.session.put = mock.Mock(return_value=resp) obj = FakeResourcePut.new(id=fake_id, parent_name=fake_parent, @@ -756,8 +770,11 @@ class ResourceTests(base.TestCase): else: body = self._get_expected_results() sentinel = [] - self.session.get.side_effect = [mock.Mock(body=body), - mock.Mock(body=sentinel)] + resp1 = mock.Mock() + resp1.json = mock.Mock(return_value=body) + resp2 = mock.Mock() + resp2.json = mock.Mock(return_value=sentinel) + self.session.get.side_effect = [resp1, resp2] objs = list(resource_class.list(self.session, path_args=fake_arguments, paginated=True)) @@ -786,8 +803,9 @@ class ResourceTests(base.TestCase): def _test_list_call_count(self, paginated): # Test that we've only made one call to receive all data results = [fake_data.copy(), fake_data.copy(), fake_data.copy()] - body = mock.Mock(body={fake_resources: results}) - attrs = {"get.return_value": body} + resp = mock.Mock() + resp.json = mock.Mock(return_value={fake_resources: results}) + attrs = {"get.return_value": resp} session = mock.Mock(**attrs) list(FakeResource.list(session, params={'limit': len(results) + 1}, @@ -812,9 +830,11 @@ class ResourceTests(base.TestCase): session = mock.MagicMock() session.get = mock.MagicMock() full_response = mock.MagicMock() - full_response.body = {FakeResource.resources_key: full_page} + response_body = {FakeResource.resources_key: full_page} + full_response.json = mock.Mock(return_value=response_body) last_response = mock.MagicMock() - last_response.body = {FakeResource.resources_key: last_page} + response_body = {FakeResource.resources_key: last_page} + last_response.json = mock.Mock(return_value=response_body) pages = [full_response, full_response, last_response] session.get.side_effect = pages @@ -832,7 +852,8 @@ class ResourceTests(base.TestCase): session = mock.Mock() session.get = mock.Mock() full_response = mock.Mock() - full_response.body = {FakeResource.resources_key: page} + response_body = {FakeResource.resources_key: page} + full_response.json = mock.Mock(return_value=response_body) pages = [full_response] session.get.side_effect = pages @@ -1152,7 +1173,9 @@ class ResourceMapping(base.TestCase): json.dumps(attrs) except TypeError as e: self.fail("Unable to serialize _attrs: %s" % e) - return mock.Mock(body=attrs) + resp = mock.Mock() + resp.json = mock.Mock(return_value=attrs) + return resp session = mock.Mock() setattr(session, session_method, mock.Mock(side_effect=fake_call)) @@ -1173,6 +1196,9 @@ class FakeResponse(object): def __init__(self, response): self.body = response + def json(self): + return self.body + class TestFind(base.TestCase): NAME = 'matrix' @@ -1188,7 +1214,7 @@ class TestFind(base.TestCase): def test_name(self): self.mock_get.side_effect = [ - exceptions.HttpException(404, 'not found'), + ksa_exceptions.http.NotFound(), FakeResponse({FakeResource.resources_key: [self.matrix]}) ] @@ -1210,7 +1236,7 @@ class TestFind(base.TestCase): self.assertEqual(self.PROP, result.prop) path = "fakes/" + fake_parent + "/data/" + self.ID - self.mock_get.assert_any_call(path, service=None) + self.mock_get.assert_any_call(path, endpoint_filter=None) def test_id_no_retrieve(self): self.mock_get.side_effect = [ @@ -1231,7 +1257,7 @@ class TestFind(base.TestCase): dupe['id'] = 'different' self.mock_get.side_effect = [ # Raise a 404 first so we get out of the ID search and into name. - exceptions.HttpException(404, 'not found'), + ksa_exceptions.http.NotFound(), FakeResponse({FakeResource.resources_key: [self.matrix, dupe]}) ] @@ -1255,11 +1281,11 @@ class TestFind(base.TestCase): p = {'ip_address': "127.0.0.1"} path = fake_path + "?limit=2" - self.mock_get.called_once_with(path, params=p, service=None) + self.mock_get.called_once_with(path, params=p, endpoint_filter=None) def test_nada(self): self.mock_get.side_effect = [ - exceptions.HttpException(404, 'not found'), + ksa_exceptions.http.NotFound(), FakeResponse({FakeResource.resources_key: []}) ] @@ -1267,7 +1293,7 @@ class TestFind(base.TestCase): def test_no_name(self): self.mock_get.side_effect = [ - exceptions.HttpException(404, 'not found'), + ksa_exceptions.http.NotFound(), FakeResponse({FakeResource.resources_key: [self.matrix]}) ] FakeResource.name_attribute = None @@ -1276,7 +1302,7 @@ class TestFind(base.TestCase): def test_nada_not_ignored(self): self.mock_get.side_effect = [ - exceptions.HttpException(404, 'not found'), + ksa_exceptions.http.NotFound(), FakeResponse({FakeResource.resources_key: []}) ] @@ -1350,7 +1376,9 @@ class TestWaitForDelete(base.TestCase): sess = mock.Mock() sot = FakeResource.new(**fake_data) sot.get = mock.MagicMock() - sot.get.side_effect = [sot, exceptions.NotFoundException(mock.Mock())] + sot.get.side_effect = [ + sot, + ksa_exceptions.http.NotFound()] self.assertEqual(sot, resource.wait_for_delete(sess, sot, 1, 2)) diff --git a/openstack/tests/unit/test_service_filter.py b/openstack/tests/unit/test_service_filter.py index 91e6d9b8a..dde2f9ac1 100644 --- a/openstack/tests/unit/test_service_filter.py +++ b/openstack/tests/unit/test_service_filter.py @@ -10,149 +10,21 @@ # License for the specific language governing permissions and limitations # under the License. -import six import testtools -from openstack import exceptions from openstack.identity import identity_service -from openstack import service_filter as filt - - -class TestServiceFilter(testtools.TestCase): - def test_minimum(self): - sot = filt.ServiceFilter() - self.assertEqual("service_type=any,interface=public", - six.text_type(sot)) - - def test_maximum(self): - sot = filt.ServiceFilter(service_type='compute', interface='admin', - region='b', service_name='c') - exp = "service_type=compute,interface=admin,region=b,service_name=c" - self.assertEqual(exp, six.text_type(sot)) - - def test_interface(self): - sot = filt.ServiceFilter(service_type='identity', interface='public') - self.assertEqual("service_type=identity,interface=public", - six.text_type(sot)) - sot = filt.ServiceFilter(service_type='identity', - interface='internal') - self.assertEqual("service_type=identity,interface=internal", - six.text_type(sot)) - sot = filt.ServiceFilter(service_type='identity', interface='admin') - self.assertEqual("service_type=identity,interface=admin", - six.text_type(sot)) - sot = filt.ServiceFilter(service_type='identity', - interface='publicURL') - self.assertEqual("service_type=identity,interface=public", - six.text_type(sot)) - sot = filt.ServiceFilter(service_type='identity', - interface='internalURL') - self.assertEqual("service_type=identity,interface=internal", - six.text_type(sot)) - sot = filt.ServiceFilter(service_type='identity', - interface='adminURL') - self.assertEqual("service_type=identity,interface=admin", - six.text_type(sot)) - self.assertRaises(exceptions.SDKException, filt.ServiceFilter, - service_type='identity', interface='b') - sot = filt.ServiceFilter(service_type='identity', interface=None) - self.assertEqual("service_type=identity", six.text_type(sot)) - - def test_match_service_type(self): - sot = filt.ServiceFilter(service_type='identity') - self.assertTrue(sot.match_service_type('identity')) - self.assertFalse(sot.match_service_type('compute')) - - def test_match_service_type_any(self): - sot = filt.ServiceFilter() - self.assertTrue(sot.match_service_type('identity')) - self.assertTrue(sot.match_service_type('compute')) - - def test_match_service_name(self): - sot = filt.ServiceFilter(service_type='identity') - self.assertTrue(sot.match_service_name('keystone')) - self.assertTrue(sot.match_service_name('ldap')) - self.assertTrue(sot.match_service_name(None)) - sot = filt.ServiceFilter(service_type='identity', - service_name='keystone') - self.assertTrue(sot.match_service_name('keystone')) - self.assertFalse(sot.match_service_name('ldap')) - self.assertFalse(sot.match_service_name(None)) - - def test_match_region(self): - sot = filt.ServiceFilter(service_type='identity') - self.assertTrue(sot.match_region('East')) - self.assertTrue(sot.match_region('West')) - self.assertTrue(sot.match_region(None)) - sot = filt.ServiceFilter(service_type='identity', region='East') - self.assertTrue(sot.match_region('East')) - self.assertFalse(sot.match_region('West')) - self.assertFalse(sot.match_region(None)) - - def test_match_interface(self): - sot = filt.ServiceFilter(service_type='identity', - interface='internal') - self.assertFalse(sot.match_interface('admin')) - self.assertTrue(sot.match_interface('internal')) - self.assertFalse(sot.match_interface('public')) - - def test_join(self): - a = filt.ServiceFilter(region='east') - b = filt.ServiceFilter(service_type='identity') - result = a.join(b) - self.assertEqual("service_type=identity,interface=public,region=east", - six.text_type(result)) - self.assertEqual("service_type=any,interface=public,region=east", - six.text_type(a)) - self.assertEqual("service_type=identity,interface=public", - six.text_type(b)) - - def test_join_interface(self): - user_preference = filt.ServiceFilter(interface='public') - service_default = filt.ServiceFilter(interface='admin') - result = user_preference.join(service_default) - self.assertEqual("public", result.interface) - user_preference = filt.ServiceFilter(interface=None) - service_default = filt.ServiceFilter(interface='admin') - result = user_preference.join(service_default) - self.assertEqual("admin", result.interface) - - def test_join_version(self): - user_preference = filt.ServiceFilter(version='v2') - service_default = filt.ServiceFilter() - self.assertEqual('v2', user_preference.join(service_default).version) - service_default = filt.ServiceFilter( - version=filt.ServiceFilter.UNVERSIONED - ) - self.assertEqual('', user_preference.join(service_default).version) - - def test_set_interface(self): - sot = filt.ServiceFilter() - sot.set_interface("PUBLICURL") - self.assertEqual('public', sot.interface) - sot.set_interface("INTERNALURL") - self.assertEqual('internal', sot.interface) - sot.set_interface("ADMINURL") - self.assertEqual('admin', sot.interface) - - def test_get_module(self): - sot = identity_service.IdentityService() - self.assertEqual('openstack.identity.v3', sot.get_module()) - self.assertEqual('identity', sot.get_service_module()) - - def test_get_version_path(self): - sot = identity_service.IdentityService() - self.assertEqual('v3', sot.get_version_path('v2')) - sot = identity_service.IdentityService(version='v2') - self.assertEqual('v2', sot.get_version_path('v3')) - sot = identity_service.IdentityService(version='v2.1') - self.assertEqual('v2.1', sot.get_version_path('v3')) - sot = identity_service.IdentityService(version='') - self.assertEqual('', sot.get_version_path('v3')) +from openstack import service_filter class TestValidVersion(testtools.TestCase): def test_constructor(self): - sot = filt.ValidVersion('v1.0', 'v1') + sot = service_filter.ValidVersion('v1.0', 'v1') self.assertEqual('v1.0', sot.module) self.assertEqual('v1', sot.path) + + +class TestServiceFilter(testtools.TestCase): + def test_get_module(self): + sot = identity_service.IdentityService() + self.assertEqual('openstack.identity.v3', sot.get_module()) + self.assertEqual('identity', sot.get_service_module()) diff --git a/openstack/tests/unit/test_session.py b/openstack/tests/unit/test_session.py index 49be47167..c1421d8e0 100644 --- a/openstack/tests/unit/test_session.py +++ b/openstack/tests/unit/test_session.py @@ -10,74 +10,26 @@ # License for the specific language governing permissions and limitations # under the License. -from openstack import service_filter +import testtools + +from openstack.image import image_service from openstack import session -from openstack.tests.unit import base -from openstack.tests.unit import fakes -class TestSession(base.TestCase): +class TestSession(testtools.TestCase): - TEST_PATH = '/test/path' - - def setUp(self): - super(TestSession, self).setUp() - self.xport = fakes.FakeTransport() - self.auth = fakes.FakeAuthenticator() - self.serv = service_filter.ServiceFilter(service_type='identity') - self.sess = session.Session(self.xport, self.auth) - self.expected = {'headers': {'X-Auth-Token': self.auth.TOKEN}} - - def test_head(self): - resp = self.sess.head(self.TEST_PATH, service=self.serv) - - self.assertEqual(self.xport.RESPONSE, resp) - self.auth.get_token.assert_called_with(self.xport) - self.auth.get_endpoint.assert_called_with(self.xport, self.serv) - url = self.auth.ENDPOINT + self.TEST_PATH - self.xport.request.assert_called_with('HEAD', url, **self.expected) - - def test_get(self): - resp = self.sess.get(self.TEST_PATH, service=self.serv) - - self.assertEqual(self.xport.RESPONSE, resp) - self.auth.get_token.assert_called_with(self.xport) - self.auth.get_endpoint.assert_called_with(self.xport, self.serv) - url = self.auth.ENDPOINT + self.TEST_PATH - self.xport.request.assert_called_with('GET', url, **self.expected) - - def test_post(self): - resp = self.sess.post(self.TEST_PATH, service=self.serv) - - self.assertEqual(self.xport.RESPONSE, resp) - self.auth.get_token.assert_called_with(self.xport) - self.auth.get_endpoint.assert_called_with(self.xport, self.serv) - url = self.auth.ENDPOINT + self.TEST_PATH - self.xport.request.assert_called_with('POST', url, **self.expected) - - def test_put(self): - resp = self.sess.put(self.TEST_PATH, service=self.serv) - - self.assertEqual(self.xport.RESPONSE, resp) - self.auth.get_token.assert_called_with(self.xport) - self.auth.get_endpoint.assert_called_with(self.xport, self.serv) - url = self.auth.ENDPOINT + self.TEST_PATH - self.xport.request.assert_called_with('PUT', url, **self.expected) - - def test_delete(self): - resp = self.sess.delete(self.TEST_PATH, service=self.serv) - - self.assertEqual(self.xport.RESPONSE, resp) - self.auth.get_token.assert_called_with(self.xport) - self.auth.get_endpoint.assert_called_with(self.xport, self.serv) - url = self.auth.ENDPOINT + self.TEST_PATH - self.xport.request.assert_called_with('DELETE', url, **self.expected) - - def test_patch(self): - resp = self.sess.patch(self.TEST_PATH, service=self.serv) - - self.assertEqual(self.xport.RESPONSE, resp) - self.auth.get_token.assert_called_with(self.xport) - self.auth.get_endpoint.assert_called_with(self.xport, self.serv) - url = self.auth.ENDPOINT + self.TEST_PATH - self.xport.request.assert_called_with('PATCH', url, **self.expected) + def test_parse_url(self): + filt = image_service.ImageService() + self.assertEqual( + "http://127.0.0.1:9292/v1", + session.parse_url(filt, "http://127.0.0.1:9292")) + self.assertEqual( + "http://127.0.0.1:9292/v2", + session.parse_url(filt, "http://127.0.0.1:9292/v2.0")) + filt.version = 'v1' + self.assertEqual( + "http://127.0.0.1:9292/v1/mytenant", + session.parse_url(filt, "http://127.0.0.1:9292/v2.0/mytenant/")) + self.assertEqual( + "http://127.0.0.1:9292/wot/v1/mytenant", + session.parse_url(filt, "http://127.0.0.1:9292/wot/v2.0/mytenant")) diff --git a/openstack/tests/unit/test_transport.py b/openstack/tests/unit/test_transport.py deleted file mode 100644 index c4b2297af..000000000 --- a/openstack/tests/unit/test_transport.py +++ /dev/null @@ -1,556 +0,0 @@ -# -*- encoding: utf-8 -*- -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json - -import mock -import requests -import requests_mock - -from openstack import exceptions -from openstack.tests.unit import base -from openstack import transport - - -fake_request = 'Now is the time...' -fake_response = 'for the quick brown fox...' -fake_response_json = '{"response": "for the quick brown fox..."}' -fake_redirect = 'redirect text' - -fake_record1 = { - 'key1': { - 'id': '123', - 'name': 'OneTwoThree', - 'random': 'qwertyuiop', - }, -} - -fake_record2 = { - 'hello': 'world', -} - - -class TestTransportBase(base.TestCase): - - TEST_URL = 'http://www.root.url' - - def assertRequestHeaderEqual(self, mocked_req, name, val): - """Verify that the last request made contains a header and its value - - """ - headers = mocked_req.last_request.headers - self.assertEqual(val, headers.get(name)) - - def assertResponseOK(self, resp, status=200, body=None): - """Verify the Response object contains expected values - - Tests our defaults for a successful request. - """ - - self.assertTrue(resp.ok) - self.assertEqual(status, resp.status_code) - if body: - self.assertEqual(body, resp.text) - - -class TestTransport(TestTransportBase): - - def setUp(self): - super(TestTransport, self).setUp() - - self._orig_user_agent = transport.USER_AGENT - self.test_user_agent = transport.USER_AGENT = "testing/1.0" - - def tearDown(self): - super(TestTransport, self).tearDown() - - transport.USER_AGENT = self._orig_user_agent - - @requests_mock.Mocker() - def test_request(self, mock_req): - mock_req.get(self.TEST_URL, text=fake_response) - req = "GET" - - xport = transport.Transport() - resp = xport.request(req, self.TEST_URL, accept=None) - - self.assertEqual(req, mock_req.last_request.method) - self.assertResponseOK(resp, body=fake_response) - - @requests_mock.Mocker() - def test_request_json(self, mock_req): - mock_req.get(self.TEST_URL, json=fake_record1) - req = "GET" - - xport = transport.Transport() - resp = xport.request(req, self.TEST_URL, accept=None) - - self.assertEqual(req, mock_req.last_request.method) - self.assertResponseOK(resp, body=json.dumps(fake_record1)) - self.assertEqual(fake_record1, resp.json()) - - @requests_mock.Mocker() - def test_delete(self, mock_req): - mock_req.delete(self.TEST_URL, text=fake_response) - - xport = transport.Transport() - resp = xport.delete(self.TEST_URL, accept=None) - - self.assertEqual("DELETE", mock_req.last_request.method) - self.assertResponseOK(resp, body=fake_response) - - @requests_mock.Mocker() - def test_get(self, mock_req): - mock_req.get(self.TEST_URL, text=fake_response) - - xport = transport.Transport() - resp = xport.get(self.TEST_URL, accept=None) - - self.assertEqual("GET", mock_req.last_request.method) - self.assertResponseOK(resp, body=fake_response) - - @requests_mock.Mocker() - def test_head(self, mock_req): - mock_req.head(self.TEST_URL, text="") - - xport = transport.Transport() - resp = xport.head(self.TEST_URL, accept=None) - - self.assertEqual("HEAD", mock_req.last_request.method) - self.assertResponseOK(resp, body='') - - @requests_mock.Mocker() - def test_patch(self, mock_req): - mock_req.patch(self.TEST_URL, text=fake_response_json) - - xport = transport.Transport() - resp = xport.patch(self.TEST_URL, json=fake_record2) - - self.assertEqual("PATCH", mock_req.last_request.method) - self.assertEqual( - json.dumps(fake_record2), - mock_req.last_request.body, - ) - self.assertResponseOK(resp, body=fake_response_json) - - @requests_mock.Mocker() - def test_post(self, mock_req): - mock_req.post(self.TEST_URL, text=fake_response_json) - - xport = transport.Transport() - resp = xport.post(self.TEST_URL, json=fake_record2) - - self.assertEqual("POST", mock_req.last_request.method) - self.assertEqual( - json.dumps(fake_record2), - mock_req.last_request.body, - ) - self.assertResponseOK(resp, body=fake_response_json) - - @requests_mock.Mocker() - def test_put(self, mock_req): - mock_req.put(self.TEST_URL, text=fake_response) - - xport = transport.Transport() - resp = xport.put(self.TEST_URL, data=fake_request, accept=None) - - self.assertEqual("PUT", mock_req.last_request.method) - self.assertEqual( - fake_request, - mock_req.last_request.body, - ) - self.assertResponseOK(resp, body=fake_response) - - @requests_mock.Mocker() - def test_put_json(self, mock_req): - mock_req.put(self.TEST_URL, text=fake_response_json) - - xport = transport.Transport() - resp = xport.put(self.TEST_URL, json=fake_record2) - - self.assertEqual("PUT", mock_req.last_request.method) - self.assertEqual( - json.dumps(fake_record2), - mock_req.last_request.body, - ) - self.assertResponseOK(resp, body=fake_response_json) - - @requests_mock.Mocker() - def test_request_accept(self, mock_req): - fake_record1_str = json.dumps(fake_record1) - mock_req.post(self.TEST_URL, text=fake_record1_str) - - xport = transport.Transport() - resp = xport.post(self.TEST_URL, json=fake_record2, accept=None) - - self.assertRequestHeaderEqual(mock_req, 'Accept', '*/*') - self.assertEqual(fake_record1, resp.json()) - - resp = xport.post(self.TEST_URL, json=fake_record2, - accept=transport.JSON) - - self.assertRequestHeaderEqual(mock_req, 'Accept', transport.JSON) - self.assertEqual(fake_record1, resp.json()) - - xport = transport.Transport(accept=transport.JSON) - resp = xport.post(self.TEST_URL, json=fake_record2) - - self.assertRequestHeaderEqual(mock_req, 'Accept', transport.JSON) - self.assertEqual(fake_record1, resp.json()) - - @requests_mock.Mocker() - def test_request_status_202(self, mock_req): - mock_req.put(self.TEST_URL, text='', status_code=202) - - xport = transport.Transport() - resp = xport.put(self.TEST_URL, json=fake_record2) - - self.assertEqual(202, resp.status_code) - self.assertEqual({}, resp.body) - - @requests_mock.Mocker() - def test_user_agent_no_arg(self, mock_req): - mock_req.get(self.TEST_URL, text=fake_response) - - xport = transport.Transport() - resp = xport.get(self.TEST_URL, accept=None) - - self.assertTrue(resp.ok) - self.assertRequestHeaderEqual(mock_req, 'User-Agent', - self.test_user_agent) - - resp = xport.get(self.TEST_URL, headers={'User-Agent': None}, - accept=None) - - self.assertTrue(resp.ok) - self.assertRequestHeaderEqual(mock_req, 'User-Agent', None) - - resp = xport.get(self.TEST_URL, user_agent=None, accept=None) - - self.assertTrue(resp.ok) - self.assertRequestHeaderEqual(mock_req, 'User-Agent', - self.test_user_agent) - - resp = xport.get(self.TEST_URL, headers={'User-Agent': ''}, - accept=None) - - self.assertTrue(resp.ok) - self.assertRequestHeaderEqual(mock_req, 'User-Agent', '') - - resp = xport.get(self.TEST_URL, user_agent='', accept=None) - - self.assertTrue(resp.ok) - self.assertRequestHeaderEqual(mock_req, 'User-Agent', - self.test_user_agent) - - new_agent = 'new-agent' - resp = xport.get(self.TEST_URL, headers={'User-Agent': new_agent}, - accept=None) - - self.assertTrue(resp.ok) - self.assertRequestHeaderEqual(mock_req, 'User-Agent', new_agent) - - resp = xport.get(self.TEST_URL, user_agent=new_agent, accept=None) - - self.assertTrue(resp.ok) - self.assertRequestHeaderEqual(mock_req, 'User-Agent', '%s %s' % ( - new_agent, self.test_user_agent)) - - custom_value = 'new-agent' - resp = xport.get(self.TEST_URL, headers={'User-Agent': custom_value}, - user_agent=None, accept=None) - - self.assertTrue(resp.ok) - self.assertRequestHeaderEqual(mock_req, 'User-Agent', custom_value) - - override = 'overrides-agent' - resp = xport.get(self.TEST_URL, headers={'User-Agent': None}, - user_agent=override, accept=None) - - self.assertTrue(resp.ok) - self.assertRequestHeaderEqual(mock_req, 'User-Agent', '%s %s' % ( - override, self.test_user_agent)) - - resp = xport.get(self.TEST_URL, headers={'User-Agent': custom_value}, - user_agent=override, accept=None) - - self.assertTrue(resp.ok) - self.assertRequestHeaderEqual(mock_req, 'User-Agent', '%s %s' % ( - override, self.test_user_agent)) - - @requests_mock.Mocker() - def test_user_agent_arg_none(self, mock_req): - # None gets converted to the transport.USER_AGENT by default. - mock_req.get(self.TEST_URL, text=fake_response) - - xport = transport.Transport(user_agent=None) - resp = xport.get(self.TEST_URL, accept=None) - - self.assertTrue(resp.ok) - self.assertRequestHeaderEqual(mock_req, 'User-Agent', - self.test_user_agent) - - @requests_mock.Mocker() - def test_user_agent_arg_default(self, mock_req): - mock_req.get(self.TEST_URL, text=fake_response) - - agent = 'test-agent' - xport = transport.Transport(user_agent=agent) - resp = xport.get(self.TEST_URL, accept=None) - - self.assertTrue(resp.ok) - self.assertRequestHeaderEqual(mock_req, 'User-Agent', '%s %s' % ( - agent, self.test_user_agent)) - - def test_verify_no_arg(self): - xport = transport.Transport() - self.assertTrue(xport.verify) - - def test_verify_arg_false(self): - xport = transport.Transport(verify=False) - self.assertFalse(xport.verify) - - def test_verify_arg_true(self): - xport = transport.Transport(verify=True) - self.assertTrue(xport.verify) - - def test_verify_arg_file(self): - xport = transport.Transport(verify='ca-file') - self.assertEqual('ca-file', xport.verify) - - @requests_mock.Mocker() - def test_not_found(self, mock_req): - xport = transport.Transport() - status = 404 - - mock_req.get(self.TEST_URL, status_code=status) - - exc = self.assertRaises(exceptions.NotFoundException, xport.get, - self.TEST_URL) - self.assertEqual(status, exc.status_code) - - @requests_mock.Mocker() - def test_server_error(self, mock_req): - xport = transport.Transport() - status = 500 - - mock_req.get(self.TEST_URL, status_code=500) - - exc = self.assertRaises(exceptions.HttpException, xport.get, - self.TEST_URL) - self.assertEqual(status, exc.status_code) - - -class TestTransportRedirects(TestTransportBase): - - REDIRECT_CHAIN = [ - 'http://myhost:3445/', - 'http://anotherhost:6555/', - 'http://thirdhost/', - 'http://finaldestination:55/', - ] - - def setup_redirects(self, mocked_req, method="GET", status_code=305, - redirect_kwargs=None, final_kwargs=None): - if redirect_kwargs is None: - redirect_kwargs = {} - - if final_kwargs is None: - final_kwargs = {} - - redirect_kwargs.setdefault('text', fake_redirect) - - for s, d in zip(self.REDIRECT_CHAIN, self.REDIRECT_CHAIN[1:]): - mocked_req.register_uri(method, s, status_code=status_code, - headers={"location": d}, **redirect_kwargs) - - final_kwargs.setdefault('status_code', 200) - final_kwargs.setdefault('text', fake_response) - mocked_req.register_uri(method, self.REDIRECT_CHAIN[-1], - **final_kwargs) - - @requests_mock.Mocker() - def test_get_redirect(self, mock_req): - self.setup_redirects(mock_req) - - xport = transport.Transport() - resp = xport.get(self.REDIRECT_CHAIN[-2], accept=None) - - self.assertResponseOK(resp, body=fake_response) - - @requests_mock.Mocker() - def test_get_redirect_json(self, mock_req): - self.setup_redirects(mock_req, - final_kwargs={'text': fake_response_json}) - - xport = transport.Transport() - resp = xport.get(self.REDIRECT_CHAIN[-2]) - - self.assertResponseOK(resp, body=fake_response_json) - - @requests_mock.Mocker() - def test_post_keeps_correct_method(self, mock_req): - self.setup_redirects(mock_req, method="POST", status_code=301) - - xport = transport.Transport() - resp = xport.post(self.REDIRECT_CHAIN[-2], accept=None) - - self.assertResponseOK(resp, body=fake_response) - - @requests_mock.Mocker() - def test_post_keeps_correct_method_json(self, mock_req): - self.setup_redirects(mock_req, method="POST", status_code=301, - final_kwargs={'text': fake_response_json}) - - xport = transport.Transport() - resp = xport.post(self.REDIRECT_CHAIN[-2]) - - self.assertResponseOK(resp, body=fake_response_json) - - @requests_mock.Mocker() - def test_redirect_forever(self, mock_req): - self.setup_redirects(mock_req) - - xport = transport.Transport() - resp = xport.get(self.REDIRECT_CHAIN[0], accept=None) - - self.assertResponseOK(resp) - # Request history length is 1 less than the source chain due to the - # last response not being a redirect and not added to the history. - self.assertEqual(len(self.REDIRECT_CHAIN) - 1, len(resp.history)) - - @requests_mock.Mocker() - def test_redirect_forever_json(self, mock_req): - self.setup_redirects(mock_req, - final_kwargs={'text': fake_response_json}) - - xport = transport.Transport() - resp = xport.get(self.REDIRECT_CHAIN[0]) - - self.assertResponseOK(resp) - # Request history length is 1 less than the source chain due to the - # last response not being a redirect and not added to the history. - self.assertEqual(len(self.REDIRECT_CHAIN) - 1, len(resp.history)) - - @requests_mock.Mocker() - def test_no_redirect(self, mock_req): - self.setup_redirects(mock_req) - - xport = transport.Transport(redirect=False) - resp = xport.get(self.REDIRECT_CHAIN[0], accept=None) - - self.assertEqual(305, resp.status_code) - self.assertEqual(self.REDIRECT_CHAIN[0], resp.url) - - @requests_mock.Mocker() - def test_no_redirect_json(self, mock_req): - self.setup_redirects(mock_req, - final_kwargs={'text': fake_response_json}) - - xport = transport.Transport(redirect=False) - self.assertRaises(exceptions.InvalidResponse, xport.get, - self.REDIRECT_CHAIN[0]) - - @requests_mock.Mocker() - def test_redirect_limit(self, mock_req): - self.setup_redirects(mock_req) - - for i in (1, 2): - xport = transport.Transport(redirect=i) - resp = xport.get(self.REDIRECT_CHAIN[0], accept=None) - - self.assertResponseOK(resp, status=305, body=fake_redirect) - self.assertEqual(self.REDIRECT_CHAIN[i], resp.url) - - @requests_mock.Mocker() - def test_redirect_limit_json(self, mock_req): - self.setup_redirects(mock_req, - final_kwargs={'text': fake_response_json}) - - for i in (1, 2): - xport = transport.Transport(redirect=i) - self.assertRaises(exceptions.InvalidResponse, xport.get, - self.REDIRECT_CHAIN[0]) - - @requests_mock.Mocker() - def test_history_matches_requests(self, mock_req): - self.setup_redirects(mock_req, status_code=301) - - xport = transport.Transport(redirect=True, accept=None) - req_resp = requests.get(self.REDIRECT_CHAIN[0], allow_redirects=True) - resp = xport.get(self.REDIRECT_CHAIN[0]) - - self.assertEqual(type(resp.history), type(req_resp.history)) - self.assertEqual(len(resp.history), len(req_resp.history)) - - for r, s in zip(req_resp.history, resp.history): - self.assertEqual(s.url, r.url) - self.assertEqual(s.status_code, r.status_code) - - @requests_mock.Mocker() - def test_history_matches_requests_json(self, mock_req): - self.setup_redirects(mock_req, status_code=301, - final_kwargs={'text': fake_response_json}) - - xport = transport.Transport(redirect=True) - req_resp = requests.get(self.REDIRECT_CHAIN[0], allow_redirects=True) - resp = xport.get(self.REDIRECT_CHAIN[0]) - - self.assertEqual(type(resp.history), type(req_resp.history)) - self.assertEqual(len(resp.history), len(req_resp.history)) - - for r, s in zip(req_resp.history, resp.history): - self.assertEqual(s.url, r.url) - self.assertEqual(s.status_code, r.status_code) - - def test_parse_error_response(self): - xport = transport.Transport(redirect=True) - resp = mock.Mock() - resp.json = mock.Mock() - resp.json.return_value = {"badRequest": {"message": "Not defined"}} - self.assertEqual("Not defined", xport._parse_error_response(resp)) - resp.json.return_value = {"message": {"response": "Not Allowed"}} - self.assertEqual("Not Allowed", xport._parse_error_response(resp)) - resp.json.return_value = {"itemNotFound": {"message": "Not found"}} - self.assertEqual("Not found", xport._parse_error_response(resp)) - resp.json.return_value = {"instanceFault": {"message": "Wot?"}} - self.assertEqual("Wot?", xport._parse_error_response(resp)) - resp.json.return_value = {"QuantumError": "Network error"} - self.assertEqual("Network error", xport._parse_error_response(resp)) - - -class TestLogging(base.TestCase): - METHOD = 'PUT' - URL = 'http://example.com/' - - def setUp(self): - super(TestLogging, self).setUp() - self.xport = transport.Transport() - mock_logger = mock.Mock() - mock_logger.isEnabledFor = mock.Mock() - mock_logger.isEnabledFor.return_value = True - self.mock_debug = mock.Mock() - mock_logger.debug = self.mock_debug - transport._logger = mock_logger - self.expected = (u"REQ: curl -i -X '%s' '%s'" % - (self.METHOD, self.URL)) - - def test_data(self): - self.xport._log_request(self.METHOD, self.URL, data="payload", - headers={}) - self.mock_debug.assert_called_with(self.expected + " --data 'payload'") - - def test_unicode(self): - self.xport._log_request(self.METHOD, self.URL, data=u'拱心石', - headers={}) - self.mock_debug.assert_called_with(self.expected + u" --data '拱心石'") diff --git a/openstack/tests/unit/test_utils.py b/openstack/tests/unit/test_utils.py index ae7734399..bc521436f 100644 --- a/openstack/tests/unit/test_utils.py +++ b/openstack/tests/unit/test_utils.py @@ -25,7 +25,7 @@ class Test_enable_logging(testtools.TestCase): utils.enable_logging(debug=debug, stream=stream) - self.assertEqual(the_logger.addHandler.call_count, 1) + self.assertEqual(the_logger.addHandler.call_count, 2) the_logger.setLevel.assert_called_with(level) def _file_tests(self, fake_logging, level, debug): @@ -36,7 +36,7 @@ class Test_enable_logging(testtools.TestCase): utils.enable_logging(debug=debug, path=fake_path) fake_logging.FileHandler.assert_called_with(fake_path) - self.assertEqual(the_logger.addHandler.call_count, 1) + self.assertEqual(the_logger.addHandler.call_count, 2) the_logger.setLevel.assert_called_with(level) def test_none(self): diff --git a/openstack/transport.py b/openstack/transport.py deleted file mode 100644 index d10e0ddf8..000000000 --- a/openstack/transport.py +++ /dev/null @@ -1,406 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -The :class:`~openstack.transport.Transport` is a subclass of -``requests.Session`` that adds some features that are common in OpenStack -APIs or can be globally controlled by an application. Its use is incredibly -similar to ``requests.Session`` such that we only will cover the differences -in detail here. - -The common OpenStack functionality added include: - -* Log all requests and responses at debug level. -* Support json encoding in the request() method. -* Set the default user_agent at Transport creation. If it is set to None to - skip the header. -* Set the default verify at Transport creation. - -Examples --------- - -Basic HTTP GET -~~~~~~~~~~~~~~ - -Making a basic HTTP GET call is very simple:: - - from openstack import transport - trans = transport.Transport() - versions = trans.get('http://cloud.example.com:5000').json() - -will retrieve the version data served by the Identity API into a Python dict. - -HTTP POST -~~~~~~~~~ - -Creating a new object in an OpenStack service is similarly simple:: - - from openstack import transport - trans = transport.Transport() - new_record = {'name': 'The White Albumn', 'artist': 'The Beatles'} - resp = trans.post('http://cloud.example.com:4999/record', json=new_record) - -Passing in the new_record dict with the ``json`` keyword argument performs the -``json.dumps()`` prior to the request being sent. This is an addition to -the capabilities of ``requests.Session``. - -Additional HTTP Methods -~~~~~~~~~~~~~~~~~~~~~~~ - -Just as in ``requests.Session``, all of the HTTP verbs have corresponding -methods in the :class:`~openstack.transport.Transport` object. - -SSL/TLS and Certificates -~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``verify`` argument to ``Transport.request()`` can now be set when the -Transport object is created. It can still be overwritten during any -individual call to ``request()`` or the HTTP verb methods. - -To set the default hostname verification for the Transport to use a custom -CA certificate file:: - - from openstack import transport - trans = transport.Transport(verify='/etc/tls/local-ca-certs.crt') - -The same usage from ``requests`` is still available. To use the default CA -certificate file for a single request:: - - versions = trans.get('https://cloud.example.com:5000', verify=True) - -Or hit on a host with a self-signed certificate:: - - versions = trans.get('https://cloud.example.com:5000', verify=None) - -Redirection -~~~~~~~~~~~ - -Redirection handling differs from ``requests`` by default as this module is -expected to be primarily used for querying REST API servers. The redirection -model differs in that ``requests`` follows some browser patterns where it -will redirect POSTs as GETs for certain statuses which is not want we want -for an API. - -See: https://en.wikipedia.org/wiki/Post/Redirect/Get - -User-Agent -~~~~~~~~~~ - -User-Agent handling as constructed by this class follows -`RFC 7231 Section 5.5.3 `_. -A well-formed user-agent is constructed on name/version product identifiers, -such that ``MyProgram/1.0`` is a proper user-agent. - -* The default :attr:`~openstack.transport.USER_AGENT` contains - the SDK version as well as RFC-compliant values from - ``requests.utils.default_user_agent``, including versions of ``requests``, - Python, and the operating system. -* Any ``user_agent`` argument passed when creating a - :class:`~openstack.transport.Transport` is prepended to the default. -* Any ``user_agent`` passed in a - :meth:`~openstack.transport.Transport.request` call is prepended - to one used for that ``Transport`` instance. -* Any string passed as the ``User-Agent`` in a dictionary of - headers to :meth:`~openstack.transport.Transport.request` will be - used directly. If at the same time a ``user_agent`` argument has been passed - to ``request()``, it will be used and follows the rules above. - -""" - -import json -import logging - -import requests -import six -from six.moves import urllib - -import openstack -from openstack import exceptions - -#: Default value for the HTTP User-Agent header. The default includes the -#: version information of the SDK as well as ``requests``, Python, -#: and the operating system. -USER_AGENT = "openstacksdk/%s %s" % ( - openstack.__version__, requests.utils.default_user_agent()) - -_logger = logging.getLogger(__name__) -JSON = 'application/json' - - -class Transport(requests.Session): - - REDIRECT_STATUSES = (301, 302, 303, 305, 307) - DEFAULT_REDIRECT_LIMIT = 30 - - def __init__( - self, - user_agent=None, - verify=True, - redirect=DEFAULT_REDIRECT_LIMIT, - accept=JSON, - ): - """Create a new :class:`~openstack.transport.Transport` object. - - In addition to those listed below, all arguments available to - ``requests.Session`` are available here: - - :param string user_agent: Set the ``User-Agent`` header. When - no value is provided, the default of - :attr:`~openstack.transport.USER_AGENT` - will be used. When a value is provided, - it will be prepended to - :attr:`~openstack.transport.USER_AGENT`. - :param boolean/string verify: If ``True``, the SSL cert will be - verified. A CA_BUNDLE path can also be - provided. - :param boolean/integer redirect: (integer) The maximum number of - redirections followed in a request. - (boolean) No redirections if False, - requests.Session handles redirection - if True. (optional) - :param string accept: Type of output to accept - - """ - - super(Transport, self).__init__() - - # Per RFC 7231 Section 5.5.3, identifiers in a user-agent should - # be ordered by decreasing significance. If a user sets their product, - # we prepend it to the SDK version and then the Python version. - if user_agent is None: - self._user_agent = USER_AGENT - else: - self._user_agent = "%s %s" % (user_agent, USER_AGENT) - - self.verify = verify - self._redirect = redirect - self._accept = accept - - def request(self, method, url, redirect=None, **kwargs): - """Send a request - - Perform an HTTP request. The following arguments differ from - ``requests.Session``: - - :param string method: Request HTTP method - :param string url: Request URL - :param boolean/integer redirect: (integer) The maximum number of - redirections followed in a request. - (boolean) No redirections if False, - requests.Session handles redirection - if True. (optional) - - The following additional keyword arguments are supported: - - :param object json: Request body to be encoded as JSON - Overwrites ``data`` argument if present - :param string accept: Set the ``Accept`` header; overwrites any value - that may be in the headers dict. Header is - omitted if ``None``. - :param string user_agent: Prepend an additional value to the existing - ``User-Agent`` header. - - Remaining keyword arguments from requests.Session.request() supported - """ - - headers = kwargs.setdefault('headers', {}) - - # JSON-encode the data in json arg if present - # Overwrites any existing 'data' value - json_data = kwargs.pop('json', None) - if json_data is not None: - kwargs['data'] = json.dumps(json_data) - headers['Content-Type'] = JSON - - # Prepend the caller's user_agent to User-Agent header if included, - # or use the default that this transport was created with. - # Note: Only attempt to work with strings and avoid needlessly - # concatenating an empty string. - user_agent = kwargs.pop('user_agent', None) - if isinstance(user_agent, six.string_types) and user_agent != '': - headers['User-Agent'] = '%s %s' % (user_agent, self._user_agent) - elif 'User-Agent' in headers: - # If they've specified their own headers with a User-Agent, - # use that directly. - pass - else: - headers.setdefault('User-Agent', self._user_agent) - - if redirect is None: - redirect = self._redirect - - if isinstance(redirect, bool) and redirect: - # Fall back to requests redirect handling - kwargs['allow_redirects'] = True - else: - # Force disable requests redirect handling, we will manage - # redirections below - kwargs['allow_redirects'] = False - if 'accept' in kwargs: - accept = kwargs.pop('accept') - else: - accept = self._accept - if accept: - headers.setdefault('Accept', accept) - - resp = self._send_request(method, url, redirect, **kwargs) - - try: - resp.raise_for_status() - except requests.RequestException as e: - if resp.status_code == 404: - exc_type = exceptions.NotFoundException - else: - exc_type = exceptions.HttpException - - raise exc_type(six.text_type(e), - details=self._parse_error_response(resp), - status_code=resp.status_code) - - if resp.status_code == 202: - resp.body = {} - elif accept == JSON: - try: - resp.body = resp.json() - except ValueError as e: - # this may be simplejson.decode.JSONDecodeError - # Re-raise into our own exception - raise exceptions.InvalidResponse(response=resp) - - return resp - - def _send_request(self, method, url, redirect, **kwargs): - # NOTE(jamielennox): We handle redirection manually because the - # requests lib follows some browser patterns where it will redirect - # POSTs as GETs for certain statuses which is not want we want for an - # API. See: https://en.wikipedia.org/wiki/Post/Redirect/Get - - self._log_request(method, url, **kwargs) - - resp = super(Transport, self).request(method, url, **kwargs) - - self._log_response(resp) - - if resp.status_code in self.REDIRECT_STATUSES: - # Be careful here in python True == 1 and False == 0 - if isinstance(redirect, bool): - redirect_allowed = redirect - else: - redirect -= 1 - redirect_allowed = redirect >= 0 - - if redirect_allowed: - try: - location = resp.headers['location'] - except KeyError: - _logger.warn( - "Redirection from %s failed, no location provided", - resp.url, - ) - else: - new_resp = self._send_request( - method, - location, - redirect, - **kwargs - ) - - new_resp.history = list(new_resp.history) - new_resp.history.insert(0, resp) - resp = new_resp - - return resp - - def _parse_error_response(self, resp): - try: - jresp = resp.json() - # compute - if "badRequest" in jresp and "message" in jresp["badRequest"]: - return jresp["badRequest"]["message"] - # identity - if "message" in jresp and "response" in jresp["message"]: - return jresp["message"]["response"] - # network - if "QuantumError" in jresp: - return jresp["QuantumError"] - # database - if "itemNotFound" in jresp and "message" in jresp["itemNotFound"]: - return jresp["itemNotFound"]["message"] - if "instanceFault" in jresp: - if "message" in jresp["instanceFault"]: - return jresp["instanceFault"]["message"] - except ValueError: - pass - return resp.text - - def _log_request(self, method, url, **kwargs): - if not _logger.isEnabledFor(logging.DEBUG): - return - - if 'params' in kwargs and kwargs['params']: - url += '?' + urllib.parse.urlencode(kwargs['params']) - - string_parts = [ - "curl -i", - "-X '%s'" % method, - "'%s'" % url, - ] - - # kwargs overrides the default - if (('verify' in kwargs and kwargs['verify'] is False) or - not self.verify): - string_parts.append('--insecure') - - for element in kwargs['headers'].items(): - header = " -H '%s: %s'" % element - string_parts.append(header) - - if 'data' in kwargs and kwargs['data'] is not None: - string_parts.append("--data") - - data = kwargs['data'] - # Only log text strings and byte strings that can be decoded - # in ascii. Raw byte strings and files both mess up the actual - # writing of the data to any log stream because we'd be mixing - # text and bytes, and they are generally overly long strings - # that would make the logs unreadable anyway. - if isinstance(data, six.binary_type): - # Some data, such as auth creds, is generally decodable - # to ascii. If it works, log it, otherwise put in a - # placeholder to specify that it's a blob of binary data. - try: - data = data.decode("ascii") - except UnicodeDecodeError: - data = "" - elif getattr(data, 'read', False): - data = "" - - string_parts.append("'" + data + "'") - - _logger.debug("REQ: %s" % " ".join(string_parts)) - - def _log_response(self, response): - _logger.debug( - "RESP: [%s] %r" % ( - response.status_code, - response.headers, - ), - ) - if response._content_consumed: - _logger.debug( - "RESP BODY: %s", - response.text, - ) - _logger.debug( - "encoding: %s", - response.encoding, - ) diff --git a/openstack/utils.py b/openstack/utils.py index f91eb4119..56eedee37 100644 --- a/openstack/utils.py +++ b/openstack/utils.py @@ -40,6 +40,7 @@ def enable_logging(debug=False, path=None, stream=None): raise ValueError("path and/or stream must be set") logger = logging.getLogger('openstack') + ksalog = logging.getLogger('keystoneauth') formatter = logging.Formatter( '%(asctime)s %(levelname)s: %(name)s %(message)s') @@ -47,13 +48,16 @@ def enable_logging(debug=False, path=None, stream=None): console = logging.StreamHandler(stream) console.setFormatter(formatter) logger.addHandler(console) + ksalog.addHandler(console) if path is not None: file_handler = logging.FileHandler(path) file_handler.setFormatter(formatter) logger.addHandler(file_handler) + ksalog.addHandler(file_handler) logger.setLevel(logging.DEBUG if debug else logging.WARNING) + ksalog.setLevel(logging.DEBUG if debug else logging.WARNING) def urljoin(*args): diff --git a/requirements.txt b/requirements.txt index d51ca5533..9bd067042 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ six>=1.9.0 stevedore>=1.5.0 # Apache-2.0 oslo.utils>=2.8.0 # Apache-2.0 os-client-config!=1.6.2,>=1.4.0 +keystoneauth1>=1.0.0 diff --git a/setup.cfg b/setup.cfg index 0e87f6175..d5e4a74dc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -48,12 +48,3 @@ output_file = openstack/locale/python-openstacksdk.pot [wheel] universal = 1 - -[entry_points] -openstack.auth.plugin = - v2password = openstack.auth.identity.v2:Password - v2token = openstack.auth.identity.v2:Token - v3password = openstack.auth.identity.v3:Password - v3token = openstack.auth.identity.v3:Token - password = openstack.auth.identity.discoverable:Auth - token = openstack.auth.identity.discoverable:Auth