From c491c71f47e02cff017b448187881948dba745aa Mon Sep 17 00:00:00 2001 From: Gorka Eguileor Date: Tue, 20 Nov 2018 16:41:59 +0100 Subject: [PATCH] Support pool-aware drivers This patch adds pool support to cinderlib. The Backend class now has the property `pool_names` with available pools in the driver. When creating a volume we can specify `pool_name` to use a specific pool. Pool is stored as part of the `host` field in volumes. The `host` field now follows Cinder's naming "host@backend#pool". --- HISTORY.rst | 1 + cinderlib/cinderlib.py | 8 ++++++++ cinderlib/objects.py | 18 ++++++++++++------ docs/topics/backends.rst | 5 +++++ docs/topics/volumes.rst | 12 +++++++++--- tests/unit/utils.py | 1 + 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 4de192d..d76a990 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -13,6 +13,7 @@ History - Provide better message when device is not available. - Backend name stored in host instead of in the AZ (backward incompatible). + - Support multi-pool drivers. 0.2.2 (2018-07-24) ------------------ diff --git a/cinderlib/cinderlib.py b/cinderlib/cinderlib.py index 5b22c61..2eab8d1 100644 --- a/cinderlib/cinderlib.py +++ b/cinderlib/cinderlib.py @@ -76,6 +76,14 @@ class Backend(object): self.driver.set_initialized() self._driver_cfg = driver_cfg self._volumes = None + # init_capabilities already calls get_volume_stats with refresh=True + # so we can call it without refresh to get pool names. + self._pool_names = tuple(pool['pool_name'] + for pool in self.get_volume_stats()['pools']) + + @property + def pool_names(self): + return self._pool_names def __repr__(self): return '' % self.id diff --git a/cinderlib/objects.py b/cinderlib/objects.py index 68344ee..785d5a5 100644 --- a/cinderlib/objects.py +++ b/cinderlib/objects.py @@ -275,17 +275,16 @@ class Volume(NamedObject): _ignore_keys = ('id', CONNECTIONS_OVO_FIELD, 'snapshots') - def __init__(self, backend_or_vol, **kwargs): + def __init__(self, backend_or_vol, pool_name=None, **kwargs): # Accept backend name for convenience if isinstance(backend_or_vol, six.string_types): - kwargs.setdefault('host', - '%s@%s' % (CONFIGURED_HOST, backend_or_vol)) + backend_name = backend_or_vol backend_or_vol = self._get_backend(backend_or_vol) elif isinstance(backend_or_vol, self.backend_class): - kwargs.setdefault('host', - '%s@%s' % (CONFIGURED_HOST, backend_or_vol.id)) - # Accept a volume as additional source data + backend_name = backend_or_vol.id elif isinstance(backend_or_vol, Volume): + backend_name, pool = backend_or_vol._ovo.host.split('#') + pool_name = pool_name or pool for key in backend_or_vol._ovo.fields: if (backend_or_vol._ovo.obj_attr_is_set(key) and key not in self._ignore_keys): @@ -307,6 +306,13 @@ class Volume(NamedObject): self._populate_data() self.local_attach = None + # If we overwrote the host, then we ignore pool_name and don't set a + # default value or copy the one from the source either. + if 'host' not in kwargs and '__ovo' not in kwargs: + pool_name = pool_name or backend_or_vol.pool_names[0] + self._ovo.host = ('%s@%s#%s' % + (CONFIGURED_HOST, backend_name, pool_name)) + if qos_specs or extra_specs: if qos_specs: qos_specs = cinder_objs.QualityOfServiceSpecs( diff --git a/docs/topics/backends.rst b/docs/topics/backends.rst index 2fd4b0a..8a4073b 100644 --- a/docs/topics/backends.rst +++ b/docs/topics/backends.rst @@ -253,4 +253,9 @@ relevant sections: - `global_setup` has been covered in the :doc:`initialization` section. +- `pool_names` tuple with all the pools available in the driver. Non pool + aware drivers will have only 1 pool and use the name of the backend as its + name. Pool aware drivers may report multiple values, which can be passed to + the `create_volume` method in the `pool_name` parameter. + .. _OpenStack's Cinder volume driver configuration documentation: https://docs.openstack.org/cinder/latest/configuration/block-storage/volume-drivers.html diff --git a/docs/topics/volumes.rst b/docs/topics/volumes.rst index e099851..abc2227 100644 --- a/docs/topics/volumes.rst +++ b/docs/topics/volumes.rst @@ -109,9 +109,15 @@ Some of the fields we could be interested in are: - `host`: Used to store the backend name information together with the host name where cinderlib is running. This information is stored as a string in - the form of *host@backend*. This is an optional parameter, and passing it to - `create_volume` will override default value. Issues will arise if parameter - doesn't contain correct information. + the form of *host@backend#pool*. This is an optional parameter, and passing + it to `create_volume` will override default value, allowing us caller to + request a specific pool for multi-pool backends, though we recommend using + the `pool_name` parameter instead. Issues will arise if parameter doesn't + contain correct information. + +- `pool_name`: Pool name to use when creating the volume. Default is to use + the first or only pool. To know possible values for a backend use the + `pool_names` property on the *Backend* instance. - `size`: Volume size in GBi. diff --git a/tests/unit/utils.py b/tests/unit/utils.py index 54504e1..4aac1d0 100644 --- a/tests/unit/utils.py +++ b/tests/unit/utils.py @@ -24,3 +24,4 @@ class FakeBackend(cinderlib.Backend): cinderlib.Backend.backends[driver_name] = self self._driver_cfg = {'volume_backend_name': driver_name} self.driver = mock.Mock() + self._pool_names = (driver_name,)