From 8597a0c234ef643905d8e356a3986b79b52989c3 Mon Sep 17 00:00:00 2001 From: David Hu Date: Thu, 23 Oct 2014 01:50:49 -0700 Subject: [PATCH] Support using the Keystone V3 API from the Nova CLI This changeset enables support for Keystone V3 authentication on the Nova CLI. This provides consistency between using novaclient as the Nova CLI and using it as a library as the Keystone V3 support already exists for the libary usecase. The bulk of the change surrounds the use of the keystoneclient session object for authentication, retriving the service catalog, and HTTP connection/session management. Co-Authored-By: Morgan Fainberg Change-Id: Iece9f41320a8770176c7eeb5acd86be4d80cc58f --- novaclient/client.py | 15 ++++ novaclient/shell.py | 112 +++++++++++++++++++++------- novaclient/tests/test_shell.py | 60 +++++++++++---- novaclient/tests/v1_1/test_shell.py | 1 + novaclient/tests/v3/test_shell.py | 1 + novaclient/v1_1/client.py | 2 - novaclient/v1_1/shell.py | 63 +++++++++++----- novaclient/v3/client.py | 2 - novaclient/v3/shell.py | 64 +++++++++++----- 9 files changed, 234 insertions(+), 86 deletions(-) diff --git a/novaclient/client.py b/novaclient/client.py index eec9a8e57..2c64ab1e1 100644 --- a/novaclient/client.py +++ b/novaclient/client.py @@ -137,20 +137,35 @@ class CompletionCache(object): class SessionClient(adapter.LegacyJsonAdapter): + def __init__(self, *args, **kwargs): + self.times = [] + super(SessionClient, self).__init__(*args, **kwargs) + def request(self, url, method, **kwargs): # NOTE(jamielennox): The standard call raises errors from # keystoneclient, where we need to raise the novaclient errors. raise_exc = kwargs.pop('raise_exc', True) + start_time = time.time() resp, body = super(SessionClient, self).request(url, method, raise_exc=False, **kwargs) + end_time = time.time() + self.times.append(('%s %s' % (method, url), + start_time, end_time)) + if raise_exc and resp.status_code >= 400: raise exceptions.from_response(resp, body, url, method) return resp, body + def get_timings(self): + return self.times + + def reset_timings(self): + self.times = [] + def _original_only(f): """Indicates and enforces that this function can only be used if we are diff --git a/novaclient/shell.py b/novaclient/shell.py index 7842c278a..027f1cc60 100644 --- a/novaclient/shell.py +++ b/novaclient/shell.py @@ -28,7 +28,11 @@ import logging import os import pkgutil import sys +import time +from keystoneclient.auth.identity.generic import password +from keystoneclient.auth.identity.generic import token +from keystoneclient.auth.identity import v3 as identity from keystoneclient import session as ksession from oslo.utils import encodeutils from oslo.utils import strutils @@ -232,6 +236,7 @@ class NovaClientArgumentParser(argparse.ArgumentParser): class OpenStackComputeShell(object): + times = [] def _append_global_identity_args(self, parser): # Register the CLI arguments that have moved to the session object. @@ -240,6 +245,14 @@ class OpenStackComputeShell(object): parser.set_defaults(insecure=utils.env('NOVACLIENT_INSECURE', default=False)) + identity.Password.register_argparse_arguments(parser) + + parser.set_defaults(os_username=utils.env('OS_USERNAME', + 'NOVA_USERNAME')) + parser.set_defaults(os_password=utils.env('OS_PASSWORD', + 'NOVA_PASSWORD')) + parser.set_defaults(os_auth_url=utils.env('OS_AUTH_URL', 'NOVA_URL')) + def get_base_parser(self): parser = NovaClientArgumentParser( prog='nova', @@ -281,22 +294,9 @@ class OpenStackComputeShell(object): default=utils.env('OS_AUTH_TOKEN'), help='Defaults to env[OS_AUTH_TOKEN]') - parser.add_argument('--os-username', - metavar='', - default=utils.env('OS_USERNAME', 'NOVA_USERNAME'), - help=_('Defaults to env[OS_USERNAME].')) parser.add_argument('--os_username', help=argparse.SUPPRESS) - parser.add_argument('--os-user-id', - metavar='', - default=utils.env('OS_USER_ID'), - help=_('Defaults to env[OS_USER_ID].')) - - parser.add_argument('--os-password', - metavar='', - default=utils.env('OS_PASSWORD', 'NOVA_PASSWORD'), - help=_('Defaults to env[OS_PASSWORD].')) parser.add_argument('--os_password', help=argparse.SUPPRESS) @@ -312,10 +312,6 @@ class OpenStackComputeShell(object): default=utils.env('OS_TENANT_ID'), help=_('Defaults to env[OS_TENANT_ID].')) - parser.add_argument('--os-auth-url', - metavar='', - default=utils.env('OS_AUTH_URL', 'NOVA_URL'), - help=_('Defaults to env[OS_AUTH_URL].')) parser.add_argument('--os_auth_url', help=argparse.SUPPRESS) @@ -511,6 +507,19 @@ class OpenStackComputeShell(object): logging.basicConfig(level=logging.DEBUG, format=streamformat) + def _get_keystone_auth(self, session, auth_url, **kwargs): + auth_token = kwargs.pop('auth_token', None) + if auth_token: + return token.Token(auth_url, auth_token, **kwargs) + else: + return password.Password(auth_url, + username=kwargs.pop('username'), + user_id=kwargs.pop('user_id'), + password=kwargs.pop('password'), + user_domain_id=kwargs.pop('user_domain_id'), + user_domain_name=kwargs.pop('user_domain_name'), + **kwargs) + def main(self, argv): # Parse args once to find version and debug settings parser = self.get_base_parser() @@ -570,6 +579,9 @@ class OpenStackComputeShell(object): cacert = args.os_cacert timeout = args.timeout + keystone_session = None + keystone_auth = None + # We may have either, both or none of these. # If we have both, we don't need USERNAME, PASSWORD etc. # Fill in the blanks from the SecretsHelper if possible. @@ -603,6 +615,13 @@ class OpenStackComputeShell(object): must_auth = not (cliutils.isunauthenticated(args.func) or (auth_token and management_url)) + # Do not use Keystone session for cases with no session support. The + # presence of auth_plugin means os_auth_system is present and is not + # keystone. + use_session = True + if auth_plugin or bypass_url or os_cache or volume_service_name: + use_session = False + # FIXME(usrleon): Here should be restrict for project id same as # for os_username or os_password but for compatibility it is not. if must_auth: @@ -615,11 +634,14 @@ class OpenStackComputeShell(object): "or user id via --os-username, --os-user-id, " "env[OS_USERNAME] or env[OS_USER_ID]")) - if not os_tenant_name and not os_tenant_id: - raise exc.CommandError(_("You must provide a tenant name " - "or tenant id via --os-tenant-name, " - "--os-tenant-id, env[OS_TENANT_NAME] " - "or env[OS_TENANT_ID]")) + if not any([args.os_tenant_name, args.os_tenant_id, + args.os_project_id, args.os_project_name]): + raise exc.CommandError(_("You must provide a project name or" + " project id via --os-project-name," + " --os-project-id, env[OS_PROJECT_ID]" + " or env[OS_PROJECT_NAME]. You may" + " use os-project and os-tenant" + " interchangeably.")) if not os_auth_url: if os_auth_system and os_auth_system != 'keystone': @@ -632,13 +654,39 @@ class OpenStackComputeShell(object): "default url with --os-auth-system " "or env[OS_AUTH_SYSTEM]")) + project_id = args.os_project_id or args.os_tenant_id + project_name = args.os_project_name or args.os_tenant_name + if use_session: + # Not using Nova auth plugin, so use keystone + start_time = time.time() + keystone_session = ksession.Session.load_from_cli_options(args) + keystone_auth = self._get_keystone_auth( + keystone_session, + 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, + auth_token=args.os_auth_token, + project_id=project_id, + project_name=project_name, + project_domain_id=args.os_project_domain_id, + project_domain_name=args.os_project_domain_name) + end_time = time.time() + self.times.append(('%s %s' % ('auth_url', args.os_auth_url), + start_time, end_time)) + if (options.os_compute_api_version and options.os_compute_api_version != '1.0'): - if not os_tenant_name and not os_tenant_id: - raise exc.CommandError(_("You must provide a tenant name " - "or tenant id via --os-tenant-name, " - "--os-tenant-id, env[OS_TENANT_NAME] " - "or env[OS_TENANT_ID]")) + if not any([args.os_tenant_id, args.os_tenant_name, + args.os_project_id, args.os_project_name]): + raise exc.CommandError(_("You must provide a project name or" + " project id via --os-project-name," + " --os-project-id, env[OS_PROJECT_ID]" + " or env[OS_PROJECT_NAME]. You may" + " use os-project and os-tenant" + " interchangeably.")) if not os_auth_url: raise exc.CommandError(_("You must provide an auth url " @@ -658,6 +706,7 @@ class OpenStackComputeShell(object): timings=args.timings, bypass_url=bypass_url, os_cache=os_cache, http_log_debug=options.debug, cacert=cacert, timeout=timeout, + session=keystone_session, auth=keystone_auth, completion_cache=completion_cache) # Now check for the password/token of which pieces of the @@ -691,7 +740,11 @@ class OpenStackComputeShell(object): # This does a couple of bits which are useful even if we've # got the token + service URL already. It exits fast in that case. if not cliutils.isunauthenticated(args.func): - self.cs.authenticate() + if not use_session: + # Only call authenticate() if Nova auth plugin is used. + # If keystone is used, authentication is handled as part + # of session. + self.cs.authenticate() except exc.Unauthorized: raise exc.CommandError(_("Invalid OpenStack Nova credentials.")) except exc.AuthorizationFailure: @@ -720,12 +773,13 @@ class OpenStackComputeShell(object): volume_service_name=volume_service_name, timings=args.timings, bypass_url=bypass_url, os_cache=os_cache, http_log_debug=options.debug, + session=keystone_session, auth=keystone_auth, cacert=cacert, timeout=timeout) args.func(self.cs, args) if args.timings: - self._dump_timings(self.cs.get_timings()) + self._dump_timings(self.times + self.cs.get_timings()) def _dump_timings(self, timings): class Tyme(object): diff --git a/novaclient/tests/test_shell.py b/novaclient/tests/test_shell.py index 329f39ddc..b9028f094 100644 --- a/novaclient/tests/test_shell.py +++ b/novaclient/tests/test_shell.py @@ -16,8 +16,10 @@ import re import sys import fixtures +from keystoneclient import fixture import mock import prettytable +import requests_mock import six from testtools import matchers @@ -29,23 +31,33 @@ from novaclient.tests import utils FAKE_ENV = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_TENANT_NAME': 'tenant_name', - 'OS_AUTH_URL': 'http://no.where'} + 'OS_AUTH_URL': 'http://no.where/v2.0'} FAKE_ENV2 = {'OS_USER_ID': 'user_id', 'OS_PASSWORD': 'password', 'OS_TENANT_ID': 'tenant_id', - 'OS_AUTH_URL': 'http://no.where'} + 'OS_AUTH_URL': 'http://no.where/v2.0'} FAKE_ENV3 = {'OS_USER_ID': 'user_id', 'OS_PASSWORD': 'password', 'OS_TENANT_ID': 'tenant_id', - 'OS_AUTH_URL': 'http://no.where', + 'OS_AUTH_URL': 'http://no.where/v2.0', 'NOVA_ENDPOINT_TYPE': 'novaURL', 'OS_ENDPOINT_TYPE': 'osURL'} +def _create_ver_list(versions): + return {'versions': {'values': versions}} + + class ShellTest(utils.TestCase): + _msg_no_tenant_project = ("You must provide a project name or project" + " id via --os-project-name, --os-project-id," + " env[OS_PROJECT_ID] or env[OS_PROJECT_NAME]." + " You may use os-project and os-tenant" + " interchangeably.") + def make_env(self, exclude=None, fake_env=FAKE_ENV): env = dict((k, v) for k, v in fake_env.items() if k != exclude) self.useFixture(fixtures.MonkeyPatch('os.environ', env)) @@ -79,6 +91,12 @@ class ShellTest(utils.TestCase): sys.stderr = orig_stderr return (stdout, stderr) + def register_keystone_discovery_fixture(self, mreq): + v2_url = "http://no.where/v2.0" + v2_version = fixture.V2Discovery(v2_url) + mreq.register_uri('GET', v2_url, json=_create_ver_list([v2_version]), + status_code=200) + def test_help_unknown_command(self): self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo') @@ -163,9 +181,7 @@ class ShellTest(utils.TestCase): self.fail('CommandError not raised') def test_no_tenant_name(self): - required = ('You must provide a tenant name or tenant id' - ' via --os-tenant-name, --os-tenant-id,' - ' env[OS_TENANT_NAME] or env[OS_TENANT_ID]') + required = self._msg_no_tenant_project self.make_env(exclude='OS_TENANT_NAME') try: self.shell('list') @@ -175,14 +191,12 @@ class ShellTest(utils.TestCase): self.fail('CommandError not raised') def test_no_tenant_id(self): - required = ('You must provide a tenant name or tenant id' - ' via --os-tenant-name, --os-tenant-id,' - ' env[OS_TENANT_NAME] or env[OS_TENANT_ID]',) + required = self._msg_no_tenant_project self.make_env(exclude='OS_TENANT_ID', fake_env=FAKE_ENV2) try: self.shell('list') except exceptions.CommandError as message: - self.assertEqual(required, message.args) + self.assertEqual(required, message.args[0]) else: self.fail('CommandError not raised') @@ -200,15 +214,19 @@ class ShellTest(utils.TestCase): self.fail('CommandError not raised') @mock.patch('novaclient.client.Client') - def test_nova_endpoint_type(self, mock_client): + @requests_mock.Mocker() + def test_nova_endpoint_type(self, mock_client, m_requests): self.make_env(fake_env=FAKE_ENV3) + self.register_keystone_discovery_fixture(m_requests) self.shell('list') client_kwargs = mock_client.call_args_list[0][1] self.assertEqual(client_kwargs['endpoint_type'], 'novaURL') @mock.patch('novaclient.client.Client') - def test_os_endpoint_type(self, mock_client): + @requests_mock.Mocker() + def test_os_endpoint_type(self, mock_client, m_requests): self.make_env(exclude='NOVA_ENDPOINT_TYPE', fake_env=FAKE_ENV3) + self.register_keystone_discovery_fixture(m_requests) self.shell('list') client_kwargs = mock_client.call_args_list[0][1] self.assertEqual(client_kwargs['endpoint_type'], 'osURL') @@ -222,7 +240,8 @@ class ShellTest(utils.TestCase): @mock.patch('sys.stdin', side_effect=mock.MagicMock) @mock.patch('getpass.getpass', return_value='password') - def test_password(self, mock_getpass, mock_stdin): + @requests_mock.Mocker() + def test_password(self, mock_getpass, mock_stdin, m_requests): mock_stdin.encoding = "utf-8" # default output of empty tables differs depending between prettytable @@ -240,6 +259,7 @@ class ShellTest(utils.TestCase): '' ]) self.make_env(exclude='OS_PASSWORD') + self.register_keystone_discovery_fixture(m_requests) stdout, stderr = self.shell('list') self.assertEqual((stdout + stderr), ex) @@ -310,3 +330,17 @@ class ShellTest(utils.TestCase): novaclient.shell.main() except SystemExit as ex: self.assertEqual(ex.code, 130) + + +class ShellTestKeystoneV3(ShellTest): + def make_env(self, exclude=None, fake_env=FAKE_ENV): + if 'OS_AUTH_URL' in fake_env: + fake_env.update({'OS_AUTH_URL': 'http://no.where/v3'}) + env = dict((k, v) for k, v in fake_env.items() if k != exclude) + self.useFixture(fixtures.MonkeyPatch('os.environ', env)) + + def register_keystone_discovery_fixture(self, mreq): + v3_url = "http://no.where/v3" + v3_version = fixture.V3Discovery(v3_url) + mreq.register_uri('GET', v3_url, json=_create_ver_list([v3_version]), + status_code=200) diff --git a/novaclient/tests/v1_1/test_shell.py b/novaclient/tests/v1_1/test_shell.py index 3fef327c9..55b6137fd 100644 --- a/novaclient/tests/v1_1/test_shell.py +++ b/novaclient/tests/v1_1/test_shell.py @@ -56,6 +56,7 @@ class ShellTest(utils.TestCase): 'NOVA_PROJECT_ID': 'project_id', 'OS_COMPUTE_API_VERSION': '1.1', 'NOVA_URL': 'http://no.where', + 'OS_AUTH_URL': 'http://no.where/v2.0', } def setUp(self): diff --git a/novaclient/tests/v3/test_shell.py b/novaclient/tests/v3/test_shell.py index 658f07fba..2418aed7c 100644 --- a/novaclient/tests/v3/test_shell.py +++ b/novaclient/tests/v3/test_shell.py @@ -51,6 +51,7 @@ class ShellTest(utils.TestCase): 'NOVA_PROJECT_ID': 'project_id', 'OS_COMPUTE_API_VERSION': '3', 'NOVA_URL': 'http://no.where', + 'OS_AUTH_URL': 'http://no.where/v2.0', } def setUp(self): diff --git a/novaclient/v1_1/client.py b/novaclient/v1_1/client.py index 2e79e5bb1..70d5298dc 100644 --- a/novaclient/v1_1/client.py +++ b/novaclient/v1_1/client.py @@ -217,11 +217,9 @@ class Client(object): def set_management_url(self, url): self.client.set_management_url(url) - @client._original_only def get_timings(self): return self.client.get_timings() - @client._original_only def reset_timings(self): self.client.reset_timings() diff --git a/novaclient/v1_1/shell.py b/novaclient/v1_1/shell.py index b36db0e44..5b5c87a32 100644 --- a/novaclient/v1_1/shell.py +++ b/novaclient/v1_1/shell.py @@ -33,6 +33,7 @@ from oslo.utils import strutils from oslo.utils import timeutils import six +from novaclient import client from novaclient import exceptions from novaclient.i18n import _ from novaclient.openstack.common import cliutils @@ -2759,7 +2760,12 @@ def do_usage(cs, args): if args.tenant: usage = cs.usage.get(args.tenant, start, end) else: - usage = cs.usage.get(cs.client.tenant_id, start, end) + if isinstance(cs.client, client.SessionClient): + auth = cs.client.auth + project_id = auth.get_auth_ref(cs.client.session).project_id + usage = cs.usage.get(project_id, start, end) + else: + usage = cs.usage.get(cs.client.tenant_id, start, end) print(_("Usage from %(start)s to %(end)s:") % {'start': start.strftime(dateformat), @@ -3256,23 +3262,32 @@ def ensure_service_catalog_present(cs): def do_endpoints(cs, _args): """Discover endpoints that get returned from the authenticate services.""" - ensure_service_catalog_present(cs) + if isinstance(cs.client, client.SessionClient): + auth = cs.client.auth + sc = auth.get_access(cs.client.session).service_catalog + for service in sc.get_data(): + _print_endpoints(service, cs.client.region_name) + else: + ensure_service_catalog_present(cs) - catalog = cs.client.service_catalog.catalog - region = cs.client.region_name + catalog = cs.client.service_catalog.catalog + region = cs.client.region_name + for service in catalog['access']['serviceCatalog']: + _print_endpoints(service, region) - for service in catalog['access']['serviceCatalog']: - name, endpoints = service["name"], service["endpoints"] - try: - endpoint = _get_first_endpoint(endpoints, region) - utils.print_dict(endpoint, name) - except LookupError: - print(_("WARNING: %(service)s has no endpoint in %(region)s! " - "Available endpoints for this service:") % - {'service': name, 'region': region}) - for other_endpoint in endpoints: - utils.print_dict(other_endpoint, name) +def _print_endpoints(service, region): + name, endpoints = service["name"], service["endpoints"] + + try: + endpoint = _get_first_endpoint(endpoints, region) + utils.print_dict(endpoint, name) + except LookupError: + print(_("WARNING: %(service)s has no endpoint in %(region)s! " + "Available endpoints for this service:") % + {'service': name, 'region': region}) + for other_endpoint in endpoints: + utils.print_dict(other_endpoint, name) def _get_first_endpoint(endpoints, region): @@ -3298,11 +3313,19 @@ def _get_first_endpoint(endpoints, region): help=_('wrap PKI tokens to a specified length, or 0 to disable')) def do_credentials(cs, _args): """Show user credentials returned from auth.""" - ensure_service_catalog_present(cs) - catalog = cs.client.service_catalog.catalog - utils.print_dict(catalog['access']['user'], "User Credentials", - wrap=int(_args.wrap)) - utils.print_dict(catalog['access']['token'], "Token", wrap=int(_args.wrap)) + if isinstance(cs.client, client.SessionClient): + auth = cs.client.auth + sc = auth.get_access(cs.client.session).service_catalog + utils.print_dict(sc.catalog['user'], 'User Credentials', + wrap=int(_args.wrap)) + utils.print_dict(sc.get_token(), 'Token', wrap=int(_args.wrap)) + else: + ensure_service_catalog_present(cs) + catalog = cs.client.service_catalog.catalog + utils.print_dict(catalog['access']['user'], "User Credentials", + wrap=int(_args.wrap)) + utils.print_dict(catalog['access']['token'], "Token", + wrap=int(_args.wrap)) @utils.arg('server', metavar='', help=_('Name or ID of server.')) diff --git a/novaclient/v3/client.py b/novaclient/v3/client.py index a30f9191e..6967ea44b 100644 --- a/novaclient/v3/client.py +++ b/novaclient/v3/client.py @@ -174,11 +174,9 @@ class Client(object): def set_management_url(self, url): self.client.set_management_url(url) - @client._original_only def get_timings(self): return self.client.get_timings() - @client._original_only def reset_timings(self): self.client.reset_timings() diff --git a/novaclient/v3/shell.py b/novaclient/v3/shell.py index 3e67104fc..f22ae0bc3 100644 --- a/novaclient/v3/shell.py +++ b/novaclient/v3/shell.py @@ -31,6 +31,7 @@ from oslo.utils import strutils from oslo.utils import timeutils import six +from novaclient import client from novaclient import exceptions from novaclient.i18n import _ from novaclient.openstack.common import cliutils @@ -2140,7 +2141,12 @@ def do_usage(cs, args): if args.tenant: usage = cs.usage.get(args.tenant, start, end) else: - usage = cs.usage.get(cs.client.tenant_id, start, end) + if isinstance(cs.client, client.SessionClient): + auth = cs.client.auth + project_id = auth.get_auth_ref(cs.client.session).project_id + usage = cs.usage.get(project_id, start, end) + else: + usage = cs.usage.get(cs.client.tenant_id, start, end) print("Usage from %s to %s:" % (start.strftime(dateformat), end.strftime(dateformat))) @@ -2692,23 +2698,33 @@ def ensure_service_catalog_present(cs): def do_endpoints(cs, _args): """Discover endpoints that get returned from the authenticate services.""" - ensure_service_catalog_present(cs) + if isinstance(cs.client, client.SessionClient): + auth = cs.client.auth + sc = auth.get_Access(cs.client.session).service_catalog + for service in sc.get_data(): + _print_endpoints(service, cs.client.region_name) + else: + ensure_service_catalog_present(cs) - catalog = cs.client.service_catalog.catalog - region = cs.client.region_name + catalog = cs.client.service_catalog.catalog + region = cs.client.region_name - for service in catalog['access']['serviceCatalog']: - name, endpoints = service["name"], service["endpoints"] + for service in catalog['access']['serviceCatalog']: + _print_endpoints(service, region) - try: - endpoint = _get_first_endpoint(endpoints, region) - utils.print_dict(endpoint, name) - except LookupError: - print(_("WARNING: %(service)s has no endpoint in %(region)s! " - "Available endpoints for this service:") % - {'service': name, 'region': region}) - for other_endpoint in endpoints: - utils.print_dict(other_endpoint, name) + +def _print_endpoints(service, region): + name, endpoints = service["name"], service["endpoints"] + + try: + endpoint = _get_first_endpoint(endpoints, region) + utils.print_dict(endpoint, name) + except LookupError: + print(_("WARNING: %(service)s has no endpoint in %(region)s! " + "Available endpoints for this service:") % + {'service': name, 'region': region}) + for other_endpoint in endpoints: + utils.print_dict(other_endpoint, name) def _get_first_endpoint(endpoints, region): @@ -2734,11 +2750,19 @@ def _get_first_endpoint(endpoints, region): help='wrap PKI tokens to a specified length, or 0 to disable') def do_credentials(cs, _args): """Show user credentials returned from auth.""" - ensure_service_catalog_present(cs) - catalog = cs.client.service_catalog.catalog - utils.print_dict(catalog['access']['user'], "User Credentials", - wrap=int(_args.wrap)) - utils.print_dict(catalog['access']['token'], "Token", wrap=int(_args.wrap)) + if isinstance(cs.client, client.SessionClient): + auth = cs.client.auth + sc = auth.get_access(cs.client.session).service_catalog + utils.print_dict(sc.catalog['user'], 'User Credentials', + wrap=int(_args.wrap)) + utils.print_dict(sc.get_token(), 'Token', wrap=int(_args.wrap)) + else: + ensure_service_catalog_present(cs) + catalog = cs.client.service_catalog.catalog + utils.print_dict(catalog['access']['user'], "User Credentials", + wrap=int(_args.wrap)) + utils.print_dict(catalog['access']['token'], "Token", + wrap=int(_args.wrap)) def do_extension_list(cs, _args):