summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-11-24 11:59:00 +0000
committerGerrit Code Review <review@openstack.org>2014-11-24 11:59:00 +0000
commitf09c4c8d3de632dc7b7b3b3a1c4eb32d7ccd3652 (patch)
tree4146a970a47f86deec11643547388a3f3549f6ee
parent781e0cb4837cd04a3dcd730d349507b019cd15db (diff)
parentf248bf6d72247dd985539ca79af86ba4de32ba89 (diff)
downloadpython-saharaclient-f09c4c8d3de632dc7b7b3b3a1c4eb32d7ccd3652.tar.gz
Merge "Add Support for Keystone V3 CLI"
-rw-r--r--saharaclient/api/client.py51
-rw-r--r--saharaclient/shell.py129
-rw-r--r--saharaclient/tests/unit/nova/test_shell.py96
3 files changed, 214 insertions, 62 deletions
diff --git a/saharaclient/api/client.py b/saharaclient/api/client.py
index 8cfb8f3..c8b2a2e 100644
--- a/saharaclient/api/client.py
+++ b/saharaclient/api/client.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from keystoneclient import adapter
from keystoneclient.v2_0 import client as keystone_client_v2
from keystoneclient.v3 import client as keystone_client_v3
@@ -33,26 +34,38 @@ class Client(object):
def __init__(self, username=None, api_key=None, project_id=None,
project_name=None, auth_url=None, sahara_url=None,
endpoint_type='publicURL', service_type='data_processing',
- input_auth_token=None):
+ service_name=None, region_name=None,
+ input_auth_token=None, session=None, auth=None):
+
+ keystone = None
+ sahara_catalog_url = sahara_url
if not input_auth_token:
- keystone = self.get_keystone_client(username=username,
- api_key=api_key,
- auth_url=auth_url,
- project_id=project_id,
- project_name=project_name)
- input_auth_token = keystone.auth_token
+
+ if session:
+ keystone = adapter.LegacyJsonAdapter(
+ session=session,
+ auth=auth,
+ interface=endpoint_type,
+ service_type=service_type,
+ service_name=service_name,
+ region_name=region_name)
+ input_auth_token = keystone.session.get_token(auth)
+ sahara_catalog_url = keystone.session.get_endpoint(
+ auth, interface=endpoint_type, service_type=service_type)
+ else:
+ keystone = self.get_keystone_client(
+ username=username,
+ api_key=api_key,
+ auth_url=auth_url,
+ project_id=project_id,
+ project_name=project_name)
+ input_auth_token = keystone.auth_token
+
if not input_auth_token:
raise RuntimeError("Not Authorized")
- sahara_catalog_url = sahara_url
- if not sahara_url:
- keystone = self.get_keystone_client(username=username,
- api_key=api_key,
- auth_url=auth_url,
- token=input_auth_token,
- project_id=project_id,
- project_name=project_name)
+ if not sahara_catalog_url:
catalog = keystone.service_catalog.get_endpoints(service_type)
if service_type not in catalog:
service_type = service_type.replace('-', '_')
@@ -86,10 +99,12 @@ class Client(object):
def get_keystone_client(self, username=None, api_key=None, auth_url=None,
token=None, project_id=None, project_name=None):
if not auth_url:
- raise RuntimeError("No auth url specified")
- imported_client = (keystone_client_v2 if "v2.0" in auth_url
- else keystone_client_v3)
+ 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,
diff --git a/saharaclient/shell.py b/saharaclient/shell.py
index 883e000..489138f 100644
--- a/saharaclient/shell.py
+++ b/saharaclient/shell.py
@@ -47,6 +47,11 @@ try:
except ImportError:
pass
+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
+
from saharaclient.api import client
from saharaclient.api import shell as shell_api
from saharaclient.openstack.common.apiclient import auth
@@ -251,6 +256,7 @@ class OpenStackSaharaShell(object):
help="Use the auth token cache. Defaults to False "
"if env[OS_CACHE] is not set.")
+
# TODO(mattf) - add get_timings support to Client
# parser.add_argument('--timings',
# default=False,
@@ -264,11 +270,6 @@ class OpenStackSaharaShell(object):
# type=positive_non_zero_float,
# help="Set HTTP call timeout (in seconds)")
- parser.add_argument('--os-tenant-id',
- metavar='<auth-tenant-id>',
- default=cliutils.env('OS_TENANT_ID'),
- help='Defaults to env[OS_TENANT_ID].')
-
# NA
# parser.add_argument('--os-region-name',
# metavar='<region-name>',
@@ -324,22 +325,6 @@ class OpenStackSaharaShell(object):
parser.add_argument('--sahara_api_version',
help=argparse.SUPPRESS)
- parser.add_argument('--os-cacert',
- metavar='<ca-certificate>',
- default=cliutils.env('OS_CACERT', default=None),
- help='Specify a CA bundle file to use in '
- 'verifying a TLS (https) server certificate. '
- 'Defaults to env[OS_CACERT].')
-
-# NA
-# parser.add_argument('--insecure',
-# default=utils.env('NOVACLIENT_INSECURE', default=False),
-# action='store_true',
-# help="Explicitly allow novaclient to perform \"insecure\" "
-# "SSL (https) requests. The server's certificate will "
-# "not be verified against any certificate authorities. "
-# "This option should be used with caution.")
-
parser.add_argument('--bypass-url',
metavar='<bypass-url>',
default=cliutils.env('BYPASS_URL', default=None),
@@ -349,8 +334,25 @@ class OpenStackSaharaShell(object):
parser.add_argument('--bypass_url',
help=argparse.SUPPRESS)
- # The auth-system-plugins might require some extra options
- auth.load_auth_system_opts(parser)
+ parser.add_argument('--os-tenant-name',
+ default=cliutils.env('OS_TENANT_NAME'),
+ help='Defaults to env[OS_TENANT_NAME].')
+
+ parser.add_argument('--os-tenant-id',
+ default=cliutils.env('OS_TENANT_ID'),
+ help='Defaults to env[OS_TENANT_ID].')
+
+ parser.add_argument('--os-auth-system',
+ default=cliutils.env('OS_AUTH_SYSTEM'),
+ help='Defaults to env[OS_AUTH_SYSTEM].')
+
+ parser.add_argument('--os-auth-token',
+ default=cliutils.env('OS_AUTH_TOKEN'),
+ help='Defaults to env[OS_AUTH_TOKEN].')
+
+ # Use Keystoneclient API to parse authentication arguments
+ session.Session.register_cli_options(parser)
+ identity.Password.register_argparse_arguments(parser)
return parser
@@ -418,12 +420,27 @@ class OpenStackSaharaShell(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()
(options, args) = parser.parse_known_args(argv)
self.setup_debugging(options.debug)
+ self.options = options
# NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse
# thinking usage-list --end is ambiguous; but it
@@ -502,12 +519,6 @@ class OpenStackSaharaShell(object):
"via either --os-username or "
"env[OS_USERNAME]")
- 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 os_auth_url:
if os_auth_system and os_auth_system != 'keystone':
os_auth_url = auth_plugin.get_auth_url()
@@ -574,21 +585,63 @@ class OpenStackSaharaShell(object):
# self.cs.client.password = os_password
# self.cs.client.keyring_saver = helper
-# NA
-# try:
-# if not utils.isunauthenticated(args.func):
-# self.cs.authenticate()
-# except exc.Unauthorized:
-# raise exc.CommandError("Invalid OpenStack Sahara credentials.")
-# except exc.AuthorizationFailure:
-# raise exc.CommandError("Unable to authorize user")
+ # V3 stuff
+ project_info_provided = (self.options.os_tenant_name or
+ self.options.os_tenant_id or
+ (self.options.os_project_name and
+ (self.options.os_project_domain_name or
+ self.options.os_project_domain_id)) or
+ self.options.os_project_id)
+
+ if (not project_info_provided):
+ raise exc.CommandError(
+ ("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])"
+ " --os-project-domain-name "
+ "(env[OS_PROJECT_DOMAIN_NAME])"))
+
+ if not os_auth_url:
+ raise exc.CommandError(
+ "You must provide an auth url "
+ "via either --os-auth-url or env[OS_AUTH_URL]")
+
+ keystone_session = None
+ keystone_auth = None
+ if not auth_plugin:
+ project_id = args.os_project_id or args.os_tenant_id
+ project_name = args.os_project_name or args.os_tenant_name
+
+ keystone_session = (session.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)
self.cs = client.Client(username=os_username,
api_key=os_password,
project_id=os_tenant_id,
project_name=os_tenant_name,
auth_url=os_auth_url,
- sahara_url=bypass_url)
+ sahara_url=bypass_url,
+ session=keystone_session,
+ auth=keystone_auth)
args.func(self.cs, args)
diff --git a/saharaclient/tests/unit/nova/test_shell.py b/saharaclient/tests/unit/nova/test_shell.py
index 95e7e96..c9572ae 100644
--- a/saharaclient/tests/unit/nova/test_shell.py
+++ b/saharaclient/tests/unit/nova/test_shell.py
@@ -170,9 +170,19 @@ 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 = (
+ '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])'
+ ' --os-project-domain-name '
+ '(env[OS_PROJECT_DOMAIN_NAME])',
+ )
self.make_env(exclude='OS_TENANT_NAME')
try:
self.shell('plugin-list')
@@ -182,9 +192,19 @@ 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 = (
+ '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])'
+ ' --os-project-domain-name '
+ '(env[OS_PROJECT_DOMAIN_NAME])',
+ )
self.make_env(exclude='OS_TENANT_ID', fake_env=FAKE_ENV2)
try:
self.shell('plugin-list')
@@ -281,3 +301,67 @@ class ShellTest(utils.TestCase):
self.make_env()
stdout, stderr = self.shell('image-list')
self.assertEqual(ex, (stdout + stderr))
+
+
+class ShellTestKeystoneV3(ShellTest):
+
+ FAKE_V3_ENV = {'OS_USERNAME': 'username',
+ 'OS_PASSWORD': 'password',
+ 'OS_PROJECT_NAME': 'project_name',
+ 'OS_PROJECT_DOMAIN_NAME': 'project_domain_name',
+ 'OS_USER_DOMAIN_NAME': 'user_domain_name',
+ 'OS_AUTH_URL': 'http://no.where/v3'}
+
+ version_id = u'v3'
+ links = [{u'href': u'http://no.where/v3', u'rel': u'self'}]
+
+ def make_env(self, exclude=None, fake_env=FAKE_V3_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 test_no_tenant_name(self):
+ # In V3, tenant_name = project_name
+ required = (
+ '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])'
+ ' --os-project-domain-name '
+ '(env[OS_PROJECT_DOMAIN_NAME])',
+ )
+ self.make_env(exclude='OS_PROJECT_NAME')
+ try:
+ self.shell('plugin-list')
+ except exceptions.CommandError as message:
+ self.assertEqual(required, message.args)
+ else:
+ self.fail('CommandError not raised')
+
+ def test_job_list(self):
+ expected = '\n'.join([
+ '+----+------------+--------+',
+ '| id | cluster_id | status |',
+ '+----+------------+--------+',
+ '+----+------------+--------+',
+ ''
+ ])
+
+ mock_session_class_name = 'keystoneclient.adapter.LegacyJsonAdapter'
+ mock_job_executions_class_name = (
+ 'saharaclient.api.job_executions.JobExecutionsManager')
+
+ with mock.patch(mock_session_class_name) as mock_session:
+ with mock.patch(mock_job_executions_class_name):
+ ms = mock_session.return_value
+ ms.session.get_endpoint.return_value = 'http://no.where'
+
+ self.make_env()
+ stdout, stderr = self.shell('job-list')
+ self.assertEqual((stdout + stderr), expected)