Merge "Don't call CS if a token + URL are provided"
This commit is contained in:
commit
e2b1de000a
novaclient
@ -48,13 +48,19 @@ class HTTPClient(object):
|
||||
timings=False, bypass_url=None,
|
||||
os_cache=False, no_cache=True,
|
||||
http_log_debug=False, auth_system='keystone',
|
||||
auth_plugin=None,
|
||||
auth_plugin=None, auth_token=None,
|
||||
cacert=None, tenant_id=None):
|
||||
self.user = user
|
||||
self.password = password
|
||||
self.projectid = projectid
|
||||
self.tenant_id = tenant_id
|
||||
|
||||
# This will be called by #_get_password if self.password is None.
|
||||
# EG if a password can only be obtained by prompting the user, but a
|
||||
# token is available, you don't want to prompt until the token has
|
||||
# been proven invalid
|
||||
self.password_func = None
|
||||
|
||||
if auth_system and auth_system != 'keystone' and not auth_plugin:
|
||||
raise exceptions.AuthSystemNotFound(auth_system)
|
||||
|
||||
@ -80,8 +86,8 @@ class HTTPClient(object):
|
||||
|
||||
self.times = [] # [("item", starttime, endtime), ...]
|
||||
|
||||
self.management_url = None
|
||||
self.auth_token = None
|
||||
self.management_url = self.bypass_url or None
|
||||
self.auth_token = auth_token
|
||||
self.proxy_token = proxy_token
|
||||
self.proxy_tenant_id = proxy_tenant_id
|
||||
self.keyring_saver = None
|
||||
@ -239,6 +245,11 @@ class HTTPClient(object):
|
||||
except exceptions.Unauthorized:
|
||||
raise e
|
||||
|
||||
def _get_password(self):
|
||||
if not self.password and self.password_func:
|
||||
self.password = self.password_func()
|
||||
return self.password
|
||||
|
||||
def get(self, url, **kwargs):
|
||||
return self._cs_request(url, 'GET', **kwargs)
|
||||
|
||||
@ -324,6 +335,10 @@ class HTTPClient(object):
|
||||
self.version = part
|
||||
break
|
||||
|
||||
if self.auth_token and self.management_url:
|
||||
self._save_keys()
|
||||
return
|
||||
|
||||
# TODO(sandy): Assume admin endpoint is 35357 for now.
|
||||
# Ideally this is going to have to be provided by the service catalog.
|
||||
new_netloc = netloc.replace(':%d' % port, ':%d' % (35357,))
|
||||
@ -367,8 +382,13 @@ class HTTPClient(object):
|
||||
elif not self.management_url:
|
||||
raise exceptions.Unauthorized('Nova Client')
|
||||
|
||||
self._save_keys()
|
||||
|
||||
def _save_keys(self):
|
||||
# Store the token/mgmt url in the keyring for later requests.
|
||||
if self.keyring_saver and self.os_cache and not self.keyring_saved:
|
||||
if (self.keyring_saver and self.os_cache and not self.keyring_saved
|
||||
and self.auth_token and self.management_url
|
||||
and self.tenant_id):
|
||||
self.keyring_saver.save(self.auth_token,
|
||||
self.management_url,
|
||||
self.tenant_id)
|
||||
@ -380,7 +400,7 @@ class HTTPClient(object):
|
||||
raise exceptions.NoTokenLookupException()
|
||||
|
||||
headers = {'X-Auth-User': self.user,
|
||||
'X-Auth-Key': self.password}
|
||||
'X-Auth-Key': self._get_password()}
|
||||
if self.projectid:
|
||||
headers['X-Auth-Project-Id'] = self.projectid
|
||||
|
||||
@ -409,7 +429,7 @@ class HTTPClient(object):
|
||||
else:
|
||||
body = {"auth": {
|
||||
"passwordCredentials": {"username": self.user,
|
||||
"password": self.password}}}
|
||||
"password": self._get_password()}}}
|
||||
|
||||
if self.tenant_id:
|
||||
body['auth']['tenantId'] = self.tenant_id
|
||||
@ -423,16 +443,6 @@ class HTTPClient(object):
|
||||
method = "POST"
|
||||
token_url = url + "/tokens"
|
||||
|
||||
# if we have a valid auth token, use it instead of generating a new one
|
||||
if self.auth_token:
|
||||
kwargs.setdefault('headers', {})['X-Auth-Token'] = self.auth_token
|
||||
token_url += "/" + self.auth_token
|
||||
method = "GET"
|
||||
body = None
|
||||
|
||||
if self.auth_token and self.tenant_id and self.management_url:
|
||||
return None
|
||||
|
||||
# Make sure we follow redirects when trying to reach Keystone
|
||||
resp, respbody = self._time_request(
|
||||
token_url,
|
||||
|
@ -82,6 +82,7 @@ class SecretsHelper(object):
|
||||
self.args = args
|
||||
self.client = client
|
||||
self.key = None
|
||||
self._password = None
|
||||
|
||||
def _validate_string(self, text):
|
||||
if text is None or len(text) == 0:
|
||||
@ -143,11 +144,21 @@ class SecretsHelper(object):
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
if self._validate_string(self.args.os_password):
|
||||
return self.args.os_password
|
||||
verify_pass = strutils.bool_from_string(
|
||||
utils.env("OS_VERIFY_PASSWORD", default=False), True)
|
||||
return self._prompt_password(verify_pass)
|
||||
# Cache password so we prompt user at most once
|
||||
if self._password:
|
||||
pass
|
||||
elif self._validate_string(self.args.os_password):
|
||||
self._password = self.args.os_password
|
||||
else:
|
||||
verify_pass = strutils.bool_from_string(
|
||||
utils.env("OS_VERIFY_PASSWORD", default=False), True)
|
||||
self._password = self._prompt_password(verify_pass)
|
||||
if not self._password:
|
||||
raise exc.CommandError(
|
||||
'Expecting a password provided via either '
|
||||
'--os-password, env[OS_PASSWORD], or '
|
||||
'prompted response')
|
||||
return self._password
|
||||
|
||||
@property
|
||||
def management_url(self):
|
||||
@ -260,6 +271,10 @@ class OpenStackComputeShell(object):
|
||||
type=positive_non_zero_float,
|
||||
help="Set HTTP call timeout (in seconds)")
|
||||
|
||||
parser.add_argument('--os-auth-token',
|
||||
default=utils.env('OS_AUTH_TOKEN'),
|
||||
help='Defaults to env[OS_AUTH_TOKEN]')
|
||||
|
||||
parser.add_argument('--os-username',
|
||||
metavar='<auth-user-name>',
|
||||
default=utils.env('OS_USERNAME', 'NOVA_USERNAME'),
|
||||
@ -491,7 +506,6 @@ class OpenStackComputeShell(object):
|
||||
format=streamformat)
|
||||
|
||||
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)
|
||||
@ -532,27 +546,37 @@ class OpenStackComputeShell(object):
|
||||
self.do_bash_completion(args)
|
||||
return 0
|
||||
|
||||
(os_username, os_tenant_name, os_tenant_id, os_auth_url,
|
||||
os_region_name, os_auth_system, endpoint_type, insecure,
|
||||
service_type, service_name, volume_service_name,
|
||||
bypass_url, os_cache, cacert, timeout) = (
|
||||
args.os_username,
|
||||
args.os_tenant_name, args.os_tenant_id,
|
||||
args.os_auth_url,
|
||||
args.os_region_name, args.os_auth_system,
|
||||
args.endpoint_type, args.insecure, args.service_type,
|
||||
args.service_name, args.volume_service_name,
|
||||
args.bypass_url, args.os_cache,
|
||||
args.os_cacert, args.timeout)
|
||||
os_username = args.os_username
|
||||
os_password = None # Fetched and set later as needed
|
||||
os_tenant_name = args.os_tenant_name
|
||||
os_tenant_id = args.os_tenant_id
|
||||
os_auth_url = args.os_auth_url
|
||||
os_region_name = args.os_region_name
|
||||
os_auth_system = args.os_auth_system
|
||||
endpoint_type = args.endpoint_type
|
||||
insecure = args.insecure
|
||||
service_type = args.service_type
|
||||
service_name = args.service_name
|
||||
volume_service_name = args.volume_service_name
|
||||
bypass_url = args.bypass_url
|
||||
os_cache = args.os_cache
|
||||
cacert = args.os_cacert
|
||||
timeout = args.timeout
|
||||
|
||||
# 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.
|
||||
# Finally, authenticate unless we have both.
|
||||
# Note if we don't auth we probably don't have a tenant ID so we can't
|
||||
# cache the token.
|
||||
auth_token = args.os_auth_token if args.os_auth_token else None
|
||||
management_url = bypass_url if bypass_url else None
|
||||
|
||||
if os_auth_system and os_auth_system != "keystone":
|
||||
auth_plugin = novaclient.auth_plugin.load_plugin(os_auth_system)
|
||||
else:
|
||||
auth_plugin = None
|
||||
|
||||
# Fetched and set later as needed
|
||||
os_password = None
|
||||
|
||||
if not endpoint_type:
|
||||
endpoint_type = DEFAULT_NOVA_ENDPOINT_TYPE
|
||||
|
||||
@ -567,9 +591,14 @@ class OpenStackComputeShell(object):
|
||||
DEFAULT_OS_COMPUTE_API_VERSION]
|
||||
service_type = utils.get_service_type(args.func) or service_type
|
||||
|
||||
# If we have an auth token but no management_url, we must auth anyway.
|
||||
# Expired tokens are handled by client.py:_cs_request
|
||||
must_auth = not (utils.isunauthenticated(args.func)
|
||||
or (auth_token and management_url))
|
||||
|
||||
#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 utils.isunauthenticated(args.func):
|
||||
if must_auth:
|
||||
if auth_plugin:
|
||||
auth_plugin.parse_opts(args)
|
||||
|
||||
@ -613,7 +642,7 @@ class OpenStackComputeShell(object):
|
||||
region_name=os_region_name, endpoint_type=endpoint_type,
|
||||
extensions=self.extensions, service_type=service_type,
|
||||
service_name=service_name, auth_system=os_auth_system,
|
||||
auth_plugin=auth_plugin,
|
||||
auth_plugin=auth_plugin, auth_token=auth_token,
|
||||
volume_service_name=volume_service_name,
|
||||
timings=args.timings, bypass_url=bypass_url,
|
||||
os_cache=os_cache, http_log_debug=options.debug,
|
||||
@ -621,7 +650,7 @@ class OpenStackComputeShell(object):
|
||||
|
||||
# Now check for the password/token of which pieces of the
|
||||
# identifying keyring key can come from the underlying client
|
||||
if not utils.isunauthenticated(args.func):
|
||||
if must_auth:
|
||||
helper = SecretsHelper(args, self.cs.client)
|
||||
if (auth_plugin and auth_plugin.opts and
|
||||
"os_password" not in auth_plugin.opts):
|
||||
@ -629,31 +658,26 @@ class OpenStackComputeShell(object):
|
||||
else:
|
||||
use_pw = True
|
||||
|
||||
tenant_id, auth_token, management_url = (helper.tenant_id,
|
||||
helper.auth_token,
|
||||
helper.management_url)
|
||||
tenant_id = helper.tenant_id
|
||||
# Allow commandline to override cache
|
||||
if not auth_token:
|
||||
auth_token = helper.auth_token
|
||||
if not management_url:
|
||||
management_url = 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
|
||||
# authenticate just sets up some values in this case, no REST
|
||||
# calls
|
||||
self.cs.authenticate()
|
||||
if use_pw:
|
||||
# Auth using token must have failed or not happened
|
||||
# at all, so now switch to password mode and save
|
||||
# the token when its gotten... using our keyring
|
||||
# saver
|
||||
os_password = helper.password
|
||||
if not os_password:
|
||||
raise exc.CommandError(
|
||||
'Expecting a password provided via either '
|
||||
'--os-password, env[OS_PASSWORD], or '
|
||||
'prompted response')
|
||||
self.cs.client.password = os_password
|
||||
self.cs.client.password_fun = lambda: helper.password
|
||||
elif use_pw:
|
||||
# We're missing something, so auth with user/pass and save
|
||||
# the result in our helper.
|
||||
self.cs.client.password = helper.password
|
||||
self.cs.client.keyring_saver = helper
|
||||
|
||||
try:
|
||||
# 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 utils.isunauthenticated(args.func):
|
||||
self.cs.authenticate()
|
||||
except exc.Unauthorized:
|
||||
|
@ -196,3 +196,23 @@ class ClientTest(utils.TestCase):
|
||||
auth_url="foo/v2")
|
||||
cs.authenticate()
|
||||
self.assertTrue(mock_authenticate.called)
|
||||
|
||||
def test_get_password_simple(self):
|
||||
cs = novaclient.client.HTTPClient("user", "password", "", "")
|
||||
cs.password_func = mock.Mock()
|
||||
self.assertEqual(cs._get_password(), "password")
|
||||
self.assertFalse(cs.password_func.called)
|
||||
|
||||
def test_get_password_none(self):
|
||||
cs = novaclient.client.HTTPClient("user", None, "", "")
|
||||
self.assertEqual(cs._get_password(), None)
|
||||
|
||||
def test_get_password_func(self):
|
||||
cs = novaclient.client.HTTPClient("user", None, "", "")
|
||||
cs.password_func = mock.Mock(return_value="password")
|
||||
self.assertEqual(cs._get_password(), "password")
|
||||
cs.password_func.assert_called_once_with()
|
||||
|
||||
cs.password_func = mock.Mock()
|
||||
self.assertEqual(cs._get_password(), "password")
|
||||
self.assertFalse(cs.password_func.called)
|
||||
|
@ -379,15 +379,22 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
'User-Agent': cs.client.USER_AGENT,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-Auth-Token': "FAKE_ID",
|
||||
}
|
||||
body = {
|
||||
'auth': {
|
||||
'token': {
|
||||
'id': cs.client.auth_token,
|
||||
},
|
||||
'tenantName': cs.client.projectid,
|
||||
},
|
||||
}
|
||||
|
||||
token_url = cs.client.auth_url + "/tokens/FAKE_ID"
|
||||
token_url = cs.client.auth_url + "/tokens"
|
||||
mock_request.assert_called_with(
|
||||
"GET",
|
||||
"POST",
|
||||
token_url,
|
||||
headers=headers,
|
||||
data="null",
|
||||
data=json.dumps(body),
|
||||
allow_redirects=True,
|
||||
**self.TEST_REQUEST_BASE)
|
||||
|
||||
|
@ -72,7 +72,7 @@ class Client(object):
|
||||
volume_service_name=None, timings=False,
|
||||
bypass_url=None, os_cache=False, no_cache=True,
|
||||
http_log_debug=False, auth_system='keystone',
|
||||
auth_plugin=None,
|
||||
auth_plugin=None, auth_token=None,
|
||||
cacert=None, tenant_id=None):
|
||||
# FIXME(comstud): Rename the api_key argument above when we
|
||||
# know it's not being used as keyword argument
|
||||
@ -129,6 +129,7 @@ class Client(object):
|
||||
projectid=project_id,
|
||||
tenant_id=tenant_id,
|
||||
auth_url=auth_url,
|
||||
auth_token=auth_token,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
auth_system=auth_system,
|
||||
|
@ -57,7 +57,7 @@ class Client(object):
|
||||
volume_service_name=None, timings=False,
|
||||
bypass_url=None, os_cache=False, no_cache=True,
|
||||
http_log_debug=False, auth_system='keystone',
|
||||
auth_plugin=None,
|
||||
auth_plugin=None, auth_token=None,
|
||||
cacert=None, tenant_id=None):
|
||||
self.projectid = project_id
|
||||
self.tenant_id = tenant_id
|
||||
@ -96,6 +96,7 @@ class Client(object):
|
||||
timeout=timeout,
|
||||
auth_system=auth_system,
|
||||
auth_plugin=auth_plugin,
|
||||
auth_token=auth_token,
|
||||
proxy_token=proxy_token,
|
||||
proxy_tenant_id=proxy_tenant_id,
|
||||
region_name=region_name,
|
||||
|
Loading…
x
Reference in New Issue
Block a user