8953627967
- Add implementation of private data storage API - Add appropriate unit tests Partially implements bp private-data-storage-api-for-drivers Change-Id: I61b3cc41f545b540101819feb603e45d0a057a4e
177 lines
6.1 KiB
Python
177 lines
6.1 KiB
Python
# Copyright 2015 Mirantis inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
"""
|
|
Module provides possibility for share drivers to store private information
|
|
related to common Manila models like Share or Snapshot.
|
|
|
|
"""
|
|
|
|
import abc
|
|
|
|
from oslo_config import cfg
|
|
from oslo_utils import importutils
|
|
from oslo_utils import uuidutils
|
|
import six
|
|
|
|
from manila.db import api as db_api
|
|
from manila.i18n import _
|
|
|
|
private_data_opts = [
|
|
cfg.StrOpt(
|
|
'drivers_private_storage_class',
|
|
default='manila.share.drivers_private_data.SqlStorageDriver',
|
|
help='The full class name of the Private Data Driver class to use.'),
|
|
]
|
|
|
|
CONF = cfg.CONF
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class StorageDriver(object):
|
|
|
|
def __init__(self, context, backend_host):
|
|
# Backend shouldn't access data stored by another backend
|
|
self.backend_host = backend_host
|
|
self.context = context
|
|
|
|
@abc.abstractmethod
|
|
def get(self, entity_id, key, default):
|
|
"""Backend implementation for DriverPrivateData.get() method.
|
|
|
|
Should return all keys for given 'entity_id' if 'key' is None.
|
|
Otherwise should return value for provided 'key'.
|
|
If values for provided 'entity_id' or 'key' not found,
|
|
should return 'default'.
|
|
|
|
See DriverPrivateData.get() method for more details.
|
|
"""
|
|
|
|
@abc.abstractmethod
|
|
def update(self, entity_id, details, delete_existing):
|
|
"""Backend implementation for DriverPrivateData.update() method.
|
|
|
|
Should update details for given 'entity_id' with behaviour defined
|
|
by 'delete_existing' boolean flag.
|
|
|
|
See DriverPrivateData.update() method for more details.
|
|
"""
|
|
|
|
@abc.abstractmethod
|
|
def delete(self, entity_id, key):
|
|
"""Backend implementation for DriverPrivateData.delete() method.
|
|
|
|
Should return delete all keys if 'key' is None.
|
|
Otherwise should delete value for provided 'key'.
|
|
|
|
See DriverPrivateData.update() method for more details.
|
|
"""
|
|
|
|
|
|
class SqlStorageDriver(StorageDriver):
|
|
|
|
def update(self, entity_id, details, delete_existing):
|
|
return db_api.driver_private_data_update(
|
|
self.context, self.backend_host, entity_id, details,
|
|
delete_existing
|
|
)
|
|
|
|
def get(self, entity_id, key, default):
|
|
return db_api.driver_private_data_get(
|
|
self.context, self.backend_host, entity_id, key, default
|
|
)
|
|
|
|
def delete(self, entity_id, key):
|
|
return db_api.driver_private_data_delete(
|
|
self.context, self.backend_host, entity_id, key
|
|
)
|
|
|
|
|
|
class DriverPrivateData(object):
|
|
def __init__(self, storage=None, *args, **kwargs):
|
|
"""Init method.
|
|
|
|
:param storage: None or inheritor of StorageDriver abstract class
|
|
:param config_group: Optional -- Config group used for loading settings
|
|
:param context: Optional -- Current context
|
|
:param backend_host: Optional -- Driver host
|
|
"""
|
|
|
|
config_group_name = kwargs.get('config_group')
|
|
CONF.register_opts(private_data_opts, group=config_group_name)
|
|
|
|
if storage is not None:
|
|
self._storage = storage
|
|
elif 'context' in kwargs and 'backend_host' in kwargs:
|
|
if config_group_name:
|
|
conf = getattr(CONF, config_group_name)
|
|
else:
|
|
conf = CONF
|
|
storage_class = conf.drivers_private_storage_class
|
|
cls = importutils.import_class(storage_class)
|
|
self._storage = cls(kwargs.get('context'),
|
|
kwargs.get('backend_host'))
|
|
else:
|
|
msg = _("You should provide 'storage' parameter or"
|
|
" 'context' and 'backend_host' parameters.")
|
|
raise ValueError(msg)
|
|
|
|
def get(self, entity_id, key=None, default=None):
|
|
"""Get one, list or all key-value pairs.
|
|
|
|
:param entity_id: Model UUID
|
|
:param key: Key string or list of keys
|
|
:param default: Default value for case when key(s) not found
|
|
:returns: string or dict
|
|
"""
|
|
self._validate_entity_id(entity_id)
|
|
return self._storage.get(entity_id, key, default)
|
|
|
|
def update(self, entity_id, details, delete_existing=False):
|
|
"""Update or create specified key-value pairs.
|
|
|
|
:param entity_id: Model UUID
|
|
:param details: dict with key-value pairs data. Keys and values should
|
|
be strings.
|
|
:param delete_existing: boolean flag which determines behaviour
|
|
for existing key-value pairs:
|
|
True - remove all existing key-value pairs
|
|
False (default) - leave as is
|
|
"""
|
|
self._validate_entity_id(entity_id)
|
|
|
|
if not isinstance(details, dict):
|
|
msg = (_("Provided details %s is not valid dict.")
|
|
% six.text_type(details))
|
|
raise ValueError(msg)
|
|
|
|
return self._storage.update(
|
|
entity_id, details, delete_existing)
|
|
|
|
def delete(self, entity_id, key=None):
|
|
"""Delete one, list or all key-value pairs.
|
|
|
|
:param entity_id: Model UUID
|
|
:param key: Key string or list of keys
|
|
"""
|
|
self._validate_entity_id(entity_id)
|
|
return self._storage.delete(entity_id, key)
|
|
|
|
@staticmethod
|
|
def _validate_entity_id(entity_id):
|
|
if not uuidutils.is_uuid_like(entity_id):
|
|
msg = (_("Provided entity_id %s is not valid UUID.")
|
|
% six.text_type(entity_id))
|
|
raise ValueError(msg)
|