diff --git a/novaclient/client.py b/novaclient/client.py index c0f81286e..404af61e5 100644 --- a/novaclient/client.py +++ b/novaclient/client.py @@ -251,6 +251,7 @@ class HTTPClient(object): service_catalog.ServiceCatalog(body) if extract_token: self.auth_token = self.service_catalog.get_token() + self.tenant_id = self.service_catalog.get_tenant_id() management_url = self.service_catalog.url_for( attr='region', @@ -356,7 +357,9 @@ class HTTPClient(object): # Store the token/mgmt url in the keyring for later requests. if self.keyring_saver and self.os_cache and not self.keyring_saved: - self.keyring_saver.save(self.auth_token, self.management_url) + self.keyring_saver.save(self.auth_token, + self.management_url, + self.tenant_id) # Don't save it again self.keyring_saved = True diff --git a/novaclient/service_catalog.py b/novaclient/service_catalog.py index bb856239a..f4a5f1adb 100644 --- a/novaclient/service_catalog.py +++ b/novaclient/service_catalog.py @@ -28,6 +28,9 @@ class ServiceCatalog(object): def get_token(self): return self.catalog['access']['token']['id'] + def get_tenant_id(self): + return self.catalog['access']['token']['tenant']['id'] + def url_for(self, attr=None, filter_value=None, service_type=None, endpoint_type='publicURL', service_name=None, volume_service_name=None): diff --git a/novaclient/shell.py b/novaclient/shell.py index c60e62892..ed2480aa1 100644 --- a/novaclient/shell.py +++ b/novaclient/shell.py @@ -114,16 +114,18 @@ class SecretsHelper(object): pass return pw - def save(self, auth_token, management_url): + def save(self, auth_token, management_url, tenant_id): if not HAS_KEYRING or not self.args.os_cache: return if (auth_token == self.auth_token and management_url == self.management_url): # Nothing changed.... return - if not all([management_url, auth_token]): + if not all([management_url, auth_token, tenant_id]): raise ValueError("Unable to save empty management url/auth token") - value = "|".join([str(auth_token), str(management_url)]) + value = "|".join([str(auth_token), + str(management_url), + str(tenant_id)]) keyring.set_password("novaclient_auth", self._make_key(), value) @property @@ -143,7 +145,7 @@ class SecretsHelper(object): try: block = keyring.get_password('novaclient_auth', self._make_key()) if block: - _token, management_url = block.split('|', 1) + _token, management_url, _tenant_id = block.split('|', 2) except ValueError: pass return management_url @@ -160,11 +162,24 @@ class SecretsHelper(object): try: block = keyring.get_password('novaclient_auth', self._make_key()) if block: - token, _management_url = block.split('|', 1) + token, _management_url, _tenant_id = block.split('|', 2) except ValueError: pass return token + @property + def tenant_id(self): + if not HAS_KEYRING: + return None + tenant_id = None + try: + block = keyring.get_password('novaclient_auth', self._make_key()) + if block: + _token, _management_url, tenant_id = block.split('|', 2) + except ValueError: + pass + return tenant_id + class NovaClientArgumentParser(argparse.ArgumentParser): @@ -609,9 +624,11 @@ class OpenStackComputeShell(object): if not utils.isunauthenticated(args.func): helper = SecretsHelper(args, self.cs.client) use_pw = True - auth_token, management_url = (helper.auth_token, - helper.management_url) - if auth_token and management_url: + tenant_id, auth_token, management_url = (helper.tenant_id, + helper.auth_token, + helper.management_url) + if tenant_id and auth_token and management_url: + self.cs.client.tenant_id = tenant_id self.cs.client.auth_token = auth_token self.cs.client.management_url = management_url # Try to auth with the given info, if it fails diff --git a/novaclient/v1_1/shell.py b/novaclient/v1_1/shell.py index eab195bb3..2a42bac09 100644 --- a/novaclient/v1_1/shell.py +++ b/novaclient/v1_1/shell.py @@ -2597,7 +2597,7 @@ def do_quota_show(cs, args): """List the quotas for a tenant.""" if not args.tenant: - raise exceptions.CommandError("you need to specify a Tenant ID ") + _quota_show(cs.quotas.get(cs.client.tenant_id)) else: _quota_show(cs.quotas.get(args.tenant)) @@ -2610,7 +2610,7 @@ def do_quota_defaults(cs, args): """List the default quotas for a tenant.""" if not args.tenant: - _quota_show(cs.quotas.defaults(cs.project_id)) + _quota_show(cs.quotas.defaults(cs.client.tenant_id)) else: _quota_show(cs.quotas.defaults(args.tenant)) diff --git a/tests/test_auth_plugins.py b/tests/test_auth_plugins.py index c4950af87..e73c3545e 100644 --- a/tests/test_auth_plugins.py +++ b/tests/test_auth_plugins.py @@ -35,6 +35,9 @@ def mock_http_request(resp=None): "token": { "expires": "12345", "id": "FAKE_ID", + "tenant": { + "id": "FAKE_TENANT_ID", + } }, "serviceCatalog": [ { diff --git a/tests/v1_1/fakes.py b/tests/v1_1/fakes.py index 48ab33d1a..2125762f6 100644 --- a/tests/v1_1/fakes.py +++ b/tests/v1_1/fakes.py @@ -37,6 +37,7 @@ class FakeHTTPClient(base_client.HTTPClient): self.username = 'username' self.password = 'password' self.auth_url = 'auth_url' + self.tenant_id = 'tenant_id' self.callstack = [] def _cs_request(self, url, method, **kwargs): @@ -863,6 +864,23 @@ class FakeHTTPClient(base_client.HTTPClient): 'security_groups': 1, 'security_group_rules': 1}}) + def get_os_quota_sets_tenant_id(self, **kw): + return (200, {}, {'quota_set': { + 'tenant_id': 'test', + 'metadata_items': [], + 'injected_file_content_bytes': 1, + 'injected_file_path_bytes': 1, + 'volumes': 1, + 'gigabytes': 1, + 'ram': 1, + 'floating_ips': 1, + 'instances': 1, + 'injected_files': 1, + 'cores': 1, + 'keypairs': 1, + 'security_groups': 1, + 'security_group_rules': 1}}) + def get_os_quota_sets_97f4c221bff44578b0300df4ef119353(self, **kw): return (200, {}, {'quota_set': { 'tenant_id': '97f4c221bff44578b0300df4ef119353', @@ -931,6 +949,23 @@ class FakeHTTPClient(base_client.HTTPClient): 'security_groups': 1, 'security_group_rules': 1}}) + def get_os_quota_sets_tenant_id_defaults(self): + return (200, {}, {'quota_set': { + 'tenant_id': 'test', + 'metadata_items': [], + 'injected_file_content_bytes': 1, + 'injected_file_path_bytes': 1, + 'volumes': 1, + 'gigabytes': 1, + 'ram': 1, + 'floating_ips': 1, + 'instances': 1, + 'injected_files': 1, + 'cores': 1, + 'keypairs': 1, + 'security_groups': 1, + 'security_group_rules': 1}}) + def put_os_quota_sets_97f4c221bff44578b0300df4ef119353(self, body, **kw): assert body.keys() == ['quota_set'] fakes.assert_has_keys(body['quota_set'], diff --git a/tests/v1_1/test_auth.py b/tests/v1_1/test_auth.py index d10b49cb2..b34f77694 100644 --- a/tests/v1_1/test_auth.py +++ b/tests/v1_1/test_auth.py @@ -18,6 +18,9 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase): "token": { "expires": "12345", "id": "FAKE_ID", + "tenant": { + "id": "FAKE_TENANT_ID", + } }, "serviceCatalog": [ { @@ -101,6 +104,9 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase): "token": { "expires": "12345", "id": "FAKE_ID", + "tenant": { + "id": "FAKE_TENANT_ID", + } }, "serviceCatalog": [ { @@ -186,6 +192,9 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase): "token": { "expires": "12345", "id": "FAKE_ID", + "tenant": { + "id": "FAKE_TENANT_ID", + } }, "serviceCatalog": [ { diff --git a/tests/v1_1/test_shell.py b/tests/v1_1/test_shell.py index 0d3395cae..4384a242b 100644 --- a/tests/v1_1/test_shell.py +++ b/tests/v1_1/test_shell.py @@ -707,14 +707,17 @@ class ShellTest(utils.TestCase): self.assert_called('GET', '/os-quota-sets/test') def test_quota_show_no_tenant(self): - self.assertRaises(exceptions.CommandError, - self.run_command, - 'quota-show') + self.run_command('quota-show') + self.assert_called('GET', '/os-quota-sets/tenant_id') def test_quota_defaults(self): self.run_command('quota-defaults --tenant test') self.assert_called('GET', '/os-quota-sets/test/defaults') + def test_quota_defaults_no_nenant(self): + self.run_command('quota-defaults') + self.assert_called('GET', '/os-quota-sets/tenant_id/defaults') + def test_quota_update(self): self.run_command( 'quota-update 97f4c221bff44578b0300df4ef119353 \