From 80f8ca818aabea9b2f6a47d21ceba186185c111b Mon Sep 17 00:00:00 2001 From: Thomas Herve Date: Sun, 12 Mar 2017 22:05:39 +0100 Subject: [PATCH] Don't share swift client instances THe swift backend was using swiftclient.Connection without any checks, but that object is not thread safe, so we need to only have one instance. This adds a wrapper to keep the same API but not share any data. Change-Id: Ic8152f8f7cb6d4d0b4ab473d2f937a9bc77b8453 --- zaqar/storage/swift/driver.py | 50 +++++++++++++++++++++++++++++------ zaqar/storage/swift/utils.py | 2 -- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/zaqar/storage/swift/driver.py b/zaqar/storage/swift/driver.py index 039c483a3..c92c742b8 100644 --- a/zaqar/storage/swift/driver.py +++ b/zaqar/storage/swift/driver.py @@ -13,6 +13,8 @@ from six.moves import urllib +from keystoneauth1.identity import v3 +from keystoneauth1 import session as keystone_session from oslo_log import log as logging import swiftclient @@ -41,7 +43,7 @@ class DataDriver(storage.DataDriverBase): @decorators.lazy_property(write=False) def connection(self): - return _get_swift_client(self) + return _ClientWrapper(self.swift_conf) def is_alive(self): return True @@ -69,10 +71,42 @@ class DataDriver(storage.DataDriverBase): pass -def _get_swift_client(driver): - conf = driver.swift_conf - parsed_url = urllib.parse.urlparse(conf.uri) - return swiftclient.Connection(conf.auth_url, parsed_url.username, - parsed_url.password, - insecure=conf.insecure, auth_version="3", - tenant_name=parsed_url.path[1:]) +class _ClientWrapper(object): + """Wrapper around swiftclient.Connection. + + This wraps swiftclient.Connection to give the same API, but provide a + thread-safe alternative with a different object for every method call. It + maintains performance by managing authentication itself, and passing the + token afterwards. + """ + + def __init__(self, conf): + self.conf = conf + self.parsed_url = urllib.parse.urlparse(conf.uri) + self.token = None + self.url = None + self.session = None + self.auth = None + + def _refresh_auth(self): + self.auth = v3.Password( + username=self.parsed_url.username, + password=self.parsed_url.password, + project_name=self.parsed_url.path[1:], + user_domain_id='default', + project_domain_id='default', + auth_url=self.conf.auth_url) + self.session = keystone_session.Session(auth=self.auth) + self.url = self.session.get_endpoint(service_type='object-store') + self.token = self.session.get_token() + + def __getattr__(self, attr): + # This part is not thread-safe, but the worst case is having a bunch of + # useless auth calls, so it should be okay. + if (self.auth is None or + self.auth.get_auth_ref(self.session).will_expire_soon()): + self._refresh_auth() + client = swiftclient.Connection( + preauthurl=self.url, + preauthtoken=self.token) + return getattr(client, attr) diff --git a/zaqar/storage/swift/utils.py b/zaqar/storage/swift/utils.py index f77c9161d..1f93ed6ef 100644 --- a/zaqar/storage/swift/utils.py +++ b/zaqar/storage/swift/utils.py @@ -96,8 +96,6 @@ def _filter_messages(messages, filters, marker, get_object, list_objects, if exc.http_status == 404: continue raise - if not obj: - continue obj = jsonutils.loads(obj) for should_skip in filters: if should_skip(obj, headers):