Add support for Keystone v3
This enables glanceclient to authenticate using Keystone v3 API and includes the addition of several new CLI arguments. DocImpact Change-Id: I863ba08d312363dc1ce4fc7822fb21ef53df1a4f
This commit is contained in:
@@ -27,53 +27,32 @@ import os
|
|||||||
from os.path import expanduser
|
from os.path import expanduser
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from keystoneclient.v2_0 import client as ksclient
|
|
||||||
import netaddr
|
import netaddr
|
||||||
|
import six.moves.urllib.parse as urlparse
|
||||||
|
|
||||||
import glanceclient
|
import glanceclient
|
||||||
from glanceclient.common import utils
|
from glanceclient.common import utils
|
||||||
from glanceclient import exc
|
from glanceclient import exc
|
||||||
from glanceclient.openstack.common import strutils
|
from glanceclient.openstack.common import strutils
|
||||||
|
|
||||||
|
from keystoneclient.auth.identity import v2 as v2_auth
|
||||||
|
from keystoneclient.auth.identity import v3 as v3_auth
|
||||||
|
from keystoneclient import discover
|
||||||
|
from keystoneclient.openstack.common.apiclient import exceptions as ks_exc
|
||||||
|
from keystoneclient import session
|
||||||
|
|
||||||
|
|
||||||
class OpenStackImagesShell(object):
|
class OpenStackImagesShell(object):
|
||||||
|
|
||||||
def get_base_parser(self):
|
def _append_global_identity_args(self, parser):
|
||||||
parser = argparse.ArgumentParser(
|
# FIXME(bobt): these are global identity (Keystone) arguments which
|
||||||
prog='glance',
|
# should be consistent and shared by all service clients. Therefore,
|
||||||
description=__doc__.strip(),
|
# they should be provided by python-keystoneclient. We will need to
|
||||||
epilog='See "glance help COMMAND" '
|
# refactor this code once this functionality is avaible in
|
||||||
'for help on a specific command.',
|
# python-keystoneclient. See
|
||||||
add_help=False,
|
#
|
||||||
formatter_class=HelpFormatter,
|
# https://bugs.launchpad.net/python-keystoneclient/+bug/1332337
|
||||||
)
|
#
|
||||||
|
|
||||||
# Global arguments
|
|
||||||
parser.add_argument('-h', '--help',
|
|
||||||
action='store_true',
|
|
||||||
help=argparse.SUPPRESS,
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument('--version',
|
|
||||||
action='version',
|
|
||||||
version=glanceclient.__version__)
|
|
||||||
|
|
||||||
parser.add_argument('-d', '--debug',
|
|
||||||
default=bool(utils.env('GLANCECLIENT_DEBUG')),
|
|
||||||
action='store_true',
|
|
||||||
help='Defaults to env[GLANCECLIENT_DEBUG].')
|
|
||||||
|
|
||||||
parser.add_argument('-v', '--verbose',
|
|
||||||
default=False, action="store_true",
|
|
||||||
help="Print more verbose output")
|
|
||||||
|
|
||||||
parser.add_argument('--get-schema',
|
|
||||||
default=False, action="store_true",
|
|
||||||
dest='get_schema',
|
|
||||||
help='Ignores cached copy and forces retrieval '
|
|
||||||
'of schema that generates portions of the '
|
|
||||||
'help text. Ignored with API version 1.')
|
|
||||||
|
|
||||||
parser.add_argument('-k', '--insecure',
|
parser.add_argument('-k', '--insecure',
|
||||||
default=False,
|
default=False,
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@@ -83,16 +62,24 @@ class OpenStackImagesShell(object):
|
|||||||
'certificate authorities. This option should '
|
'certificate authorities. This option should '
|
||||||
'be used with caution.')
|
'be used with caution.')
|
||||||
|
|
||||||
parser.add_argument('--cert-file',
|
parser.add_argument('--os-cert',
|
||||||
help='Path of certificate file to use in SSL '
|
help='Path of certificate file to use in SSL '
|
||||||
'connection. This file can optionally be '
|
'connection. This file can optionally be '
|
||||||
'prepended with the private key.')
|
'prepended with the private key.')
|
||||||
|
|
||||||
parser.add_argument('--key-file',
|
parser.add_argument('--cert-file',
|
||||||
|
dest='os_cert',
|
||||||
|
help='DEPRECATED! Use --os-cert.')
|
||||||
|
|
||||||
|
parser.add_argument('--os-key',
|
||||||
help='Path of client key to use in SSL '
|
help='Path of client key to use in SSL '
|
||||||
'connection. This option is not necessary '
|
'connection. This option is not necessary '
|
||||||
'if your key is prepended to your cert file.')
|
'if your key is prepended to your cert file.')
|
||||||
|
|
||||||
|
parser.add_argument('--key-file',
|
||||||
|
dest='os_key',
|
||||||
|
help='DEPRECATED! Use --os-key.')
|
||||||
|
|
||||||
parser.add_argument('--os-cacert',
|
parser.add_argument('--os-cacert',
|
||||||
metavar='<ca-certificate-file>',
|
metavar='<ca-certificate-file>',
|
||||||
dest='os_cacert',
|
dest='os_cacert',
|
||||||
@@ -106,51 +93,6 @@ class OpenStackImagesShell(object):
|
|||||||
dest='os_cacert',
|
dest='os_cacert',
|
||||||
help='DEPRECATED! Use --os-cacert.')
|
help='DEPRECATED! Use --os-cacert.')
|
||||||
|
|
||||||
parser.add_argument('--timeout',
|
|
||||||
default=600,
|
|
||||||
help='Number of seconds to wait for a response')
|
|
||||||
|
|
||||||
parser.add_argument('--no-ssl-compression',
|
|
||||||
dest='ssl_compression',
|
|
||||||
default=True, action='store_false',
|
|
||||||
help='Disable SSL compression when using https.')
|
|
||||||
|
|
||||||
parser.add_argument('-f', '--force',
|
|
||||||
dest='force',
|
|
||||||
default=False, action='store_true',
|
|
||||||
help='Prevent select actions from requesting '
|
|
||||||
'user confirmation.')
|
|
||||||
|
|
||||||
#NOTE(bcwaldon): DEPRECATED
|
|
||||||
parser.add_argument('--dry-run',
|
|
||||||
default=False,
|
|
||||||
action='store_true',
|
|
||||||
help='DEPRECATED! Only used for deprecated '
|
|
||||||
'legacy commands.')
|
|
||||||
|
|
||||||
#NOTE(bcwaldon): DEPRECATED
|
|
||||||
parser.add_argument('--ssl',
|
|
||||||
dest='use_ssl',
|
|
||||||
default=False,
|
|
||||||
action='store_true',
|
|
||||||
help='DEPRECATED! Send a fully-formed endpoint '
|
|
||||||
'using --os-image-url instead.')
|
|
||||||
|
|
||||||
#NOTE(bcwaldon): DEPRECATED
|
|
||||||
parser.add_argument('-H', '--host',
|
|
||||||
metavar='ADDRESS',
|
|
||||||
help='DEPRECATED! Send a fully-formed endpoint '
|
|
||||||
'using --os-image-url instead.')
|
|
||||||
|
|
||||||
#NOTE(bcwaldon): DEPRECATED
|
|
||||||
parser.add_argument('-p', '--port',
|
|
||||||
dest='port',
|
|
||||||
metavar='PORT',
|
|
||||||
type=int,
|
|
||||||
default=9292,
|
|
||||||
help='DEPRECATED! Send a fully-formed endpoint '
|
|
||||||
'using --os-image-url instead.')
|
|
||||||
|
|
||||||
parser.add_argument('--os-username',
|
parser.add_argument('--os-username',
|
||||||
default=utils.env('OS_USERNAME'),
|
default=utils.env('OS_USERNAME'),
|
||||||
help='Defaults to env[OS_USERNAME].')
|
help='Defaults to env[OS_USERNAME].')
|
||||||
@@ -158,6 +100,40 @@ class OpenStackImagesShell(object):
|
|||||||
parser.add_argument('--os_username',
|
parser.add_argument('--os_username',
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument('--os-user-id',
|
||||||
|
default=utils.env('OS_USER_ID'),
|
||||||
|
help='Defaults to env[OS_USER_ID].')
|
||||||
|
|
||||||
|
parser.add_argument('--os-user-domain-id',
|
||||||
|
default=utils.env('OS_USER_DOMAIN_ID'),
|
||||||
|
help='Defaults to env[OS_USER_DOMAIN_ID].')
|
||||||
|
|
||||||
|
parser.add_argument('--os-user-domain-name',
|
||||||
|
default=utils.env('OS_USER_DOMAIN_NAME'),
|
||||||
|
help='Defaults to env[OS_USER_DOMAIN_NAME].')
|
||||||
|
|
||||||
|
parser.add_argument('--os-project-id',
|
||||||
|
default=utils.env('OS_PROJECT_ID'),
|
||||||
|
help='Another way to specify tenant ID. '
|
||||||
|
'This option is mutually exclusive with '
|
||||||
|
' --os-tenant-id. '
|
||||||
|
'Defaults to env[OS_PROJECT_ID].')
|
||||||
|
|
||||||
|
parser.add_argument('--os-project-name',
|
||||||
|
default=utils.env('OS_PROJECT_NAME'),
|
||||||
|
help='Another way to specify tenant name. '
|
||||||
|
'This option is mutually exclusive with '
|
||||||
|
' --os-tenant-name. '
|
||||||
|
'Defaults to env[OS_PROJECT_NAME].')
|
||||||
|
|
||||||
|
parser.add_argument('--os-project-domain-id',
|
||||||
|
default=utils.env('OS_PROJECT_DOMAIN_ID'),
|
||||||
|
help='Defaults to env[OS_PROJECT_DOMAIN_ID].')
|
||||||
|
|
||||||
|
parser.add_argument('--os-project-domain-name',
|
||||||
|
default=utils.env('OS_PROJECT_DOMAIN_NAME'),
|
||||||
|
help='Defaults to env[OS_PROJECT_DOMAIN_NAME].')
|
||||||
|
|
||||||
#NOTE(bcwaldon): DEPRECATED
|
#NOTE(bcwaldon): DEPRECATED
|
||||||
parser.add_argument('-I',
|
parser.add_argument('-I',
|
||||||
dest='os_username',
|
dest='os_username',
|
||||||
@@ -230,6 +206,101 @@ class OpenStackImagesShell(object):
|
|||||||
dest='os_auth_token',
|
dest='os_auth_token',
|
||||||
help='DEPRECATED! Use --os-auth-token.')
|
help='DEPRECATED! Use --os-auth-token.')
|
||||||
|
|
||||||
|
parser.add_argument('--os-service-type',
|
||||||
|
default=utils.env('OS_SERVICE_TYPE'),
|
||||||
|
help='Defaults to env[OS_SERVICE_TYPE].')
|
||||||
|
|
||||||
|
parser.add_argument('--os_service_type',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
parser.add_argument('--os-endpoint-type',
|
||||||
|
default=utils.env('OS_ENDPOINT_TYPE'),
|
||||||
|
help='Defaults to env[OS_ENDPOINT_TYPE].')
|
||||||
|
|
||||||
|
parser.add_argument('--os_endpoint_type',
|
||||||
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
def get_base_parser(self):
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog='glance',
|
||||||
|
description=__doc__.strip(),
|
||||||
|
epilog='See "glance help COMMAND" '
|
||||||
|
'for help on a specific command.',
|
||||||
|
add_help=False,
|
||||||
|
formatter_class=HelpFormatter,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Global arguments
|
||||||
|
parser.add_argument('-h', '--help',
|
||||||
|
action='store_true',
|
||||||
|
help=argparse.SUPPRESS,
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument('--version',
|
||||||
|
action='version',
|
||||||
|
version=glanceclient.__version__)
|
||||||
|
|
||||||
|
parser.add_argument('-d', '--debug',
|
||||||
|
default=bool(utils.env('GLANCECLIENT_DEBUG')),
|
||||||
|
action='store_true',
|
||||||
|
help='Defaults to env[GLANCECLIENT_DEBUG].')
|
||||||
|
|
||||||
|
parser.add_argument('-v', '--verbose',
|
||||||
|
default=False, action="store_true",
|
||||||
|
help="Print more verbose output")
|
||||||
|
|
||||||
|
parser.add_argument('--get-schema',
|
||||||
|
default=False, action="store_true",
|
||||||
|
dest='get_schema',
|
||||||
|
help='Ignores cached copy and forces retrieval '
|
||||||
|
'of schema that generates portions of the '
|
||||||
|
'help text. Ignored with API version 1.')
|
||||||
|
|
||||||
|
parser.add_argument('--timeout',
|
||||||
|
default=600,
|
||||||
|
help='Number of seconds to wait for a response')
|
||||||
|
|
||||||
|
parser.add_argument('--no-ssl-compression',
|
||||||
|
dest='ssl_compression',
|
||||||
|
default=True, action='store_false',
|
||||||
|
help='Disable SSL compression when using https.')
|
||||||
|
|
||||||
|
parser.add_argument('-f', '--force',
|
||||||
|
dest='force',
|
||||||
|
default=False, action='store_true',
|
||||||
|
help='Prevent select actions from requesting '
|
||||||
|
'user confirmation.')
|
||||||
|
|
||||||
|
#NOTE(bcwaldon): DEPRECATED
|
||||||
|
parser.add_argument('--dry-run',
|
||||||
|
default=False,
|
||||||
|
action='store_true',
|
||||||
|
help='DEPRECATED! Only used for deprecated '
|
||||||
|
'legacy commands.')
|
||||||
|
|
||||||
|
#NOTE(bcwaldon): DEPRECATED
|
||||||
|
parser.add_argument('--ssl',
|
||||||
|
dest='use_ssl',
|
||||||
|
default=False,
|
||||||
|
action='store_true',
|
||||||
|
help='DEPRECATED! Send a fully-formed endpoint '
|
||||||
|
'using --os-image-url instead.')
|
||||||
|
|
||||||
|
#NOTE(bcwaldon): DEPRECATED
|
||||||
|
parser.add_argument('-H', '--host',
|
||||||
|
metavar='ADDRESS',
|
||||||
|
help='DEPRECATED! Send a fully-formed endpoint '
|
||||||
|
'using --os-image-url instead.')
|
||||||
|
|
||||||
|
#NOTE(bcwaldon): DEPRECATED
|
||||||
|
parser.add_argument('-p', '--port',
|
||||||
|
dest='port',
|
||||||
|
metavar='PORT',
|
||||||
|
type=int,
|
||||||
|
default=9292,
|
||||||
|
help='DEPRECATED! Send a fully-formed endpoint '
|
||||||
|
'using --os-image-url instead.')
|
||||||
|
|
||||||
parser.add_argument('--os-image-url',
|
parser.add_argument('--os-image-url',
|
||||||
default=utils.env('OS_IMAGE_URL'),
|
default=utils.env('OS_IMAGE_URL'),
|
||||||
help='Defaults to env[OS_IMAGE_URL].')
|
help='Defaults to env[OS_IMAGE_URL].')
|
||||||
@@ -250,25 +321,14 @@ class OpenStackImagesShell(object):
|
|||||||
parser.add_argument('--os_image_api_version',
|
parser.add_argument('--os_image_api_version',
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
|
|
||||||
parser.add_argument('--os-service-type',
|
|
||||||
default=utils.env('OS_SERVICE_TYPE'),
|
|
||||||
help='Defaults to env[OS_SERVICE_TYPE].')
|
|
||||||
|
|
||||||
parser.add_argument('--os_service_type',
|
|
||||||
help=argparse.SUPPRESS)
|
|
||||||
|
|
||||||
parser.add_argument('--os-endpoint-type',
|
|
||||||
default=utils.env('OS_ENDPOINT_TYPE'),
|
|
||||||
help='Defaults to env[OS_ENDPOINT_TYPE].')
|
|
||||||
|
|
||||||
parser.add_argument('--os_endpoint_type',
|
|
||||||
help=argparse.SUPPRESS)
|
|
||||||
|
|
||||||
#NOTE(bcwaldon): DEPRECATED
|
#NOTE(bcwaldon): DEPRECATED
|
||||||
parser.add_argument('-S', '--os_auth_strategy',
|
parser.add_argument('-S', '--os_auth_strategy',
|
||||||
help='DEPRECATED! This option is '
|
help='DEPRECATED! This option is '
|
||||||
'completely ignored.')
|
'completely ignored.')
|
||||||
|
|
||||||
|
# FIXME(bobt): this method should come from python-keystoneclient
|
||||||
|
self._append_global_identity_args(parser)
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def get_subcommand_parser(self, version):
|
def get_subcommand_parser(self, version):
|
||||||
@@ -306,36 +366,6 @@ class OpenStackImagesShell(object):
|
|||||||
subparser.add_argument(*args, **kwargs)
|
subparser.add_argument(*args, **kwargs)
|
||||||
subparser.set_defaults(func=callback)
|
subparser.set_defaults(func=callback)
|
||||||
|
|
||||||
def _get_ksclient(self, **kwargs):
|
|
||||||
"""Get an endpoint and auth token from Keystone.
|
|
||||||
|
|
||||||
:param username: name of user
|
|
||||||
:param password: user's password
|
|
||||||
:param tenant_id: unique identifier of tenant
|
|
||||||
:param tenant_name: name of tenant
|
|
||||||
:param auth_url: endpoint to authenticate against
|
|
||||||
"""
|
|
||||||
return ksclient.Client(username=kwargs.get('username'),
|
|
||||||
password=kwargs.get('password'),
|
|
||||||
tenant_id=kwargs.get('tenant_id'),
|
|
||||||
tenant_name=kwargs.get('tenant_name'),
|
|
||||||
auth_url=kwargs.get('auth_url'),
|
|
||||||
cacert=kwargs.get('cacert'),
|
|
||||||
insecure=kwargs.get('insecure'))
|
|
||||||
|
|
||||||
def _get_endpoint(self, client, **kwargs):
|
|
||||||
"""Get an endpoint using the provided keystone client."""
|
|
||||||
endpoint_kwargs = {
|
|
||||||
'service_type': kwargs.get('service_type') or 'image',
|
|
||||||
'endpoint_type': kwargs.get('endpoint_type') or 'publicURL',
|
|
||||||
}
|
|
||||||
|
|
||||||
if kwargs.get('region_name'):
|
|
||||||
endpoint_kwargs['attr'] = 'region'
|
|
||||||
endpoint_kwargs['filter_value'] = kwargs.get('region_name')
|
|
||||||
|
|
||||||
return client.service_catalog.url_for(**endpoint_kwargs)
|
|
||||||
|
|
||||||
def _get_image_url(self, args):
|
def _get_image_url(self, args):
|
||||||
"""Translate the available url-related options into a single string.
|
"""Translate the available url-related options into a single string.
|
||||||
|
|
||||||
@@ -353,6 +383,101 @@ class OpenStackImagesShell(object):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _discover_auth_versions(self, session, auth_url):
|
||||||
|
# discover the API versions the server is supporting base on the
|
||||||
|
# given URL
|
||||||
|
v2_auth_url = None
|
||||||
|
v3_auth_url = None
|
||||||
|
try:
|
||||||
|
ks_discover = discover.Discover(session=session, auth_url=auth_url)
|
||||||
|
v2_auth_url = ks_discover.url_for('2.0')
|
||||||
|
v3_auth_url = ks_discover.url_for('3.0')
|
||||||
|
except ks_exc.ClientException as e:
|
||||||
|
# Identity service may not support discover API version.
|
||||||
|
# Lets trying to figure out the API version from the original URL.
|
||||||
|
url_parts = urlparse.urlparse(auth_url)
|
||||||
|
(scheme, netloc, path, params, query, fragment) = url_parts
|
||||||
|
path = path.lower()
|
||||||
|
if path.startswith('/v3'):
|
||||||
|
v3_auth_url = auth_url
|
||||||
|
elif path.startswith('/v2'):
|
||||||
|
v2_auth_url = auth_url
|
||||||
|
else:
|
||||||
|
# not enough information to determine the auth version
|
||||||
|
msg = ('Unable to determine the Keystone version '
|
||||||
|
'to authenticate with using the given '
|
||||||
|
'auth_url. Identity service may not support API '
|
||||||
|
'version discovery. Please provide a versioned '
|
||||||
|
'auth_url instead. error=%s') % (e)
|
||||||
|
raise exc.CommandError(msg)
|
||||||
|
|
||||||
|
return (v2_auth_url, v3_auth_url)
|
||||||
|
|
||||||
|
def _get_keystone_session(self, **kwargs):
|
||||||
|
ks_session = session.Session.construct(kwargs)
|
||||||
|
|
||||||
|
# discover the supported keystone versions using the given auth url
|
||||||
|
auth_url = kwargs.pop('auth_url', None)
|
||||||
|
(v2_auth_url, v3_auth_url) = self._discover_auth_versions(
|
||||||
|
session=ks_session,
|
||||||
|
auth_url=auth_url)
|
||||||
|
|
||||||
|
# Determine which authentication plugin to use. First inspect the
|
||||||
|
# auth_url to see the supported version. If both v3 and v2 are
|
||||||
|
# supported, then use the highest version if possible.
|
||||||
|
user_id = kwargs.pop('user_id', None)
|
||||||
|
username = kwargs.pop('username', None)
|
||||||
|
password = kwargs.pop('password', None)
|
||||||
|
user_domain_name = kwargs.pop('user_domain_name', None)
|
||||||
|
user_domain_id = kwargs.pop('user_domain_id', None)
|
||||||
|
# project and tenant can be used interchangeably
|
||||||
|
project_id = (kwargs.pop('project_id', None) or
|
||||||
|
kwargs.pop('tenant_id', None))
|
||||||
|
project_name = (kwargs.pop('project_name', None) or
|
||||||
|
kwargs.pop('tenant_name', None))
|
||||||
|
project_domain_id = kwargs.pop('project_domain_id', None)
|
||||||
|
project_domain_name = kwargs.pop('project_domain_name', None)
|
||||||
|
auth = None
|
||||||
|
|
||||||
|
use_domain = (user_domain_id or
|
||||||
|
user_domain_name or
|
||||||
|
project_domain_id or
|
||||||
|
project_domain_name)
|
||||||
|
use_v3 = v3_auth_url and (use_domain or (not v2_auth_url))
|
||||||
|
use_v2 = v2_auth_url and not use_domain
|
||||||
|
|
||||||
|
if use_v3:
|
||||||
|
auth = v3_auth.Password(
|
||||||
|
v3_auth_url,
|
||||||
|
user_id=user_id,
|
||||||
|
username=username,
|
||||||
|
password=password,
|
||||||
|
user_domain_id=user_domain_id,
|
||||||
|
user_domain_name=user_domain_name,
|
||||||
|
project_id=project_id,
|
||||||
|
project_name=project_name,
|
||||||
|
project_domain_id=project_domain_id,
|
||||||
|
project_domain_name=project_domain_name)
|
||||||
|
elif use_v2:
|
||||||
|
auth = v2_auth.Password(
|
||||||
|
v2_auth_url,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
tenant_id=project_id,
|
||||||
|
tenant_name=project_name)
|
||||||
|
else:
|
||||||
|
# if we get here it means domain information is provided
|
||||||
|
# (caller meant to use Keystone V3) but the auth url is
|
||||||
|
# actually Keystone V2. Obviously we can't authenticate a V3
|
||||||
|
# user using V2.
|
||||||
|
exc.CommandError("Credential and auth_url mismatch. The given "
|
||||||
|
"auth_url is using Keystone V2 endpoint, which "
|
||||||
|
"may not able to handle Keystone V3 credentials. "
|
||||||
|
"Please provide a correct Keystone V3 auth_url.")
|
||||||
|
|
||||||
|
ks_session.auth = auth
|
||||||
|
return ks_session
|
||||||
|
|
||||||
def _get_endpoint_and_token(self, args, force_auth=False):
|
def _get_endpoint_and_token(self, args, force_auth=False):
|
||||||
image_url = self._get_image_url(args)
|
image_url = self._get_image_url(args)
|
||||||
auth_token = args.os_auth_token
|
auth_token = args.os_auth_token
|
||||||
@@ -364,42 +489,74 @@ class OpenStackImagesShell(object):
|
|||||||
endpoint = image_url
|
endpoint = image_url
|
||||||
token = args.os_auth_token
|
token = args.os_auth_token
|
||||||
else:
|
else:
|
||||||
|
|
||||||
if not args.os_username:
|
if not args.os_username:
|
||||||
raise exc.CommandError("You must provide a username via"
|
raise exc.CommandError(
|
||||||
|
_("You must provide a username via"
|
||||||
" either --os-username or "
|
" either --os-username or "
|
||||||
"env[OS_USERNAME]")
|
"env[OS_USERNAME]"))
|
||||||
|
|
||||||
if not args.os_password:
|
if not args.os_password:
|
||||||
raise exc.CommandError("You must provide a password via"
|
raise exc.CommandError(
|
||||||
|
_("You must provide a password via"
|
||||||
" either --os-password or "
|
" either --os-password or "
|
||||||
"env[OS_PASSWORD]")
|
"env[OS_PASSWORD]"))
|
||||||
|
|
||||||
if not (args.os_tenant_id or args.os_tenant_name):
|
# Validate password flow auth
|
||||||
raise exc.CommandError("You must provide a tenant_id via"
|
project_info = (args.os_tenant_name or
|
||||||
" either --os-tenant-id or "
|
args.os_tenant_id or
|
||||||
"via env[OS_TENANT_ID]")
|
(args.os_project_name and
|
||||||
|
(args.project_domain_name or
|
||||||
|
args.project_domain_id)) or
|
||||||
|
args.os_project_id)
|
||||||
|
|
||||||
|
if (not project_info):
|
||||||
|
# tenent is deprecated in Keystone v3. Use the latest
|
||||||
|
# terminology instead.
|
||||||
|
raise exc.CommandError(
|
||||||
|
_("You must provide a project_id or project_name ("
|
||||||
|
"with project_domain_name or project_domain_id) "
|
||||||
|
"via "
|
||||||
|
" --os-project-id (env[OS_PROJECT_ID])"
|
||||||
|
" --os-project-name (env[OS_PROJECT_NAME]),"
|
||||||
|
" --os-project-domain-id "
|
||||||
|
"(env[OS_PROJECT_DOMAIN_ID])"
|
||||||
|
" --os-project-domain-name "
|
||||||
|
"(env[OS_PROJECT_DOMAIN_NAME])"))
|
||||||
|
|
||||||
if not args.os_auth_url:
|
if not args.os_auth_url:
|
||||||
raise exc.CommandError("You must provide an auth url via"
|
raise exc.CommandError(
|
||||||
|
_("You must provide an auth url via"
|
||||||
" either --os-auth-url or "
|
" either --os-auth-url or "
|
||||||
"via env[OS_AUTH_URL]")
|
"via env[OS_AUTH_URL]"))
|
||||||
kwargs = {
|
|
||||||
'username': args.os_username,
|
|
||||||
'password': args.os_password,
|
|
||||||
'tenant_id': args.os_tenant_id,
|
|
||||||
'tenant_name': args.os_tenant_name,
|
|
||||||
'auth_url': args.os_auth_url,
|
|
||||||
'service_type': args.os_service_type,
|
|
||||||
'endpoint_type': args.os_endpoint_type,
|
|
||||||
'cacert': args.os_cacert,
|
|
||||||
'insecure': args.insecure,
|
|
||||||
'region_name': args.os_region_name,
|
|
||||||
}
|
|
||||||
_ksclient = self._get_ksclient(**kwargs)
|
|
||||||
token = args.os_auth_token or _ksclient.auth_token
|
|
||||||
|
|
||||||
endpoint = args.os_image_url or self._get_endpoint(_ksclient,
|
kwargs = {
|
||||||
**kwargs)
|
'auth_url': args.os_auth_url,
|
||||||
|
'username': args.os_username,
|
||||||
|
'user_id': args.os_user_id,
|
||||||
|
'user_domain_id': args.os_user_domain_id,
|
||||||
|
'user_domain_name': args.os_user_domain_name,
|
||||||
|
'password': args.os_password,
|
||||||
|
'tenant_name': args.os_tenant_name,
|
||||||
|
'tenant_id': args.os_tenant_id,
|
||||||
|
'project_name': args.os_project_name,
|
||||||
|
'project_id': args.os_project_id,
|
||||||
|
'project_domain_name': args.os_project_domain_name,
|
||||||
|
'project_domain_id': args.os_project_domain_id,
|
||||||
|
'insecure': args.insecure,
|
||||||
|
'cacert': args.os_cacert,
|
||||||
|
'cert': args.os_cert,
|
||||||
|
'key': args.os_key
|
||||||
|
}
|
||||||
|
ks_session = self._get_keystone_session(**kwargs)
|
||||||
|
token = args.os_auth_token or ks_session.get_token()
|
||||||
|
|
||||||
|
endpoint_type = args.os_endpoint_type or 'public'
|
||||||
|
service_type = args.os_service_type or 'image'
|
||||||
|
endpoint = args.os_image_url or ks_session.get_endpoint(
|
||||||
|
service_type=service_type,
|
||||||
|
endpoint_type=endpoint_type,
|
||||||
|
region_name=args.os_region_name)
|
||||||
|
|
||||||
return endpoint, token
|
return endpoint, token
|
||||||
|
|
||||||
@@ -412,8 +569,8 @@ class OpenStackImagesShell(object):
|
|||||||
'insecure': args.insecure,
|
'insecure': args.insecure,
|
||||||
'timeout': args.timeout,
|
'timeout': args.timeout,
|
||||||
'cacert': args.os_cacert,
|
'cacert': args.os_cacert,
|
||||||
'cert_file': args.cert_file,
|
'cert': args.os_cert,
|
||||||
'key_file': args.key_file,
|
'key': args.os_key,
|
||||||
'ssl_compression': args.ssl_compression
|
'ssl_compression': args.ssl_compression
|
||||||
}
|
}
|
||||||
client = glanceclient.Client(api_version, endpoint, **kwargs)
|
client = glanceclient.Client(api_version, endpoint, **kwargs)
|
||||||
|
|||||||
188
tests/keystone_client_fixtures.py
Normal file
188
tests/keystone_client_fixtures.py
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
# 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 json
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
# these are copied from python-keystoneclient tests
|
||||||
|
BASE_HOST = 'http://keystone.example.com'
|
||||||
|
BASE_URL = "%s:5000/" % BASE_HOST
|
||||||
|
UPDATED = '2013-03-06T00:00:00Z'
|
||||||
|
|
||||||
|
V2_URL = "%sv2.0" % BASE_URL
|
||||||
|
V2_DESCRIBED_BY_HTML = {'href': 'http://docs.openstack.org/api/'
|
||||||
|
'openstack-identity-service/2.0/content/',
|
||||||
|
'rel': 'describedby',
|
||||||
|
'type': 'text/html'}
|
||||||
|
V2_DESCRIBED_BY_PDF = {'href': 'http://docs.openstack.org/api/openstack-ident'
|
||||||
|
'ity-service/2.0/identity-dev-guide-2.0.pdf',
|
||||||
|
'rel': 'describedby',
|
||||||
|
'type': 'application/pdf'}
|
||||||
|
|
||||||
|
V2_VERSION = {'id': 'v2.0',
|
||||||
|
'links': [{'href': V2_URL, 'rel': 'self'},
|
||||||
|
V2_DESCRIBED_BY_HTML, V2_DESCRIBED_BY_PDF],
|
||||||
|
'status': 'stable',
|
||||||
|
'updated': UPDATED}
|
||||||
|
|
||||||
|
V3_URL = "%sv3" % BASE_URL
|
||||||
|
V3_MEDIA_TYPES = [{'base': 'application/json',
|
||||||
|
'type': 'application/vnd.openstack.identity-v3+json'},
|
||||||
|
{'base': 'application/xml',
|
||||||
|
'type': 'application/vnd.openstack.identity-v3+xml'}]
|
||||||
|
|
||||||
|
V3_VERSION = {'id': 'v3.0',
|
||||||
|
'links': [{'href': V3_URL, 'rel': 'self'}],
|
||||||
|
'media-types': V3_MEDIA_TYPES,
|
||||||
|
'status': 'stable',
|
||||||
|
'updated': UPDATED}
|
||||||
|
|
||||||
|
|
||||||
|
def _create_version_list(versions):
|
||||||
|
return json.dumps({'versions': {'values': versions}})
|
||||||
|
|
||||||
|
|
||||||
|
def _create_single_version(version):
|
||||||
|
return json.dumps({'version': version})
|
||||||
|
|
||||||
|
|
||||||
|
V3_VERSION_LIST = _create_version_list([V3_VERSION, V2_VERSION])
|
||||||
|
V2_VERSION_LIST = _create_version_list([V2_VERSION])
|
||||||
|
|
||||||
|
V3_VERSION_ENTRY = _create_single_version(V3_VERSION)
|
||||||
|
V2_VERSION_ENTRY = _create_single_version(V2_VERSION)
|
||||||
|
|
||||||
|
GLANCE_ENDPOINT = 'http://glance.example.com/v1'
|
||||||
|
|
||||||
|
|
||||||
|
def _get_normalized_token_data(**kwargs):
|
||||||
|
ref = copy.deepcopy(kwargs)
|
||||||
|
# normalized token data
|
||||||
|
ref['user_id'] = ref.get('user_id', uuid.uuid4().hex)
|
||||||
|
ref['username'] = ref.get('username', uuid.uuid4().hex)
|
||||||
|
ref['project_id'] = ref.get('project_id',
|
||||||
|
ref.get('tenant_id', uuid.uuid4().hex))
|
||||||
|
ref['project_name'] = ref.get('tenant_name',
|
||||||
|
ref.get('tenant_name', uuid.uuid4().hex))
|
||||||
|
ref['user_domain_id'] = ref.get('user_domain_id', uuid.uuid4().hex)
|
||||||
|
ref['user_domain_name'] = ref.get('user_domain_name', uuid.uuid4().hex)
|
||||||
|
ref['project_domain_id'] = ref.get('project_domain_id', uuid.uuid4().hex)
|
||||||
|
ref['project_domain_name'] = ref.get('project_domain_name',
|
||||||
|
uuid.uuid4().hex)
|
||||||
|
ref['roles'] = ref.get('roles', [{'name': uuid.uuid4().hex,
|
||||||
|
'id': uuid.uuid4().hex}])
|
||||||
|
ref['roles_link'] = ref.get('roles_link', [])
|
||||||
|
ref['glance_url'] = ref.get('glance_url', GLANCE_ENDPOINT)
|
||||||
|
|
||||||
|
return ref
|
||||||
|
|
||||||
|
|
||||||
|
def generate_v2_project_scoped_token(**kwargs):
|
||||||
|
"""Generate a Keystone V2 token based on auth request."""
|
||||||
|
ref = _get_normalized_token_data(**kwargs)
|
||||||
|
|
||||||
|
o = {'access': {'token': {'id': uuid.uuid4().hex,
|
||||||
|
'expires': '2099-05-22T00:02:43.941430Z',
|
||||||
|
'issued_at': '2013-05-21T00:02:43.941473Z',
|
||||||
|
'tenant': {'enabled': True,
|
||||||
|
'id': ref.get('project_id'),
|
||||||
|
'name': ref.get('project_id')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'user': {'id': ref.get('user_id'),
|
||||||
|
'name': uuid.uuid4().hex,
|
||||||
|
'username': ref.get('username'),
|
||||||
|
'roles': ref.get('roles'),
|
||||||
|
'roles_links': ref.get('roles_links')
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
# we only care about Glance and Keystone endpoints
|
||||||
|
o['access']['serviceCatalog'] = [
|
||||||
|
{'endpoints': [
|
||||||
|
{'publicURL': ref.get('glance_url'),
|
||||||
|
'id': uuid.uuid4().hex,
|
||||||
|
'region': 'RegionOne'
|
||||||
|
}],
|
||||||
|
'endpoints_links': [],
|
||||||
|
'name': 'Glance',
|
||||||
|
'type': 'keystore'},
|
||||||
|
{'endpoints': [
|
||||||
|
{'publicURL': ref.get('auth_url'),
|
||||||
|
'adminURL': ref.get('auth_url'),
|
||||||
|
'id': uuid.uuid4().hex,
|
||||||
|
'region': 'RegionOne'
|
||||||
|
}],
|
||||||
|
'endpoint_links': [],
|
||||||
|
'name': 'keystone',
|
||||||
|
'type': 'identity'}]
|
||||||
|
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
def generate_v3_project_scoped_token(**kwargs):
|
||||||
|
"""Generate a Keystone V3 token based on auth request."""
|
||||||
|
ref = _get_normalized_token_data(**kwargs)
|
||||||
|
|
||||||
|
o = {'token': {'expires_at': '2099-05-22T00:02:43.941430Z',
|
||||||
|
'issued_at': '2013-05-21T00:02:43.941473Z',
|
||||||
|
'methods': ['password'],
|
||||||
|
'project': {'id': ref.get('project_id'),
|
||||||
|
'name': ref.get('project_name'),
|
||||||
|
'domain': {'id': ref.get('project_domain_id'),
|
||||||
|
'name': ref.get(
|
||||||
|
'project_domain_name')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'user': {'id': ref.get('user_id'),
|
||||||
|
'name': ref.get('username'),
|
||||||
|
'domain': {'id': ref.get('user_domain_id'),
|
||||||
|
'name': ref.get('user_domain_name')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'roles': ref.get('roles')
|
||||||
|
}}
|
||||||
|
|
||||||
|
# we only care about Glance and Keystone endpoints
|
||||||
|
o['token']['catalog'] = [
|
||||||
|
{'endpoints': [
|
||||||
|
{
|
||||||
|
'id': uuid.uuid4().hex,
|
||||||
|
'interface': 'public',
|
||||||
|
'region': 'RegionTwo',
|
||||||
|
'url': ref.get('glance_url')
|
||||||
|
}],
|
||||||
|
'id': uuid.uuid4().hex,
|
||||||
|
'type': 'keystore'},
|
||||||
|
{'endpoints': [
|
||||||
|
{
|
||||||
|
'id': uuid.uuid4().hex,
|
||||||
|
'interface': 'public',
|
||||||
|
'region': 'RegionTwo',
|
||||||
|
'url': ref.get('auth_url')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': uuid.uuid4().hex,
|
||||||
|
'interface': 'admin',
|
||||||
|
'region': 'RegionTwo',
|
||||||
|
'url': ref.get('auth_url')
|
||||||
|
}],
|
||||||
|
'id': uuid.uuid4().hex,
|
||||||
|
'type': 'identity'}]
|
||||||
|
|
||||||
|
# token ID is conveyed via the X-Subject-Token header so we are generating
|
||||||
|
# one to stash there
|
||||||
|
token_id = uuid.uuid4().hex
|
||||||
|
|
||||||
|
return token_id, o
|
||||||
@@ -25,31 +25,50 @@ from glanceclient import shell as openstack_shell
|
|||||||
#NOTE (esheffield) Used for the schema caching tests
|
#NOTE (esheffield) Used for the schema caching tests
|
||||||
from glanceclient.v2 import schemas as schemas
|
from glanceclient.v2 import schemas as schemas
|
||||||
import json
|
import json
|
||||||
|
from tests import keystone_client_fixtures
|
||||||
from tests import utils
|
from tests import utils
|
||||||
|
|
||||||
|
import keystoneclient
|
||||||
|
from keystoneclient.openstack.common.apiclient import exceptions as ks_exc
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_IMAGE_URL = 'http://127.0.0.1:5000/'
|
DEFAULT_IMAGE_URL = 'http://127.0.0.1:5000/'
|
||||||
DEFAULT_USERNAME = 'username'
|
DEFAULT_USERNAME = 'username'
|
||||||
DEFAULT_PASSWORD = 'password'
|
DEFAULT_PASSWORD = 'password'
|
||||||
DEFAULT_TENANT_ID = 'tenant_id'
|
DEFAULT_TENANT_ID = 'tenant_id'
|
||||||
DEFAULT_TENANT_NAME = 'tenant_name'
|
DEFAULT_TENANT_NAME = 'tenant_name'
|
||||||
DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v2.0/'
|
DEFAULT_PROJECT_ID = '0123456789'
|
||||||
|
DEFAULT_USER_DOMAIN_NAME = 'user_domain_name'
|
||||||
|
DEFAULT_UNVERSIONED_AUTH_URL = 'http://127.0.0.1:5000/'
|
||||||
|
DEFAULT_V2_AUTH_URL = 'http://127.0.0.1:5000/v2.0/'
|
||||||
|
DEFAULT_V3_AUTH_URL = 'http://127.0.0.1:5000/v3/'
|
||||||
DEFAULT_AUTH_TOKEN = ' 3bcc3d3a03f44e3d8377f9247b0ad155'
|
DEFAULT_AUTH_TOKEN = ' 3bcc3d3a03f44e3d8377f9247b0ad155'
|
||||||
TEST_SERVICE_URL = 'http://127.0.0.1:5000/'
|
TEST_SERVICE_URL = 'http://127.0.0.1:5000/'
|
||||||
|
|
||||||
|
FAKE_V2_ENV = {'OS_USERNAME': DEFAULT_USERNAME,
|
||||||
|
'OS_PASSWORD': DEFAULT_PASSWORD,
|
||||||
|
'OS_TENANT_NAME': DEFAULT_TENANT_NAME,
|
||||||
|
'OS_AUTH_URL': DEFAULT_V2_AUTH_URL,
|
||||||
|
'OS_IMAGE_URL': DEFAULT_IMAGE_URL}
|
||||||
|
|
||||||
|
FAKE_V3_ENV = {'OS_USERNAME': DEFAULT_USERNAME,
|
||||||
|
'OS_PASSWORD': DEFAULT_PASSWORD,
|
||||||
|
'OS_PROJECT_ID': DEFAULT_PROJECT_ID,
|
||||||
|
'OS_USER_DOMAIN_NAME': DEFAULT_USER_DOMAIN_NAME,
|
||||||
|
'OS_AUTH_URL': DEFAULT_V3_AUTH_URL,
|
||||||
|
'OS_IMAGE_URL': DEFAULT_IMAGE_URL}
|
||||||
|
|
||||||
|
|
||||||
class ShellTest(utils.TestCase):
|
class ShellTest(utils.TestCase):
|
||||||
|
# auth environment to use
|
||||||
|
auth_env = FAKE_V2_ENV.copy()
|
||||||
|
# expected auth plugin to invoke
|
||||||
|
auth_plugin = 'keystoneclient.auth.identity.v2.Password'
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(ShellTest, self).setUp()
|
super(ShellTest, self).setUp()
|
||||||
global _old_env
|
global _old_env
|
||||||
fake_env = {
|
_old_env, os.environ = os.environ, self.auth_env
|
||||||
'OS_USERNAME': DEFAULT_USERNAME,
|
|
||||||
'OS_PASSWORD': DEFAULT_PASSWORD,
|
|
||||||
'OS_TENANT_NAME': DEFAULT_TENANT_NAME,
|
|
||||||
'OS_AUTH_URL': DEFAULT_AUTH_URL,
|
|
||||||
'OS_IMAGE_URL': DEFAULT_IMAGE_URL,
|
|
||||||
'OS_AUTH_TOKEN': DEFAULT_AUTH_TOKEN}
|
|
||||||
_old_env, os.environ = os.environ, fake_env.copy()
|
|
||||||
|
|
||||||
global shell, _shell, assert_called, assert_called_anytime
|
global shell, _shell, assert_called, assert_called_anytime
|
||||||
_shell = openstack_shell.OpenStackImagesShell()
|
_shell = openstack_shell.OpenStackImagesShell()
|
||||||
@@ -99,6 +118,184 @@ class ShellTest(utils.TestCase):
|
|||||||
targeted_image_url = test_shell._get_image_url(fake_args)
|
targeted_image_url = test_shell._get_image_url(fake_args)
|
||||||
self.assertEqual(expected_image_url, targeted_image_url)
|
self.assertEqual(expected_image_url, targeted_image_url)
|
||||||
|
|
||||||
|
@mock.patch.object(openstack_shell.OpenStackImagesShell,
|
||||||
|
'_get_versioned_client')
|
||||||
|
def test_cert_and_key_args_interchangeable(self,
|
||||||
|
mock_versioned_client):
|
||||||
|
# make sure --os-cert and --os-key are passed correctly
|
||||||
|
args = '--os-cert mycert --os-key mykey image-list'
|
||||||
|
shell(args)
|
||||||
|
assert mock_versioned_client.called
|
||||||
|
((api_version, args), kwargs) = mock_versioned_client.call_args
|
||||||
|
self.assertEqual(args.os_cert, 'mycert')
|
||||||
|
self.assertEqual(args.os_key, 'mykey')
|
||||||
|
|
||||||
|
# make sure we get the same thing with --cert-file and --key-file
|
||||||
|
args = '--cert-file mycertfile --key-file mykeyfile image-list'
|
||||||
|
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||||
|
glance_shell.main(args.split())
|
||||||
|
assert mock_versioned_client.called
|
||||||
|
((api_version, args), kwargs) = mock_versioned_client.call_args
|
||||||
|
self.assertEqual(args.os_cert, 'mycertfile')
|
||||||
|
self.assertEqual(args.os_key, 'mykeyfile')
|
||||||
|
|
||||||
|
@mock.patch('glanceclient.v1.client.Client')
|
||||||
|
def test_no_auth_with_token_and_image_url_with_v1(self, v1_client):
|
||||||
|
# test no authentication is required if both token and endpoint url
|
||||||
|
# are specified
|
||||||
|
args = ('--os-auth-token mytoken --os-image-url https://image:1234/v1 '
|
||||||
|
'image-list')
|
||||||
|
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||||
|
glance_shell.main(args.split())
|
||||||
|
assert v1_client.called
|
||||||
|
(args, kwargs) = v1_client.call_args
|
||||||
|
self.assertEqual(kwargs['token'], 'mytoken')
|
||||||
|
self.assertEqual(args[0], 'https://image:1234/v1')
|
||||||
|
|
||||||
|
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schema')
|
||||||
|
def test_no_auth_with_token_and_image_url_with_v2(self,
|
||||||
|
cache_schema):
|
||||||
|
with mock.patch('glanceclient.v2.client.Client') as v2_client:
|
||||||
|
# test no authentication is required if both token and endpoint url
|
||||||
|
# are specified
|
||||||
|
args = ('--os-auth-token mytoken '
|
||||||
|
'--os-image-url https://image:1234/v2 '
|
||||||
|
'--os-image-api-version 2 image-list')
|
||||||
|
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||||
|
glance_shell.main(args.split())
|
||||||
|
((args), kwargs) = v2_client.call_args
|
||||||
|
self.assertEqual(args[0], 'https://image:1234/v2')
|
||||||
|
self.assertEqual(kwargs['token'], 'mytoken')
|
||||||
|
|
||||||
|
def _assert_auth_plugin_args(self, mock_auth_plugin):
|
||||||
|
# make sure our auth plugin is invoked with the correct args
|
||||||
|
mock_auth_plugin.assert_called_once_with(
|
||||||
|
keystone_client_fixtures.V2_URL,
|
||||||
|
self.auth_env['OS_USERNAME'],
|
||||||
|
self.auth_env['OS_PASSWORD'],
|
||||||
|
tenant_name=self.auth_env['OS_TENANT_NAME'],
|
||||||
|
tenant_id='')
|
||||||
|
|
||||||
|
@mock.patch('glanceclient.v1.client.Client')
|
||||||
|
@mock.patch('keystoneclient.session.Session')
|
||||||
|
@mock.patch.object(keystoneclient.discover.Discover, 'url_for',
|
||||||
|
side_effect=[keystone_client_fixtures.V2_URL, None])
|
||||||
|
def test_auth_plugin_invocation_with_v1(self,
|
||||||
|
v1_client,
|
||||||
|
ks_session,
|
||||||
|
url_for):
|
||||||
|
with mock.patch(self.auth_plugin) as mock_auth_plugin:
|
||||||
|
args = 'image-list'
|
||||||
|
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||||
|
glance_shell.main(args.split())
|
||||||
|
self._assert_auth_plugin_args(mock_auth_plugin)
|
||||||
|
|
||||||
|
@mock.patch('glanceclient.v2.client.Client')
|
||||||
|
@mock.patch('keystoneclient.session.Session')
|
||||||
|
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schema')
|
||||||
|
@mock.patch.object(keystoneclient.discover.Discover, 'url_for',
|
||||||
|
side_effect=[keystone_client_fixtures.V2_URL, None])
|
||||||
|
def test_auth_plugin_invocation_with_v2(self,
|
||||||
|
v2_client,
|
||||||
|
ks_session,
|
||||||
|
url_for,
|
||||||
|
cache_schema):
|
||||||
|
with mock.patch(self.auth_plugin) as mock_auth_plugin:
|
||||||
|
args = '--os-image-api-version 2 image-list'
|
||||||
|
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||||
|
glance_shell.main(args.split())
|
||||||
|
self._assert_auth_plugin_args(mock_auth_plugin)
|
||||||
|
|
||||||
|
@mock.patch('glanceclient.v1.client.Client')
|
||||||
|
@mock.patch('keystoneclient.session.Session')
|
||||||
|
@mock.patch.object(keystoneclient.discover.Discover, 'url_for',
|
||||||
|
side_effect=[keystone_client_fixtures.V2_URL,
|
||||||
|
keystone_client_fixtures.V3_URL])
|
||||||
|
def test_auth_plugin_invocation_with_unversioned_auth_url_with_v1(
|
||||||
|
self, v1_client, ks_session, url_for):
|
||||||
|
with mock.patch(self.auth_plugin) as mock_auth_plugin:
|
||||||
|
args = '--os-auth-url %s image-list' % (
|
||||||
|
keystone_client_fixtures.BASE_URL)
|
||||||
|
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||||
|
glance_shell.main(args.split())
|
||||||
|
self._assert_auth_plugin_args(mock_auth_plugin)
|
||||||
|
|
||||||
|
@mock.patch('glanceclient.v2.client.Client')
|
||||||
|
@mock.patch('keystoneclient.session.Session')
|
||||||
|
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schema')
|
||||||
|
@mock.patch.object(keystoneclient.discover.Discover, 'url_for',
|
||||||
|
side_effect=[keystone_client_fixtures.V2_URL,
|
||||||
|
keystone_client_fixtures.V3_URL])
|
||||||
|
def test_auth_plugin_invocation_with_unversioned_auth_url_with_v2(
|
||||||
|
self, v2_client, ks_session, cache_schema, url_for):
|
||||||
|
with mock.patch(self.auth_plugin) as mock_auth_plugin:
|
||||||
|
args = ('--os-auth-url %s --os-image-api-version 2 '
|
||||||
|
'image-list') % (keystone_client_fixtures.BASE_URL)
|
||||||
|
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||||
|
glance_shell.main(args.split())
|
||||||
|
self._assert_auth_plugin_args(mock_auth_plugin)
|
||||||
|
|
||||||
|
|
||||||
|
class ShellTestWithKeystoneV3Auth(ShellTest):
|
||||||
|
# auth environment to use
|
||||||
|
auth_env = FAKE_V3_ENV.copy()
|
||||||
|
# expected auth plugin to invoke
|
||||||
|
auth_plugin = 'keystoneclient.auth.identity.v3.Password'
|
||||||
|
|
||||||
|
def _assert_auth_plugin_args(self, mock_auth_plugin):
|
||||||
|
mock_auth_plugin.assert_called_once_with(
|
||||||
|
keystone_client_fixtures.V3_URL,
|
||||||
|
user_id='',
|
||||||
|
username=self.auth_env['OS_USERNAME'],
|
||||||
|
password=self.auth_env['OS_PASSWORD'],
|
||||||
|
user_domain_id='',
|
||||||
|
user_domain_name=self.auth_env['OS_USER_DOMAIN_NAME'],
|
||||||
|
project_id=self.auth_env['OS_PROJECT_ID'],
|
||||||
|
project_name='',
|
||||||
|
project_domain_id='',
|
||||||
|
project_domain_name='')
|
||||||
|
|
||||||
|
@mock.patch('glanceclient.v1.client.Client')
|
||||||
|
@mock.patch('keystoneclient.session.Session')
|
||||||
|
@mock.patch.object(keystoneclient.discover.Discover, 'url_for',
|
||||||
|
side_effect=[None, keystone_client_fixtures.V3_URL])
|
||||||
|
def test_auth_plugin_invocation_with_v1(self,
|
||||||
|
v1_client,
|
||||||
|
ks_session,
|
||||||
|
url_for):
|
||||||
|
with mock.patch(self.auth_plugin) as mock_auth_plugin:
|
||||||
|
args = 'image-list'
|
||||||
|
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||||
|
glance_shell.main(args.split())
|
||||||
|
self._assert_auth_plugin_args(mock_auth_plugin)
|
||||||
|
|
||||||
|
@mock.patch('glanceclient.v2.client.Client')
|
||||||
|
@mock.patch('keystoneclient.session.Session')
|
||||||
|
@mock.patch.object(openstack_shell.OpenStackImagesShell, '_cache_schema')
|
||||||
|
@mock.patch.object(keystoneclient.discover.Discover, 'url_for',
|
||||||
|
side_effect=[None, keystone_client_fixtures.V3_URL])
|
||||||
|
def test_auth_plugin_invocation_with_v2(self,
|
||||||
|
v2_client,
|
||||||
|
ks_session,
|
||||||
|
url_for,
|
||||||
|
cache_schema):
|
||||||
|
with mock.patch(self.auth_plugin) as mock_auth_plugin:
|
||||||
|
args = '--os-image-api-version 2 image-list'
|
||||||
|
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||||
|
glance_shell.main(args.split())
|
||||||
|
self._assert_auth_plugin_args(mock_auth_plugin)
|
||||||
|
|
||||||
|
@mock.patch('keystoneclient.session.Session')
|
||||||
|
@mock.patch('keystoneclient.discover.Discover',
|
||||||
|
side_effect=ks_exc.ClientException())
|
||||||
|
def test_api_discovery_failed_with_unversioned_auth_url(self,
|
||||||
|
ks_session,
|
||||||
|
discover):
|
||||||
|
args = '--os-auth-url %s image-list' % (
|
||||||
|
keystone_client_fixtures.BASE_URL)
|
||||||
|
glance_shell = openstack_shell.OpenStackImagesShell()
|
||||||
|
self.assertRaises(exc.CommandError, glance_shell.main, args.split())
|
||||||
|
|
||||||
|
|
||||||
class ShellCacheSchemaTest(utils.TestCase):
|
class ShellCacheSchemaTest(utils.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user