|
Jakub Ruzicka |
fcc2a65 |
From 9d2d572eaa0c4fbc48588b103a176a132e0476d9 Mon Sep 17 00:00:00 2001
|
|
Jakub Ruzicka |
fcc2a65 |
From: Boris Pavlovic <boris@pavlovic.me>
|
|
Jakub Ruzicka |
fcc2a65 |
Date: Wed, 26 Mar 2014 15:22:03 +0400
|
|
Jakub Ruzicka |
fcc2a65 |
Subject: [PATCH] Fix session handling in novaclient
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
Prior to this patch, novaclient was handling sessions in an inconsistent
|
|
Jakub Ruzicka |
fcc2a65 |
manner.
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
Every time we created a client instance, it would use a global
|
|
Jakub Ruzicka |
fcc2a65 |
connection pool, which made it difficult to use in a process that is
|
|
Jakub Ruzicka |
fcc2a65 |
meant to be forked.
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
Obviously sessions like the ones provided by the requests library that
|
|
Jakub Ruzicka |
fcc2a65 |
will automatically cause connections to be kept alive should not be
|
|
Jakub Ruzicka |
fcc2a65 |
implicit. This patch moves the novaclient back to the age of a single
|
|
Jakub Ruzicka |
fcc2a65 |
session-less request call by default, but also adds two more
|
|
Jakub Ruzicka |
fcc2a65 |
resource-reuse friendly options that a user needs to be explicit about.
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
The first one is that both v1_1 and v3 clients can now be used as
|
|
Jakub Ruzicka |
fcc2a65 |
context managers,. where the session will be kept open (and thus the
|
|
Jakub Ruzicka |
fcc2a65 |
connection kept-alive) for the duration of the with block. This is far
|
|
Jakub Ruzicka |
fcc2a65 |
more ideal for a web worker use-case as the session can be made
|
|
Jakub Ruzicka |
fcc2a65 |
request-long.
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
The second one is the per-instance session. This is very similar to what
|
|
Jakub Ruzicka |
fcc2a65 |
we had up until now, except it is not a global object so forking is
|
|
Jakub Ruzicka |
fcc2a65 |
possible as long as each child instantiates it's own client. The session
|
|
Jakub Ruzicka |
fcc2a65 |
once created will be kept open for the duration of the client object
|
|
Jakub Ruzicka |
fcc2a65 |
lifetime.
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
Please note: client instances are not thread safe. As can be seen from
|
|
Jakub Ruzicka |
fcc2a65 |
above forking example - if you wish to use threading/multiprocessing,
|
|
Jakub Ruzicka |
fcc2a65 |
you *must not* share client instances.
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
DocImpact
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
Related-bug: #1247056
|
|
Jakub Ruzicka |
fcc2a65 |
Closes-Bug: #1297796
|
|
Jakub Ruzicka |
fcc2a65 |
Co-authored-by: Nikola Dipanov <ndipanov@redhat.com>
|
|
Jakub Ruzicka |
fcc2a65 |
Change-Id: Id59e48f61bb3f3c6223302355c849e1e99673410
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
Conflicts:
|
|
Jakub Ruzicka |
fcc2a65 |
novaclient/client.py
|
|
Jakub Ruzicka |
fcc2a65 |
novaclient/tests/test_client.py
|
|
Jakub Ruzicka |
fcc2a65 |
novaclient/tests/test_http.py
|
|
Jakub Ruzicka |
fcc2a65 |
novaclient/v1_1/client.py
|
|
Jakub Ruzicka |
fcc2a65 |
novaclient/v3/client.py
|
|
Jakub Ruzicka |
fcc2a65 |
---
|
|
Jakub Ruzicka |
fcc2a65 |
novaclient/client.py | 74 +++++++++++++++--------
|
|
Jakub Ruzicka |
fcc2a65 |
novaclient/tests/test_auth_plugins.py | 8 +--
|
|
Jakub Ruzicka |
fcc2a65 |
novaclient/tests/test_client.py | 110 +++++++++++++++++++++++++++++++++-
|
|
Jakub Ruzicka |
fcc2a65 |
novaclient/tests/test_http.py | 8 +--
|
|
Jakub Ruzicka |
fcc2a65 |
novaclient/tests/v1_1/test_auth.py | 12 ++--
|
|
Jakub Ruzicka |
fcc2a65 |
novaclient/v1_1/client.py | 27 ++++++++-
|
|
Jakub Ruzicka |
fcc2a65 |
novaclient/v3/client.py | 27 ++++++++-
|
|
Jakub Ruzicka |
fcc2a65 |
7 files changed, 221 insertions(+), 45 deletions(-)
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
diff --git a/novaclient/client.py b/novaclient/client.py
|
|
Jakub Ruzicka |
fcc2a65 |
index 0b9aeee..7f5032b 100644
|
|
Jakub Ruzicka |
fcc2a65 |
--- a/novaclient/client.py
|
|
Jakub Ruzicka |
fcc2a65 |
+++ b/novaclient/client.py
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -39,17 +39,19 @@ from novaclient import service_catalog
|
|
Jakub Ruzicka |
fcc2a65 |
from novaclient import utils
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
-_ADAPTERS = {}
|
|
Jakub Ruzicka |
fcc2a65 |
+class _ClientConnectionPool(object):
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
+ def __init__(self):
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._adapters = {}
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
-def _adapter_pool(url):
|
|
Jakub Ruzicka |
fcc2a65 |
- """
|
|
Jakub Ruzicka |
fcc2a65 |
- Store and reuse HTTP adapters per Service URL.
|
|
Jakub Ruzicka |
fcc2a65 |
- """
|
|
Jakub Ruzicka |
fcc2a65 |
- if url not in _ADAPTERS:
|
|
Jakub Ruzicka |
fcc2a65 |
- _ADAPTERS[url] = adapters.HTTPAdapter()
|
|
Jakub Ruzicka |
fcc2a65 |
+ def get(self, url):
|
|
Jakub Ruzicka |
fcc2a65 |
+ """
|
|
Jakub Ruzicka |
fcc2a65 |
+ Store and reuse HTTP adapters per Service URL.
|
|
Jakub Ruzicka |
fcc2a65 |
+ """
|
|
Jakub Ruzicka |
fcc2a65 |
+ if url not in self._adapters:
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._adapters[url] = adapters.HTTPAdapter()
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
- return _ADAPTERS[url]
|
|
Jakub Ruzicka |
fcc2a65 |
+ return self._adapters[url]
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
class HTTPClient(object):
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -64,12 +66,16 @@ class HTTPClient(object):
|
|
Jakub Ruzicka |
fcc2a65 |
os_cache=False, no_cache=True,
|
|
Jakub Ruzicka |
fcc2a65 |
http_log_debug=False, auth_system='keystone',
|
|
Jakub Ruzicka |
fcc2a65 |
auth_plugin=None, auth_token=None,
|
|
Jakub Ruzicka |
fcc2a65 |
- cacert=None, tenant_id=None):
|
|
Jakub Ruzicka |
fcc2a65 |
+ cacert=None, tenant_id=None,
|
|
Jakub Ruzicka |
fcc2a65 |
+ connection_pool=False):
|
|
Jakub Ruzicka |
fcc2a65 |
self.user = user
|
|
Jakub Ruzicka |
fcc2a65 |
self.password = password
|
|
Jakub Ruzicka |
fcc2a65 |
self.projectid = projectid
|
|
Jakub Ruzicka |
fcc2a65 |
self.tenant_id = tenant_id
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._connection_pool = (_ClientConnectionPool()
|
|
Jakub Ruzicka |
fcc2a65 |
+ if connection_pool else None)
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
# This will be called by #_get_password if self.password is None.
|
|
Jakub Ruzicka |
fcc2a65 |
# EG if a password can only be obtained by prompting the user, but a
|
|
Jakub Ruzicka |
fcc2a65 |
# token is available, you don't want to prompt until the token has
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -118,8 +124,8 @@ class HTTPClient(object):
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
self.auth_system = auth_system
|
|
Jakub Ruzicka |
fcc2a65 |
self.auth_plugin = auth_plugin
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._session = None
|
|
Jakub Ruzicka |
fcc2a65 |
self._current_url = None
|
|
Jakub Ruzicka |
fcc2a65 |
- self._http = None
|
|
Jakub Ruzicka |
fcc2a65 |
self._logger = logging.getLogger(__name__)
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
if self.http_log_debug and not self._logger.handlers:
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -180,19 +186,33 @@ class HTTPClient(object):
|
|
Jakub Ruzicka |
fcc2a65 |
'headers': resp.headers,
|
|
Jakub Ruzicka |
fcc2a65 |
'text': resp.text})
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
- def http(self, url):
|
|
Jakub Ruzicka |
fcc2a65 |
- magic_tuple = parse.urlsplit(url)
|
|
Jakub Ruzicka |
fcc2a65 |
- scheme, netloc, path, query, frag = magic_tuple
|
|
Jakub Ruzicka |
fcc2a65 |
- service_url = '%s://%s' % (scheme, netloc)
|
|
Jakub Ruzicka |
fcc2a65 |
- if self._current_url != service_url:
|
|
Jakub Ruzicka |
fcc2a65 |
- # Invalidate Session object in case the url is somehow changed
|
|
Jakub Ruzicka |
fcc2a65 |
- if self._http:
|
|
Jakub Ruzicka |
fcc2a65 |
- self._http.close()
|
|
Jakub Ruzicka |
fcc2a65 |
- self._current_url = service_url
|
|
Jakub Ruzicka |
fcc2a65 |
- self._logger.debug("New session created for: (%s)" % service_url)
|
|
Jakub Ruzicka |
fcc2a65 |
- self._http = requests.Session()
|
|
Jakub Ruzicka |
fcc2a65 |
- self._http.mount(service_url, _adapter_pool(service_url))
|
|
Jakub Ruzicka |
fcc2a65 |
- return self._http
|
|
Jakub Ruzicka |
fcc2a65 |
+ def open_session(self):
|
|
Jakub Ruzicka |
fcc2a65 |
+ if not self._connection_pool:
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._session = requests.Session()
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ def close_session(self):
|
|
Jakub Ruzicka |
fcc2a65 |
+ if self._session and not self._connection_pool:
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._session.close()
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._session = None
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ def _get_session(self, url):
|
|
Jakub Ruzicka |
fcc2a65 |
+ if self._connection_pool:
|
|
Jakub Ruzicka |
fcc2a65 |
+ magic_tuple = parse.urlsplit(url)
|
|
Jakub Ruzicka |
fcc2a65 |
+ scheme, netloc, path, query, frag = magic_tuple
|
|
Jakub Ruzicka |
fcc2a65 |
+ service_url = '%s://%s' % (scheme, netloc)
|
|
Jakub Ruzicka |
fcc2a65 |
+ if self._current_url != service_url:
|
|
Jakub Ruzicka |
fcc2a65 |
+ # Invalidate Session object in case the url is somehow changed
|
|
Jakub Ruzicka |
fcc2a65 |
+ if self._session:
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._session.close()
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._current_url = service_url
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._logger.debug(
|
|
Jakub Ruzicka |
fcc2a65 |
+ "New session created for: (%s)" % service_url)
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._session = requests.Session()
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._session.mount(service_url,
|
|
Jakub Ruzicka |
fcc2a65 |
+ self._connection_pool.get(service_url))
|
|
Jakub Ruzicka |
fcc2a65 |
+ return self._session
|
|
Jakub Ruzicka |
fcc2a65 |
+ elif self._session:
|
|
Jakub Ruzicka |
fcc2a65 |
+ return self._session
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
def request(self, url, method, **kwargs):
|
|
Jakub Ruzicka |
fcc2a65 |
kwargs.setdefault('headers', kwargs.get('headers', {}))
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -207,7 +227,13 @@ class HTTPClient(object):
|
|
Jakub Ruzicka |
fcc2a65 |
kwargs['verify'] = self.verify_cert
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
self.http_log_req(method, url, kwargs)
|
|
Jakub Ruzicka |
fcc2a65 |
- resp = self.http(url).request(
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ request_func = requests.request
|
|
Jakub Ruzicka |
fcc2a65 |
+ session = self._get_session(url)
|
|
Jakub Ruzicka |
fcc2a65 |
+ if session:
|
|
Jakub Ruzicka |
fcc2a65 |
+ request_func = session.request
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ resp = request_func(
|
|
Jakub Ruzicka |
fcc2a65 |
method,
|
|
Jakub Ruzicka |
fcc2a65 |
url,
|
|
Jakub Ruzicka |
fcc2a65 |
**kwargs)
|
|
Jakub Ruzicka |
fcc2a65 |
diff --git a/novaclient/tests/test_auth_plugins.py b/novaclient/tests/test_auth_plugins.py
|
|
Jakub Ruzicka |
fcc2a65 |
index a084594..1761b98 100644
|
|
Jakub Ruzicka |
fcc2a65 |
--- a/novaclient/tests/test_auth_plugins.py
|
|
Jakub Ruzicka |
fcc2a65 |
+++ b/novaclient/tests/test_auth_plugins.py
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -92,7 +92,7 @@ class DeprecatedAuthPluginTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
@mock.patch.object(pkg_resources, "iter_entry_points",
|
|
Jakub Ruzicka |
fcc2a65 |
mock_iter_entry_points)
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
def test_auth_call():
|
|
Jakub Ruzicka |
fcc2a65 |
plugin = auth_plugin.DeprecatedAuthPlugin("fake")
|
|
Jakub Ruzicka |
fcc2a65 |
cs = client.Client("username", "password", "project_id",
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -121,7 +121,7 @@ class DeprecatedAuthPluginTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
@mock.patch.object(pkg_resources, "iter_entry_points",
|
|
Jakub Ruzicka |
fcc2a65 |
mock_iter_entry_points)
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
def test_auth_call():
|
|
Jakub Ruzicka |
fcc2a65 |
auth_plugin.discover_auth_systems()
|
|
Jakub Ruzicka |
fcc2a65 |
plugin = auth_plugin.DeprecatedAuthPlugin("notexists")
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -164,7 +164,7 @@ class DeprecatedAuthPluginTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
@mock.patch.object(pkg_resources, "iter_entry_points",
|
|
Jakub Ruzicka |
fcc2a65 |
mock_iter_entry_points)
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
def test_auth_call():
|
|
Jakub Ruzicka |
fcc2a65 |
plugin = auth_plugin.DeprecatedAuthPlugin("fakewithauthurl")
|
|
Jakub Ruzicka |
fcc2a65 |
cs = client.Client("username", "password", "project_id",
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -197,7 +197,7 @@ class DeprecatedAuthPluginTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
class AuthPluginTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request")
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request")
|
|
Jakub Ruzicka |
fcc2a65 |
@mock.patch.object(pkg_resources, "iter_entry_points")
|
|
Jakub Ruzicka |
fcc2a65 |
def test_auth_system_success(self, mock_iter_entry_points, mock_request):
|
|
Jakub Ruzicka |
fcc2a65 |
"""Test that we can authenticate using the auth system."""
|
|
Jakub Ruzicka |
fcc2a65 |
diff --git a/novaclient/tests/test_client.py b/novaclient/tests/test_client.py
|
|
Jakub Ruzicka |
fcc2a65 |
index d586702..96901b9 100644
|
|
Jakub Ruzicka |
fcc2a65 |
--- a/novaclient/tests/test_client.py
|
|
Jakub Ruzicka |
fcc2a65 |
+++ b/novaclient/tests/test_client.py
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -27,6 +27,16 @@ import novaclient.v3.client
|
|
Jakub Ruzicka |
fcc2a65 |
import json
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
+class ClientConnectionPoolTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch("novaclient.client.adapters.HTTPAdapter")
|
|
Jakub Ruzicka |
fcc2a65 |
+ def test_get(self, mock_http_adapter):
|
|
Jakub Ruzicka |
fcc2a65 |
+ mock_http_adapter.side_effect = lambda: mock.Mock()
|
|
Jakub Ruzicka |
fcc2a65 |
+ pool = novaclient.client._ClientConnectionPool()
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertEqual(pool.get("abc"), pool.get("abc"))
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertNotEqual(pool.get("abc"), pool.get("def"))
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
class ClientTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
def test_client_with_timeout(self):
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -43,9 +53,9 @@ class ClientTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
'x-server-management-url': 'blah.com',
|
|
Jakub Ruzicka |
fcc2a65 |
'x-auth-token': 'blah',
|
|
Jakub Ruzicka |
fcc2a65 |
}
|
|
Jakub Ruzicka |
fcc2a65 |
- with mock.patch('requests.Session.request', mock_request):
|
|
Jakub Ruzicka |
fcc2a65 |
+ with mock.patch('requests.request', mock_request):
|
|
Jakub Ruzicka |
fcc2a65 |
instance.authenticate()
|
|
Jakub Ruzicka |
fcc2a65 |
- requests.Session.request.assert_called_with(mock.ANY, mock.ANY,
|
|
Jakub Ruzicka |
fcc2a65 |
+ requests.request.assert_called_with(mock.ANY, mock.ANY,
|
|
Jakub Ruzicka |
fcc2a65 |
timeout=2,
|
|
Jakub Ruzicka |
fcc2a65 |
headers=mock.ANY,
|
|
Jakub Ruzicka |
fcc2a65 |
verify=mock.ANY)
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -61,7 +71,7 @@ class ClientTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
instance.version = 'v2.0'
|
|
Jakub Ruzicka |
fcc2a65 |
mock_request = mock.Mock()
|
|
Jakub Ruzicka |
fcc2a65 |
mock_request.side_effect = novaclient.exceptions.Unauthorized(401)
|
|
Jakub Ruzicka |
fcc2a65 |
- with mock.patch('requests.Session.request', mock_request):
|
|
Jakub Ruzicka |
fcc2a65 |
+ with mock.patch('requests.request', mock_request):
|
|
Jakub Ruzicka |
fcc2a65 |
try:
|
|
Jakub Ruzicka |
fcc2a65 |
instance.get('/servers/detail')
|
|
Jakub Ruzicka |
fcc2a65 |
except Exception:
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -197,6 +207,26 @@ class ClientTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
cs.authenticate()
|
|
Jakub Ruzicka |
fcc2a65 |
self.assertTrue(mock_authenticate.called)
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch('novaclient.client.HTTPClient')
|
|
Jakub Ruzicka |
fcc2a65 |
+ def test_contextmanager_v1_1(self, mock_http_client):
|
|
Jakub Ruzicka |
fcc2a65 |
+ fake_client = mock.Mock()
|
|
Jakub Ruzicka |
fcc2a65 |
+ mock_http_client.return_value = fake_client
|
|
Jakub Ruzicka |
fcc2a65 |
+ with novaclient.v1_1.client.Client("user", "password", "project_id",
|
|
Jakub Ruzicka |
fcc2a65 |
+ auth_url="foo/v2") as client:
|
|
Jakub Ruzicka |
fcc2a65 |
+ pass
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertTrue(fake_client.open_session.called)
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertTrue(fake_client.close_session.called)
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch('novaclient.client.HTTPClient')
|
|
Jakub Ruzicka |
fcc2a65 |
+ def test_contextmanager_v3(self, mock_http_client):
|
|
Jakub Ruzicka |
fcc2a65 |
+ fake_client = mock.Mock()
|
|
Jakub Ruzicka |
fcc2a65 |
+ mock_http_client.return_value = fake_client
|
|
Jakub Ruzicka |
fcc2a65 |
+ with novaclient.v3.client.Client("user", "password", "project_id",
|
|
Jakub Ruzicka |
fcc2a65 |
+ auth_url="foo/v2") as client:
|
|
Jakub Ruzicka |
fcc2a65 |
+ pass
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertTrue(fake_client.open_session.called)
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertTrue(fake_client.close_session.called)
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
def test_get_password_simple(self):
|
|
Jakub Ruzicka |
fcc2a65 |
cs = novaclient.client.HTTPClient("user", "password", "", "")
|
|
Jakub Ruzicka |
fcc2a65 |
cs.password_func = mock.Mock()
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -216,3 +246,77 @@ class ClientTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
cs.password_func = mock.Mock()
|
|
Jakub Ruzicka |
fcc2a65 |
self.assertEqual(cs._get_password(), "password")
|
|
Jakub Ruzicka |
fcc2a65 |
self.assertFalse(cs.password_func.called)
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ def test_auth_url_rstrip_slash(self):
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs = novaclient.client.HTTPClient("user", "password", "project_id",
|
|
Jakub Ruzicka |
fcc2a65 |
+ auth_url="foo/v2/")
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertEqual(cs.auth_url, "foo/v2")
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ def test_token_and_bypass_url(self):
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs = novaclient.client.HTTPClient(None, None, None,
|
|
Jakub Ruzicka |
fcc2a65 |
+ auth_token="12345",
|
|
Jakub Ruzicka |
fcc2a65 |
+ bypass_url="compute/v100/")
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertIsNone(cs.auth_url)
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertEqual(cs.auth_token, "12345")
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertEqual(cs.bypass_url, "compute/v100")
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertEqual(cs.management_url, "compute/v100")
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch("novaclient.client.requests.Session")
|
|
Jakub Ruzicka |
fcc2a65 |
+ def test_session(self, mock_session):
|
|
Jakub Ruzicka |
fcc2a65 |
+ fake_session = mock.Mock()
|
|
Jakub Ruzicka |
fcc2a65 |
+ mock_session.return_value = fake_session
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs = novaclient.client.HTTPClient("user", None, "", "")
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs.open_session()
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertEqual(cs._session, fake_session)
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs.close_session()
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertIsNone(cs._session)
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ def test_session_connection_pool(self):
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs = novaclient.client.HTTPClient("user", None, "",
|
|
Jakub Ruzicka |
fcc2a65 |
+ "", connection_pool=True)
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs.open_session()
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertIsNone(cs._session)
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs.close_session()
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertIsNone(cs._session)
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ def test_get_session(self):
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs = novaclient.client.HTTPClient("user", None, "", "")
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertIsNone(cs._get_session("http://nooooooooo.com"))
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch("novaclient.client.requests.Session")
|
|
Jakub Ruzicka |
fcc2a65 |
+ def test_get_session_open_session(self, mock_session):
|
|
Jakub Ruzicka |
fcc2a65 |
+ fake_session = mock.Mock()
|
|
Jakub Ruzicka |
fcc2a65 |
+ mock_session.return_value = fake_session
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs = novaclient.client.HTTPClient("user", None, "", "")
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs.open_session()
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertEqual(fake_session, cs._get_session("http://example.com"))
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch("novaclient.client.requests.Session")
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch("novaclient.client._ClientConnectionPool")
|
|
Jakub Ruzicka |
fcc2a65 |
+ def test_get_session_connection_pool(self, mock_pool, mock_session):
|
|
Jakub Ruzicka |
fcc2a65 |
+ service_url = "http://example.com"
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ pool = mock.MagicMock()
|
|
Jakub Ruzicka |
fcc2a65 |
+ pool.get.return_value = "http_adapter"
|
|
Jakub Ruzicka |
fcc2a65 |
+ mock_pool.return_value = pool
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs = novaclient.client.HTTPClient("user", None, "",
|
|
Jakub Ruzicka |
fcc2a65 |
+ "", connection_pool=True)
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs._current_url = "http://another.com"
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ session = cs._get_session(service_url)
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertEqual(session, mock_session.return_value)
|
|
Jakub Ruzicka |
fcc2a65 |
+ pool.get.assert_called_once_with(service_url)
|
|
Jakub Ruzicka |
fcc2a65 |
+ mock_session().mount.assert_called_once_with(service_url,
|
|
Jakub Ruzicka |
fcc2a65 |
+ 'http_adapter')
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ def test_init_without_connection_pool(self):
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs = novaclient.client.HTTPClient("user", None, "", "")
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertIsNone(cs._connection_pool)
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch("novaclient.client._ClientConnectionPool")
|
|
Jakub Ruzicka |
fcc2a65 |
+ def test_init_with_proper_connection_pool(self, mock_pool):
|
|
Jakub Ruzicka |
fcc2a65 |
+ fake_pool = mock.Mock()
|
|
Jakub Ruzicka |
fcc2a65 |
+ mock_pool.return_value = fake_pool
|
|
Jakub Ruzicka |
fcc2a65 |
+ cs = novaclient.client.HTTPClient("user", None, "",
|
|
Jakub Ruzicka |
fcc2a65 |
+ connection_pool=True)
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.assertEqual(cs._connection_pool, fake_pool)
|
|
Jakub Ruzicka |
fcc2a65 |
diff --git a/novaclient/tests/test_http.py b/novaclient/tests/test_http.py
|
|
Jakub Ruzicka |
fcc2a65 |
index e2fc4fa..aaa3a46 100644
|
|
Jakub Ruzicka |
fcc2a65 |
--- a/novaclient/tests/test_http.py
|
|
Jakub Ruzicka |
fcc2a65 |
+++ b/novaclient/tests/test_http.py
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -56,7 +56,7 @@ class ClientTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
def test_get(self):
|
|
Jakub Ruzicka |
fcc2a65 |
cl = get_authed_client()
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
@mock.patch('time.time', mock.Mock(return_value=1234))
|
|
Jakub Ruzicka |
fcc2a65 |
def test_get_call():
|
|
Jakub Ruzicka |
fcc2a65 |
resp, body = cl.get("/hi")
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -78,7 +78,7 @@ class ClientTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
def test_post(self):
|
|
Jakub Ruzicka |
fcc2a65 |
cl = get_authed_client()
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
def test_post_call():
|
|
Jakub Ruzicka |
fcc2a65 |
cl.post("/hi", body=[1, 2, 3])
|
|
Jakub Ruzicka |
fcc2a65 |
headers = {
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -110,7 +110,7 @@ class ClientTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
def test_connection_refused(self):
|
|
Jakub Ruzicka |
fcc2a65 |
cl = get_client()
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request", refused_mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request", refused_mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
def test_refused_call():
|
|
Jakub Ruzicka |
fcc2a65 |
self.assertRaises(exceptions.ConnectionRefused, cl.get, "/hi")
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -119,7 +119,7 @@ class ClientTest(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
def test_bad_request(self):
|
|
Jakub Ruzicka |
fcc2a65 |
cl = get_client()
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request", bad_req_mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request", bad_req_mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
def test_refused_call():
|
|
Jakub Ruzicka |
fcc2a65 |
self.assertRaises(exceptions.BadRequest, cl.get, "/hi")
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
diff --git a/novaclient/tests/v1_1/test_auth.py b/novaclient/tests/v1_1/test_auth.py
|
|
Jakub Ruzicka |
fcc2a65 |
index 7344bc7..7877145 100644
|
|
Jakub Ruzicka |
fcc2a65 |
--- a/novaclient/tests/v1_1/test_auth.py
|
|
Jakub Ruzicka |
fcc2a65 |
+++ b/novaclient/tests/v1_1/test_auth.py
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -57,7 +57,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
mock_request = mock.Mock(return_value=(auth_response))
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
def test_auth_call():
|
|
Jakub Ruzicka |
fcc2a65 |
cs.client.authenticate()
|
|
Jakub Ruzicka |
fcc2a65 |
headers = {
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -160,7 +160,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
mock_request = mock.Mock(side_effect=side_effect)
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
def test_auth_call():
|
|
Jakub Ruzicka |
fcc2a65 |
cs.client.authenticate()
|
|
Jakub Ruzicka |
fcc2a65 |
headers = {
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -248,7 +248,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
mock_request = mock.Mock(side_effect=side_effect)
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
def test_auth_call():
|
|
Jakub Ruzicka |
fcc2a65 |
cs.client.authenticate()
|
|
Jakub Ruzicka |
fcc2a65 |
headers = {
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -373,7 +373,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
mock_request = mock.Mock(return_value=(auth_response))
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
- with mock.patch.object(requests.Session, "request", mock_request):
|
|
Jakub Ruzicka |
fcc2a65 |
+ with mock.patch.object(requests, "request", mock_request):
|
|
Jakub Ruzicka |
fcc2a65 |
cs.client.authenticate()
|
|
Jakub Ruzicka |
fcc2a65 |
headers = {
|
|
Jakub Ruzicka |
fcc2a65 |
'User-Agent': cs.client.USER_AGENT,
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -432,7 +432,7 @@ class AuthenticationTests(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
})
|
|
Jakub Ruzicka |
fcc2a65 |
mock_request = mock.Mock(return_value=(auth_response))
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
def test_auth_call():
|
|
Jakub Ruzicka |
fcc2a65 |
cs.client.authenticate()
|
|
Jakub Ruzicka |
fcc2a65 |
headers = {
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -460,7 +460,7 @@ class AuthenticationTests(utils.TestCase):
|
|
Jakub Ruzicka |
fcc2a65 |
auth_response = utils.TestResponse({'status_code': 401})
|
|
Jakub Ruzicka |
fcc2a65 |
mock_request = mock.Mock(return_value=(auth_response))
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
- @mock.patch.object(requests.Session, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
+ @mock.patch.object(requests, "request", mock_request)
|
|
Jakub Ruzicka |
fcc2a65 |
def test_auth_call():
|
|
Jakub Ruzicka |
fcc2a65 |
self.assertRaises(exceptions.Unauthorized, cs.client.authenticate)
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
diff --git a/novaclient/v1_1/client.py b/novaclient/v1_1/client.py
|
|
Jakub Ruzicka |
fcc2a65 |
index efeb5c9..ce4aea1 100644
|
|
Jakub Ruzicka |
fcc2a65 |
--- a/novaclient/v1_1/client.py
|
|
Jakub Ruzicka |
fcc2a65 |
+++ b/novaclient/v1_1/client.py
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -61,6 +61,20 @@ class Client(object):
|
|
Jakub Ruzicka |
fcc2a65 |
>>> client.flavors.list()
|
|
Jakub Ruzicka |
fcc2a65 |
...
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
+ It is also possible to use an instance as a context manager in which
|
|
Jakub Ruzicka |
fcc2a65 |
+ case there will be a session kept alive for the duration of the with
|
|
Jakub Ruzicka |
fcc2a65 |
+ statement::
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ >>> with Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) as client:
|
|
Jakub Ruzicka |
fcc2a65 |
+ ... client.servers.list()
|
|
Jakub Ruzicka |
fcc2a65 |
+ ... client.flavors.list()
|
|
Jakub Ruzicka |
fcc2a65 |
+ ...
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ It is also possible to have a permanent (process-long) connection pool,
|
|
Jakub Ruzicka |
fcc2a65 |
+ by passing a connection_pool=True::
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ >>> client = Client(USERNAME, PASSWORD, PROJECT_ID,
|
|
Jakub Ruzicka |
fcc2a65 |
+ ... AUTH_URL, connection_pool=True)
|
|
Jakub Ruzicka |
fcc2a65 |
"""
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
# FIXME(jesse): project_id isn't required to authenticate
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -73,7 +87,8 @@ class Client(object):
|
|
Jakub Ruzicka |
fcc2a65 |
bypass_url=None, os_cache=False, no_cache=True,
|
|
Jakub Ruzicka |
fcc2a65 |
http_log_debug=False, auth_system='keystone',
|
|
Jakub Ruzicka |
fcc2a65 |
auth_plugin=None, auth_token=None,
|
|
Jakub Ruzicka |
fcc2a65 |
- cacert=None, tenant_id=None):
|
|
Jakub Ruzicka |
fcc2a65 |
+ cacert=None, tenant_id=None,
|
|
Jakub Ruzicka |
fcc2a65 |
+ connection_pool=False):
|
|
Jakub Ruzicka |
fcc2a65 |
# FIXME(comstud): Rename the api_key argument above when we
|
|
Jakub Ruzicka |
fcc2a65 |
# know it's not being used as keyword argument
|
|
Jakub Ruzicka |
fcc2a65 |
password = api_key
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -145,7 +160,15 @@ class Client(object):
|
|
Jakub Ruzicka |
fcc2a65 |
bypass_url=bypass_url,
|
|
Jakub Ruzicka |
fcc2a65 |
os_cache=self.os_cache,
|
|
Jakub Ruzicka |
fcc2a65 |
http_log_debug=http_log_debug,
|
|
Jakub Ruzicka |
fcc2a65 |
- cacert=cacert)
|
|
Jakub Ruzicka |
fcc2a65 |
+ cacert=cacert,
|
|
Jakub Ruzicka |
fcc2a65 |
+ connection_pool=connection_pool)
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ def __enter__(self):
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.client.open_session()
|
|
Jakub Ruzicka |
fcc2a65 |
+ return self
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ def __exit__(self, t, v, tb):
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.client.close_session()
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
def set_management_url(self, url):
|
|
Jakub Ruzicka |
fcc2a65 |
self.client.set_management_url(url)
|
|
Jakub Ruzicka |
fcc2a65 |
diff --git a/novaclient/v3/client.py b/novaclient/v3/client.py
|
|
Jakub Ruzicka |
fcc2a65 |
index f26fdcf..214ba57 100644
|
|
Jakub Ruzicka |
fcc2a65 |
--- a/novaclient/v3/client.py
|
|
Jakub Ruzicka |
fcc2a65 |
+++ b/novaclient/v3/client.py
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -47,6 +47,20 @@ class Client(object):
|
|
Jakub Ruzicka |
fcc2a65 |
>>> client.flavors.list()
|
|
Jakub Ruzicka |
fcc2a65 |
...
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
+ It is also possible to use an instance as a context manager in which
|
|
Jakub Ruzicka |
fcc2a65 |
+ case there will be a session kept alive for the duration of the with
|
|
Jakub Ruzicka |
fcc2a65 |
+ statement::
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ >>> with Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) as client:
|
|
Jakub Ruzicka |
fcc2a65 |
+ ... client.servers.list()
|
|
Jakub Ruzicka |
fcc2a65 |
+ ... client.flavors.list()
|
|
Jakub Ruzicka |
fcc2a65 |
+ ...
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ It is also possible to have a permanent (process-long) connection pool,
|
|
Jakub Ruzicka |
fcc2a65 |
+ by passing a connection_pool=True::
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ >>> client = Client(USERNAME, PASSWORD, PROJECT_ID,
|
|
Jakub Ruzicka |
fcc2a65 |
+ ... AUTH_URL, connection_pool=True)
|
|
Jakub Ruzicka |
fcc2a65 |
"""
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
# FIXME(jesse): project_id isn't required to authenticate
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -59,7 +73,8 @@ class Client(object):
|
|
Jakub Ruzicka |
fcc2a65 |
bypass_url=None, os_cache=False, no_cache=True,
|
|
Jakub Ruzicka |
fcc2a65 |
http_log_debug=False, auth_system='keystone',
|
|
Jakub Ruzicka |
fcc2a65 |
auth_plugin=None, auth_token=None,
|
|
Jakub Ruzicka |
fcc2a65 |
- cacert=None, tenant_id=None):
|
|
Jakub Ruzicka |
fcc2a65 |
+ cacert=None, tenant_id=None,
|
|
Jakub Ruzicka |
fcc2a65 |
+ connection_pool=False):
|
|
Jakub Ruzicka |
fcc2a65 |
self.projectid = project_id
|
|
Jakub Ruzicka |
fcc2a65 |
self.tenant_id = tenant_id
|
|
Jakub Ruzicka |
fcc2a65 |
self.os_cache = os_cache or not no_cache
|
|
Jakub Ruzicka |
fcc2a65 |
@@ -110,7 +125,15 @@ class Client(object):
|
|
Jakub Ruzicka |
fcc2a65 |
bypass_url=bypass_url,
|
|
Jakub Ruzicka |
fcc2a65 |
os_cache=os_cache,
|
|
Jakub Ruzicka |
fcc2a65 |
http_log_debug=http_log_debug,
|
|
Jakub Ruzicka |
fcc2a65 |
- cacert=cacert)
|
|
Jakub Ruzicka |
fcc2a65 |
+ cacert=cacert,
|
|
Jakub Ruzicka |
fcc2a65 |
+ connection_pool=connection_pool)
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ def __enter__(self):
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.client.open_session()
|
|
Jakub Ruzicka |
fcc2a65 |
+ return self
|
|
Jakub Ruzicka |
fcc2a65 |
+
|
|
Jakub Ruzicka |
fcc2a65 |
+ def __exit__(self, t, v, tb):
|
|
Jakub Ruzicka |
fcc2a65 |
+ self.client.close_session()
|
|
Jakub Ruzicka |
fcc2a65 |
|
|
Jakub Ruzicka |
fcc2a65 |
def set_management_url(self, url):
|
|
Jakub Ruzicka |
fcc2a65 |
self.client.set_management_url(url)
|