Keystone auth integration

Keystoneauth integration.

Change-Id: I692705731c02b2c3abf1643c5b12dfa1c7da05cf
This commit is contained in:
TerryHowe 2015-05-27 13:46:26 -06:00 committed by Terry Howe
parent dadb1c0f3c
commit a0e791b775
89 changed files with 602 additions and 4580 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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}}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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 = {

View File

@ -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.

View File

@ -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."""

View File

@ -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'],

View File

@ -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)

View File

@ -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):

View File

@ -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):

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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}

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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 ''

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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
},
}

View File

@ -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())

View File

@ -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)

View File

@ -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)

View File

@ -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={})

View File

@ -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))

View File

@ -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)

View File

@ -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)

View File

@ -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"

View File

@ -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}

View File

@ -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)

View File

@ -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])

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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))

View File

@ -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': ''})

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -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")

View File

@ -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):

View File

@ -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')

View File

@ -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}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -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))

View File

@ -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())

View File

@ -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"))

View File

@ -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 '拱心石'")

View File

@ -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):

View File

@ -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 <http://tools.ietf.org/html/rfc7231#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 = "<binary data>"
elif getattr(data, 'read', False):
data = "<file 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,
)

View File

@ -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):

View File

@ -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

View File

@ -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