Merge "remove oslo-incubator apiclient"
This commit is contained in:
		@@ -1,45 +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.
 | 
			
		||||
 | 
			
		||||
"""oslo.i18n integration module.
 | 
			
		||||
 | 
			
		||||
See http://docs.openstack.org/developer/oslo.i18n/usage.html
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import oslo_i18n
 | 
			
		||||
 | 
			
		||||
    # NOTE(dhellmann): This reference to o-s-l-o will be replaced by the
 | 
			
		||||
    # application name when this module is synced into the separate
 | 
			
		||||
    # repository. It is OK to have more than one translation function
 | 
			
		||||
    # using the same domain, since there will still only be one message
 | 
			
		||||
    # catalog.
 | 
			
		||||
    _translators = oslo_i18n.TranslatorFactory(domain='keystoneclient')
 | 
			
		||||
 | 
			
		||||
    # The primary translation function using the well-known name "_"
 | 
			
		||||
    _ = _translators.primary
 | 
			
		||||
 | 
			
		||||
    # Translators for log levels.
 | 
			
		||||
    #
 | 
			
		||||
    # The abbreviated names are meant to reflect the usual use of a short
 | 
			
		||||
    # name like '_'. The "L" is for "log" and the other letter comes from
 | 
			
		||||
    # the level.
 | 
			
		||||
    _LI = _translators.log_info
 | 
			
		||||
    _LW = _translators.log_warning
 | 
			
		||||
    _LE = _translators.log_error
 | 
			
		||||
    _LC = _translators.log_critical
 | 
			
		||||
except ImportError:
 | 
			
		||||
    # NOTE(dims): Support for cases where a project wants to use
 | 
			
		||||
    # code from oslo-incubator, but is not ready to be internationalized
 | 
			
		||||
    # (like tempest)
 | 
			
		||||
    _ = _LI = _LW = _LE = _LC = lambda x: x
 | 
			
		||||
@@ -1,22 +0,0 @@
 | 
			
		||||
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from debtcollector import removals
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
removals.removed_module('keystoneclient.openstack.common.apiclient',
 | 
			
		||||
                        version='0.7.1',
 | 
			
		||||
                        removal_version='2.0')
 | 
			
		||||
@@ -1,235 +0,0 @@
 | 
			
		||||
# Copyright 2013 OpenStack Foundation
 | 
			
		||||
# Copyright 2013 Spanish National Research Council.
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
# E0202: An attribute inherited from %s hide this method
 | 
			
		||||
# pylint: disable=E0202
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
#
 | 
			
		||||
# THIS MODULE IS DEPRECATED
 | 
			
		||||
#
 | 
			
		||||
# Please refer to
 | 
			
		||||
# https://etherpad.openstack.org/p/kilo-keystoneclient-library-proposals for
 | 
			
		||||
# the discussion leading to this deprecation.
 | 
			
		||||
#
 | 
			
		||||
# We recommend checking out the python-openstacksdk project
 | 
			
		||||
# (https://launchpad.net/python-openstacksdk) instead.
 | 
			
		||||
#
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
import abc
 | 
			
		||||
import argparse
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
from stevedore import extension
 | 
			
		||||
 | 
			
		||||
from keystoneclient.openstack.common.apiclient import exceptions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
_discovered_plugins = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def discover_auth_systems():
 | 
			
		||||
    """Discover the available auth-systems.
 | 
			
		||||
 | 
			
		||||
    This won't take into account the old style auth-systems.
 | 
			
		||||
    """
 | 
			
		||||
    global _discovered_plugins
 | 
			
		||||
    _discovered_plugins = {}
 | 
			
		||||
 | 
			
		||||
    def add_plugin(ext):
 | 
			
		||||
        _discovered_plugins[ext.name] = ext.plugin
 | 
			
		||||
 | 
			
		||||
    ep_namespace = "keystoneclient.openstack.common.apiclient.auth"
 | 
			
		||||
    mgr = extension.ExtensionManager(ep_namespace)
 | 
			
		||||
    mgr.map(add_plugin)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_auth_system_opts(parser):
 | 
			
		||||
    """Load options needed by the available auth-systems into a parser.
 | 
			
		||||
 | 
			
		||||
    This function will try to populate the parser with options from the
 | 
			
		||||
    available plugins.
 | 
			
		||||
    """
 | 
			
		||||
    group = parser.add_argument_group("Common auth options")
 | 
			
		||||
    BaseAuthPlugin.add_common_opts(group)
 | 
			
		||||
    for name, auth_plugin in six.iteritems(_discovered_plugins):
 | 
			
		||||
        group = parser.add_argument_group(
 | 
			
		||||
            "Auth-system '%s' options" % name,
 | 
			
		||||
            conflict_handler="resolve")
 | 
			
		||||
        auth_plugin.add_opts(group)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_plugin(auth_system):
 | 
			
		||||
    try:
 | 
			
		||||
        plugin_class = _discovered_plugins[auth_system]
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        raise exceptions.AuthSystemNotFound(auth_system)
 | 
			
		||||
    return plugin_class(auth_system=auth_system)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_plugin_from_args(args):
 | 
			
		||||
    """Load required plugin and populate it with options.
 | 
			
		||||
 | 
			
		||||
    Try to guess auth system if it is not specified. Systems are tried in
 | 
			
		||||
    alphabetical order.
 | 
			
		||||
 | 
			
		||||
    :type args: argparse.Namespace
 | 
			
		||||
    :raises: AuthPluginOptionsMissing
 | 
			
		||||
    """
 | 
			
		||||
    auth_system = args.os_auth_system
 | 
			
		||||
    if auth_system:
 | 
			
		||||
        plugin = load_plugin(auth_system)
 | 
			
		||||
        plugin.parse_opts(args)
 | 
			
		||||
        plugin.sufficient_options()
 | 
			
		||||
        return plugin
 | 
			
		||||
 | 
			
		||||
    for plugin_auth_system in sorted(six.iterkeys(_discovered_plugins)):
 | 
			
		||||
        plugin_class = _discovered_plugins[plugin_auth_system]
 | 
			
		||||
        plugin = plugin_class()
 | 
			
		||||
        plugin.parse_opts(args)
 | 
			
		||||
        try:
 | 
			
		||||
            plugin.sufficient_options()
 | 
			
		||||
        except exceptions.AuthPluginOptionsMissing:
 | 
			
		||||
            continue
 | 
			
		||||
        return plugin
 | 
			
		||||
    raise exceptions.AuthPluginOptionsMissing(["auth_system"])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@six.add_metaclass(abc.ABCMeta)
 | 
			
		||||
class BaseAuthPlugin(object):
 | 
			
		||||
    """Base class for authentication plugins.
 | 
			
		||||
 | 
			
		||||
    An authentication plugin needs to override at least the authenticate
 | 
			
		||||
    method to be a valid plugin.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    auth_system = None
 | 
			
		||||
    opt_names = []
 | 
			
		||||
    common_opt_names = [
 | 
			
		||||
        "auth_system",
 | 
			
		||||
        "username",
 | 
			
		||||
        "password",
 | 
			
		||||
        "tenant_name",
 | 
			
		||||
        "token",
 | 
			
		||||
        "auth_url",
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def __init__(self, auth_system=None, **kwargs):
 | 
			
		||||
        self.auth_system = auth_system or self.auth_system
 | 
			
		||||
        self.opts = dict((name, kwargs.get(name))
 | 
			
		||||
                         for name in self.opt_names)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _parser_add_opt(parser, opt):
 | 
			
		||||
        """Add an option to parser in two variants.
 | 
			
		||||
 | 
			
		||||
        :param opt: option name (with underscores)
 | 
			
		||||
        """
 | 
			
		||||
        dashed_opt = opt.replace("_", "-")
 | 
			
		||||
        env_var = "OS_%s" % opt.upper()
 | 
			
		||||
        arg_default = os.environ.get(env_var, "")
 | 
			
		||||
        arg_help = "Defaults to env[%s]." % env_var
 | 
			
		||||
        parser.add_argument(
 | 
			
		||||
            "--os-%s" % dashed_opt,
 | 
			
		||||
            metavar="<%s>" % dashed_opt,
 | 
			
		||||
            default=arg_default,
 | 
			
		||||
            help=arg_help)
 | 
			
		||||
        parser.add_argument(
 | 
			
		||||
            "--os_%s" % opt,
 | 
			
		||||
            metavar="<%s>" % dashed_opt,
 | 
			
		||||
            help=argparse.SUPPRESS)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def add_opts(cls, parser):
 | 
			
		||||
        """Populate the parser with the options for this plugin.
 | 
			
		||||
        """
 | 
			
		||||
        for opt in cls.opt_names:
 | 
			
		||||
            # use `BaseAuthPlugin.common_opt_names` since it is never
 | 
			
		||||
            # changed in child classes
 | 
			
		||||
            if opt not in BaseAuthPlugin.common_opt_names:
 | 
			
		||||
                cls._parser_add_opt(parser, opt)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def add_common_opts(cls, parser):
 | 
			
		||||
        """Add options that are common for several plugins.
 | 
			
		||||
        """
 | 
			
		||||
        for opt in cls.common_opt_names:
 | 
			
		||||
            cls._parser_add_opt(parser, opt)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_opt(opt_name, args):
 | 
			
		||||
        """Return option name and value.
 | 
			
		||||
 | 
			
		||||
        :param opt_name: name of the option, e.g., "username"
 | 
			
		||||
        :param args: parsed arguments
 | 
			
		||||
        """
 | 
			
		||||
        return (opt_name, getattr(args, "os_%s" % opt_name, None))
 | 
			
		||||
 | 
			
		||||
    def parse_opts(self, args):
 | 
			
		||||
        """Parse the actual auth-system options if any.
 | 
			
		||||
 | 
			
		||||
        This method is expected to populate the attribute `self.opts` with a
 | 
			
		||||
        dict containing the options and values needed to make authentication.
 | 
			
		||||
        """
 | 
			
		||||
        self.opts.update(dict(self.get_opt(opt_name, args)
 | 
			
		||||
                              for opt_name in self.opt_names))
 | 
			
		||||
 | 
			
		||||
    def authenticate(self, http_client):
 | 
			
		||||
        """Authenticate using plugin defined method.
 | 
			
		||||
 | 
			
		||||
        The method usually analyses `self.opts` and performs
 | 
			
		||||
        a request to authentication server.
 | 
			
		||||
 | 
			
		||||
        :param http_client: client object that needs authentication
 | 
			
		||||
        :type http_client: HTTPClient
 | 
			
		||||
        :raises: AuthorizationFailure
 | 
			
		||||
        """
 | 
			
		||||
        self.sufficient_options()
 | 
			
		||||
        self._do_authenticate(http_client)
 | 
			
		||||
 | 
			
		||||
    @abc.abstractmethod
 | 
			
		||||
    def _do_authenticate(self, http_client):
 | 
			
		||||
        """Protected method for authentication."""
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def sufficient_options(self):
 | 
			
		||||
        """Check if all required options are present.
 | 
			
		||||
 | 
			
		||||
        :raises: AuthPluginOptionsMissing
 | 
			
		||||
        """
 | 
			
		||||
        missing = [opt
 | 
			
		||||
                   for opt in self.opt_names
 | 
			
		||||
                   if not self.opts.get(opt)]
 | 
			
		||||
        if missing:
 | 
			
		||||
            raise exceptions.AuthPluginOptionsMissing(missing)
 | 
			
		||||
 | 
			
		||||
    @abc.abstractmethod
 | 
			
		||||
    def token_and_endpoint(self, endpoint_type, service_type):
 | 
			
		||||
        """Return token and endpoint.
 | 
			
		||||
 | 
			
		||||
        :param service_type: Service type of the endpoint
 | 
			
		||||
        :type service_type: string
 | 
			
		||||
        :param endpoint_type: Type of endpoint.
 | 
			
		||||
                              Possible values: public or publicURL,
 | 
			
		||||
                              internal or internalURL,
 | 
			
		||||
                              admin or adminURL
 | 
			
		||||
        :type endpoint_type: string
 | 
			
		||||
        :returns: tuple of token and endpoint strings
 | 
			
		||||
        :raises: EndpointException
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
@@ -1,444 +0,0 @@
 | 
			
		||||
# Copyright 2010 Jacob Kaplan-Moss
 | 
			
		||||
# Copyright 2011 OpenStack Foundation
 | 
			
		||||
# Copyright 2012 Grid Dynamics
 | 
			
		||||
# Copyright 2013 OpenStack Foundation
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Base utilities to build API operation managers and objects on top of.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
#
 | 
			
		||||
# THIS MODULE IS DEPRECATED
 | 
			
		||||
#
 | 
			
		||||
# Please refer to
 | 
			
		||||
# https://etherpad.openstack.org/p/kilo-keystoneclient-library-proposals for
 | 
			
		||||
# the discussion leading to this deprecation.
 | 
			
		||||
#
 | 
			
		||||
# We recommend checking out the python-openstacksdk project
 | 
			
		||||
# (https://launchpad.net/python-openstacksdk) instead.
 | 
			
		||||
#
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
# NOTE(blk-u): This module is not being synced with oslo-incubator
 | 
			
		||||
# anymore. We need to deprecate property and get rid of it.
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# E1102: %s is not callable
 | 
			
		||||
# pylint: disable=E1102
 | 
			
		||||
 | 
			
		||||
import abc
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
from six.moves.urllib import parse
 | 
			
		||||
 | 
			
		||||
from keystoneclient.openstack.common._i18n import _
 | 
			
		||||
from keystoneclient import base as _base
 | 
			
		||||
from keystoneclient.openstack.common.apiclient import exceptions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getid(obj):
 | 
			
		||||
    """Return id if argument is a Resource.
 | 
			
		||||
 | 
			
		||||
    Abstracts the common pattern of allowing both an object or an object's ID
 | 
			
		||||
    (UUID) as a parameter when dealing with relationships.
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        if obj.uuid:
 | 
			
		||||
            return obj.uuid
 | 
			
		||||
    except AttributeError:
 | 
			
		||||
        pass
 | 
			
		||||
    try:
 | 
			
		||||
        return obj.id
 | 
			
		||||
    except AttributeError:
 | 
			
		||||
        return obj
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# TODO(aababilov): call run_hooks() in HookableMixin's child classes
 | 
			
		||||
class HookableMixin(object):
 | 
			
		||||
    """Mixin so classes can register and run hooks."""
 | 
			
		||||
    _hooks_map = {}
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def add_hook(cls, hook_type, hook_func):
 | 
			
		||||
        """Add a new hook of specified type.
 | 
			
		||||
 | 
			
		||||
        :param cls: class that registers hooks
 | 
			
		||||
        :param hook_type: hook type, e.g., '__pre_parse_args__'
 | 
			
		||||
        :param hook_func: hook function
 | 
			
		||||
        """
 | 
			
		||||
        if hook_type not in cls._hooks_map:
 | 
			
		||||
            cls._hooks_map[hook_type] = []
 | 
			
		||||
 | 
			
		||||
        cls._hooks_map[hook_type].append(hook_func)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def run_hooks(cls, hook_type, *args, **kwargs):
 | 
			
		||||
        """Run all hooks of specified type.
 | 
			
		||||
 | 
			
		||||
        :param cls: class that registers hooks
 | 
			
		||||
        :param hook_type: hook type, e.g., '__pre_parse_args__'
 | 
			
		||||
        :param args: args to be passed to every hook function
 | 
			
		||||
        :param kwargs: kwargs to be passed to every hook function
 | 
			
		||||
        """
 | 
			
		||||
        hook_funcs = cls._hooks_map.get(hook_type) or []
 | 
			
		||||
        for hook_func in hook_funcs:
 | 
			
		||||
            hook_func(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseManager(HookableMixin):
 | 
			
		||||
    """Basic manager type providing common operations.
 | 
			
		||||
 | 
			
		||||
    Managers interact with a particular type of API (servers, flavors, images,
 | 
			
		||||
    etc.) and provide CRUD operations for them.
 | 
			
		||||
    """
 | 
			
		||||
    resource_class = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, client):
 | 
			
		||||
        """Initializes BaseManager with `client`.
 | 
			
		||||
 | 
			
		||||
        :param client: instance of BaseClient descendant for HTTP requests
 | 
			
		||||
        """
 | 
			
		||||
        super(BaseManager, self).__init__()
 | 
			
		||||
        self.client = client
 | 
			
		||||
 | 
			
		||||
    def _list(self, url, response_key=None, obj_class=None, json=None):
 | 
			
		||||
        """List the collection.
 | 
			
		||||
 | 
			
		||||
        :param url: a partial URL, e.g., '/servers'
 | 
			
		||||
        :param response_key: the key to be looked up in response dictionary,
 | 
			
		||||
            e.g., 'servers'. If response_key is None - all response body
 | 
			
		||||
            will be used.
 | 
			
		||||
        :param obj_class: class for constructing the returned objects
 | 
			
		||||
            (self.resource_class will be used by default)
 | 
			
		||||
        :param json: data that will be encoded as JSON and passed in POST
 | 
			
		||||
            request (GET will be sent by default)
 | 
			
		||||
        """
 | 
			
		||||
        if json:
 | 
			
		||||
            body = self.client.post(url, json=json).json()
 | 
			
		||||
        else:
 | 
			
		||||
            body = self.client.get(url).json()
 | 
			
		||||
 | 
			
		||||
        if obj_class is None:
 | 
			
		||||
            obj_class = self.resource_class
 | 
			
		||||
 | 
			
		||||
        data = body[response_key] if response_key is not None else body
 | 
			
		||||
        # NOTE(ja): keystone returns values as list as {'values': [ ... ]}
 | 
			
		||||
        #           unlike other services which just return the list...
 | 
			
		||||
        try:
 | 
			
		||||
            data = data['values']
 | 
			
		||||
        except (KeyError, TypeError):
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        return [obj_class(self, res, loaded=True) for res in data if res]
 | 
			
		||||
 | 
			
		||||
    def _get(self, url, response_key=None):
 | 
			
		||||
        """Get an object from collection.
 | 
			
		||||
 | 
			
		||||
        :param url: a partial URL, e.g., '/servers'
 | 
			
		||||
        :param response_key: the key to be looked up in response dictionary,
 | 
			
		||||
            e.g., 'server'. If response_key is None - all response body
 | 
			
		||||
            will be used.
 | 
			
		||||
        """
 | 
			
		||||
        body = self.client.get(url).json()
 | 
			
		||||
        data = body[response_key] if response_key is not None else body
 | 
			
		||||
        return self.resource_class(self, data, loaded=True)
 | 
			
		||||
 | 
			
		||||
    def _head(self, url):
 | 
			
		||||
        """Retrieve request headers for an object.
 | 
			
		||||
 | 
			
		||||
        :param url: a partial URL, e.g., '/servers'
 | 
			
		||||
        """
 | 
			
		||||
        resp = self.client.head(url)
 | 
			
		||||
        return resp.status_code == 204
 | 
			
		||||
 | 
			
		||||
    def _post(self, url, json, response_key=None, return_raw=False):
 | 
			
		||||
        """Create an object.
 | 
			
		||||
 | 
			
		||||
        :param url: a partial URL, e.g., '/servers'
 | 
			
		||||
        :param json: data that will be encoded as JSON and passed in POST
 | 
			
		||||
            request (GET will be sent by default)
 | 
			
		||||
        :param response_key: the key to be looked up in response dictionary,
 | 
			
		||||
            e.g., 'server'. If response_key is None - all response body
 | 
			
		||||
            will be used.
 | 
			
		||||
        :param return_raw: flag to force returning raw JSON instead of
 | 
			
		||||
            Python object of self.resource_class
 | 
			
		||||
        """
 | 
			
		||||
        body = self.client.post(url, json=json).json()
 | 
			
		||||
        data = body[response_key] if response_key is not None else body
 | 
			
		||||
        if return_raw:
 | 
			
		||||
            return data
 | 
			
		||||
        return self.resource_class(self, data)
 | 
			
		||||
 | 
			
		||||
    def _put(self, url, json=None, response_key=None):
 | 
			
		||||
        """Update an object with PUT method.
 | 
			
		||||
 | 
			
		||||
        :param url: a partial URL, e.g., '/servers'
 | 
			
		||||
        :param json: data that will be encoded as JSON and passed in POST
 | 
			
		||||
            request (GET will be sent by default)
 | 
			
		||||
        :param response_key: the key to be looked up in response dictionary,
 | 
			
		||||
            e.g., 'servers'. If response_key is None - all response body
 | 
			
		||||
            will be used.
 | 
			
		||||
        """
 | 
			
		||||
        resp = self.client.put(url, json=json)
 | 
			
		||||
        # PUT requests may not return a body
 | 
			
		||||
        if resp.content:
 | 
			
		||||
            body = resp.json()
 | 
			
		||||
            if response_key is not None:
 | 
			
		||||
                return self.resource_class(self, body[response_key])
 | 
			
		||||
            else:
 | 
			
		||||
                return self.resource_class(self, body)
 | 
			
		||||
 | 
			
		||||
    def _patch(self, url, json=None, response_key=None):
 | 
			
		||||
        """Update an object with PATCH method.
 | 
			
		||||
 | 
			
		||||
        :param url: a partial URL, e.g., '/servers'
 | 
			
		||||
        :param json: data that will be encoded as JSON and passed in POST
 | 
			
		||||
            request (GET will be sent by default)
 | 
			
		||||
        :param response_key: the key to be looked up in response dictionary,
 | 
			
		||||
            e.g., 'servers'. If response_key is None - all response body
 | 
			
		||||
            will be used.
 | 
			
		||||
        """
 | 
			
		||||
        body = self.client.patch(url, json=json).json()
 | 
			
		||||
        if response_key is not None:
 | 
			
		||||
            return self.resource_class(self, body[response_key])
 | 
			
		||||
        else:
 | 
			
		||||
            return self.resource_class(self, body)
 | 
			
		||||
 | 
			
		||||
    def _delete(self, url):
 | 
			
		||||
        """Delete an object.
 | 
			
		||||
 | 
			
		||||
        :param url: a partial URL, e.g., '/servers/my-server'
 | 
			
		||||
        """
 | 
			
		||||
        return self.client.delete(url)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@six.add_metaclass(abc.ABCMeta)
 | 
			
		||||
class ManagerWithFind(BaseManager):
 | 
			
		||||
    """Manager with additional `find()`/`findall()` methods."""
 | 
			
		||||
 | 
			
		||||
    @abc.abstractmethod
 | 
			
		||||
    def list(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def find(self, **kwargs):
 | 
			
		||||
        """Find a single item with attributes matching ``**kwargs``.
 | 
			
		||||
 | 
			
		||||
        This isn't very efficient: it loads the entire list then filters on
 | 
			
		||||
        the Python side.
 | 
			
		||||
        """
 | 
			
		||||
        matches = self.findall(**kwargs)
 | 
			
		||||
        num_matches = len(matches)
 | 
			
		||||
        if num_matches == 0:
 | 
			
		||||
            msg = _("No %(name)s matching %(args)s.") % {
 | 
			
		||||
                'name': self.resource_class.__name__,
 | 
			
		||||
                'args': kwargs
 | 
			
		||||
            }
 | 
			
		||||
            raise exceptions.NotFound(msg)
 | 
			
		||||
        elif num_matches > 1:
 | 
			
		||||
            raise exceptions.NoUniqueMatch()
 | 
			
		||||
        else:
 | 
			
		||||
            return matches[0]
 | 
			
		||||
 | 
			
		||||
    def findall(self, **kwargs):
 | 
			
		||||
        """Find all items with attributes matching ``**kwargs``.
 | 
			
		||||
 | 
			
		||||
        This isn't very efficient: it loads the entire list then filters on
 | 
			
		||||
        the Python side.
 | 
			
		||||
        """
 | 
			
		||||
        found = []
 | 
			
		||||
        searches = kwargs.items()
 | 
			
		||||
 | 
			
		||||
        for obj in self.list():
 | 
			
		||||
            try:
 | 
			
		||||
                if all(getattr(obj, attr) == value
 | 
			
		||||
                       for (attr, value) in searches):
 | 
			
		||||
                    found.append(obj)
 | 
			
		||||
            except AttributeError:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
        return found
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CrudManager(BaseManager):
 | 
			
		||||
    """Base manager class for manipulating entities.
 | 
			
		||||
 | 
			
		||||
    Children of this class are expected to define a `collection_key` and `key`.
 | 
			
		||||
 | 
			
		||||
    - `collection_key`: Usually a plural noun by convention (e.g. `entities`);
 | 
			
		||||
      used to refer collections in both URL's (e.g.  `/v3/entities`) and JSON
 | 
			
		||||
      objects containing a list of member resources (e.g. `{'entities': [{},
 | 
			
		||||
      {}, {}]}`).
 | 
			
		||||
    - `key`: Usually a singular noun by convention (e.g. `entity`); used to
 | 
			
		||||
      refer to an individual member of the collection.
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    collection_key = None
 | 
			
		||||
    key = None
 | 
			
		||||
 | 
			
		||||
    def build_url(self, base_url=None, **kwargs):
 | 
			
		||||
        """Builds a resource URL for the given kwargs.
 | 
			
		||||
 | 
			
		||||
        Given an example collection where `collection_key = 'entities'` and
 | 
			
		||||
        `key = 'entity'`, the following URL's could be generated.
 | 
			
		||||
 | 
			
		||||
        By default, the URL will represent a collection of entities, e.g.::
 | 
			
		||||
 | 
			
		||||
            /entities
 | 
			
		||||
 | 
			
		||||
        If kwargs contains an `entity_id`, then the URL will represent a
 | 
			
		||||
        specific member, e.g.::
 | 
			
		||||
 | 
			
		||||
            /entities/{entity_id}
 | 
			
		||||
 | 
			
		||||
        :param base_url: if provided, the generated URL will be appended to it
 | 
			
		||||
        """
 | 
			
		||||
        url = base_url if base_url is not None else ''
 | 
			
		||||
 | 
			
		||||
        url += '/%s' % self.collection_key
 | 
			
		||||
 | 
			
		||||
        # do we have a specific entity?
 | 
			
		||||
        entity_id = kwargs.get('%s_id' % self.key)
 | 
			
		||||
        if entity_id is not None:
 | 
			
		||||
            url += '/%s' % entity_id
 | 
			
		||||
 | 
			
		||||
        return url
 | 
			
		||||
 | 
			
		||||
    def _filter_kwargs(self, kwargs):
 | 
			
		||||
        """Drop null values and handle ids."""
 | 
			
		||||
        for key, ref in six.iteritems(kwargs.copy()):
 | 
			
		||||
            if ref is None:
 | 
			
		||||
                kwargs.pop(key)
 | 
			
		||||
            else:
 | 
			
		||||
                if isinstance(ref, Resource):
 | 
			
		||||
                    kwargs.pop(key)
 | 
			
		||||
                    kwargs['%s_id' % key] = getid(ref)
 | 
			
		||||
        return kwargs
 | 
			
		||||
 | 
			
		||||
    def create(self, **kwargs):
 | 
			
		||||
        kwargs = self._filter_kwargs(kwargs)
 | 
			
		||||
        return self._post(
 | 
			
		||||
            self.build_url(**kwargs),
 | 
			
		||||
            {self.key: kwargs},
 | 
			
		||||
            self.key)
 | 
			
		||||
 | 
			
		||||
    def get(self, **kwargs):
 | 
			
		||||
        kwargs = self._filter_kwargs(kwargs)
 | 
			
		||||
        return self._get(
 | 
			
		||||
            self.build_url(**kwargs),
 | 
			
		||||
            self.key)
 | 
			
		||||
 | 
			
		||||
    def head(self, **kwargs):
 | 
			
		||||
        kwargs = self._filter_kwargs(kwargs)
 | 
			
		||||
        return self._head(self.build_url(**kwargs))
 | 
			
		||||
 | 
			
		||||
    def list(self, base_url=None, **kwargs):
 | 
			
		||||
        """List the collection.
 | 
			
		||||
 | 
			
		||||
        :param base_url: if provided, the generated URL will be appended to it
 | 
			
		||||
        """
 | 
			
		||||
        kwargs = self._filter_kwargs(kwargs)
 | 
			
		||||
 | 
			
		||||
        return self._list(
 | 
			
		||||
            '%(base_url)s%(query)s' % {
 | 
			
		||||
                'base_url': self.build_url(base_url=base_url, **kwargs),
 | 
			
		||||
                'query': '?%s' % parse.urlencode(kwargs) if kwargs else '',
 | 
			
		||||
            },
 | 
			
		||||
            self.collection_key)
 | 
			
		||||
 | 
			
		||||
    def put(self, base_url=None, **kwargs):
 | 
			
		||||
        """Update an element.
 | 
			
		||||
 | 
			
		||||
        :param base_url: if provided, the generated URL will be appended to it
 | 
			
		||||
        """
 | 
			
		||||
        kwargs = self._filter_kwargs(kwargs)
 | 
			
		||||
 | 
			
		||||
        return self._put(self.build_url(base_url=base_url, **kwargs))
 | 
			
		||||
 | 
			
		||||
    def update(self, **kwargs):
 | 
			
		||||
        kwargs = self._filter_kwargs(kwargs)
 | 
			
		||||
        params = kwargs.copy()
 | 
			
		||||
        params.pop('%s_id' % self.key)
 | 
			
		||||
 | 
			
		||||
        return self._patch(
 | 
			
		||||
            self.build_url(**kwargs),
 | 
			
		||||
            {self.key: params},
 | 
			
		||||
            self.key)
 | 
			
		||||
 | 
			
		||||
    def delete(self, **kwargs):
 | 
			
		||||
        kwargs = self._filter_kwargs(kwargs)
 | 
			
		||||
 | 
			
		||||
        return self._delete(
 | 
			
		||||
            self.build_url(**kwargs))
 | 
			
		||||
 | 
			
		||||
    def find(self, base_url=None, **kwargs):
 | 
			
		||||
        """Find a single item with attributes matching ``**kwargs``.
 | 
			
		||||
 | 
			
		||||
        :param base_url: if provided, the generated URL will be appended to it
 | 
			
		||||
        """
 | 
			
		||||
        kwargs = self._filter_kwargs(kwargs)
 | 
			
		||||
 | 
			
		||||
        rl = self._list(
 | 
			
		||||
            '%(base_url)s%(query)s' % {
 | 
			
		||||
                'base_url': self.build_url(base_url=base_url, **kwargs),
 | 
			
		||||
                'query': '?%s' % parse.urlencode(kwargs) if kwargs else '',
 | 
			
		||||
            },
 | 
			
		||||
            self.collection_key)
 | 
			
		||||
        num = len(rl)
 | 
			
		||||
 | 
			
		||||
        if num == 0:
 | 
			
		||||
            msg = _("No %(name)s matching %(args)s.") % {
 | 
			
		||||
                'name': self.resource_class.__name__,
 | 
			
		||||
                'args': kwargs
 | 
			
		||||
            }
 | 
			
		||||
            raise exceptions.NotFound(msg)
 | 
			
		||||
        elif num > 1:
 | 
			
		||||
            raise exceptions.NoUniqueMatch
 | 
			
		||||
        else:
 | 
			
		||||
            return rl[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Extension(HookableMixin):
 | 
			
		||||
    """Extension descriptor."""
 | 
			
		||||
 | 
			
		||||
    SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__')
 | 
			
		||||
    manager_class = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, name, module):
 | 
			
		||||
        super(Extension, self).__init__()
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.module = module
 | 
			
		||||
        self._parse_extension_module()
 | 
			
		||||
 | 
			
		||||
    def _parse_extension_module(self):
 | 
			
		||||
        self.manager_class = None
 | 
			
		||||
        for attr_name, attr_value in self.module.__dict__.items():
 | 
			
		||||
            if attr_name in self.SUPPORTED_HOOKS:
 | 
			
		||||
                self.add_hook(attr_name, attr_value)
 | 
			
		||||
            else:
 | 
			
		||||
                try:
 | 
			
		||||
                    if issubclass(attr_value, BaseManager):
 | 
			
		||||
                        self.manager_class = attr_value
 | 
			
		||||
                except TypeError:
 | 
			
		||||
                    pass
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return "<Extension '%s'>" % self.name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Resource = _base.Resource
 | 
			
		||||
@@ -1,388 +0,0 @@
 | 
			
		||||
# Copyright 2010 Jacob Kaplan-Moss
 | 
			
		||||
# Copyright 2011 OpenStack Foundation
 | 
			
		||||
# Copyright 2011 Piston Cloud Computing, Inc.
 | 
			
		||||
# Copyright 2013 Alessio Ababilov
 | 
			
		||||
# Copyright 2013 Grid Dynamics
 | 
			
		||||
# Copyright 2013 OpenStack Foundation
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
OpenStack Client interface. Handles the REST calls and responses.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
# E0202: An attribute inherited from %s hide this method
 | 
			
		||||
# pylint: disable=E0202
 | 
			
		||||
 | 
			
		||||
import hashlib
 | 
			
		||||
import logging
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import simplejson as json
 | 
			
		||||
except ImportError:
 | 
			
		||||
    import json
 | 
			
		||||
 | 
			
		||||
from oslo_utils import encodeutils
 | 
			
		||||
from oslo_utils import importutils
 | 
			
		||||
import requests
 | 
			
		||||
 | 
			
		||||
from keystoneclient.openstack.common._i18n import _
 | 
			
		||||
from keystoneclient.openstack.common.apiclient import exceptions
 | 
			
		||||
 | 
			
		||||
_logger = logging.getLogger(__name__)
 | 
			
		||||
SENSITIVE_HEADERS = ('X-Auth-Token', 'X-Subject-Token',)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HTTPClient(object):
 | 
			
		||||
    """This client handles sending HTTP requests to OpenStack servers.
 | 
			
		||||
 | 
			
		||||
    Features:
 | 
			
		||||
 | 
			
		||||
    - share authentication information between several clients to different
 | 
			
		||||
      services (e.g., for compute and image clients);
 | 
			
		||||
    - reissue authentication request for expired tokens;
 | 
			
		||||
    - encode/decode JSON bodies;
 | 
			
		||||
    - raise exceptions on HTTP errors;
 | 
			
		||||
    - pluggable authentication;
 | 
			
		||||
    - store authentication information in a keyring;
 | 
			
		||||
    - store time spent for requests;
 | 
			
		||||
    - register clients for particular services, so one can use
 | 
			
		||||
      `http_client.identity` or `http_client.compute`;
 | 
			
		||||
    - log requests and responses in a format that is easy to copy-and-paste
 | 
			
		||||
      into terminal and send the same request with curl.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    user_agent = "keystoneclient.openstack.common.apiclient"
 | 
			
		||||
 | 
			
		||||
    def __init__(self,
 | 
			
		||||
                 auth_plugin,
 | 
			
		||||
                 region_name=None,
 | 
			
		||||
                 endpoint_type="publicURL",
 | 
			
		||||
                 original_ip=None,
 | 
			
		||||
                 verify=True,
 | 
			
		||||
                 cert=None,
 | 
			
		||||
                 timeout=None,
 | 
			
		||||
                 timings=False,
 | 
			
		||||
                 keyring_saver=None,
 | 
			
		||||
                 debug=False,
 | 
			
		||||
                 user_agent=None,
 | 
			
		||||
                 http=None):
 | 
			
		||||
        self.auth_plugin = auth_plugin
 | 
			
		||||
 | 
			
		||||
        self.endpoint_type = endpoint_type
 | 
			
		||||
        self.region_name = region_name
 | 
			
		||||
 | 
			
		||||
        self.original_ip = original_ip
 | 
			
		||||
        self.timeout = timeout
 | 
			
		||||
        self.verify = verify
 | 
			
		||||
        self.cert = cert
 | 
			
		||||
 | 
			
		||||
        self.keyring_saver = keyring_saver
 | 
			
		||||
        self.debug = debug
 | 
			
		||||
        self.user_agent = user_agent or self.user_agent
 | 
			
		||||
 | 
			
		||||
        self.times = []  # [("item", starttime, endtime), ...]
 | 
			
		||||
        self.timings = timings
 | 
			
		||||
 | 
			
		||||
        # requests within the same session can reuse TCP connections from pool
 | 
			
		||||
        self.http = http or requests.Session()
 | 
			
		||||
 | 
			
		||||
        self.cached_token = None
 | 
			
		||||
        self.last_request_id = None
 | 
			
		||||
 | 
			
		||||
    def _safe_header(self, name, value):
 | 
			
		||||
        if name in SENSITIVE_HEADERS:
 | 
			
		||||
            # because in python3 byte string handling is ... ug
 | 
			
		||||
            v = value.encode('utf-8')
 | 
			
		||||
            h = hashlib.sha1(v)
 | 
			
		||||
            d = h.hexdigest()
 | 
			
		||||
            return encodeutils.safe_decode(name), "{SHA1}%s" % d
 | 
			
		||||
        else:
 | 
			
		||||
            return (encodeutils.safe_decode(name),
 | 
			
		||||
                    encodeutils.safe_decode(value))
 | 
			
		||||
 | 
			
		||||
    def _http_log_req(self, method, url, kwargs):
 | 
			
		||||
        if not self.debug:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        string_parts = [
 | 
			
		||||
            "curl -g -i",
 | 
			
		||||
            "-X '%s'" % method,
 | 
			
		||||
            "'%s'" % url,
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        for element in kwargs['headers']:
 | 
			
		||||
            header = ("-H '%s: %s'" %
 | 
			
		||||
                      self._safe_header(element, kwargs['headers'][element]))
 | 
			
		||||
            string_parts.append(header)
 | 
			
		||||
 | 
			
		||||
        _logger.debug("REQ: %s" % " ".join(string_parts))
 | 
			
		||||
        if 'data' in kwargs:
 | 
			
		||||
            _logger.debug("REQ BODY: %s\n" % (kwargs['data']))
 | 
			
		||||
 | 
			
		||||
    def _http_log_resp(self, resp):
 | 
			
		||||
        if not self.debug:
 | 
			
		||||
            return
 | 
			
		||||
        _logger.debug(
 | 
			
		||||
            "RESP: [%s] %s\n",
 | 
			
		||||
            resp.status_code,
 | 
			
		||||
            resp.headers)
 | 
			
		||||
        if resp._content_consumed:
 | 
			
		||||
            _logger.debug(
 | 
			
		||||
                "RESP BODY: %s\n",
 | 
			
		||||
                resp.text)
 | 
			
		||||
 | 
			
		||||
    def serialize(self, kwargs):
 | 
			
		||||
        if kwargs.get('json') is not None:
 | 
			
		||||
            kwargs['headers']['Content-Type'] = 'application/json'
 | 
			
		||||
            kwargs['data'] = json.dumps(kwargs['json'])
 | 
			
		||||
        try:
 | 
			
		||||
            del kwargs['json']
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    def get_timings(self):
 | 
			
		||||
        return self.times
 | 
			
		||||
 | 
			
		||||
    def reset_timings(self):
 | 
			
		||||
        self.times = []
 | 
			
		||||
 | 
			
		||||
    def request(self, method, url, **kwargs):
 | 
			
		||||
        """Send an http request with the specified characteristics.
 | 
			
		||||
 | 
			
		||||
        Wrapper around `requests.Session.request` to handle tasks such as
 | 
			
		||||
        setting headers, JSON encoding/decoding, and error handling.
 | 
			
		||||
 | 
			
		||||
        :param method: method of HTTP request
 | 
			
		||||
        :param url: URL of HTTP request
 | 
			
		||||
        :param kwargs: any other parameter that can be passed to
 | 
			
		||||
             requests.Session.request (such as `headers`) or `json`
 | 
			
		||||
             that will be encoded as JSON and used as `data` argument
 | 
			
		||||
        """
 | 
			
		||||
        kwargs.setdefault("headers", {})
 | 
			
		||||
        kwargs["headers"]["User-Agent"] = self.user_agent
 | 
			
		||||
        if self.original_ip:
 | 
			
		||||
            kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % (
 | 
			
		||||
                self.original_ip, self.user_agent)
 | 
			
		||||
        if self.timeout is not None:
 | 
			
		||||
            kwargs.setdefault("timeout", self.timeout)
 | 
			
		||||
        kwargs.setdefault("verify", self.verify)
 | 
			
		||||
        if self.cert is not None:
 | 
			
		||||
            kwargs.setdefault("cert", self.cert)
 | 
			
		||||
        self.serialize(kwargs)
 | 
			
		||||
 | 
			
		||||
        self._http_log_req(method, url, kwargs)
 | 
			
		||||
        if self.timings:
 | 
			
		||||
            start_time = time.time()
 | 
			
		||||
        resp = self.http.request(method, url, **kwargs)
 | 
			
		||||
        if self.timings:
 | 
			
		||||
            self.times.append(("%s %s" % (method, url),
 | 
			
		||||
                               start_time, time.time()))
 | 
			
		||||
        self._http_log_resp(resp)
 | 
			
		||||
 | 
			
		||||
        self.last_request_id = resp.headers.get('x-openstack-request-id')
 | 
			
		||||
 | 
			
		||||
        if resp.status_code >= 400:
 | 
			
		||||
            _logger.debug(
 | 
			
		||||
                "Request returned failure status: %s",
 | 
			
		||||
                resp.status_code)
 | 
			
		||||
            raise exceptions.from_response(resp, method, url)
 | 
			
		||||
 | 
			
		||||
        return resp
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def concat_url(endpoint, url):
 | 
			
		||||
        """Concatenate endpoint and final URL.
 | 
			
		||||
 | 
			
		||||
        E.g., "http://keystone/v2.0/" and "/tokens" are concatenated to
 | 
			
		||||
        "http://keystone/v2.0/tokens".
 | 
			
		||||
 | 
			
		||||
        :param endpoint: the base URL
 | 
			
		||||
        :param url: the final URL
 | 
			
		||||
        """
 | 
			
		||||
        return "%s/%s" % (endpoint.rstrip("/"), url.strip("/"))
 | 
			
		||||
 | 
			
		||||
    def client_request(self, client, method, url, **kwargs):
 | 
			
		||||
        """Send an http request using `client`'s endpoint and specified `url`.
 | 
			
		||||
 | 
			
		||||
        If request was rejected as unauthorized (possibly because the token is
 | 
			
		||||
        expired), issue one authorization attempt and send the request once
 | 
			
		||||
        again.
 | 
			
		||||
 | 
			
		||||
        :param client: instance of BaseClient descendant
 | 
			
		||||
        :param method: method of HTTP request
 | 
			
		||||
        :param url: URL of HTTP request
 | 
			
		||||
        :param kwargs: any other parameter that can be passed to
 | 
			
		||||
            `HTTPClient.request`
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        filter_args = {
 | 
			
		||||
            "endpoint_type": client.endpoint_type or self.endpoint_type,
 | 
			
		||||
            "service_type": client.service_type,
 | 
			
		||||
        }
 | 
			
		||||
        token, endpoint = (self.cached_token, client.cached_endpoint)
 | 
			
		||||
        just_authenticated = False
 | 
			
		||||
        if not (token and endpoint):
 | 
			
		||||
            try:
 | 
			
		||||
                token, endpoint = self.auth_plugin.token_and_endpoint(
 | 
			
		||||
                    **filter_args)
 | 
			
		||||
            except exceptions.EndpointException:
 | 
			
		||||
                pass
 | 
			
		||||
            if not (token and endpoint):
 | 
			
		||||
                self.authenticate()
 | 
			
		||||
                just_authenticated = True
 | 
			
		||||
                token, endpoint = self.auth_plugin.token_and_endpoint(
 | 
			
		||||
                    **filter_args)
 | 
			
		||||
                if not (token and endpoint):
 | 
			
		||||
                    raise exceptions.AuthorizationFailure(
 | 
			
		||||
                        _("Cannot find endpoint or token for request"))
 | 
			
		||||
 | 
			
		||||
        old_token_endpoint = (token, endpoint)
 | 
			
		||||
        kwargs.setdefault("headers", {})["X-Auth-Token"] = token
 | 
			
		||||
        self.cached_token = token
 | 
			
		||||
        client.cached_endpoint = endpoint
 | 
			
		||||
        # Perform the request once. If we get Unauthorized, then it
 | 
			
		||||
        # might be because the auth token expired, so try to
 | 
			
		||||
        # re-authenticate and try again. If it still fails, bail.
 | 
			
		||||
        try:
 | 
			
		||||
            return self.request(
 | 
			
		||||
                method, self.concat_url(endpoint, url), **kwargs)
 | 
			
		||||
        except exceptions.Unauthorized as unauth_ex:
 | 
			
		||||
            if just_authenticated:
 | 
			
		||||
                raise
 | 
			
		||||
            self.cached_token = None
 | 
			
		||||
            client.cached_endpoint = None
 | 
			
		||||
            if self.auth_plugin.opts.get('token'):
 | 
			
		||||
                self.auth_plugin.opts['token'] = None
 | 
			
		||||
            if self.auth_plugin.opts.get('endpoint'):
 | 
			
		||||
                self.auth_plugin.opts['endpoint'] = None
 | 
			
		||||
            self.authenticate()
 | 
			
		||||
            try:
 | 
			
		||||
                token, endpoint = self.auth_plugin.token_and_endpoint(
 | 
			
		||||
                    **filter_args)
 | 
			
		||||
            except exceptions.EndpointException:
 | 
			
		||||
                raise unauth_ex
 | 
			
		||||
            if (not (token and endpoint) or
 | 
			
		||||
                    old_token_endpoint == (token, endpoint)):
 | 
			
		||||
                raise unauth_ex
 | 
			
		||||
            self.cached_token = token
 | 
			
		||||
            client.cached_endpoint = endpoint
 | 
			
		||||
            kwargs["headers"]["X-Auth-Token"] = token
 | 
			
		||||
            return self.request(
 | 
			
		||||
                method, self.concat_url(endpoint, url), **kwargs)
 | 
			
		||||
 | 
			
		||||
    def add_client(self, base_client_instance):
 | 
			
		||||
        """Add a new instance of :class:`BaseClient` descendant.
 | 
			
		||||
 | 
			
		||||
        `self` will store a reference to `base_client_instance`.
 | 
			
		||||
 | 
			
		||||
        Example:
 | 
			
		||||
 | 
			
		||||
        >>> def test_clients():
 | 
			
		||||
        ...     from keystoneclient.auth import keystone
 | 
			
		||||
        ...     from openstack.common.apiclient import client
 | 
			
		||||
        ...     auth = keystone.KeystoneAuthPlugin(
 | 
			
		||||
        ...         username="user", password="pass", tenant_name="tenant",
 | 
			
		||||
        ...         auth_url="http://auth:5000/v2.0")
 | 
			
		||||
        ...     openstack_client = client.HTTPClient(auth)
 | 
			
		||||
        ...     # create nova client
 | 
			
		||||
        ...     from novaclient.v1_1 import client
 | 
			
		||||
        ...     client.Client(openstack_client)
 | 
			
		||||
        ...     # create keystone client
 | 
			
		||||
        ...     from keystoneclient.v2_0 import client
 | 
			
		||||
        ...     client.Client(openstack_client)
 | 
			
		||||
        ...     # use them
 | 
			
		||||
        ...     openstack_client.identity.tenants.list()
 | 
			
		||||
        ...     openstack_client.compute.servers.list()
 | 
			
		||||
        """
 | 
			
		||||
        service_type = base_client_instance.service_type
 | 
			
		||||
        if service_type and not hasattr(self, service_type):
 | 
			
		||||
            setattr(self, service_type, base_client_instance)
 | 
			
		||||
 | 
			
		||||
    def authenticate(self):
 | 
			
		||||
        self.auth_plugin.authenticate(self)
 | 
			
		||||
        # Store the authentication results in the keyring for later requests
 | 
			
		||||
        if self.keyring_saver:
 | 
			
		||||
            self.keyring_saver.save(self)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseClient(object):
 | 
			
		||||
    """Top-level object to access the OpenStack API.
 | 
			
		||||
 | 
			
		||||
    This client uses :class:`HTTPClient` to send requests. :class:`HTTPClient`
 | 
			
		||||
    will handle a bunch of issues such as authentication.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    service_type = None
 | 
			
		||||
    endpoint_type = None  # "publicURL" will be used
 | 
			
		||||
    cached_endpoint = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, http_client, extensions=None):
 | 
			
		||||
        self.http_client = http_client
 | 
			
		||||
        http_client.add_client(self)
 | 
			
		||||
 | 
			
		||||
        # Add in any extensions...
 | 
			
		||||
        if extensions:
 | 
			
		||||
            for extension in extensions:
 | 
			
		||||
                if extension.manager_class:
 | 
			
		||||
                    setattr(self, extension.name,
 | 
			
		||||
                            extension.manager_class(self))
 | 
			
		||||
 | 
			
		||||
    def client_request(self, method, url, **kwargs):
 | 
			
		||||
        return self.http_client.client_request(
 | 
			
		||||
            self, method, url, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def last_request_id(self):
 | 
			
		||||
        return self.http_client.last_request_id
 | 
			
		||||
 | 
			
		||||
    def head(self, url, **kwargs):
 | 
			
		||||
        return self.client_request("HEAD", url, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get(self, url, **kwargs):
 | 
			
		||||
        return self.client_request("GET", url, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def post(self, url, **kwargs):
 | 
			
		||||
        return self.client_request("POST", url, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def put(self, url, **kwargs):
 | 
			
		||||
        return self.client_request("PUT", url, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def delete(self, url, **kwargs):
 | 
			
		||||
        return self.client_request("DELETE", url, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def patch(self, url, **kwargs):
 | 
			
		||||
        return self.client_request("PATCH", url, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_class(api_name, version, version_map):
 | 
			
		||||
        """Returns the client class for the requested API version
 | 
			
		||||
 | 
			
		||||
        :param api_name: the name of the API, e.g. 'compute', 'image', etc
 | 
			
		||||
        :param version: the requested API version
 | 
			
		||||
        :param version_map: a dict of client classes keyed by version
 | 
			
		||||
        :rtype: a client class for the requested API version
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            client_path = version_map[str(version)]
 | 
			
		||||
        except (KeyError, ValueError):
 | 
			
		||||
            msg = _("Invalid %(api_name)s client version '%(version)s'. "
 | 
			
		||||
                    "Must be one of: %(version_map)s") % {
 | 
			
		||||
                        'api_name': api_name,
 | 
			
		||||
                        'version': version,
 | 
			
		||||
                        'version_map': ', '.join(version_map.keys())}
 | 
			
		||||
            raise exceptions.UnsupportedVersion(msg)
 | 
			
		||||
 | 
			
		||||
        return importutils.import_class(client_path)
 | 
			
		||||
@@ -1,93 +0,0 @@
 | 
			
		||||
# Copyright 2010 Jacob Kaplan-Moss
 | 
			
		||||
# Copyright 2011 Nebula, Inc.
 | 
			
		||||
# Copyright 2013 Alessio Ababilov
 | 
			
		||||
# Copyright 2013 OpenStack Foundation
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Exception definitions.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
#
 | 
			
		||||
# THIS MODULE IS DEPRECATED
 | 
			
		||||
#
 | 
			
		||||
# Please refer to
 | 
			
		||||
# https://etherpad.openstack.org/p/kilo-keystoneclient-library-proposals for
 | 
			
		||||
# the discussion leading to this deprecation.
 | 
			
		||||
#
 | 
			
		||||
# We recommend checking out the python-openstacksdk project
 | 
			
		||||
# (https://launchpad.net/python-openstacksdk) instead.
 | 
			
		||||
#
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
#
 | 
			
		||||
# THIS MODULE IS NOT SYNCED WITH OSLO-INCUBATOR.
 | 
			
		||||
# WE'RE JUST TRYING TO GET RID OF IT.
 | 
			
		||||
#
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
from keystoneclient.openstack.common._i18n import _
 | 
			
		||||
 | 
			
		||||
from keystoneclient import exceptions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
"""Exception definitions."""
 | 
			
		||||
 | 
			
		||||
ClientException = exceptions.ClientException
 | 
			
		||||
ValidationError = exceptions.ValidationError
 | 
			
		||||
UnsupportedVersion = exceptions.UnsupportedVersion
 | 
			
		||||
CommandError = exceptions.CommandError
 | 
			
		||||
AuthorizationFailure = exceptions.AuthorizationFailure
 | 
			
		||||
ConnectionError = exceptions.ConnectionError
 | 
			
		||||
ConnectionRefused = exceptions.ConnectionRefused
 | 
			
		||||
AuthPluginOptionsMissing = exceptions.AuthPluginOptionsMissing
 | 
			
		||||
AuthSystemNotFound = exceptions.AuthSystemNotFound
 | 
			
		||||
NoUniqueMatch = exceptions.NoUniqueMatch
 | 
			
		||||
EndpointException = exceptions.EndpointException
 | 
			
		||||
EndpointNotFound = exceptions.EndpointNotFound
 | 
			
		||||
AmbiguousEndpoints = exceptions.AmbiguousEndpoints
 | 
			
		||||
HttpError = exceptions.HttpError
 | 
			
		||||
HTTPRedirection = exceptions.HTTPRedirection
 | 
			
		||||
HTTPClientError = exceptions.HTTPClientError
 | 
			
		||||
HttpServerError = exceptions.HttpServerError
 | 
			
		||||
MultipleChoices = exceptions.MultipleChoices
 | 
			
		||||
BadRequest = exceptions.BadRequest
 | 
			
		||||
Unauthorized = exceptions.Unauthorized
 | 
			
		||||
PaymentRequired = exceptions.PaymentRequired
 | 
			
		||||
Forbidden = exceptions.Forbidden
 | 
			
		||||
NotFound = exceptions.NotFound
 | 
			
		||||
MethodNotAllowed = exceptions.MethodNotAllowed
 | 
			
		||||
NotAcceptable = exceptions.NotAcceptable
 | 
			
		||||
ProxyAuthenticationRequired = exceptions.ProxyAuthenticationRequired
 | 
			
		||||
RequestTimeout = exceptions.RequestTimeout
 | 
			
		||||
Conflict = exceptions.Conflict
 | 
			
		||||
Gone = exceptions.Gone
 | 
			
		||||
LengthRequired = exceptions.LengthRequired
 | 
			
		||||
PreconditionFailed = exceptions.PreconditionFailed
 | 
			
		||||
RequestEntityTooLarge = exceptions.RequestEntityTooLarge
 | 
			
		||||
RequestUriTooLong = exceptions.RequestUriTooLong
 | 
			
		||||
UnsupportedMediaType = exceptions.UnsupportedMediaType
 | 
			
		||||
RequestedRangeNotSatisfiable = exceptions.RequestedRangeNotSatisfiable
 | 
			
		||||
ExpectationFailed = exceptions.ExpectationFailed
 | 
			
		||||
UnprocessableEntity = exceptions.UnprocessableEntity
 | 
			
		||||
InternalServerError = exceptions.InternalServerError
 | 
			
		||||
HttpNotImplemented = exceptions.HttpNotImplemented
 | 
			
		||||
BadGateway = exceptions.BadGateway
 | 
			
		||||
ServiceUnavailable = exceptions.ServiceUnavailable
 | 
			
		||||
GatewayTimeout = exceptions.GatewayTimeout
 | 
			
		||||
HttpVersionNotSupported = exceptions.HttpVersionNotSupported
 | 
			
		||||
from_response = exceptions.from_response
 | 
			
		||||
@@ -1,190 +0,0 @@
 | 
			
		||||
# Copyright 2013 OpenStack Foundation
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
A fake server that "responds" to API methods with pre-canned responses.
 | 
			
		||||
 | 
			
		||||
All of these responses come from the spec, so if for some reason the spec's
 | 
			
		||||
wrong the tests might raise AssertionError. I've indicated in comments the
 | 
			
		||||
places where actual behavior differs from the spec.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
#
 | 
			
		||||
# THIS MODULE IS DEPRECATED
 | 
			
		||||
#
 | 
			
		||||
# Please refer to
 | 
			
		||||
# https://etherpad.openstack.org/p/kilo-keystoneclient-library-proposals for
 | 
			
		||||
# the discussion leading to this deprecation.
 | 
			
		||||
#
 | 
			
		||||
# We recommend checking out the python-openstacksdk project
 | 
			
		||||
# (https://launchpad.net/python-openstacksdk) instead.
 | 
			
		||||
#
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
# W0102: Dangerous default value %s as argument
 | 
			
		||||
# pylint: disable=W0102
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
import requests
 | 
			
		||||
import six
 | 
			
		||||
from six.moves.urllib import parse
 | 
			
		||||
 | 
			
		||||
from keystoneclient.openstack.common.apiclient import client
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def assert_has_keys(dct, required=None, optional=None):
 | 
			
		||||
    required = required or []
 | 
			
		||||
    optional = optional or []
 | 
			
		||||
    for k in required:
 | 
			
		||||
        try:
 | 
			
		||||
            assert k in dct
 | 
			
		||||
        except AssertionError:
 | 
			
		||||
            extra_keys = set(dct.keys()).difference(set(required + optional))
 | 
			
		||||
            raise AssertionError("found unexpected keys: %s" %
 | 
			
		||||
                                 list(extra_keys))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestResponse(requests.Response):
 | 
			
		||||
    """Wrap requests.Response and provide a convenient initialization.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, data):
 | 
			
		||||
        super(TestResponse, self).__init__()
 | 
			
		||||
        self._content_consumed = True
 | 
			
		||||
        if isinstance(data, dict):
 | 
			
		||||
            self.status_code = data.get('status_code', 200)
 | 
			
		||||
            # Fake the text attribute to streamline Response creation
 | 
			
		||||
            text = data.get('text', "")
 | 
			
		||||
            if isinstance(text, (dict, list)):
 | 
			
		||||
                self._content = json.dumps(text)
 | 
			
		||||
                default_headers = {
 | 
			
		||||
                    "Content-Type": "application/json",
 | 
			
		||||
                }
 | 
			
		||||
            else:
 | 
			
		||||
                self._content = text
 | 
			
		||||
                default_headers = {}
 | 
			
		||||
            if six.PY3 and isinstance(self._content, six.string_types):
 | 
			
		||||
                self._content = self._content.encode('utf-8', 'strict')
 | 
			
		||||
            self.headers = data.get('headers') or default_headers
 | 
			
		||||
        else:
 | 
			
		||||
            self.status_code = data
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        return (self.status_code == other.status_code and
 | 
			
		||||
                self.headers == other.headers and
 | 
			
		||||
                self._content == other._content)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FakeHTTPClient(client.HTTPClient):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        self.callstack = []
 | 
			
		||||
        self.fixtures = kwargs.pop("fixtures", None) or {}
 | 
			
		||||
        if not args and "auth_plugin" not in kwargs:
 | 
			
		||||
            args = (None, )
 | 
			
		||||
        super(FakeHTTPClient, self).__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def assert_called(self, method, url, body=None, pos=-1):
 | 
			
		||||
        """Assert than an API method was just called.
 | 
			
		||||
        """
 | 
			
		||||
        expected = (method, url)
 | 
			
		||||
        called = self.callstack[pos][0:2]
 | 
			
		||||
        assert self.callstack, \
 | 
			
		||||
            "Expected %s %s but no calls were made." % expected
 | 
			
		||||
 | 
			
		||||
        assert expected == called, 'Expected %s %s; got %s %s' % \
 | 
			
		||||
            (expected + called)
 | 
			
		||||
 | 
			
		||||
        if body is not None:
 | 
			
		||||
            if self.callstack[pos][3] != body:
 | 
			
		||||
                raise AssertionError('%r != %r' %
 | 
			
		||||
                                     (self.callstack[pos][3], body))
 | 
			
		||||
 | 
			
		||||
    def assert_called_anytime(self, method, url, body=None):
 | 
			
		||||
        """Assert than an API method was called anytime in the test.
 | 
			
		||||
        """
 | 
			
		||||
        expected = (method, url)
 | 
			
		||||
 | 
			
		||||
        assert self.callstack, \
 | 
			
		||||
            "Expected %s %s but no calls were made." % expected
 | 
			
		||||
 | 
			
		||||
        found = False
 | 
			
		||||
        entry = None
 | 
			
		||||
        for entry in self.callstack:
 | 
			
		||||
            if expected == entry[0:2]:
 | 
			
		||||
                found = True
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
        assert found, 'Expected %s %s; got %s' % \
 | 
			
		||||
            (method, url, self.callstack)
 | 
			
		||||
        if body is not None:
 | 
			
		||||
            assert entry[3] == body, "%s != %s" % (entry[3], body)
 | 
			
		||||
 | 
			
		||||
        self.callstack = []
 | 
			
		||||
 | 
			
		||||
    def clear_callstack(self):
 | 
			
		||||
        self.callstack = []
 | 
			
		||||
 | 
			
		||||
    def authenticate(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def client_request(self, client, method, url, **kwargs):
 | 
			
		||||
        # Check that certain things are called correctly
 | 
			
		||||
        if method in ["GET", "DELETE"]:
 | 
			
		||||
            assert "json" not in kwargs
 | 
			
		||||
 | 
			
		||||
        # Note the call
 | 
			
		||||
        self.callstack.append(
 | 
			
		||||
            (method,
 | 
			
		||||
             url,
 | 
			
		||||
             kwargs.get("headers") or {},
 | 
			
		||||
             kwargs.get("json") or kwargs.get("data")))
 | 
			
		||||
        try:
 | 
			
		||||
            fixture = self.fixtures[url][method]
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            pass
 | 
			
		||||
        else:
 | 
			
		||||
            return TestResponse({"headers": fixture[0],
 | 
			
		||||
                                 "text": fixture[1]})
 | 
			
		||||
 | 
			
		||||
        # Call the method
 | 
			
		||||
        args = parse.parse_qsl(parse.urlparse(url)[4])
 | 
			
		||||
        kwargs.update(args)
 | 
			
		||||
        munged_url = url.rsplit('?', 1)[0]
 | 
			
		||||
        munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_')
 | 
			
		||||
        munged_url = munged_url.replace('-', '_')
 | 
			
		||||
 | 
			
		||||
        callback = "%s_%s" % (method.lower(), munged_url)
 | 
			
		||||
 | 
			
		||||
        if not hasattr(self, callback):
 | 
			
		||||
            raise AssertionError('Called unknown API method: %s %s, '
 | 
			
		||||
                                 'expected fakes method name: %s' %
 | 
			
		||||
                                 (method, url, callback))
 | 
			
		||||
 | 
			
		||||
        resp = getattr(self, callback)(**kwargs)
 | 
			
		||||
        if len(resp) == 3:
 | 
			
		||||
            status, headers, body = resp
 | 
			
		||||
        else:
 | 
			
		||||
            status, body = resp
 | 
			
		||||
            headers = {}
 | 
			
		||||
        self.last_request_id = headers.get('x-openstack-request-id',
 | 
			
		||||
                                           'req-test')
 | 
			
		||||
        return TestResponse({
 | 
			
		||||
            "status_code": status,
 | 
			
		||||
            "text": body,
 | 
			
		||||
            "headers": headers,
 | 
			
		||||
        })
 | 
			
		||||
@@ -1,100 +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.
 | 
			
		||||
 | 
			
		||||
########################################################################
 | 
			
		||||
#
 | 
			
		||||
# THIS MODULE IS DEPRECATED
 | 
			
		||||
#
 | 
			
		||||
# Please refer to
 | 
			
		||||
# https://etherpad.openstack.org/p/kilo-keystoneclient-library-proposals for
 | 
			
		||||
# the discussion leading to this deprecation.
 | 
			
		||||
#
 | 
			
		||||
# We recommend checking out the python-openstacksdk project
 | 
			
		||||
# (https://launchpad.net/python-openstacksdk) instead.
 | 
			
		||||
#
 | 
			
		||||
########################################################################
 | 
			
		||||
 | 
			
		||||
from oslo_utils import encodeutils
 | 
			
		||||
from oslo_utils import uuidutils
 | 
			
		||||
import six
 | 
			
		||||
 | 
			
		||||
from keystoneclient.openstack.common._i18n import _
 | 
			
		||||
from keystoneclient.openstack.common.apiclient import exceptions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def find_resource(manager, name_or_id, **find_args):
 | 
			
		||||
    """Look for resource in a given manager.
 | 
			
		||||
 | 
			
		||||
    Used as a helper for the _find_* methods.
 | 
			
		||||
    Example:
 | 
			
		||||
 | 
			
		||||
    .. code-block:: python
 | 
			
		||||
 | 
			
		||||
        def _find_hypervisor(cs, hypervisor):
 | 
			
		||||
            #Get a hypervisor by name or ID.
 | 
			
		||||
            return cliutils.find_resource(cs.hypervisors, hypervisor)
 | 
			
		||||
    """
 | 
			
		||||
    # first try to get entity as integer id
 | 
			
		||||
    try:
 | 
			
		||||
        return manager.get(int(name_or_id))
 | 
			
		||||
    except (TypeError, ValueError, exceptions.NotFound):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    # now try to get entity as uuid
 | 
			
		||||
    try:
 | 
			
		||||
        if six.PY2:
 | 
			
		||||
            tmp_id = encodeutils.safe_encode(name_or_id)
 | 
			
		||||
        else:
 | 
			
		||||
            tmp_id = encodeutils.safe_decode(name_or_id)
 | 
			
		||||
 | 
			
		||||
        if uuidutils.is_uuid_like(tmp_id):
 | 
			
		||||
            return manager.get(tmp_id)
 | 
			
		||||
    except (TypeError, ValueError, exceptions.NotFound):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    # for str id which is not uuid
 | 
			
		||||
    if getattr(manager, 'is_alphanum_id_allowed', False):
 | 
			
		||||
        try:
 | 
			
		||||
            return manager.get(name_or_id)
 | 
			
		||||
        except exceptions.NotFound:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        try:
 | 
			
		||||
            return manager.find(human_id=name_or_id, **find_args)
 | 
			
		||||
        except exceptions.NotFound:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        # finally try to find entity by name
 | 
			
		||||
        try:
 | 
			
		||||
            resource = getattr(manager, 'resource_class', None)
 | 
			
		||||
            name_attr = resource.NAME_ATTR if resource else 'name'
 | 
			
		||||
            kwargs = {name_attr: name_or_id}
 | 
			
		||||
            kwargs.update(find_args)
 | 
			
		||||
            return manager.find(**kwargs)
 | 
			
		||||
        except exceptions.NotFound:
 | 
			
		||||
            msg = _("No %(name)s with a name or "
 | 
			
		||||
                    "ID of '%(name_or_id)s' exists.") % \
 | 
			
		||||
                {
 | 
			
		||||
                    "name": manager.resource_class.__name__.lower(),
 | 
			
		||||
                    "name_or_id": name_or_id
 | 
			
		||||
                }
 | 
			
		||||
            raise exceptions.CommandError(msg)
 | 
			
		||||
    except exceptions.NoUniqueMatch:
 | 
			
		||||
        msg = _("Multiple %(name)s matches found for "
 | 
			
		||||
                "'%(name_or_id)s', use an ID to be more specific.") % \
 | 
			
		||||
            {
 | 
			
		||||
                "name": manager.resource_class.__name__.lower(),
 | 
			
		||||
                "name_or_id": name_or_id
 | 
			
		||||
            }
 | 
			
		||||
        raise exceptions.CommandError(msg)
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
[DEFAULT]
 | 
			
		||||
 | 
			
		||||
# The list of modules to copy from oslo-incubator
 | 
			
		||||
module=apiclient
 | 
			
		||||
 | 
			
		||||
# The base module to hold the copy of openstack.common
 | 
			
		||||
base=keystoneclient
 | 
			
		||||
@@ -0,0 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
other:
 | 
			
		||||
  - >
 | 
			
		||||
    Removed `keystoneclient.apiclient.exceptions`. This file was deprecated
 | 
			
		||||
    in v0.7.1 and has now been replaced by `keystoneclient.exceptions`.
 | 
			
		||||
		Reference in New Issue
	
	Block a user