Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

openstack: use OS_AUTH_TYPE=token_endpoint when possible #949

Merged
merged 1 commit into from Sep 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 52 additions & 11 deletions teuthology/openstack/__init__.py
Expand Up @@ -215,31 +215,66 @@ def cache_token(self):
if self.provider != 'ovh':
return False
if (OpenStack.token is None and
os.environ.get('OS_AUTH_TYPE') == 'v2token' and
'OS_TOKEN' in os.environ and
'OS_TOKEN_VALUE' in os.environ and
'OS_TOKEN_EXPIRES' in os.environ):
log.debug("get token from the environment of the parent process")
OpenStack.token = os.environ['OS_TOKEN']
OpenStack.token = os.environ['OS_TOKEN_VALUE']
OpenStack.token_expires = int(os.environ['OS_TOKEN_EXPIRES'])
if (OpenStack.token_expires is not None and
OpenStack.token_expires < time.time()):
log.debug("token discarded because it has expired")
OpenStack.token = None
if OpenStack.token is None:
if os.environ.get('OS_AUTH_TYPE') == 'v2token':
del os.environ['OS_AUTH_TYPE']
if 'OS_TOKEN_VALUE' in os.environ:
del os.environ['OS_TOKEN_VALUE']
OpenStack.token = misc.sh("openstack -q token issue -c id -f value").strip()
os.environ['OS_AUTH_TYPE'] = 'v2token'
os.environ['OS_TOKEN'] = OpenStack.token
os.environ['OS_TOKEN_VALUE'] = OpenStack.token
OpenStack.token_expires = int(time.time() + OpenStack.token_cache_duration)
os.environ['OS_TOKEN_EXPIRES'] = str(OpenStack.token_expires)
log.info("caching OS_TOKEN and setting OS_AUTH_TYPE=v2token "
log.info("caching OS_TOKEN_VALUE "
"during %s seconds" % OpenStack.token_cache_duration)
return True

def get_os_url(self, cmd, type=None):
if self.provider != 'ovh':
return ""
url = ""
if (type == 'compute' or
cmd.startswith("server ") or
cmd.startswith("flavor ")):
url = "https://compute.{reg}.cloud.ovh.net/v2/{tenant}"
elif (type == 'network' or
cmd.startswith("ip ") or
cmd.startswith("security ") or
cmd.startswith("network ")):
url = "https://network.compute.{reg}.cloud.ovh.net/"
elif (type == 'image' or
cmd.startswith("image ")):
url = "https://image.compute.{reg}.cloud.ovh.net/"
elif (type == 'volume' or
cmd.startswith("volume ")):
url = "https://volume.compute.{reg}.cloud.ovh.net/v2/{tenant}"
if url != "":
url = url.format(reg=os.environ['OS_REGION_NAME'],
tenant=os.environ['OS_TENANT_ID'])
return url

def run(self, cmd, *args, **kwargs):
self.cache_token()
return misc.sh("openstack --quiet " + cmd, *args, **kwargs)
url = self.get_os_url(cmd, kwargs.get('type'))
if url != "":
if self.cache_token():
os.environ['OS_TOKEN'] = os.environ['OS_TOKEN_VALUE']
os.environ['OS_URL'] = url
if re.match('(server|flavor|ip|security|network|image|volume)', cmd):
cmd = "openstack --quiet " + cmd
try:
status = misc.sh(cmd)
finally:
if 'OS_TOKEN' in os.environ:
del os.environ['OS_TOKEN']
if 'OS_URL' in os.environ:
del os.environ['OS_URL']
return status

def set_provider(self):
if 'OS_AUTH_URL' not in os.environ:
Expand Down Expand Up @@ -681,6 +716,8 @@ def get_user_data(self):
template = open(user_data).read()
openrc = ''
for (var, value) in os.environ.iteritems():
if var in ('OS_TOKEN_VALUE', 'OS_TOKEN_EXPIRES'):
continue
if var.startswith('OS_'):
openrc += ' ' + var + '=' + value
if self.args.upload:
Expand Down Expand Up @@ -745,7 +782,11 @@ def get_unassociated_floating_ip():

@staticmethod
def create_floating_ip():
pools = json.loads(OpenStack().run("ip floating pool list -f json"))
try:
pools = json.loads(OpenStack().run("ip floating pool list -f json"))
except subprocess.CalledProcessError:
log.debug("create_floating_ip: ip floating pool list failed")
return None
if not pools:
return None
pool = pools[0]['Name']
Expand Down
51 changes: 34 additions & 17 deletions teuthology/openstack/test/test_openstack.py
@@ -1,5 +1,5 @@
#
# Copyright (c) 2015 Red Hat, Inc.
# Copyright (c) 2015,2016 Red Hat, Inc.
#
# Author: Loic Dachary <loic@dachary.org>
#
Expand Down Expand Up @@ -274,6 +274,31 @@ def test_get_provider(self):
else:
del os.environ['OS_AUTH_URL']

def test_get_os_url(self):
o = OpenStack()
#
# Only for OVH
#
o.provider = 'something'
assert "" == o.get_os_url("server ")
o.provider = 'ovh'
assert "" == o.get_os_url("unknown ")
type2cmd = {
'compute': ('server', 'flavor'),
'network': ('ip', 'security', 'network'),
'image': ('image',),
'volume': ('volume',),
}
os.environ['OS_REGION_NAME'] = 'REGION'
os.environ['OS_TENANT_ID'] = 'TENANT'
for (type, cmds) in type2cmd.iteritems():
for cmd in cmds:
assert ("//" + type) in o.get_os_url(cmd + " ")
for type in type2cmd.keys():
assert ("//" + type) in o.get_os_url("whatever ", type=type)
del os.environ['OS_REGION_NAME']
del os.environ['OS_TENANT_ID']

@patch('teuthology.misc.sh')
def test_cache_token(self, m_sh):
token = 'TOKEN VALUE'
Expand All @@ -289,13 +314,11 @@ def test_cache_token(self, m_sh):
#
# Set the environment with the token
#
assert 'OS_AUTH_TYPE' not in os.environ
assert 'OS_TOKEN' not in os.environ
assert 'OS_TOKEN_VALUE' not in os.environ
assert 'OS_TOKEN_EXPIRES' not in os.environ
assert True == o.cache_token()
m_sh.assert_called_with('openstack -q token issue -c id -f value')
assert 'v2token' == os.environ['OS_AUTH_TYPE']
assert token == os.environ['OS_TOKEN']
assert token == os.environ['OS_TOKEN_VALUE']
assert token == OpenStack.token
assert time.time() < int(os.environ['OS_TOKEN_EXPIRES'])
assert time.time() < OpenStack.token_expires
Expand All @@ -307,8 +330,7 @@ def test_cache_token(self, m_sh):
assert True == o.cache_token()
assert time.time() < int(os.environ['OS_TOKEN_EXPIRES'])
assert time.time() < OpenStack.token_expires
del os.environ['OS_AUTH_TYPE']
del os.environ['OS_TOKEN']
del os.environ['OS_TOKEN_VALUE']
del os.environ['OS_TOKEN_EXPIRES']

@patch('teuthology.misc.sh')
Expand All @@ -317,16 +339,14 @@ def test_cache_token_from_environment(self, m_sh):
o = OpenStack()
o.provider = 'ovh'
token = 'TOKEN VALUE'
os.environ['OS_AUTH_TYPE'] = 'v2token'
os.environ['OS_TOKEN'] = token
os.environ['OS_TOKEN_VALUE'] = token
token_expires = int(time.time()) + OpenStack.token_cache_duration
os.environ['OS_TOKEN_EXPIRES'] = str(token_expires)
assert True == o.cache_token()
assert token == OpenStack.token
assert token_expires == OpenStack.token_expires
m_sh.assert_not_called()
del os.environ['OS_AUTH_TYPE']
del os.environ['OS_TOKEN']
del os.environ['OS_TOKEN_VALUE']
del os.environ['OS_TOKEN_EXPIRES']

@patch('teuthology.misc.sh')
Expand All @@ -336,19 +356,16 @@ def test_cache_token_expired_environment(self, m_sh):
OpenStack.token = None
o = OpenStack()
o.provider = 'ovh'
os.environ['OS_AUTH_TYPE'] = 'v2token'
os.environ['OS_TOKEN'] = token
os.environ['OS_TOKEN_VALUE'] = token
token_expires = int(time.time()) - 2000
os.environ['OS_TOKEN_EXPIRES'] = str(token_expires)
assert True == o.cache_token()
m_sh.assert_called_with('openstack -q token issue -c id -f value')
assert 'v2token' == os.environ['OS_AUTH_TYPE']
assert token == os.environ['OS_TOKEN']
assert token == os.environ['OS_TOKEN_VALUE']
assert token == OpenStack.token
assert time.time() < int(os.environ['OS_TOKEN_EXPIRES'])
assert time.time() < OpenStack.token_expires
del os.environ['OS_AUTH_TYPE']
del os.environ['OS_TOKEN']
del os.environ['OS_TOKEN_VALUE']
del os.environ['OS_TOKEN_EXPIRES']

class TestTeuthologyOpenStack(object):
Expand Down
39 changes: 25 additions & 14 deletions teuthology/provision/openstack.py
Expand Up @@ -66,22 +66,33 @@ def attach_volumes(self, name, volumes):
except subprocess.CalledProcessError as e:
if 'No volume with a name or ID' not in e.output:
raise e
self.run("volume create -f json " +
config['openstack'].get('volume-create', '') + " " +
" --property ownedby=" + config.openstack['ip'] +
" --size " + str(volumes['size']) + " " +
volume_name)
# do not use OpenStack().run because its
# bugous for volume create as of openstackclient 3.2.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"it's buggy" or "it's bogus" (I would assume you really mean the former)

# https://bugs.launchpad.net/python-openstackclient/+bug/1619726
misc.sh(
"openstack volume create -f json " +
config['openstack'].get('volume-create', '') + " " +
" --property ownedby=" + config.openstack['ip'] +
" --size " + str(volumes['size']) + " " +
volume_name)
with safe_while(sleep=2, tries=100,
action="volume " + volume_name) as proceed:
while proceed():
r = self.run("volume show -f json " + volume_name)
status = self.get_value(json.loads(r), 'status')
if status == 'available':
break
else:
log.info("volume " + volume_name +
" not available yet")
self.run("server add volume " + name + " " + volume_name)
try:
r = OpenStack().run("volume show -f json " +
volume_name)
status = self.get_value(json.loads(r), 'status')
if status == 'available':
break
else:
log.info("volume " + volume_name +
" not available yet")
except subprocess.CalledProcessError:
log.info("volume " + volume_name +
" not information available yet")
# do not use OpenStack().run because its
# bugous for volume
misc.sh("openstack server add volume " + name + " " + volume_name)

@staticmethod
def ip2name(prefix, ip):
Expand Down Expand Up @@ -127,7 +138,7 @@ def create(self, num, os_type, os_version, arch, resources_hint):
" --wait " +
" " + self.basename)
try:
misc.sh(cmd)
self.run(cmd, type='compute')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as does this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without this change the cmd will use password based authentication instead of reusing an existing token

except CalledProcessError as exc:
if "quota exceeded" in exc.output.lower():
raise QuotaExceededError(message=exc.output)
Expand Down