From 082f4c17dc739407caf3affe7f6f8fddbe73fef8 Mon Sep 17 00:00:00 2001 From: Loic Dachary Date: Fri, 2 Sep 2016 08:49:18 +0200 Subject: [PATCH] openstack: use OS_AUTH_TYPE=token_endpoint when possible It requires setting the OS_URL depending on the command and avoids calling the auth endpoint entirely. Fixes: http://tracker.ceph.com/issues/16893 Signed-off-by: Loic Dachary --- teuthology/openstack/__init__.py | 63 +++++++++++++++++---- teuthology/openstack/test/test_openstack.py | 51 +++++++++++------ teuthology/provision/openstack.py | 39 ++++++++----- 3 files changed, 111 insertions(+), 42 deletions(-) diff --git a/teuthology/openstack/__init__.py b/teuthology/openstack/__init__.py index 13947e2d52..a13c4cf61d 100644 --- a/teuthology/openstack/__init__.py +++ b/teuthology/openstack/__init__.py @@ -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: @@ -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: @@ -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'] diff --git a/teuthology/openstack/test/test_openstack.py b/teuthology/openstack/test/test_openstack.py index 3c9155e254..6140957e6c 100644 --- a/teuthology/openstack/test/test_openstack.py +++ b/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 # @@ -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' @@ -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 @@ -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') @@ -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') @@ -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): diff --git a/teuthology/provision/openstack.py b/teuthology/provision/openstack.py index 27f5e6529b..b16041e502 100644 --- a/teuthology/provision/openstack.py +++ b/teuthology/provision/openstack.py @@ -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 + # 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): @@ -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') except CalledProcessError as exc: if "quota exceeded" in exc.output.lower(): raise QuotaExceededError(message=exc.output)