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
	 Bob Thyne
					Bob Thyne