Add Keystone v3 API support

Keystone is used for authentication in Manila and the latter's
client does not support latest version of it that exists for long time.

So, add support of Keystone v3 API and make Manila client to use it
at first priority if it is available.

Change-Id: I3e2f505f46f7ab60c66bcb0b1d65a38bea602df4
Closes-Bug: #1499260
This commit is contained in:
Valeriy Ponomaryov 2015-10-08 16:55:18 +03:00
parent 0a731dcafa
commit 997cc43433
7 changed files with 552 additions and 99 deletions

@ -139,6 +139,13 @@ class OpenStackManilaShell(object):
action='store_true',
help='Delete cached password and auth token.')
parser.add_argument('--os-user-id',
metavar='<auth-user-id>',
default=cliutils.env('OS_USER_ID'),
help=('Defaults to env [OS_USER_ID].'))
parser.add_argument('--os_user_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-username',
metavar='<auth-user-name>',
default=cliutils.env('OS_USERNAME',
@ -163,6 +170,16 @@ class OpenStackManilaShell(object):
parser.add_argument('--os_tenant_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-name',
metavar='<auth-project-name>',
default=cliutils.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_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-tenant-id',
metavar='<auth-tenant-id>',
default=cliutils.env('OS_TENANT_ID',
@ -171,6 +188,46 @@ class OpenStackManilaShell(object):
parser.add_argument('--os_tenant_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-id',
metavar='<auth-project-id>',
default=cliutils.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_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-user-domain-id',
metavar='<auth-user-domain-id>',
default=cliutils.env('OS_USER_DOMAIN_ID'),
help=('OpenStack user domain ID. '
'Defaults to env[OS_USER_DOMAIN_ID].'))
parser.add_argument('--os_user_domain_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-user-domain-name',
metavar='<auth-user-domain-name>',
default=cliutils.env('OS_USER_DOMAIN_NAME'),
help=('OpenStack user domain name. '
'Defaults to env[OS_USER_DOMAIN_NAME].'))
parser.add_argument('--os_user_domain_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-domain-id',
metavar='<auth-project-domain-id>',
default=cliutils.env('OS_PROJECT_DOMAIN_ID'),
help='Defaults to env[OS_PROJECT_DOMAIN_ID].')
parser.add_argument('--os_project_domain_id',
help=argparse.SUPPRESS)
parser.add_argument('--os-project-domain-name',
metavar='<auth-project-domain-name>',
default=cliutils.env('OS_PROJECT_DOMAIN_NAME'),
help='Defaults to env[OS_PROJECT_DOMAIN_NAME].')
parser.add_argument('--os_project_domain_name',
help=argparse.SUPPRESS)
parser.add_argument('--os-auth-url',
metavar='<auth-url>',
default=cliutils.env('OS_AUTH_URL',
@ -246,6 +303,13 @@ class OpenStackManilaShell(object):
default=0,
help='Number of retries.')
parser.add_argument('--os-cert',
metavar='<certificate>',
default=cliutils.env('OS_CERT'),
help='Defaults to env[OS_CERT].')
parser.add_argument('--os_cert',
help=argparse.SUPPRESS)
return parser
def get_subcommand_parser(self, version):
@ -386,12 +450,18 @@ class OpenStackManilaShell(object):
(os_username, os_password, os_tenant_name, os_auth_url,
os_region_name, os_tenant_id, endpoint_type, insecure,
service_type, service_name, share_service_name,
cacert, os_cache, os_reset_cache) = (
cacert, os_cache, os_reset_cache, os_user_id, os_user_domain_id,
os_user_domain_name, os_project_domain_id, os_project_domain_name,
os_project_name, os_project_id, os_cert) = (
args.os_username, args.os_password, args.os_tenant_name,
args.os_auth_url, args.os_region_name, args.os_tenant_id,
args.endpoint_type, args.insecure, args.service_type,
args.service_name, args.share_service_name,
args.os_cacert, args.os_cache, args.os_reset_cache)
args.os_cacert, args.os_cache, args.os_reset_cache,
args.os_user_id, args.os_user_domain_id, args.os_user_domain_name,
args.os_project_domain_id, args.os_project_domain_name,
args.os_project_name, args.os_project_id, args.os_cert,
)
if share_service_name:
service_name = share_service_name
@ -403,29 +473,19 @@ class OpenStackManilaShell(object):
service_type = DEFAULT_MANILA_SERVICE_TYPE
service_type = cliutils.get_service_type(args.func) or service_type
# FIXME(usrleon): Here should be restrict for project id same as
# for os_username or os_password but for compatibility it is not.
if not cliutils.isunauthenticated(args.func):
if not os_username:
raise exc.CommandError(
"You must provide a username "
"via either --os-username or env[OS_USERNAME]")
if not (os_tenant_name or os_tenant_id):
raise exc.CommandError("You must provide a tenant_id "
"via either --os-tenant-id or "
"env[OS_TENANT_ID]")
if not os_auth_url:
raise exc.CommandError(
"You must provide an auth url "
"via either --os-auth-url or env[OS_AUTH_URL]")
if not (os_tenant_name or os_tenant_id):
if not (os_tenant_name or os_tenant_id or os_project_name or
os_project_id):
raise exc.CommandError(
"You must provide a tenant_id "
"via either --os-tenant-id or env[OS_TENANT_ID]")
"You must provide a tenant_name, tenant_id, "
"project_id or project_name (with "
"project_domain_name or project_domain_id) via "
"--os-tenant-name (env[OS_TENANT_NAME]), "
"--os-tenant-id (env[OS_TENANT_ID]), "
"--os-project-id (env[OS_PROJECT_ID]), "
"--os-project-name (env[OS_PROJECT_NAME]), "
"--os-project-domain-id (env[OS_PROJECT_DOMAIN_ID]) and "
"--os-project-domain-name (env[OS_PROJECT_DOMAIN_NAME])."
)
if not os_auth_url:
raise exc.CommandError(
@ -434,11 +494,12 @@ class OpenStackManilaShell(object):
self.cs = client.Client(options.os_share_api_version,
username=os_username,
api_key=os_password,
project_name=os_tenant_name,
password=os_password,
project_name=os_project_name or os_tenant_name,
auth_url=os_auth_url,
insecure=insecure, region_name=os_region_name,
tenant_id=os_tenant_id,
insecure=insecure,
region_name=os_region_name,
tenant_id=os_project_id or os_tenant_id,
endpoint_type=endpoint_type,
extensions=self.extensions,
service_type=service_type,
@ -448,7 +509,13 @@ class OpenStackManilaShell(object):
cacert=cacert,
use_keyring=os_cache,
force_new_token=os_reset_cache,
api_version=options.os_share_api_version)
api_version=options.os_share_api_version,
user_id=os_user_id,
user_domain_id=os_user_domain_id,
user_domain_name=os_user_domain_name,
project_domain_id=os_project_domain_id,
project_domain_name=os_project_domain_name,
cert=os_cert)
args.func(self.cs, args)

@ -13,16 +13,20 @@
import re
import sys
import ddt
import fixtures
import mock
from six import moves
from testtools import matchers
from manilaclient.common import constants
from manilaclient import exceptions
from manilaclient import shell
from manilaclient.tests.unit import utils
class ShellTest(utils.TestCase):
@ddt.ddt
class OpenstackManilaShellTest(utils.TestCase):
FAKE_ENV = {
'OS_USERNAME': 'username',
@ -32,11 +36,9 @@ class ShellTest(utils.TestCase):
}
# Patch os.environ to avoid required auth info.
def setUp(self):
super(ShellTest, self).setUp()
for var in self.FAKE_ENV:
self.useFixture(fixtures.EnvironmentVariable(var,
self.FAKE_ENV[var]))
def set_env_vars(self, env_vars):
for k, v in env_vars.items():
self.useFixture(fixtures.EnvironmentVariable(k, v))
def shell(self, argstr):
orig = sys.stdout
@ -54,6 +56,70 @@ class ShellTest(utils.TestCase):
return out
@ddt.data(
{},
{'OS_AUTH_URL': 'http://foo.bar'},
{'OS_AUTH_URL': 'http://foo.bar', 'OS_USERNAME': 'foo'},
{'OS_AUTH_URL': 'http://foo.bar', 'OS_USERNAME': 'foo_user',
'OS_PASSWORD': 'foo_password'},
{'OS_TENANT_NAME': 'foo_tenant', 'OS_USERNAME': 'foo_user',
'OS_PASSWORD': 'foo_password'},
)
def test_main_failure(self, env_vars):
self.set_env_vars(env_vars)
with mock.patch.object(shell, 'client') as mock_client:
self.assertRaises(exceptions.CommandError, self.shell, 'list')
self.assertFalse(mock_client.Client.called)
def test_main_success(self):
env_vars = {
'OS_AUTH_URL': 'http://foo.bar',
'OS_USERNAME': 'foo_username',
'OS_USER_ID': 'foo_user_id',
'OS_PASSWORD': 'foo_password',
'OS_TENANT_NAME': 'foo_tenant',
'OS_TENANT_ID': 'foo_tenant_id',
'OS_PROJECT_NAME': 'foo_project',
'OS_PROJECT_ID': 'foo_project_id',
'OS_PROJECT_DOMAIN_ID': 'foo_project_domain_id',
'OS_PROJECT_DOMAIN_NAME': 'foo_project_domain_name',
'OS_PROJECT_DOMAIN_ID': 'foo_project_domain_id',
'OS_USER_DOMAIN_NAME': 'foo_user_domain_name',
'OS_USER_DOMAIN_ID': 'foo_user_domain_id',
'OS_CERT': 'foo_cert',
}
self.set_env_vars(env_vars)
with mock.patch.object(shell, 'client') as mock_client:
self.shell('list')
mock_client.Client.assert_called_once_with(
constants.MAX_API_VERSION,
username=env_vars['OS_USERNAME'],
password=env_vars['OS_PASSWORD'],
project_name=env_vars['OS_PROJECT_NAME'],
auth_url=env_vars['OS_AUTH_URL'],
insecure=False,
region_name='',
tenant_id=env_vars['OS_PROJECT_ID'],
endpoint_type='publicURL',
extensions=mock.ANY,
service_type='sharev2',
service_name='',
retries=0,
http_log_debug=False,
cacert=None,
use_keyring=False,
force_new_token=False,
api_version=constants.MAX_API_VERSION,
user_id=env_vars['OS_USER_ID'],
user_domain_id=env_vars['OS_USER_DOMAIN_ID'],
user_domain_name=env_vars['OS_USER_DOMAIN_NAME'],
project_domain_id=env_vars['OS_PROJECT_DOMAIN_ID'],
project_domain_name=env_vars['OS_PROJECT_DOMAIN_NAME'],
cert=env_vars['OS_CERT'],
)
def test_help_unknown_command(self):
self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo')
@ -79,3 +145,20 @@ class ShellTest(utils.TestCase):
for r in required:
self.assertThat(help_text,
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
def test_common_args_in_help_message(self):
expected_args = (
'--version', '', '--debug', '--os-cache', '--os-reset-cache',
'--os-user-id', '--os-username', '--os-password',
'--os-tenant-name', '--os-project-name', '--os-tenant-id',
'--os-project-id', '--os-user-domain-id', '--os-user-domain-name',
'--os-project-domain-id', '--os-project-domain-name',
'--os-auth-url', '--os-region-name', '--service-type',
'--service-name', '--share-service-name', '--endpoint-type',
'--os-share-api-version', '--os-cacert', '--retries', '--os-cert',
)
help_text = self.shell('help')
for expected_arg in expected_args:
self.assertIn(expected_arg, help_text)

@ -13,6 +13,7 @@
import os
import fixtures
import mock
import requests
import testtools
@ -33,6 +34,20 @@ class TestCase(testtools.TestCase):
stderr = self.useFixture(fixtures.StringStream('stderr')).stream
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
def mock_object(self, obj, attr_name, new_attr=None, **kwargs):
"""Mock an object attribute.
Use python mock to mock an object attribute
Mocks the specified objects attribute with the given value.
Automatically performs 'addCleanup' for the mock.
"""
if not new_attr:
new_attr = mock.Mock()
patcher = mock.patch.object(obj, attr_name, new_attr, **kwargs)
patcher.start()
self.addCleanup(patcher.stop)
return new_attr
class TestResponse(requests.Response):
"""Class used to wrap requests.Response.

@ -12,19 +12,19 @@
import uuid
from keystoneclient import session
import ddt
import mock
from manilaclient.common import constants
from manilaclient import exceptions
from manilaclient import httpclient
from manilaclient.tests.unit import utils
from manilaclient.v1 import client
@ddt.ddt
class ClientTest(utils.TestCase):
def setUp(self):
super(ClientTest, self).setUp()
super(self.__class__, self).setUp()
self.catalog = {
'share': [
{'region': 'TestRegion', 'publicURL': 'http://1.2.3.4'},
@ -36,7 +36,7 @@ class ClientTest(utils.TestCase):
retries = 3
base_url = uuid.uuid4().hex
s = session.Session()
s = client.session.Session()
c = client.Client(session=s, api_version=constants.MAX_API_VERSION,
service_catalog_url=base_url, retries=retries,
input_auth_token='token')
@ -50,7 +50,7 @@ class ClientTest(utils.TestCase):
input_auth_token="token")
def test_auth_via_token_and_session(self):
s = session.Session()
s = client.session.Session()
base_url = uuid.uuid4().hex
c = client.Client(input_auth_token='token',
service_catalog_url=base_url, session=s,
@ -69,9 +69,9 @@ class ClientTest(utils.TestCase):
self.assertIsNotNone(c.client)
self.assertIsNone(c.keystone_client)
@mock.patch.object(httpclient, 'HTTPClient', mock.Mock())
@mock.patch.object(client.Client, '_get_keystone_client', mock.Mock())
def test_valid_region_name(self):
self.mock_object(client.httpclient, 'HTTPClient')
kc = client.Client._get_keystone_client.return_value
kc.service_catalog = mock.Mock()
kc.service_catalog.get_endpoints = mock.Mock(return_value=self.catalog)
@ -79,7 +79,7 @@ class ClientTest(utils.TestCase):
region_name='TestRegion')
self.assertTrue(client.Client._get_keystone_client.called)
kc.service_catalog.get_endpoints.assert_called_once_with('share')
httpclient.HTTPClient.assert_called_once_with(
client.httpclient.HTTPClient.assert_called_once_with(
'http://1.2.3.4',
mock.ANY,
'python-manilaclient',
@ -99,12 +99,13 @@ class ClientTest(utils.TestCase):
self.assertRaises(RuntimeError, client.Client,
api_version=constants.MAX_API_VERSION,
region_name='FakeRegion')
self.assertTrue(client.Client._get_keystone_client.called)
kc.service_catalog.get_endpoints.assert_called_once_with('share')
@mock.patch.object(httpclient, 'HTTPClient', mock.Mock())
@mock.patch.object(client.Client, '_get_keystone_client', mock.Mock())
def test_regions_with_same_name(self):
self.mock_object(client.httpclient, 'HTTPClient')
catalog = {
'share': [
{'region': 'FirstRegion', 'publicURL': 'http://1.2.3.4'},
@ -119,7 +120,7 @@ class ClientTest(utils.TestCase):
region_name='SecondRegion')
self.assertTrue(client.Client._get_keystone_client.called)
kc.service_catalog.get_endpoints.assert_called_once_with('share')
httpclient.HTTPClient.assert_called_once_with(
client.httpclient.HTTPClient.assert_called_once_with(
'http://2.2.2.2',
mock.ANY,
'python-manilaclient',
@ -130,3 +131,166 @@ class ClientTest(utils.TestCase):
http_log_debug=False,
api_version=constants.MAX_API_VERSION)
self.assertIsNotNone(c.client)
def _get_client_args(self, **kwargs):
client_args = {
'auth_url': 'both',
'api_version': constants.MAX_API_VERSION,
'username': 'fake_username',
'service_type': 'sharev2',
'region_name': 'SecondRegion',
'input_auth_token': None,
'session': None,
'service_catalog_url': None,
'user_id': 'foo_user_id',
'user_domain_name': 'foo_user_domain_name',
'user_domain_id': 'foo_user_domain_id',
'project_name': 'foo_project_name',
'project_domain_name': 'foo_project_domain_name',
'project_domain_id': 'foo_project_domain_id',
'endpoint_type': 'publicUrl',
'cert': 'foo_cert',
}
client_args.update(kwargs)
return client_args
@ddt.data(
{'auth_url': 'only_v3', 'api_key': 'password_backward_compat',
'endpoint_type': 'publicURL', 'project_id': 'foo_tenant_project_id'},
{'password': 'renamed_api_key', 'endpoint_type': 'public',
'tenant_id': 'foo_tenant_project_id'},
)
def test_client_init_no_session_no_auth_token_v3(self, kwargs):
def fake_url_for(version):
if version == 'v3.0':
return 'url_v3.0'
elif version == 'v2.0' and self.auth_url == 'both':
return 'url_v2.0'
else:
return None
self.mock_object(client.httpclient, 'HTTPClient')
self.mock_object(client.ks_client, 'Client')
self.mock_object(client.discover, 'Discover')
self.mock_object(client.session, 'Session')
client_args = self._get_client_args(**kwargs)
self.auth_url = client_args['auth_url']
catalog = {
'share': [
{'region': 'SecondRegion', 'region_id': 'SecondRegion',
'url': 'http://4.4.4.4', 'interface': 'public',
},
],
'sharev2': [
{'region': 'FirstRegion', 'interface': 'public',
'region_id': 'SecondRegion', 'url': 'http://1.1.1.1'},
{'region': 'secondregion', 'interface': 'public',
'region_id': 'SecondRegion', 'url': 'http://2.2.2.2'},
{'region': 'SecondRegion', 'interface': 'internal',
'region_id': 'SecondRegion', 'url': 'http://3.3.3.1'},
{'region': 'SecondRegion', 'interface': 'public',
'region_id': 'SecondRegion', 'url': 'http://3.3.3.3'},
{'region': 'SecondRegion', 'interface': 'admin',
'region_id': 'SecondRegion', 'url': 'http://3.3.3.2'},
],
}
client.discover.Discover.return_value.url_for.side_effect = (
fake_url_for)
client.ks_client.Client.return_value.auth_token.return_value = (
'fake_token')
mocked_ks_client = client.ks_client.Client.return_value
mocked_ks_client.service_catalog.get_endpoints.return_value = catalog
client.Client(**client_args)
client.httpclient.HTTPClient.assert_called_once_with(
'http://3.3.3.3', mock.ANY, 'python-manilaclient', insecure=False,
cacert=None, timeout=None, retries=None, http_log_debug=False,
api_version=constants.MAX_API_VERSION)
client.ks_client.Client.assert_called_once_with(
version=(3, 0), auth_url='url_v3.0',
username=client_args['username'],
password=client_args.get('password', client_args.get('api_key')),
user_id=client_args['user_id'],
user_domain_name=client_args['user_domain_name'],
user_domain_id=client_args['user_domain_id'],
project_id=client_args.get('tenant_id',
client_args.get('project_id')),
project_name=client_args['project_name'],
project_domain_name=client_args['project_domain_name'],
project_domain_id=client_args['project_domain_id'],
region_name=client_args['region_name'],
)
mocked_ks_client.service_catalog.get_endpoints.assert_called_once_with(
client_args['service_type'])
mocked_ks_client.authenticate.assert_called_once_with()
@ddt.data(
{'auth_url': 'only_v2', 'api_key': 'foo', 'project_id': 'bar'},
{'password': 'foo', 'tenant_id': 'bar'},
)
def test_client_init_no_session_no_auth_token_v2(self, kwargs):
self.mock_object(client.httpclient, 'HTTPClient')
self.mock_object(client.ks_client, 'Client')
self.mock_object(client.discover, 'Discover')
self.mock_object(client.session, 'Session')
client_args = self._get_client_args(**kwargs)
self.auth_url = client_args['auth_url']
catalog = {
'share': [
{'region': 'SecondRegion', 'publicUrl': 'http://4.4.4.4'},
],
'sharev2': [
{'region': 'FirstRegion', 'publicUrl': 'http://1.1.1.1'},
{'region': 'secondregion', 'publicUrl': 'http://2.2.2.2'},
{'region': 'SecondRegion', 'internalUrl': 'http://3.3.3.1',
'publicUrl': 'http://3.3.3.3', 'adminUrl': 'http://3.3.3.2'},
],
}
client.discover.Discover.return_value.url_for.side_effect = (
lambda v: 'url_v2.0' if v == 'v2.0' else None)
client.ks_client.Client.return_value.auth_token.return_value = (
'fake_token')
mocked_ks_client = client.ks_client.Client.return_value
mocked_ks_client.service_catalog.get_endpoints.return_value = catalog
client.Client(**client_args)
client.httpclient.HTTPClient.assert_called_once_with(
'http://3.3.3.3', mock.ANY, 'python-manilaclient', insecure=False,
cacert=None, timeout=None, retries=None, http_log_debug=False,
api_version=constants.MAX_API_VERSION)
client.ks_client.Client.assert_called_once_with(
version=(2, 0), auth_url='url_v2.0',
username=client_args['username'],
password=client_args.get('password', client_args.get('api_key')),
tenant_id=client_args.get('tenant_id',
client_args.get('project_id')),
tenant_name=client_args['project_name'],
region_name=client_args['region_name'], cert=client_args['cert'],
use_keyring=False, force_new_token=False, stale_duration=300)
mocked_ks_client.service_catalog.get_endpoints.assert_called_once_with(
client_args['service_type'])
mocked_ks_client.authenticate.assert_called_once_with()
@mock.patch.object(client.ks_client, 'Client', mock.Mock())
@mock.patch.object(client.discover, 'Discover', mock.Mock())
@mock.patch.object(client.session, 'Session', mock.Mock())
def test_client_init_no_session_no_auth_token_endpoint_not_found(self):
self.mock_object(client.httpclient, 'HTTPClient')
client_args = self._get_client_args(
auth_urli='fake_url',
password='foo_password',
tenant_id='foo_tenant_id')
client.discover.Discover.return_value.url_for.return_value = None
mocked_ks_client = client.ks_client.Client.return_value
self.assertRaises(
exceptions.CommandError, client.Client, **client_args)
self.assertTrue(client.session.Session.called)
self.assertTrue(client.discover.Discover.called)
self.assertFalse(client.httpclient.HTTPClient.called)
self.assertFalse(client.ks_client.Client.called)
self.assertFalse(mocked_ks_client.service_catalog.get_endpoints.called)
self.assertFalse(mocked_ks_client.authenticate.called)

@ -99,6 +99,69 @@ class ShellTest(test_utils.TestCase):
def assert_called_anytime(self, method, url, body=None):
return self.shell.cs.assert_called_anytime(method, url, body)
@ddt.data(
{'serviceCatalog': [{'name': 'foo', 'endpoints': ['bar']}]},
{'catalog': [{'name': 'foo', 'endpoints': ['bar']}]},
{'serviceCatalog': [{'name': 'foo', 'endpoints': ['bar']}],
'catalog': 'fake'},
)
def test_do_endpoints(self, catalog):
cs = type('Fake', (object, ), {'keystone_client': type(
'FakeKeystoneClient', (object, ), {
'service_catalog': type('FakeCatalog', (object, ), {
'catalog': catalog})})})
with mock.patch.object(
shell_v1.cliutils, 'print_dict') as mock_print_dict:
shell_v1.do_endpoints(cs, ('no', 'args'))
mock_print_dict.assert_has_calls([
mock.call('bar', 'foo'),
])
@ddt.data(
{'version': 'v3',
'user': 'foo_user',
'issued_at': 'foo_issued_at',
'expires_at': 'foo_expires',
'auth_token': 'foo_ids',
'audit_ids': 'foo_audit_ids',
'project': 'foo_tenant_project',
'redundant_key': 'should not be used',
},
{'version': 'v2.0',
'user': 'foo_user',
'token': {
'issued_at': 'foo_issued_at',
'expires': 'foo_expires',
'id': 'foo_ids',
'audit_ids': 'foo_audit_ids',
'tenant': 'foo_tenant_project',
},
},
)
def test_do_credentials(self, catalog):
cs = type('Fake', (object, ), {'keystone_client': type(
'FakeKeystoneClient', (object, ), {
'service_catalog': type('FakeCatalog', (object, ), {
'catalog': catalog})})})
expected_call_data = {
'issued_at': 'foo_issued_at',
'expires': 'foo_expires',
'id': 'foo_ids',
'audit_ids': 'foo_audit_ids',
'tenant': 'foo_tenant_project',
}
with mock.patch.object(
shell_v1.cliutils, 'print_dict') as mock_print_dict:
shell_v1.do_credentials(cs, ('no', 'args'))
mock_print_dict.assert_has_calls([
mock.call('foo_user', 'User Credentials'),
mock.call(expected_call_data, 'Token'),
])
def test_list(self):
self.run_command('list')
# NOTE(jdg): we default to detail currently

@ -13,8 +13,9 @@
import warnings
from keystoneclient import adapter
from keystoneclient.v2_0 import client as keystone_client_v2
from keystoneclient.v3 import client as keystone_client_v3
from keystoneclient import client as ks_client
from keystoneclient import discover
from keystoneclient import session
import six
from manilaclient.common import constants
@ -72,7 +73,41 @@ class Client(object):
service_catalog_url=None, user_agent='python-manilaclient',
use_keyring=False, force_new_token=False,
cached_token_lifetime=300,
api_version=constants.V1_API_VERSION, **kwargs):
api_version=constants.V1_API_VERSION,
user_id=None,
user_domain_id=None,
user_domain_name=None,
project_domain_id=None,
project_domain_name=None,
cert=None,
password=None,
**kwargs):
self.username = username
self.password = password or api_key
self.tenant_id = tenant_id or project_id
self.tenant_name = project_name
self.user_id = user_id
self.project_id = project_id or tenant_id
self.project_name = project_name
self.user_domain_id = user_domain_id
self.user_domain_name = user_domain_name
self.project_domain_id = project_domain_id
self.project_domain_name = project_domain_name
self.endpoint_type = endpoint_type
self.auth_url = auth_url
self.region_name = region_name
self.cacert = cacert
self.cert = cert
self.insecure = insecure
self.use_keyring = use_keyring
self.force_new_token = force_new_token
self.cached_token_lifetime = cached_token_lifetime
service_name = kwargs.get("share_service_name", service_name)
def check_deprecated_arguments():
@ -80,7 +115,8 @@ class Client(object):
'share_service_name': 'service_name',
'proxy_tenant_id': None,
'proxy_token': None,
'os_cache': 'use_keyring'
'os_cache': 'use_keyring',
'api_key': 'password',
}
for arg, replacement in six.iteritems(deprecated):
@ -124,15 +160,7 @@ class Client(object):
input_auth_token = self.keystone_client.session.get_token(auth)
else:
self.keystone_client = self._get_keystone_client(
username=username,
api_key=api_key,
auth_url=auth_url,
project_id=self.project_id,
project_name=project_name,
use_keyring=use_keyring,
force_new_token=force_new_token,
stale_duration=cached_token_lifetime)
self.keystone_client = self._get_keystone_client()
input_auth_token = self.keystone_client.auth_token
if not input_auth_token:
@ -145,19 +173,18 @@ class Client(object):
elif not service_catalog_url:
catalog = self.keystone_client.service_catalog.get_endpoints(
service_type)
if service_type in catalog:
if not region_name:
catalog_entry = catalog.get(service_type)[0]
else:
for catalog_entry in catalog.get(service_type):
if catalog_entry.get("region") == region_name:
break
else:
catalog_entry = {}
for e_type, endpoint in catalog_entry.items():
if str(e_type).lower() == str(endpoint_type).lower():
service_catalog_url = endpoint
break
for catalog_entry in catalog.get(service_type, []):
if (catalog_entry.get("interface") == (
endpoint_type.lower().split("url")[0]) or
catalog_entry.get(endpoint_type)):
if (region_name and not region_name == (
catalog_entry.get(
"region",
catalog_entry.get("region_id")))):
continue
service_catalog_url = catalog_entry.get(
"url", catalog_entry.get(endpoint_type))
break
if not service_catalog_url:
raise RuntimeError("Could not find Manila endpoint in catalog")
@ -203,33 +230,6 @@ class Client(object):
if extension.manager_class:
setattr(self, extension.name, extension.manager_class(self))
def _get_keystone_client(self, username=None, api_key=None, auth_url=None,
token=None, project_id=None, project_name=None,
use_keyring=False, force_new_token=False,
stale_duration=0):
if not auth_url:
raise RuntimeError("No auth url specified")
if not getattr(self, "keystone_client", None):
imported_client = (keystone_client_v2 if "v2.0" in auth_url
else keystone_client_v3)
self.keystone_client = imported_client.Client(
username=username,
password=api_key,
token=token,
tenant_id=project_id,
tenant_name=project_name,
auth_url=auth_url,
endpoint=auth_url,
use_keyring=use_keyring,
force_new_token=force_new_token,
stale_duration=stale_duration)
self.keystone_client.authenticate()
return self.keystone_client
def authenticate(self):
"""Authenticate against the server.
@ -242,3 +242,54 @@ class Client(object):
warnings.warn("authenticate() method is deprecated. "
"Client automatically makes authentication call "
"in the constructor.")
def _get_keystone_client(self):
# First create a Keystone session
if self.insecure:
verify = False
else:
verify = self.cacert or True
ks_session = session.Session(verify=verify, cert=self.cert)
# Discover the supported keystone versions using the given url
ks_discover = discover.Discover(
session=ks_session, auth_url=self.auth_url)
# Inspect the auth_url to see the supported version. If both v3 and v2
# are supported, then use the highest version if possible.
v2_auth_url = ks_discover.url_for('v2.0')
v3_auth_url = ks_discover.url_for('v3.0')
if v3_auth_url:
keystone_client = ks_client.Client(
version=(3, 0),
auth_url=v3_auth_url,
username=self.username,
password=self.password,
user_id=self.user_id,
user_domain_name=self.user_domain_name,
user_domain_id=self.user_domain_id,
project_id=self.project_id or self.tenant_id,
project_name=self.project_name,
project_domain_name=self.project_domain_name,
project_domain_id=self.project_domain_id,
region_name=self.region_name)
elif v2_auth_url:
keystone_client = ks_client.Client(
version=(2, 0),
auth_url=v2_auth_url,
username=self.username,
password=self.password,
tenant_id=self.tenant_id,
tenant_name=self.tenant_name,
region_name=self.region_name,
cert=self.cert,
use_keyring=self.use_keyring,
force_new_token=self.force_new_token,
stale_duration=self.cached_token_lifetime)
else:
raise exceptions.CommandError(
'Unable to determine the Keystone version to authenticate '
'with using the given auth_url.')
keystone_client.authenticate()
return keystone_client

@ -201,7 +201,7 @@ def do_api_version(cs, args):
def do_endpoints(cs, args):
"""Discover endpoints that get returned from the authenticate services."""
catalog = cs.keystone_client.service_catalog.catalog
for e in catalog['serviceCatalog']:
for e in catalog.get('serviceCatalog', catalog.get('catalog')):
cliutils.print_dict(e['endpoints'][0], e['name'])
@ -209,7 +209,17 @@ def do_credentials(cs, args):
"""Show user credentials returned from auth."""
catalog = cs.keystone_client.service_catalog.catalog
cliutils.print_dict(catalog['user'], "User Credentials")
cliutils.print_dict(catalog['token'], "Token")
if not catalog['version'] == 'v3':
data = catalog['token']
else:
data = {
'issued_at': catalog['issued_at'],
'expires': catalog['expires_at'],
'id': catalog['auth_token'],
'audit_ids': catalog['audit_ids'],
'tenant': catalog['project'],
}
cliutils.print_dict(data, "Token")
_quota_resources = [
'shares',