Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions docs/compute/drivers/openstack.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ OpenStack Compute Driver Documentation
======================================

`OpenStack`_ is an open-source project which allows you to build and run your
own public or a private cloud.
own public or private cloud.

.. figure:: /_static/images/provider_logos/openstack.png
:align: center
Expand Down Expand Up @@ -100,7 +100,7 @@ Available arguments:
driver obtains API endpoint URL from the server catalog, but if this argument
is provided, this step is skipped and the provided value is used directly. Only valid
in case of api_version >= 2.0.
* ``ex_force_volume_url`` - Base URL to the OpenStack cinder API endpoint. By default,
* ``ex_force_volume_url`` - Base URL to the OpenStack cinder API endpoint. By default,
driver obtains API endpoint URL from the server catalog, but if this argument
is provided, this step is skipped and the provided value is used directly. Only valid
in case of api_version >= 2.0.
Expand Down Expand Up @@ -179,6 +179,21 @@ public cloud providers support it.
:language: python
.. _`Cloud-Init examples`: http://cloudinit.readthedocs.org/en/latest/topics/examples.html

8. Authentication token cache
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Since version ???, authentication tokens can be stored in an external cache.
This enables multiple processes to reuse the tokens, reducing the number of
token allocations in the OpenStack authentication service.

.. literalinclude:: /examples/compute/openstack/auth_cache.py
:language: python

If the cache implementation stores tokens somewhere outside the process - for
instance, to disk or a remote system - running this program twice will
allocate a token from OpenStack, store it in the cache, then reuse that token
on the second run.

Non-standard functionality and extension methods
------------------------------------------------

Expand Down Expand Up @@ -210,6 +225,9 @@ token on the first request or if the auth token is about to expire.
As noted in the example 4 above, this doesn't hold true if you use
``ex_force_auth_token`` argument.

Tokens can also be stored in an external cache for shared use among multiple
processes; see example 8 above.

Troubleshooting
---------------

Expand Down
18 changes: 18 additions & 0 deletions docs/examples/compute/openstack/auth_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from libcloud.common.openstack_identity import OpenStackAuthenticationCache
from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver


class MyAuthenticationCache(OpenStackAuthenticationCache):
pass # implement...


auth_cache = MyAuthenticationCache(...)

OpenStack = get_driver(Provider.OPENSTACK)
driver = OpenStack('your_auth_username', 'your_auth_password',
ex_force_auth_url='http://192.168.1.101:5000',
ex_force_auth_version='3.x_password',
ex_auth_cache=auth_cache)

driver.list_sizes()
10 changes: 10 additions & 0 deletions docs/upgrade_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ This page describes how to upgrade from a previous version to a new version
which contains backward incompatible or semi-incompatible changes and how to
preserve the old behavior when this is possible.

Libcloud 3.3.2
--------------

* Exception message changed in OpenStack drivers

Attempting to use an identity API that requires authentication without an
authentication token raises a ValueError. The exception message used to be
"Not to be authenticated to perform this request", but has now been changed
to "Need to be authenticated to perform this request".

Libcloud 3.3.0
--------------

Expand Down
2 changes: 1 addition & 1 deletion libcloud/common/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ def request(self, action, params=None, data=None, headers=None,

if errno == -5:
# Throw a more-friendly exception on "no address associated
# with hostname" error. This error could simpli indicate that
# with hostname" error. This error could simply indicate that
# "host" Connection class attribute is set to an incorrect
# value
class_name = self.__class__.__name__
Expand Down
39 changes: 29 additions & 10 deletions libcloud/common/openstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
from libcloud.utils.py3 import httplib

from libcloud.common.base import ConnectionUserAndKey, Response
from libcloud.common.exceptions import BaseHTTPError
from libcloud.common.types import ProviderError
from libcloud.compute.types import (LibcloudError, MalformedResponseError)
from libcloud.compute.types import KeyPairDoesNotExistError
from libcloud.common.openstack_identity import get_class_for_auth_version
from libcloud.common.openstack_identity import (AUTH_TOKEN_HEADER,
get_class_for_auth_version)

# Imports for backward compatibility reasons
from libcloud.common.openstack_identity import (OpenStackServiceCatalog,
Expand Down Expand Up @@ -132,6 +134,13 @@ class OpenStackBaseConnection(ConnectionUserAndKey):
If not specified, a provider specific
default will be used.
:type ex_force_service_region: ``str``

:param ex_auth_cache: External cache where authentication tokens are
stored for reuse by other processes. Tokens are
always cached in memory on the driver instance. To
share tokens among multiple drivers, processes, or
systems, pass a cache here.
:type ex_auth_cache: :class:`OpenStackAuthenticationCache`
"""

auth_url = None # type: str
Expand All @@ -158,6 +167,7 @@ def __init__(self, user_id, key, secure=True,
ex_force_service_type=None,
ex_force_service_name=None,
ex_force_service_region=None,
ex_auth_cache=None,
retry_delay=None, backoff=None):
super(OpenStackBaseConnection, self).__init__(
user_id, key, secure=secure, timeout=timeout,
Expand All @@ -177,6 +187,7 @@ def __init__(self, user_id, key, secure=True,
self._ex_force_service_type = ex_force_service_type
self._ex_force_service_name = ex_force_service_name
self._ex_force_service_region = ex_force_service_region
self._ex_auth_cache = ex_auth_cache
self._osa = None

if ex_force_auth_token and not ex_force_base_url:
Expand Down Expand Up @@ -215,7 +226,8 @@ def get_auth_class(self):
token_scope=self._ex_token_scope,
timeout=self.timeout,
proxy_url=self.proxy_url,
parent_conn=self)
parent_conn=self,
auth_cache=self._ex_auth_cache)

return self._osa

Expand All @@ -229,12 +241,15 @@ def request(self, action, params=None, data='', headers=None,
if method.upper() in ['POST', 'PUT'] and default_content_type:
headers = {'Content-Type': default_content_type}

return super(OpenStackBaseConnection, self).request(action=action,
params=params,
data=data,
method=method,
headers=headers,
raw=raw)
try:
return super().request(action=action, params=params, data=data,
method=method, headers=headers, raw=raw)
except BaseHTTPError as ex:
# Evict cached auth token if we receive Unauthorized while using it
if (ex.code == httplib.UNAUTHORIZED
and self._ex_force_auth_token is None):
self.get_auth_class().clear_cached_auth_context()
raise

def _get_auth_url(self):
"""
Expand Down Expand Up @@ -296,7 +311,7 @@ def get_endpoint(self):
return url

def add_default_headers(self, headers):
headers['X-Auth-Token'] = self.auth_token
headers[AUTH_TOKEN_HEADER] = self.auth_token
headers['Accept'] = self.accept_format
return headers

Expand Down Expand Up @@ -437,7 +452,8 @@ def __init__(self,
ex_tenant_domain_id='default',
ex_force_service_type=None,
ex_force_service_name=None,
ex_force_service_region=None, *args, **kwargs):
ex_force_service_region=None,
ex_auth_cache=None, *args, **kwargs):
self._ex_force_base_url = ex_force_base_url
self._ex_force_auth_url = ex_force_auth_url
self._ex_force_auth_version = ex_force_auth_version
Expand All @@ -449,6 +465,7 @@ def __init__(self,
self._ex_force_service_type = ex_force_service_type
self._ex_force_service_name = ex_force_service_name
self._ex_force_service_region = ex_force_service_region
self._ex_auth_cache = ex_auth_cache

def openstack_connection_kwargs(self):
"""
Expand Down Expand Up @@ -479,4 +496,6 @@ def openstack_connection_kwargs(self):
rv['ex_force_service_name'] = self._ex_force_service_name
if self._ex_force_service_region:
rv['ex_force_service_region'] = self._ex_force_service_region
if self._ex_auth_cache is not None:
rv['ex_auth_cache'] = self._ex_auth_cache
return rv
Loading