diff --git a/cinder/backup/drivers/ceph.py b/cinder/backup/drivers/ceph.py index 6f0b0c66456..65970d2f15b 100644 --- a/cinder/backup/drivers/ceph.py +++ b/cinder/backup/drivers/ceph.py @@ -58,6 +58,7 @@ from six.moves import range from cinder.backup import driver from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder import utils import cinder.volume.drivers.rbd as rbd_driver @@ -152,6 +153,7 @@ class VolumeMetadataBackup(object): self.name) +@interface.backupdriver class CephBackupDriver(driver.BackupDriver): """Backup Cinder volumes to Ceph Object Store. diff --git a/cinder/backup/drivers/glusterfs.py b/cinder/backup/drivers/glusterfs.py index 7e274b5057c..c8bb548b0c6 100644 --- a/cinder/backup/drivers/glusterfs.py +++ b/cinder/backup/drivers/glusterfs.py @@ -24,6 +24,7 @@ from oslo_config import cfg from cinder.backup.drivers import posix from cinder import exception +from cinder import interface from cinder import utils @@ -41,6 +42,7 @@ CONF = cfg.CONF CONF.register_opts(glusterfsbackup_service_opts) +@interface.backupdriver class GlusterfsBackupDriver(posix.PosixBackupDriver): """Provides backup, restore and delete using GlusterFS repository.""" diff --git a/cinder/backup/drivers/google.py b/cinder/backup/drivers/google.py index f2a7c46aad8..534ef72e8b6 100644 --- a/cinder/backup/drivers/google.py +++ b/cinder/backup/drivers/google.py @@ -42,6 +42,7 @@ import six from cinder.backup import chunkeddriver from cinder import exception from cinder.i18n import _ +from cinder import interface LOG = logging.getLogger(__name__) @@ -110,6 +111,7 @@ def gcs_logger(func): return func_wrapper +@interface.backupdriver class GoogleBackupDriver(chunkeddriver.ChunkedBackupDriver): """Provides backup, restore and delete of backup objects within GCS.""" diff --git a/cinder/backup/drivers/nfs.py b/cinder/backup/drivers/nfs.py index b3d4d36512a..da000fba21a 100644 --- a/cinder/backup/drivers/nfs.py +++ b/cinder/backup/drivers/nfs.py @@ -23,6 +23,7 @@ from oslo_log import log as logging from cinder.backup.drivers import posix from cinder import exception from cinder.i18n import _ +from cinder import interface from cinder import utils LOG = logging.getLogger(__name__) @@ -44,6 +45,7 @@ CONF = cfg.CONF CONF.register_opts(nfsbackup_service_opts) +@interface.backupdriver class NFSBackupDriver(posix.PosixBackupDriver): """Provides backup, restore and delete using NFS supplied repository.""" diff --git a/cinder/backup/drivers/posix.py b/cinder/backup/drivers/posix.py index 5a00e223128..b7f151b2542 100644 --- a/cinder/backup/drivers/posix.py +++ b/cinder/backup/drivers/posix.py @@ -26,6 +26,7 @@ from oslo_log import log as logging from cinder.backup import chunkeddriver from cinder import exception +from cinder import interface LOG = logging.getLogger(__name__) @@ -63,6 +64,7 @@ CONF = cfg.CONF CONF.register_opts(posixbackup_service_opts) +@interface.backupdriver class PosixBackupDriver(chunkeddriver.ChunkedBackupDriver): """Provides backup, restore and delete using a Posix file system.""" diff --git a/cinder/backup/drivers/swift.py b/cinder/backup/drivers/swift.py index 7e1c661c693..f9469efb4ec 100644 --- a/cinder/backup/drivers/swift.py +++ b/cinder/backup/drivers/swift.py @@ -56,6 +56,7 @@ from cinder.backup import chunkeddriver from cinder import exception from cinder.i18n import _ from cinder.i18n import _LE +from cinder import interface LOG = logging.getLogger(__name__) @@ -138,6 +139,7 @@ CONF = cfg.CONF CONF.register_opts(swiftbackup_service_opts) +@interface.backupdriver class SwiftBackupDriver(chunkeddriver.ChunkedBackupDriver): """Provides backup, restore and delete of backup objects within Swift.""" diff --git a/cinder/backup/drivers/tsm.py b/cinder/backup/drivers/tsm.py index 57b6cf6202d..aab738dc797 100644 --- a/cinder/backup/drivers/tsm.py +++ b/cinder/backup/drivers/tsm.py @@ -36,6 +36,7 @@ from oslo_log import log as logging from cinder.backup import driver from cinder import exception from cinder.i18n import _LE, _ +from cinder import interface from cinder import utils LOG = logging.getLogger(__name__) @@ -259,6 +260,7 @@ def _cleanup_device_hardlink(hardlink_path, volume_path, volume_id): 'err': exc.stderr}) +@interface.backupdriver class TSMBackupDriver(driver.BackupDriver): """Provides backup, restore and delete of volumes backup for TSM.""" diff --git a/cinder/interface/__init__.py b/cinder/interface/__init__.py new file mode 100644 index 00000000000..223cc4dec95 --- /dev/null +++ b/cinder/interface/__init__.py @@ -0,0 +1,37 @@ +# Copyright 2016 Dell 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. +# + +_volume_register = [] +_backup_register = [] +_fczm_register = [] + + +def volumedriver(cls): + """Decorator for concrete volume driver implementations.""" + _volume_register.append(cls) + return cls + + +def backupdriver(cls): + """Decorator for concrete backup driver implementations.""" + _backup_register.append(cls) + return cls + + +def fczmdriver(cls): + """Decorator for concrete fibre channel zone manager drivers.""" + _fczm_register.append(cls) + return cls diff --git a/cinder/interface/backup_chunked_driver.py b/cinder/interface/backup_chunked_driver.py new file mode 100644 index 00000000000..3f32cc2937e --- /dev/null +++ b/cinder/interface/backup_chunked_driver.py @@ -0,0 +1,79 @@ +# Copyright 2016 Dell 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. +# + +""" +Backup driver with 'chunked' backup operations. +""" + +from cinder.interface import backup_driver + + +class BackupDriverWithVerify(backup_driver.BackupDriver): + """Backup driver that supports 'chunked' backups.""" + + def put_container(self, container): + """Create the container if needed. No failure if it pre-exists. + + :param container: The container to write into. + """ + + def get_container_entries(self, container, prefix): + """Get container entry names. + + :param container: The container from which to get entries. + :param prefix: The prefix used to match entries. + """ + + def get_object_writer(self, container, object_name, extra_metadata=None): + """Returns a writer which stores the chunk data in backup repository. + + :param container: The container to write to. + :param object_name: The object name to write. + :param extra_metadata: Extra metadata to be included. + :returns: A context handler that can be used in a "with" context. + """ + + def get_object_reader(self, container, object_name, extra_metadata=None): + """Returns a reader object for the backed up chunk. + + :param container: The container to read from. + :param object_name: The object name to read. + :param extra_metadata: Extra metadata to be included. + """ + + def delete_object(self, container, object_name): + """Delete object from container. + + :param container: The container to modify. + :param object_name: The object name delete. + """ + + def update_container_name(self, backup, container): + """Allows sub-classes to override container name. + + This method exists so that sub-classes can override the container name + as it comes in to the driver in the backup object. Implementations + should return None if no change to the container name is desired. + """ + + def get_extra_metadata(self, backup, volume): + """Return extra metadata to use in prepare_backup. + + This method allows for collection of extra metadata in prepare_backup() + which will be passed to get_object_reader() and get_object_writer(). + Subclass extensions can use this extra information to optimize + data transfers. Return a json serializable object. + """ diff --git a/cinder/interface/backup_driver.py b/cinder/interface/backup_driver.py new file mode 100644 index 00000000000..f2de4167f18 --- /dev/null +++ b/cinder/interface/backup_driver.py @@ -0,0 +1,111 @@ +# Copyright 2016 Dell 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. +# + +""" +Core backup driver interface. + +All backup drivers should support this interface as a bare minimum. +""" + +from cinder.interface import base + + +class BackupDriver(base.CinderInterface): + """Backup driver required interface.""" + + def get_metadata(self, volume_id): + """Get volume metadata. + + Returns a json-encoded dict containing all metadata and the restore + version i.e. the version used to decide what actually gets restored + from this container when doing a backup restore. + + Typically best to use py:class:`BackupMetadataAPI` for this. + + :param volume_id: The ID of the volume. + :returns: json-encoded dict of metadata. + """ + + def put_metadata(self, volume_id, json_metadata): + """Set volume metadata. + + Typically best to use py:class:`BackupMetadataAPI` for this. + + :param volume_id: The ID of the volume. + :param json_metadata: The json-encoded dict of metadata. + """ + + def backup(self, backup, volume_file, backup_metadata=False): + """Start a backup of a specified volume. + + If backup['parent_id'] is given, then an incremental backup + should be performed is supported. + + If the parent backup is a different size, a full backup should be + performed to ensure all data is included. + + TODO(smcginnis) Document backup variable structure. + + :param backup: The backup information. + :param volume_file: The volume or file to write the backup to. + :param backup_metadata: Whether to include volume metadata in the + backup. + """ + + def restore(self, backup, volume_id, volume_file): + """Restore data from a backup. + + :param backup: The backup information. + :param volume_id: The volume to be restored. + :param volume_file: The volume or file to read the data from. + """ + + def delete(self, backup): + """Delete a backup from the backup store. + + :param backup: The backup to be deleted. + """ + + def export_record(self, backup): + """Export driver specific backup record information. + + If backup backend needs additional driver specific information to + import backup record back into the system it must overwrite this method + and return it here as a dictionary so it can be serialized into a + string. + + Default backup driver implementation has no extra information. + + :param backup: backup object to export + :returns: driver_info - dictionary with extra information + """ + + def import_record(self, backup, driver_info): + """Import driver specific backup record information. + + If backup backend needs additional driver specific information to + import backup record back into the system it must overwrite this method + since it will be called with the extra information that was provided by + export_record when exporting the backup. + + Default backup driver implementation does nothing since it didn't + export any specific data in export_record. + + :param backup: backup object to export + :param driver_info: dictionary with driver specific backup record + information + :returns: nothing + """ diff --git a/cinder/interface/backup_verify_driver.py b/cinder/interface/backup_verify_driver.py new file mode 100644 index 00000000000..5f4d34b5bd0 --- /dev/null +++ b/cinder/interface/backup_verify_driver.py @@ -0,0 +1,38 @@ +# Copyright 2016 Dell 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. +# + +""" +Backup driver with verification interface. + +Used for backup drivers that support the option to verify the backup after +completion. +""" + +from cinder.interface import backup_driver + + +class BackupDriverWithVerify(backup_driver.BackupDriver): + """Backup driver that supports the optional verification.""" + + def verify(self, backup): + """Verify that the backup exists on the backend. + + Verify that the backup is OK, possibly following an import record + operation. + + :param backup: Backup id of the backup to verify. + :raises: InvalidBackup, NotImplementedError + """ diff --git a/cinder/interface/base.py b/cinder/interface/base.py new file mode 100644 index 00000000000..3cea6df1ca7 --- /dev/null +++ b/cinder/interface/base.py @@ -0,0 +1,105 @@ +# Copyright 2016 Dell 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. +# + +import abc +import inspect + +import six + + +def _get_arg_count(method): + """Get the number of args for a method. + + :param method: The method to check. + :returns: The number of args for the method. + """ + if not method: + return 0 + + arg_spec = inspect.getargspec(method) + return len(arg_spec[0]) + + +def _get_method_info(cls): + """Get all methods defined in a class. + + Note: This will only return public methods and their associated arg count. + + :param cls: The class to inspect. + :returns: `Dict` of method names with a tuple of the method and their arg + counts. + """ + result = {} + + methods = inspect.getmembers(cls, inspect.ismethod) + for (name, method) in methods: + if name.startswith('_'): + # Skip non-public methods + continue + result[name] = (method, _get_arg_count(method)) + + return result + + +@six.add_metaclass(abc.ABCMeta) +class CinderInterface(object): + """Interface base class for Cinder. + + Cinder interfaces should inherit from this class to support indirect + inheritance evaluation. + + This can be used to validate compliance to an interface without requiring + that the class actually be inherited from the same base class. + """ + + _method_cache = None + + @classmethod + def _get_methods(cls): + if not cls._method_cache: + cls._method_cache = _get_method_info(cls) + return cls._method_cache + + @classmethod + def __subclasshook__(cls, other_cls): + """Custom class inheritance evaluation. + + :param cls: The CinderInterface to check against. + :param other_cls: The class to be checked if it implements + our interface. + """ + interface_methods = cls._get_methods() + driver_methods = _get_method_info(other_cls) + + interface_keys = interface_methods.keys() + driver_keys = driver_methods.keys() + + matching_count = len(set(interface_keys) & set(driver_keys)) + if matching_count != len(interface_keys): + # Missing some methods, does not implement this interface or is + # missing something. + return NotImplemented + + # TODO(smcginnis) Add method signature checking. + # We know all methods are there, now make sure they look right. + # Unfortunately the methods can be obfuscated by certain decorators, + # so we need to find a better way to pull out the real method + # signatures. + # driver_methods[method_name][0].func_closure.cell_contents works + # for most cases but not all. + # AST might work instead of using introspect. + + return True diff --git a/cinder/interface/fczm_driver.py b/cinder/interface/fczm_driver.py new file mode 100644 index 00000000000..208cb58562c --- /dev/null +++ b/cinder/interface/fczm_driver.py @@ -0,0 +1,78 @@ +# Copyright 2016 Dell 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. +# + +""" +Core fibre channel zone manager driver interface. + +All fczm drivers should support this interface as a bare minimum. +""" + +from cinder.interface import base + + +class FibreChannelZoneManagerDriver(base.CinderInterface): + """FCZM driver required interface.""" + + def add_connection(self, fabric, initiator_target_map, host_name=None, + storage_system=None): + """Add a new initiator<>target connection. + + All implementing drivers should provide concrete implementation + for this API. + + :param fabric: Fabric name from cinder.conf file + :param initiator_target_map: Mapping of initiator to list of targets + + .. code-block:: python + + Example initiator_target_map: + + { + '10008c7cff523b01': ['20240002ac000a50', '20240002ac000a40'] + } + + Note that WWPN can be in lower or upper case and can be ':' + separated strings. + """ + + def delete_connection(self, fabric, initiator_target_map, host_name=None, + storage_system=None): + """Delete an initiator<>target connection. + + :param fabric: Fabric name from cinder.conf file + :param initiator_target_map: Mapping of initiator to list of targets + + .. code-block:: python + + Example initiator_target_map: + + { + '10008c7cff523b01': ['20240002ac000a50', '20240002ac000a40'] + } + + Note that WWPN can be in lower or upper case and can be ':' + separated strings. + """ + + def get_san_context(self, target_wwn_list): + """Get SAN context for end devices. + + :param target_wwn_list: Mapping of initiator to list of targets + + Example initiator_target_map: ['20240002ac000a50', '20240002ac000a40'] + Note that WWPN can be in lower or upper case and can be + ':' separated strings. + """ diff --git a/cinder/interface/util.py b/cinder/interface/util.py new file mode 100644 index 00000000000..32adb022286 --- /dev/null +++ b/cinder/interface/util.py @@ -0,0 +1,78 @@ +# Copyright 2016 Dell 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. +# +import fnmatch +import inspect +import os + +from cinder import interface + + +def _ensure_loaded(start_path): + """Loads everything in a given path. + + This will make sure all classes have been loaded and therefore all + decorators have registered class. + + :param start_path: The starting path to load. + """ + for root, folder, files in os.walk(start_path): + for phile in fnmatch.filter(files, '*.py'): + path = os.path.join(root, phile) + try: + __import__( + path.replace('/', '.')[:-3], globals(), locals()) + except Exception: + # Really don't care here + pass + + +def get_volume_drivers(): + """Get a list of all volume drivers.""" + _ensure_loaded('cinder/volume/drivers') + return [DriverInfo(x) for x in interface._volume_register] + + +def get_backup_drivers(): + """Get a list of all backup drivers.""" + _ensure_loaded('cinder/backup/drivers') + return [DriverInfo(x) for x in interface._backup_register] + + +def get_fczm_drivers(): + """Get a list of all fczm drivers.""" + _ensure_loaded('cinder/zonemanager/drivers') + return [DriverInfo(x) for x in interface._fczm_register] + + +class DriverInfo(object): + """Information about driver implementations.""" + + def __init__(self, cls): + self.cls = cls + self.desc = cls.__doc__ + self.class_name = cls.__name__ + self.class_fqn = '{}.{}'.format(inspect.getmodule(cls).__name__, + self.class_name) + self.version = getattr(cls, 'VERSION', None) + + def __str__(self): + return self.class_name + + def __repr__(self): + return self.class_fqn + + def __hash__(self): + return hash(self.class_fqn) diff --git a/cinder/interface/volume_consistencygroup_driver.py b/cinder/interface/volume_consistencygroup_driver.py new file mode 100644 index 00000000000..ebb5c197d03 --- /dev/null +++ b/cinder/interface/volume_consistencygroup_driver.py @@ -0,0 +1,231 @@ +# Copyright 2016 Dell 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. +# + +""" +Consistency group volume driver interface. +""" + +from cinder.interface import base + + +class VolumeConsistencyGroupDriver(base.CinderInterface): + """Interface for drivers that support consistency groups.""" + + def create_consistencygroup(self, context, group): + """Creates a consistencygroup. + + :param context: the context of the caller. + :param group: the dictionary of the consistency group to be created. + :returns: model_update + + model_update will be in this format: {'status': xxx, ......}. + + If the status in model_update is 'error', the manager will throw + an exception and it will be caught in the try-except block in the + manager. If the driver throws an exception, the manager will also + catch it in the try-except block. The group status in the db will + be changed to 'error'. + + For a successful operation, the driver can either build the + model_update and return it or return None. The group status will + be set to 'available'. + """ + + def create_consistencygroup_from_src(self, context, group, volumes, + cgsnapshot=None, snapshots=None, + source_cg=None, source_vols=None): + """Creates a consistencygroup from source. + + :param context: the context of the caller. + :param group: the dictionary of the consistency group to be created. + :param volumes: a list of volume dictionaries in the group. + :param cgsnapshot: the dictionary of the cgsnapshot as source. + :param snapshots: a list of snapshot dictionaries in the cgsnapshot. + :param source_cg: the dictionary of a consistency group as source. + :param source_vols: a list of volume dictionaries in the source_cg. + :returns: model_update, volumes_model_update + + The source can be cgsnapshot or a source cg. + + param volumes is retrieved directly from the db. It is a list of + cinder.db.sqlalchemy.models.Volume to be precise. It cannot be + assigned to volumes_model_update. volumes_model_update is a list of + dictionaries. It has to be built by the driver. An entry will be + in this format: {'id': xxx, 'status': xxx, ......}. model_update + will be in this format: {'status': xxx, ......}. + + To be consistent with other volume operations, the manager will + assume the operation is successful if no exception is thrown by + the driver. For a successful operation, the driver can either build + the model_update and volumes_model_update and return them or + return None, None. + """ + + def delete_consistencygroup(self, context, group, volumes): + """Deletes a consistency group. + + :param context: the context of the caller. + :param group: the dictionary of the consistency group to be deleted. + :param volumes: a list of volume dictionaries in the group. + :returns: model_update, volumes_model_update + + param volumes is retrieved directly from the db. It is a list of + cinder.db.sqlalchemy.models.Volume to be precise. It cannot be + assigned to volumes_model_update. volumes_model_update is a list of + dictionaries. It has to be built by the driver. An entry will be + in this format: {'id': xxx, 'status': xxx, ......}. model_update + will be in this format: {'status': xxx, ......}. + + The driver should populate volumes_model_update and model_update + and return them. + + The manager will check volumes_model_update and update db accordingly + for each volume. If the driver successfully deleted some volumes + but failed to delete others, it should set statuses of the volumes + accordingly so that the manager can update db correctly. + + If the status in any entry of volumes_model_update is 'error_deleting' + or 'error', the status in model_update will be set to the same if it + is not already 'error_deleting' or 'error'. + + If the status in model_update is 'error_deleting' or 'error', the + manager will raise an exception and the status of the group will be + set to 'error' in the db. If volumes_model_update is not returned by + the driver, the manager will set the status of every volume in the + group to 'error' in the except block. + + If the driver raises an exception during the operation, it will be + caught by the try-except block in the manager. The statuses of the + group and all volumes in it will be set to 'error'. + + For a successful operation, the driver can either build the + model_update and volumes_model_update and return them or + return None, None. The statuses of the group and all volumes + will be set to 'deleted' after the manager deletes them from db. + """ + + def update_consistencygroup(self, context, group, + add_volumes=None, remove_volumes=None): + """Updates a consistency group. + + :param context: the context of the caller. + :param group: the dictionary of the consistency group to be updated. + :param add_volumes: a list of volume dictionaries to be added. + :param remove_volumes: a list of volume dictionaries to be removed. + :returns: model_update, add_volumes_update, remove_volumes_update + + model_update is a dictionary that the driver wants the manager + to update upon a successful return. If None is returned, the manager + will set the status to 'available'. + + add_volumes_update and remove_volumes_update are lists of dictionaries + that the driver wants the manager to update upon a successful return. + Note that each entry requires a {'id': xxx} so that the correct + volume entry can be updated. If None is returned, the volume will + remain its original status. Also note that you cannot directly + assign add_volumes to add_volumes_update as add_volumes is a list of + cinder.db.sqlalchemy.models.Volume objects and cannot be used for + db update directly. Same with remove_volumes. + + If the driver throws an exception, the status of the group as well as + those of the volumes to be added/removed will be set to 'error'. + """ + + def create_cgsnapshot(self, context, cgsnapshot, snapshots): + """Creates a cgsnapshot. + + :param context: the context of the caller. + :param cgsnapshot: the dictionary of the cgsnapshot to be created. + :param snapshots: a list of snapshot dictionaries in the cgsnapshot. + :returns: model_update, snapshots_model_update + + param snapshots is retrieved directly from the db. It is a list of + cinder.db.sqlalchemy.models.Snapshot to be precise. It cannot be + assigned to snapshots_model_update. snapshots_model_update is a list + of dictionaries. It has to be built by the driver. An entry will be + in this format: {'id': xxx, 'status': xxx, ......}. model_update + will be in this format: {'status': xxx, ......}. + + The driver should populate snapshots_model_update and model_update + and return them. + + The manager will check snapshots_model_update and update db accordingly + for each snapshot. If the driver successfully deleted some snapshots + but failed to delete others, it should set statuses of the snapshots + accordingly so that the manager can update db correctly. + + If the status in any entry of snapshots_model_update is 'error', the + status in model_update will be set to the same if it is not already + 'error'. + + If the status in model_update is 'error', the manager will raise an + exception and the status of cgsnapshot will be set to 'error' in the + db. If snapshots_model_update is not returned by the driver, the + manager will set the status of every snapshot to 'error' in the except + block. + + If the driver raises an exception during the operation, it will be + caught by the try-except block in the manager and the statuses of + cgsnapshot and all snapshots will be set to 'error'. + + For a successful operation, the driver can either build the + model_update and snapshots_model_update and return them or + return None, None. The statuses of cgsnapshot and all snapshots + will be set to 'available' at the end of the manager function. + """ + + def delete_cgsnapshot(self, context, cgsnapshot, snapshots): + """Deletes a cgsnapshot. + + :param context: the context of the caller. + :param cgsnapshot: the dictionary of the cgsnapshot to be deleted. + :param snapshots: a list of snapshot dictionaries in the cgsnapshot. + :returns: model_update, snapshots_model_update + + param snapshots is retrieved directly from the db. It is a list of + cinder.db.sqlalchemy.models.Snapshot to be precise. It cannot be + assigned to snapshots_model_update. snapshots_model_update is a list + of dictionaries. It has to be built by the driver. An entry will be + in this format: {'id': xxx, 'status': xxx, ......}. model_update + will be in this format: {'status': xxx, ......}. + + The driver should populate snapshots_model_update and model_update + and return them. + + The manager will check snapshots_model_update and update db accordingly + for each snapshot. If the driver successfully deleted some snapshots + but failed to delete others, it should set statuses of the snapshots + accordingly so that the manager can update db correctly. + + If the status in any entry of snapshots_model_update is + 'error_deleting' or 'error', the status in model_update will be set to + the same if it is not already 'error_deleting' or 'error'. + + If the status in model_update is 'error_deleting' or 'error', the + manager will raise an exception and the status of cgsnapshot will be + set to 'error' in the db. If snapshots_model_update is not returned by + the driver, the manager will set the status of every snapshot to + 'error' in the except block. + + If the driver raises an exception during the operation, it will be + caught by the try-except block in the manager and the statuses of + cgsnapshot and all snapshots will be set to 'error'. + + For a successful operation, the driver can either build the + model_update and snapshots_model_update and return them or + return None, None. The statuses of cgsnapshot and all snapshots + will be set to 'deleted' after the manager deletes them from db. + """ diff --git a/cinder/interface/volume_driver.py b/cinder/interface/volume_driver.py new file mode 100644 index 00000000000..05bd8f21a8b --- /dev/null +++ b/cinder/interface/volume_driver.py @@ -0,0 +1,247 @@ +# Copyright 2016 Dell 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. +# + +""" +Core backend volume driver interface. + +All backend drivers should support this interface as a bare minimum. +""" + +from cinder.interface import base + + +class VolumeDriverCore(base.CinderInterface): + """Core backend driver required interface.""" + + def do_setup(self, context): + """Any initialization the volume driver needs to do while starting. + + Called once by the manager after the driver is loaded. + Can be used to set up clients, check licenses, set up protocol + specific helpers, etc. + + :param context: The admin context. + """ + + def check_for_setup_error(self): + """Validate there are no issues with the driver configuration. + + Called after do_setup(). Driver initialization can occur there or in + this call, but must be complete by the time this returns. + + If this method raises an exception, the driver will be left in an + "uninitialized" state by the volume manager, which means that it will + not be sent requests for volume operations. + + This method typically checks things like whether the configured + credentials can be used to log in the storage backend, and whether any + external dependencies are present and working. + + :raises: VolumeBackendAPIException in case of setup error. + """ + + def get_volume_stats(self, refresh=False): + """Collects volume backend stats. + + The get_volume_stats method is used by the volume manager to collect + information from the driver instance related to information about the + driver, available and used space, and driver/backend capabilities. + + It returns a dict with the following required fields: + + * volume_backend_name + This is an identifier for the backend taken from cinder.conf. + Useful when using multi-backend. + * vendor_name + Vendor/author of the driver who serves as the contact for the + driver's development and support. + * driver_version + The driver version is logged at cinder-volume startup and is useful + for tying volume service logs to a specific release of the code. + There are currently no rules for how or when this is updated, but + it tends to follow typical major.minor.revision ideas. + * storage_protocol + The protocol used to connect to the storage, this should be a short + string such as: "iSCSI", "FC", "nfs", "ceph", etc. + * total_capacity_gb + The total capacity in gigabytes (GiB) of the storage backend being + used to store Cinder volumes. + * free_capacity_gb + The free capacity in gigabytes (GiB). + + And the following optional fields: + + * reserved_percentage (integer) + Percentage of backend capacity which is not used by the scheduler. + * location_info (string) + Driver-specific information used by the driver and storage backend + to correlate Cinder volumes and backend LUNs/files. + * QoS_support (Boolean) + Whether the backend supports quality of service. + * provisioned_capacity_gb + The total provisioned capacity on the storage backend, in gigabytes + (GiB), including space consumed by any user other than Cinder + itself. + * max_over_subscription_ratio + The maximum amount a backend can be over subscribed. + * thin_provisioning_support (Boolean) + Whether the backend is capable of allocating thinly provisioned + volumes. + * thick_provisioning_support (Boolean) + Whether the backend is capable of allocating thick provisioned + volumes. (Typically True.) + * total_volumes (integer) + Total number of volumes on the storage backend. This can be used in + custom driver filter functions. + * filter_function (string) + A custom function used by the scheduler to determine whether a + volume should be allocated to this backend or not. Example: + + capabilities.total_volumes < 10 + + * goodness_function (string) + Similar to filter_function, but used to weigh multiple volume + backends. Example: + + capabilities.capacity_utilization < 0.6 ? 100 : 25 + + * multiattach (Boolean) + Whether the backend supports multiattach or not. Defaults to False. + * sparse_copy_volume (Boolean) + Whether copies performed by the volume manager for operations such + as migration should attempt to preserve sparseness. + + The returned dict may also contain a list, "pools", which has a similar + dict for each pool being used with the backend. + + :param refresh: Whether to discard any cached values and force a full + refresh of stats. + :returns: dict of appropriate values (see above). + """ + + def create_volume(self, volume): + """Create a new volume on the backend. + + This method is responsible only for storage allocation on the backend. + It should not export a LUN or actually make this storage available for + use, this is done in a later call. + + # TODO(smcginnis) - Add example data structure of volume object. + :param volume: Volume object containing specifics to create. + :returns: (Optional) dict of database updates for the new volume. + :raises: VolumeBackendAPIException if creation failed. + """ + + def delete_volume(self, volume): + """Delete a volume from the backend. + + If the driver can talk to the backend and detects that the volume is no + longer present, this call should succeed and allow Cinder to complete + the process of deleting the volume. + + :param volume: The volume to delete. + :raises: VolumeIsBusy if the volume is still attached or has snapshots. + VolumeBackendAPIException on error. + """ + + def initialize_connection(self, volume, connector, initiator_data=None): + """Allow connection to connector and return connection info. + + :param volume: The volume to be attached. + :param connector: Dictionary containing information about what is being + connected to. + :param initiator_data: (Optional) A dictionary of driver_initiator_data + objects with key-value pairs that have been + saved for this initiator by a driver in previous + initialize_connection calls. + :returns: A dictionary of connection information. This can optionally + include a "initiator_updates" field. + + The "initiator_updates" field must be a dictionary containing a + "set_values" and/or "remove_values" field. The "set_values" field must + be a dictionary of key-value pairs to be set/updated in the db. The + "remove_values" field must be a list of keys, previously set with + "set_values", that will be deleted from the db. + + May be called multiple times to get connection information after a + volume has already been attached. + """ + + def attach_volume(self, context, volume, instance_uuid, host_name, + mountpoint): + """Lets the driver know Nova has attached the volume to an instance. + + :param context: Security/policy info for the request. + :param volume: Volume being attached. + :param instance_uuid: ID of the instance being attached to. + :param host_name: The host name. + :param mountpoint: Device mount point on the instance. + """ + + def terminate_connection(self, volume, connector): + """Remove access to a volume. + + :param volume: The volume to remove. + :param connector: The Dictionary containing information about the + connection. + """ + + def detach_volume(self, context, volume, attachment=None): + """Detach volume from an instance. + + :param context: Security/policy info for the request. + :param volume: Volume being detached. + :param attachment: (Optional) Attachment information. + """ + + def clone_image(self, volume, image_location, image_id, image_metadata, + image_service): + """Clone an image to a volume. + + :param volume: The volume to create. + :param image_location: Where to pull the image from. + :param image_id: The image identifier. + :param image_metadata: Information about the image. + :param image_service: The image service to use. + :returns: Model updates. + """ + + def copy_image_to_volume(self, context, volume, image_service, image_id): + """Fetch the image from image_service and write it to the volume. + + :param context: Security/policy info for the request. + :param volume: The volume to create. + :param image_service: The image service to use. + :param image_id: The image identifier. + :returns: Model updates. + """ + + def copy_volume_to_image(self, context, volume, image_service, image_meta): + """Copy the volume to the specified image. + + :param context: Security/policy info for the request. + :param volume: The volume to copy. + :param image_service: The image service to use. + :param image_meta: Information about the image. + :returns: Model updates. + """ + + def extend_volume(self, volume, new_size): + """Extend the size of a volume. + + :param volume: The volume to extend. + :param new_size: The new desired size of the volume. + """ diff --git a/cinder/interface/volume_management_driver.py b/cinder/interface/volume_management_driver.py new file mode 100644 index 00000000000..966cb50bcee --- /dev/null +++ b/cinder/interface/volume_management_driver.py @@ -0,0 +1,81 @@ +# Copyright 2016 Dell 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. +# + +""" +Manage/unmanage existing volume driver interface. +""" + +from cinder.interface import base + + +class VolumeManagementDriver(base.CinderInterface): + """Interface for drivers that support managing existing volumes.""" + + def manage_existing(self, volume, existing_ref): + """Brings an existing backend storage object under Cinder management. + + existing_ref is passed straight through from the API request's + manage_existing_ref value, and it is up to the driver how this should + be interpreted. It should be sufficient to identify a storage object + that the driver should somehow associate with the newly-created cinder + volume structure. + + There are two ways to do this: + + 1. Rename the backend storage object so that it matches the, + volume['name'] which is how drivers traditionally map between a + cinder volume and the associated backend storage object. + + 2. Place some metadata on the volume, or somewhere in the backend, that + allows other driver requests (e.g. delete, clone, attach, detach...) + to locate the backend storage object when required. + + If the existing_ref doesn't make sense, or doesn't refer to an existing + backend storage object, raise a ManageExistingInvalidReference + exception. + + The volume may have a volume_type, and the driver can inspect that and + compare against the properties of the referenced backend storage + object. If they are incompatible, raise a + ManageExistingVolumeTypeMismatch, specifying a reason for the failure. + + :param volume: Cinder volume to manage + :param existing_ref: Driver-specific information used to identify a + volume + """ + + def manage_existing_get_size(self, volume, existing_ref): + """Return size of volume to be managed by manage_existing. + + When calculating the size, round up to the next GB. + + :param volume: Cinder volume to manage + :param existing_ref: Driver-specific information used to identify a + volume + """ + + def unmanage(self, volume): + """Removes the specified volume from Cinder management. + + Does not delete the underlying backend storage object. + + For most drivers, this will not need to do anything. However, some + drivers might use this call as an opportunity to clean up any + Cinder-specific configuration that they have associated with the + backend storage object. + + :param volume: Cinder volume to unmanage + """ diff --git a/cinder/interface/volume_snapshot_driver.py b/cinder/interface/volume_snapshot_driver.py new file mode 100644 index 00000000000..51ba4f08eb6 --- /dev/null +++ b/cinder/interface/volume_snapshot_driver.py @@ -0,0 +1,57 @@ +# Copyright 2016 Dell 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. +# + +""" +Snapshot capable volume driver interface. +""" + +from cinder.interface import base + + +class VolumeSnapshotDriver(base.CinderInterface): + """Interface for drivers that support snapshots. + + TODO(smcginnis) Merge into VolumeDriverBase once NFS driver supports + snapshots. + """ + + def create_snapshot(self, snapshot): + """Creates a snapshot. + + :param snapshot: Information for the snapshot to be created. + """ + + def delete_snapshot(self, snapshot): + """Deletes a snapshot. + + :param snapshot: The snapshot to delete. + """ + + def create_volume_from_snapshot(self, volume, snapshot): + """Creates a volume from a snapshot. + + If volume_type extra specs includes 'replication: True' + the driver needs to create a volume replica (secondary), + and setup replication between the newly created volume and + the secondary volume. + + An optional larger size for the new snapshot can be specified. Drivers + should check this value and create or expand the new volume to match. + + :param volume: The volume to be created. + :param snapshot: The snapshot from which to create the volume. + :returns: A dict of database updates for the new volume. + """ diff --git a/cinder/interface/volume_snapshotmanagement_driver.py b/cinder/interface/volume_snapshotmanagement_driver.py new file mode 100644 index 00000000000..8bce8949cac --- /dev/null +++ b/cinder/interface/volume_snapshotmanagement_driver.py @@ -0,0 +1,73 @@ +# Copyright 2016 Dell 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. +# + +""" +Manage/unmanage existing volume snapshots driver interface. +""" + +from cinder.interface import base + + +class VolumeSnapshotManagementDriver(base.CinderInterface): + """Interface for drivers that support managing existing snapshots.""" + + def manage_existing_snapshot(self, snapshot, existing_ref): + """Brings an existing backend storage object under Cinder management. + + existing_ref is passed straight through from the API request's + manage_existing_ref value, and it is up to the driver how this should + be interpreted. It should be sufficient to identify a storage object + that the driver should somehow associate with the newly-created cinder + snapshot structure. + + There are two ways to do this: + + 1. Rename the backend storage object so that it matches the + snapshot['name'] which is how drivers traditionally map between a + cinder snapshot and the associated backend storage object. + + 2. Place some metadata on the snapshot, or somewhere in the backend, + that allows other driver requests (e.g. delete) to locate the + backend storage object when required. + + :param snapshot: The snapshot to manage. + :param existing_ref: A reference to the existing snap. + :raises: ManageExistingInvalidReference If the existing_ref doesn't + make sense, or doesn't refer to an existing backend storage + object. + """ + + def manage_existing_snapshot_get_size(self, snapshot, existing_ref): + """Return size of snapshot to be managed by manage_existing. + + When calculating the size, round up to the next GB. + + :param snapshot: The snapshot. + :param existing_ref: A reference to the existing snap. + """ + + def unmanage_snapshot(self, snapshot): + """Removes the specified snapshot from Cinder management. + + Does not delete the underlying backend storage object. + + For most drivers, this will not need to do anything. However, some + drivers might use this call as an opportunity to clean up any + Cinder-specific configuration that they have associated with the + backend storage object. + + :param snapshot: The snapshot to unmanage. + """ diff --git a/cinder/tests/compliance/__init__.py b/cinder/tests/compliance/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cinder/tests/compliance/test_backup_drivers.py b/cinder/tests/compliance/test_backup_drivers.py new file mode 100644 index 00000000000..e429b7b50d2 --- /dev/null +++ b/cinder/tests/compliance/test_backup_drivers.py @@ -0,0 +1,45 @@ +# Copyright 2016 Dell 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. +# + +import ddt + +from cinder.interface import backup_driver +from cinder.interface import util +from cinder import test + +BACKUP_DRIVERS = util.get_backup_drivers() + + +@ddt.ddt +class TestBackupDrivers(test.TestCase): + + def test_backup_driver_decorator(self): + """Sanity check on the decorator. + + The interface code is somewhat implicitly tested. We don't need unit + tests for all of that code, but as a minimum we should make sure it + returns at least one registered driver, else the compliance test will + never even run. + """ + self.assertTrue(len(BACKUP_DRIVERS) > 0) + + @ddt.data(*BACKUP_DRIVERS) + def test_backup_driver_compliance(self, driver): + """Makes sure all backup drivers support the minimum requirements.""" + self.assertTrue( + issubclass(driver.cls, backup_driver.BackupDriver), + "Driver {} does not conform to minimum backup driver " + "requirements!".format(driver.class_fqn)) diff --git a/cinder/tests/compliance/test_fczm_drivers.py b/cinder/tests/compliance/test_fczm_drivers.py new file mode 100644 index 00000000000..3bc530abd4d --- /dev/null +++ b/cinder/tests/compliance/test_fczm_drivers.py @@ -0,0 +1,45 @@ +# Copyright 2016 Dell 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. +# + +import ddt + +from cinder.interface import fczm_driver +from cinder.interface import util +from cinder import test + +FCZM_DRIVERS = util.get_fczm_drivers() + + +@ddt.ddt +class TestFibreChannelZoneManagerDrivers(test.TestCase): + + def test_fczm_driver_decorator(self): + """Sanity check on the decorator. + + The interface code is somewhat implicitly tested. We don't need unit + tests for all of that code, but as a minimum we should make sure it + returns at least one registered driver, else the compliance test will + never even run. + """ + self.assertTrue(len(FCZM_DRIVERS) > 0) + + @ddt.data(*FCZM_DRIVERS) + def test_fczm_driver_compliance(self, driver): + """Makes sure all fczm drivers support the minimum requirements.""" + self.assertTrue( + issubclass(driver.cls, fczm_driver.FibreChannelZoneManagerDriver), + "Driver {} does not conform to minimum fczm driver " + "requirements!".format(driver.class_fqn)) diff --git a/cinder/tests/compliance/test_volume_drivers.py b/cinder/tests/compliance/test_volume_drivers.py new file mode 100644 index 00000000000..9e928cb101c --- /dev/null +++ b/cinder/tests/compliance/test_volume_drivers.py @@ -0,0 +1,44 @@ +# Copyright 2016 Dell 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. +# + +import ddt + +from cinder.interface import util +from cinder.interface import volume_driver +from cinder import test + +VOLUME_DRIVERS = util.get_volume_drivers() + + +@ddt.ddt +class TestVolumeDrivers(test.TestCase): + + def test_volume_driver_decorator(self): + """Sanity check on the decorator. + + The interface code is somewhat implicitly tested. We don't need unit + tests for all of that code, but as a minimum we should make sure it + returns at least one registered driver, else the compliance test will + never even run. + """ + self.assertTrue(len(VOLUME_DRIVERS) > 0) + + @ddt.data(*VOLUME_DRIVERS) + def test_volume_driver_compliance(self, driver): + self.assertTrue( + issubclass(driver.cls, volume_driver.VolumeDriverCore), + "Driver {} does not conform to minimum volume driver " + "requirements!".format(driver.class_fqn)) diff --git a/cinder/volume/drivers/block_device.py b/cinder/volume/drivers/block_device.py index 8c3ecd4fd06..03b809bc57b 100644 --- a/cinder/volume/drivers/block_device.py +++ b/cinder/volume/drivers/block_device.py @@ -24,6 +24,7 @@ from cinder import context from cinder import exception from cinder.i18n import _, _LI, _LW from cinder.image import image_utils +from cinder import interface from cinder import objects from cinder import utils from cinder.volume import driver @@ -42,6 +43,7 @@ CONF = cfg.CONF CONF.register_opts(volume_opts) +@interface.volumedriver class BlockDeviceDriver(driver.BaseVD, driver.LocalVD, driver.CloneableImageVD, driver.TransferVD): VERSION = '2.2.0' diff --git a/cinder/volume/drivers/blockbridge.py b/cinder/volume/drivers/blockbridge.py index f961e66edd4..99d0aa98dae 100644 --- a/cinder/volume/drivers/blockbridge.py +++ b/cinder/volume/drivers/blockbridge.py @@ -29,6 +29,7 @@ from six.moves import urllib from cinder import context from cinder import exception from cinder.i18n import _ +from cinder import interface from cinder.volume import driver from cinder.volume import utils as volume_utils @@ -168,6 +169,7 @@ class BlockbridgeAPIClient(object): return rsp_data +@interface.volumedriver class BlockbridgeISCSIDriver(driver.ISCSIDriver): """Manages volumes hosted on Blockbridge EPS.""" diff --git a/cinder/volume/drivers/cloudbyte/cloudbyte.py b/cinder/volume/drivers/cloudbyte/cloudbyte.py index 1a5aaea380c..93c0ce4d2e2 100644 --- a/cinder/volume/drivers/cloudbyte/cloudbyte.py +++ b/cinder/volume/drivers/cloudbyte/cloudbyte.py @@ -26,6 +26,7 @@ from six.moves import urllib from cinder import context from cinder import exception from cinder.i18n import _, _LE, _LI +from cinder import interface from cinder.volume.drivers.cloudbyte import options from cinder.volume.drivers.san import san from cinder.volume import qos_specs @@ -34,6 +35,7 @@ from cinder.volume import volume_types LOG = logging.getLogger(__name__) +@interface.volumedriver class CloudByteISCSIDriver(san.SanISCSIDriver): """CloudByte ISCSI Driver. diff --git a/cinder/volume/drivers/coho.py b/cinder/volume/drivers/coho.py index a5fcb560d1c..255c92bf175 100644 --- a/cinder/volume/drivers/coho.py +++ b/cinder/volume/drivers/coho.py @@ -25,6 +25,7 @@ from random import randint from cinder import exception from cinder.i18n import _ +from cinder import interface from cinder import utils from cinder.volume.drivers import nfs @@ -292,6 +293,7 @@ CONF = cfg.CONF CONF.register_opts(coho_opts) +@interface.volumedriver class CohoDriver(nfs.NfsDriver): """Coho Data NFS based cinder driver. diff --git a/cinder/volume/drivers/datera.py b/cinder/volume/drivers/datera.py index 39e812f9245..f360c2b8d16 100644 --- a/cinder/volume/drivers/datera.py +++ b/cinder/volume/drivers/datera.py @@ -29,6 +29,7 @@ import six from cinder import context from cinder import exception from cinder.i18n import _, _LE, _LI +from cinder import interface from cinder import utils from cinder.volume.drivers.san import san from cinder.volume import qos_specs @@ -111,6 +112,7 @@ def _authenticated(func): return func_wrapper +@interface.volumedriver @six.add_metaclass(utils.TraceWrapperWithABCMetaclass) class DateraDriver(san.SanISCSIDriver): diff --git a/cinder/volume/drivers/dell/dell_storagecenter_fc.py b/cinder/volume/drivers/dell/dell_storagecenter_fc.py index c91a4765da3..9fe94392c42 100644 --- a/cinder/volume/drivers/dell/dell_storagecenter_fc.py +++ b/cinder/volume/drivers/dell/dell_storagecenter_fc.py @@ -19,6 +19,7 @@ from oslo_utils import excutils from cinder import exception from cinder.i18n import _, _LE +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.dell import dell_storagecenter_common from cinder.zonemanager import utils as fczm_utils @@ -26,6 +27,7 @@ from cinder.zonemanager import utils as fczm_utils LOG = logging.getLogger(__name__) +@interface.volumedriver class DellStorageCenterFCDriver(dell_storagecenter_common.DellCommonDriver, driver.FibreChannelDriver): diff --git a/cinder/volume/drivers/dell/dell_storagecenter_iscsi.py b/cinder/volume/drivers/dell/dell_storagecenter_iscsi.py index 845c42a04b3..846b353d2e7 100644 --- a/cinder/volume/drivers/dell/dell_storagecenter_iscsi.py +++ b/cinder/volume/drivers/dell/dell_storagecenter_iscsi.py @@ -19,11 +19,13 @@ from oslo_utils import excutils from cinder import exception from cinder.i18n import _, _LE, _LI +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.dell import dell_storagecenter_common LOG = logging.getLogger(__name__) +@interface.volumedriver class DellStorageCenterISCSIDriver(dell_storagecenter_common.DellCommonDriver, driver.ISCSIDriver): diff --git a/cinder/volume/drivers/disco/disco.py b/cinder/volume/drivers/disco/disco.py index 9ed70b4d95d..6788b90d319 100644 --- a/cinder/volume/drivers/disco/disco.py +++ b/cinder/volume/drivers/disco/disco.py @@ -31,6 +31,7 @@ from cinder.db.sqlalchemy import api from cinder import exception from cinder.i18n import _ from cinder.image import image_utils +from cinder import interface from cinder import utils from cinder.volume import driver @@ -82,6 +83,7 @@ CONF.register_opts(disco_opts) # Driver to communicate with DISCO storage solution +@interface.volumedriver class DiscoDriver(driver.VolumeDriver): """Execute commands related to DISCO Volumes.""" diff --git a/cinder/volume/drivers/dothill/dothill_fc.py b/cinder/volume/drivers/dothill/dothill_fc.py index d088bf087a8..bc5da70d136 100644 --- a/cinder/volume/drivers/dothill/dothill_fc.py +++ b/cinder/volume/drivers/dothill/dothill_fc.py @@ -14,12 +14,14 @@ # under the License. # +from cinder import interface import cinder.volume.driver from cinder.volume.drivers.dothill import dothill_common from cinder.volume.drivers.san import san from cinder.zonemanager import utils as fczm_utils +@interface.volumedriver class DotHillFCDriver(cinder.volume.driver.FibreChannelDriver): """OpenStack Fibre Channel cinder drivers for DotHill Arrays. diff --git a/cinder/volume/drivers/dothill/dothill_iscsi.py b/cinder/volume/drivers/dothill/dothill_iscsi.py index 2b78c0ed963..b92a71b01b6 100644 --- a/cinder/volume/drivers/dothill/dothill_iscsi.py +++ b/cinder/volume/drivers/dothill/dothill_iscsi.py @@ -18,6 +18,7 @@ from oslo_log import log as logging from cinder import exception from cinder.i18n import _ +from cinder import interface import cinder.volume.driver from cinder.volume.drivers.dothill import dothill_common as dothillcommon from cinder.volume.drivers.san import san @@ -27,6 +28,7 @@ DEFAULT_ISCSI_PORT = "3260" LOG = logging.getLogger(__name__) +@interface.volumedriver class DotHillISCSIDriver(cinder.volume.driver.ISCSIDriver): """OpenStack iSCSI cinder drivers for DotHill Arrays. diff --git a/cinder/volume/drivers/drbdmanagedrv.py b/cinder/volume/drivers/drbdmanagedrv.py index 258ec4ee245..01d0b2403fa 100644 --- a/cinder/volume/drivers/drbdmanagedrv.py +++ b/cinder/volume/drivers/drbdmanagedrv.py @@ -38,6 +38,7 @@ from oslo_utils import units from cinder import exception from cinder.i18n import _, _LW, _LI, _LE +from cinder import interface from cinder.volume import driver try: @@ -758,7 +759,7 @@ class DrbdManageBaseDriver(driver.VolumeDriver): # Class with iSCSI interface methods - +@interface.volumedriver class DrbdManageIscsiDriver(DrbdManageBaseDriver): """Cinder driver that uses the iSCSI protocol. """ @@ -820,6 +821,7 @@ DrbdManageDriver = DrbdManageIscsiDriver # Class with DRBD transport mode +@interface.volumedriver class DrbdManageDrbdDriver(DrbdManageBaseDriver): """Cinder driver that uses the DRBD protocol. """ diff --git a/cinder/volume/drivers/emc/emc_cli_fc.py b/cinder/volume/drivers/emc/emc_cli_fc.py index 6437cb07d0a..00236e85530 100644 --- a/cinder/volume/drivers/emc/emc_cli_fc.py +++ b/cinder/volume/drivers/emc/emc_cli_fc.py @@ -16,6 +16,7 @@ from oslo_log import log as logging +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.emc import emc_vnx_cli from cinder.zonemanager import utils as zm_utils @@ -24,6 +25,7 @@ from cinder.zonemanager import utils as zm_utils LOG = logging.getLogger(__name__) +@interface.volumedriver class EMCCLIFCDriver(driver.FibreChannelDriver): """EMC FC Driver for VNX using CLI. diff --git a/cinder/volume/drivers/emc/emc_cli_iscsi.py b/cinder/volume/drivers/emc/emc_cli_iscsi.py index aaf12392a9c..fd0d67261a0 100644 --- a/cinder/volume/drivers/emc/emc_cli_iscsi.py +++ b/cinder/volume/drivers/emc/emc_cli_iscsi.py @@ -16,12 +16,14 @@ from oslo_log import log as logging +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.emc import emc_vnx_cli LOG = logging.getLogger(__name__) +@interface.volumedriver class EMCCLIISCSIDriver(driver.ISCSIDriver): """EMC ISCSI Drivers for VNX using CLI. diff --git a/cinder/volume/drivers/emc/emc_vmax_fc.py b/cinder/volume/drivers/emc/emc_vmax_fc.py index 66f6536cfac..dd2559a550e 100644 --- a/cinder/volume/drivers/emc/emc_vmax_fc.py +++ b/cinder/volume/drivers/emc/emc_vmax_fc.py @@ -20,6 +20,7 @@ import six from cinder import context from cinder.i18n import _LW +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.emc import emc_vmax_common from cinder.zonemanager import utils as fczm_utils @@ -27,6 +28,7 @@ from cinder.zonemanager import utils as fczm_utils LOG = logging.getLogger(__name__) +@interface.volumedriver class EMCVMAXFCDriver(driver.FibreChannelDriver): """EMC FC Drivers for VMAX using SMI-S. diff --git a/cinder/volume/drivers/emc/emc_vmax_iscsi.py b/cinder/volume/drivers/emc/emc_vmax_iscsi.py index e79c0c3ebeb..a6de60d7c4f 100644 --- a/cinder/volume/drivers/emc/emc_vmax_iscsi.py +++ b/cinder/volume/drivers/emc/emc_vmax_iscsi.py @@ -24,6 +24,7 @@ import six from cinder import context from cinder import exception from cinder.i18n import _, _LE, _LI +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.emc import emc_vmax_common @@ -33,6 +34,7 @@ LOG = logging.getLogger(__name__) CINDER_CONF = '/etc/cinder/cinder.conf' +@interface.volumedriver class EMCVMAXISCSIDriver(driver.ISCSIDriver): """EMC ISCSI Drivers for VMAX using SMI-S. diff --git a/cinder/volume/drivers/emc/scaleio.py b/cinder/volume/drivers/emc/scaleio.py index 57ee3de77c7..346c6645c39 100644 --- a/cinder/volume/drivers/emc/scaleio.py +++ b/cinder/volume/drivers/emc/scaleio.py @@ -32,6 +32,7 @@ from cinder import context from cinder import exception from cinder.i18n import _, _LI, _LW, _LE from cinder.image import image_utils +from cinder import interface from cinder import utils from cinder.volume import driver from cinder.volume.drivers.san import san @@ -88,6 +89,7 @@ VOLUME_NOT_MAPPED_ERROR = 84 VOLUME_ALREADY_MAPPED_ERROR = 81 +@interface.volumedriver class ScaleIODriver(driver.VolumeDriver): """EMC ScaleIO Driver.""" diff --git a/cinder/volume/drivers/emc/xtremio.py b/cinder/volume/drivers/emc/xtremio.py index 6cd2b4e8ed1..5d7b503f6a9 100644 --- a/cinder/volume/drivers/emc/xtremio.py +++ b/cinder/volume/drivers/emc/xtremio.py @@ -44,6 +44,7 @@ import six from cinder import context from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder import objects from cinder.objects import fields from cinder import utils @@ -774,6 +775,7 @@ class XtremIOVolumeDriver(san.SanDriver): (data=_("Failed to create IG, %s") % name)) +@interface.volumedriver class XtremIOISCSIDriver(XtremIOVolumeDriver, driver.ISCSIDriver): """Executes commands relating to ISCSI volumes. @@ -918,6 +920,7 @@ class XtremIOISCSIDriver(XtremIOVolumeDriver, driver.ISCSIDriver): return connector['initiator'] +@interface.volumedriver class XtremIOFibreChannelDriver(XtremIOVolumeDriver, driver.FibreChannelDriver): diff --git a/cinder/volume/drivers/eqlx.py b/cinder/volume/drivers/eqlx.py index c1d7956f38b..4011452dad9 100644 --- a/cinder/volume/drivers/eqlx.py +++ b/cinder/volume/drivers/eqlx.py @@ -30,6 +30,7 @@ from six.moves import range from cinder import exception from cinder.i18n import _, _LE, _LW, _LI +from cinder import interface from cinder import ssh_utils from cinder import utils from cinder.volume.drivers import san @@ -102,6 +103,7 @@ def with_timeout(f): return __inner +@interface.volumedriver class DellEQLSanISCSIDriver(san.SanISCSIDriver): """Implements commands for Dell EqualLogic SAN ISCSI management. diff --git a/cinder/volume/drivers/fujitsu/eternus_dx_fc.py b/cinder/volume/drivers/fujitsu/eternus_dx_fc.py index 1c377ea32e1..2d9c3ebba10 100644 --- a/cinder/volume/drivers/fujitsu/eternus_dx_fc.py +++ b/cinder/volume/drivers/fujitsu/eternus_dx_fc.py @@ -22,6 +22,7 @@ FibreChannel Cinder Volume driver for Fujitsu ETERNUS DX S3 series. from oslo_log import log as logging import six +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.fujitsu import eternus_dx_common from cinder.zonemanager import utils as fczm_utils @@ -29,6 +30,7 @@ from cinder.zonemanager import utils as fczm_utils LOG = logging.getLogger(__name__) +@interface.volumedriver class FJDXFCDriver(driver.FibreChannelDriver): """FC Cinder Volume Driver for Fujitsu ETERNUS DX S3 series.""" diff --git a/cinder/volume/drivers/fujitsu/eternus_dx_iscsi.py b/cinder/volume/drivers/fujitsu/eternus_dx_iscsi.py index 0070e47f293..45626960bba 100644 --- a/cinder/volume/drivers/fujitsu/eternus_dx_iscsi.py +++ b/cinder/volume/drivers/fujitsu/eternus_dx_iscsi.py @@ -22,6 +22,7 @@ iSCSI Cinder Volume driver for Fujitsu ETERNUS DX S3 series. import six from cinder.i18n import _LI +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.fujitsu import eternus_dx_common from oslo_log import log as logging @@ -29,6 +30,7 @@ from oslo_log import log as logging LOG = logging.getLogger(__name__) +@interface.volumedriver class FJDXISCSIDriver(driver.ISCSIDriver): """iSCSI Cinder Volume Driver for Fujitsu ETERNUS DX S3 series.""" diff --git a/cinder/volume/drivers/glusterfs.py b/cinder/volume/drivers/glusterfs.py index 341598cbfe5..980c1f02bb5 100644 --- a/cinder/volume/drivers/glusterfs.py +++ b/cinder/volume/drivers/glusterfs.py @@ -27,6 +27,7 @@ from oslo_utils import units from cinder import exception from cinder.i18n import _, _LE, _LI, _LW from cinder.image import image_utils +from cinder import interface from cinder import utils from cinder.volume import driver from cinder.volume.drivers import remotefs as remotefs_drv @@ -47,6 +48,7 @@ CONF = cfg.CONF CONF.register_opts(volume_opts) +@interface.volumedriver class GlusterfsDriver(remotefs_drv.RemoteFSSnapDriver, driver.ExtendVD): """Gluster based cinder driver. diff --git a/cinder/volume/drivers/hgst.py b/cinder/volume/drivers/hgst.py index 19528f4cc37..19865346da2 100644 --- a/cinder/volume/drivers/hgst.py +++ b/cinder/volume/drivers/hgst.py @@ -38,6 +38,7 @@ from cinder.i18n import _ from cinder.i18n import _LE from cinder.i18n import _LW from cinder.image import image_utils +from cinder import interface from cinder.volume import driver from cinder.volume import utils as volutils @@ -70,6 +71,7 @@ CONF = cfg.CONF CONF.register_opts(hgst_opts) +@interface.volumedriver class HGSTDriver(driver.VolumeDriver): """This is the Class to set in cinder.conf (volume_driver). diff --git a/cinder/volume/drivers/hitachi/hbsd_fc.py b/cinder/volume/drivers/hitachi/hbsd_fc.py index b9146f2d1c4..afd2e0e6db8 100644 --- a/cinder/volume/drivers/hitachi/hbsd_fc.py +++ b/cinder/volume/drivers/hitachi/hbsd_fc.py @@ -26,6 +26,7 @@ import six from cinder import exception from cinder.i18n import _LI, _LW +from cinder import interface from cinder import utils import cinder.volume.driver from cinder.volume.drivers.hitachi import hbsd_basiclib as basic_lib @@ -44,6 +45,7 @@ CONF = cfg.CONF CONF.register_opts(volume_opts) +@interface.volumedriver class HBSDFCDriver(cinder.volume.driver.FibreChannelDriver): VERSION = common.VERSION diff --git a/cinder/volume/drivers/hitachi/hbsd_iscsi.py b/cinder/volume/drivers/hitachi/hbsd_iscsi.py index 48925283670..904290a81b9 100644 --- a/cinder/volume/drivers/hitachi/hbsd_iscsi.py +++ b/cinder/volume/drivers/hitachi/hbsd_iscsi.py @@ -25,6 +25,7 @@ import six from cinder import exception from cinder.i18n import _LE, _LI +from cinder import interface from cinder import utils import cinder.volume.driver from cinder.volume.drivers.hitachi import hbsd_basiclib as basic_lib @@ -52,6 +53,7 @@ CONF = cfg.CONF CONF.register_opts(volume_opts) +@interface.volumedriver class HBSDISCSIDriver(cinder.volume.driver.ISCSIDriver): VERSION = common.VERSION diff --git a/cinder/volume/drivers/hitachi/hnas_iscsi.py b/cinder/volume/drivers/hitachi/hnas_iscsi.py index bbbb1b48dec..36e41fe43ab 100644 --- a/cinder/volume/drivers/hitachi/hnas_iscsi.py +++ b/cinder/volume/drivers/hitachi/hnas_iscsi.py @@ -30,6 +30,7 @@ from oslo_utils import units from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder import utils as cinder_utils from cinder.volume import driver from cinder.volume.drivers.hitachi import hnas_backend @@ -164,6 +165,7 @@ def _read_config(xml_config_file): return config +@interface.volumedriver class HDSISCSIDriver(driver.ISCSIDriver): """HDS HNAS volume driver. diff --git a/cinder/volume/drivers/hitachi/hnas_nfs.py b/cinder/volume/drivers/hitachi/hnas_nfs.py index 0f85694f8a1..c79c89a9243 100644 --- a/cinder/volume/drivers/hitachi/hnas_nfs.py +++ b/cinder/volume/drivers/hitachi/hnas_nfs.py @@ -33,6 +33,7 @@ from oslo_utils import units from cinder import exception from cinder.i18n import _, _LE, _LI from cinder.image import image_utils +from cinder import interface from cinder import utils as cutils from cinder.volume.drivers.hitachi import hnas_backend from cinder.volume.drivers import nfs @@ -150,6 +151,7 @@ def factory_bend(drv_config): return hnas_backend.HnasBackend(drv_config) +@interface.volumedriver class HDSNFSDriver(nfs.NfsDriver): """Base class for Hitachi NFS driver. diff --git a/cinder/volume/drivers/hpe/hpe_3par_fc.py b/cinder/volume/drivers/hpe/hpe_3par_fc.py index 853b70355e5..76972a042a9 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_fc.py +++ b/cinder/volume/drivers/hpe/hpe_3par_fc.py @@ -38,6 +38,7 @@ from oslo_log import log as logging from cinder import exception from cinder.i18n import _, _LI, _LW +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.hpe import hpe_3par_common as hpecommon from cinder.volume.drivers.san import san @@ -46,6 +47,7 @@ from cinder.zonemanager import utils as fczm_utils LOG = logging.getLogger(__name__) +@interface.volumedriver class HPE3PARFCDriver(driver.TransferVD, driver.ManageableVD, driver.ExtendVD, diff --git a/cinder/volume/drivers/hpe/hpe_3par_iscsi.py b/cinder/volume/drivers/hpe/hpe_3par_iscsi.py index 149cc990abf..9619d9f9d67 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_iscsi.py +++ b/cinder/volume/drivers/hpe/hpe_3par_iscsi.py @@ -40,6 +40,7 @@ import six from cinder import exception from cinder.i18n import _, _LE, _LW +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.hpe import hpe_3par_common as hpecommon from cinder.volume.drivers.san import san @@ -51,6 +52,7 @@ CHAP_USER_KEY = "HPQ-cinder-CHAP-name" CHAP_PASS_KEY = "HPQ-cinder-CHAP-secret" +@interface.volumedriver class HPE3PARISCSIDriver(driver.TransferVD, driver.ManageableVD, driver.ExtendVD, diff --git a/cinder/volume/drivers/hpe/hpe_lefthand_iscsi.py b/cinder/volume/drivers/hpe/hpe_lefthand_iscsi.py index 13448537842..9d6630c5f48 100644 --- a/cinder/volume/drivers/hpe/hpe_lefthand_iscsi.py +++ b/cinder/volume/drivers/hpe/hpe_lefthand_iscsi.py @@ -44,6 +44,7 @@ from oslo_utils import units from cinder import context from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder.objects import fields from cinder.volume import driver from cinder.volume.drivers.san import san @@ -120,6 +121,7 @@ extra_specs_value_map = { } +@interface.volumedriver class HPELeftHandISCSIDriver(driver.ISCSIDriver): """Executes REST commands relating to HPE/LeftHand SAN ISCSI volumes. diff --git a/cinder/volume/drivers/hpe/hpe_xp_fc.py b/cinder/volume/drivers/hpe/hpe_xp_fc.py index 1ec37b049ff..d0e76f9f348 100644 --- a/cinder/volume/drivers/hpe/hpe_xp_fc.py +++ b/cinder/volume/drivers/hpe/hpe_xp_fc.py @@ -18,6 +18,7 @@ Fibre channel Cinder volume driver for Hewlett Packard Enterprise storage. from oslo_utils import importutils +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.hpe import hpe_xp_opts as opts from cinder.zonemanager import utils as fczm_utils @@ -26,6 +27,7 @@ _DRIVER_DIR = 'cinder.volume.drivers.hpe' _DRIVER_CLASS = 'hpe_xp_horcm_fc.HPEXPHORCMFC' +@interface.volumedriver class HPEXPFCDriver(driver.FibreChannelDriver): """OpenStack Fibre Channel driver to enable HPE XP storage.""" diff --git a/cinder/volume/drivers/huawei/huawei_driver.py b/cinder/volume/drivers/huawei/huawei_driver.py index 704ec67a41f..fd746473e2c 100644 --- a/cinder/volume/drivers/huawei/huawei_driver.py +++ b/cinder/volume/drivers/huawei/huawei_driver.py @@ -26,6 +26,7 @@ from oslo_utils import units from cinder import context from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder import utils from cinder.volume import driver from cinder.volume.drivers.huawei import constants @@ -1586,6 +1587,7 @@ class HuaweiBaseDriver(driver.VolumeDriver): return secondary_id, volumes_update +@interface.volumedriver class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver): """ISCSI driver for Huawei storage arrays. @@ -1777,6 +1779,7 @@ class HuaweiISCSIDriver(HuaweiBaseDriver, driver.ISCSIDriver): self.client.delete_mapping_view(view_id) +@interface.volumedriver class HuaweiFCDriver(HuaweiBaseDriver, driver.FibreChannelDriver): """FC driver for Huawei OceanStor storage arrays. diff --git a/cinder/volume/drivers/ibm/flashsystem_fc.py b/cinder/volume/drivers/ibm/flashsystem_fc.py index 978ef9bfcb3..ef587c48c80 100644 --- a/cinder/volume/drivers/ibm/flashsystem_fc.py +++ b/cinder/volume/drivers/ibm/flashsystem_fc.py @@ -33,6 +33,7 @@ import six from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder import utils import cinder.volume.driver from cinder.volume.drivers.ibm import flashsystem_common as fscommon @@ -53,6 +54,7 @@ CONF = cfg.CONF CONF.register_opts(flashsystem_fc_opts) +@interface.volumedriver class FlashSystemFCDriver(fscommon.FlashSystemDriver, cinder.volume.driver.FibreChannelDriver): """IBM FlashSystem FC volume driver. diff --git a/cinder/volume/drivers/ibm/flashsystem_iscsi.py b/cinder/volume/drivers/ibm/flashsystem_iscsi.py index cd3636983b6..29aa5030d28 100644 --- a/cinder/volume/drivers/ibm/flashsystem_iscsi.py +++ b/cinder/volume/drivers/ibm/flashsystem_iscsi.py @@ -33,6 +33,7 @@ import six from cinder import exception from cinder.i18n import _, _LE, _LW +from cinder import interface from cinder import utils import cinder.volume.driver from cinder.volume.drivers.ibm import flashsystem_common as fscommon @@ -51,6 +52,7 @@ CONF = cfg.CONF CONF.register_opts(flashsystem_iscsi_opts) +@interface.volumedriver class FlashSystemISCSIDriver(fscommon.FlashSystemDriver, cinder.volume.driver.ISCSIDriver): """IBM FlashSystem iSCSI volume driver. diff --git a/cinder/volume/drivers/ibm/gpfs.py b/cinder/volume/drivers/ibm/gpfs.py index 339156cfcf9..55f1fe7f0ba 100644 --- a/cinder/volume/drivers/ibm/gpfs.py +++ b/cinder/volume/drivers/ibm/gpfs.py @@ -31,6 +31,7 @@ from cinder import context from cinder import exception from cinder.i18n import _, _LE, _LI from cinder.image import image_utils +from cinder import interface from cinder.objects import fields from cinder import utils from cinder.volume import driver @@ -105,6 +106,7 @@ def _sizestr(size_in_g): return '%sG' % size_in_g +@interface.volumedriver class GPFSDriver(driver.ConsistencyGroupVD, driver.ExtendVD, driver.LocalVD, driver.TransferVD, driver.CloneableImageVD, driver.SnapshotVD, @@ -1240,6 +1242,7 @@ class GPFSDriver(driver.ConsistencyGroupVD, driver.ExtendVD, return model_update, snapshots_model_update +@interface.volumedriver class GPFSNFSDriver(GPFSDriver, nfs.NfsDriver, san.SanDriver): """GPFS cinder driver extension. diff --git a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_fc.py b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_fc.py index 2c1993fd45c..f13d831f1e9 100644 --- a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_fc.py +++ b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_fc.py @@ -40,6 +40,7 @@ from oslo_utils import excutils from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder import utils from cinder.volume.drivers.ibm.storwize_svc import ( storwize_svc_common as storwize_common) @@ -58,6 +59,7 @@ CONF = cfg.CONF CONF.register_opts(storwize_svc_fc_opts) +@interface.volumedriver class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver): """IBM Storwize V7000 and SVC FC volume driver. diff --git a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_iscsi.py b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_iscsi.py index 6265997a7c3..5cc7c0691b7 100644 --- a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_iscsi.py +++ b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_iscsi.py @@ -40,6 +40,7 @@ from oslo_utils import excutils from cinder import exception from cinder.i18n import _, _LE, _LW +from cinder import interface from cinder import utils from cinder.volume.drivers.ibm.storwize_svc import ( @@ -58,6 +59,7 @@ CONF = cfg.CONF CONF.register_opts(storwize_svc_iscsi_opts) +@interface.volumedriver class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver): """IBM Storwize V7000 and SVC iSCSI volume driver. diff --git a/cinder/volume/drivers/ibm/xiv_ds8k.py b/cinder/volume/drivers/ibm/xiv_ds8k.py index 4e4a899889b..de34e46572b 100644 --- a/cinder/volume/drivers/ibm/xiv_ds8k.py +++ b/cinder/volume/drivers/ibm/xiv_ds8k.py @@ -27,6 +27,7 @@ from oslo_log import log as logging from oslo_utils import importutils from cinder import exception +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.san import san @@ -58,6 +59,7 @@ CONF.register_opts(xiv_ds8k_opts) LOG = logging.getLogger(__name__) +@interface.volumedriver class XIVDS8KDriver(san.SanDriver, driver.ManageableVD, driver.ExtendVD, diff --git a/cinder/volume/drivers/infortrend/infortrend_fc_cli.py b/cinder/volume/drivers/infortrend/infortrend_fc_cli.py index c6ed860a721..6fda5dfcaa3 100644 --- a/cinder/volume/drivers/infortrend/infortrend_fc_cli.py +++ b/cinder/volume/drivers/infortrend/infortrend_fc_cli.py @@ -19,6 +19,7 @@ Fibre Channel Driver for Infortrend Eonstor based on CLI. from oslo_log import log as logging +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.infortrend.eonstor_ds_cli import common_cli from cinder.zonemanager import utils as fczm_utils @@ -26,6 +27,7 @@ from cinder.zonemanager import utils as fczm_utils LOG = logging.getLogger(__name__) +@interface.volumedriver class InfortrendCLIFCDriver(driver.FibreChannelDriver): """Infortrend Fibre Channel Driver for Eonstor DS using CLI. diff --git a/cinder/volume/drivers/infortrend/infortrend_iscsi_cli.py b/cinder/volume/drivers/infortrend/infortrend_iscsi_cli.py index 6e363b813e7..461776cefe2 100644 --- a/cinder/volume/drivers/infortrend/infortrend_iscsi_cli.py +++ b/cinder/volume/drivers/infortrend/infortrend_iscsi_cli.py @@ -18,12 +18,14 @@ iSCSI Driver for Infortrend Eonstor based on CLI. from oslo_log import log as logging +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.infortrend.eonstor_ds_cli import common_cli LOG = logging.getLogger(__name__) +@interface.volumedriver class InfortrendCLIISCSIDriver(driver.ISCSIDriver): """Infortrend iSCSI Driver for Eonstor DS using CLI. diff --git a/cinder/volume/drivers/lenovo/lenovo_fc.py b/cinder/volume/drivers/lenovo/lenovo_fc.py index 79573eecb0d..ba24af36cd6 100644 --- a/cinder/volume/drivers/lenovo/lenovo_fc.py +++ b/cinder/volume/drivers/lenovo/lenovo_fc.py @@ -14,10 +14,12 @@ # under the License. # +from cinder import interface from cinder.volume.drivers.dothill import dothill_fc from cinder.volume.drivers.lenovo import lenovo_common +@interface.volumedriver class LenovoFCDriver(dothill_fc.DotHillFCDriver): """OpenStack Fibre Channel cinder drivers for Lenovo Storage arrays. diff --git a/cinder/volume/drivers/lenovo/lenovo_iscsi.py b/cinder/volume/drivers/lenovo/lenovo_iscsi.py index e4387b2b4ac..77b7cbec43e 100644 --- a/cinder/volume/drivers/lenovo/lenovo_iscsi.py +++ b/cinder/volume/drivers/lenovo/lenovo_iscsi.py @@ -14,10 +14,12 @@ # under the License. # +from cinder import interface from cinder.volume.drivers.dothill import dothill_iscsi from cinder.volume.drivers.lenovo import lenovo_common +@interface.volumedriver class LenovoISCSIDriver(dothill_iscsi.DotHillISCSIDriver): """OpenStack iSCSI cinder drivers for Lenovo Storage arrays. diff --git a/cinder/volume/drivers/lvm.py b/cinder/volume/drivers/lvm.py index 4926e45bcdb..be7534d8890 100644 --- a/cinder/volume/drivers/lvm.py +++ b/cinder/volume/drivers/lvm.py @@ -31,6 +31,7 @@ from cinder.brick.local_dev import lvm as lvm from cinder import exception from cinder.i18n import _, _LE, _LI, _LW from cinder.image import image_utils +from cinder import interface from cinder import objects from cinder import utils from cinder.volume import driver @@ -75,6 +76,7 @@ CONF = cfg.CONF CONF.register_opts(volume_opts) +@interface.volumedriver class LVMVolumeDriver(driver.VolumeDriver): """Executes commands relating to Volumes.""" diff --git a/cinder/volume/drivers/netapp/dataontap/fc_7mode.py b/cinder/volume/drivers/netapp/dataontap/fc_7mode.py index 1b98faa193a..bd5113c8624 100644 --- a/cinder/volume/drivers/netapp/dataontap/fc_7mode.py +++ b/cinder/volume/drivers/netapp/dataontap/fc_7mode.py @@ -16,11 +16,13 @@ Volume driver for NetApp Data ONTAP (7-mode) FibreChannel storage systems. """ +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.netapp.dataontap import block_7mode from cinder.zonemanager import utils as fczm_utils +@interface.volumedriver class NetApp7modeFibreChannelDriver(driver.BaseVD, driver.ConsistencyGroupVD, driver.ManageableVD, diff --git a/cinder/volume/drivers/netapp/dataontap/fc_cmode.py b/cinder/volume/drivers/netapp/dataontap/fc_cmode.py index f5bdac31391..391f4222339 100644 --- a/cinder/volume/drivers/netapp/dataontap/fc_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/fc_cmode.py @@ -16,11 +16,13 @@ Volume driver for NetApp Data ONTAP (C-mode) FibreChannel storage systems. """ +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.netapp.dataontap import block_cmode from cinder.zonemanager import utils as fczm_utils +@interface.volumedriver class NetAppCmodeFibreChannelDriver(driver.BaseVD, driver.ConsistencyGroupVD, driver.ManageableVD, diff --git a/cinder/volume/drivers/netapp/dataontap/iscsi_7mode.py b/cinder/volume/drivers/netapp/dataontap/iscsi_7mode.py index 06a9be0471c..f523cb5fe1d 100644 --- a/cinder/volume/drivers/netapp/dataontap/iscsi_7mode.py +++ b/cinder/volume/drivers/netapp/dataontap/iscsi_7mode.py @@ -16,10 +16,12 @@ Volume driver for NetApp Data ONTAP (7-mode) iSCSI storage systems. """ +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.netapp.dataontap import block_7mode +@interface.volumedriver class NetApp7modeISCSIDriver(driver.BaseVD, driver.ConsistencyGroupVD, driver.ManageableVD, diff --git a/cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py b/cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py index ce8b286b410..29e8d25d9df 100644 --- a/cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/iscsi_cmode.py @@ -16,10 +16,12 @@ Volume driver for NetApp Data ONTAP (C-mode) iSCSI storage systems. """ +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.netapp.dataontap import block_cmode +@interface.volumedriver class NetAppCmodeISCSIDriver(driver.BaseVD, driver.ConsistencyGroupVD, driver.ManageableVD, diff --git a/cinder/volume/drivers/netapp/dataontap/nfs_7mode.py b/cinder/volume/drivers/netapp/dataontap/nfs_7mode.py index 305fb5dacab..e4bed330213 100644 --- a/cinder/volume/drivers/netapp/dataontap/nfs_7mode.py +++ b/cinder/volume/drivers/netapp/dataontap/nfs_7mode.py @@ -28,6 +28,7 @@ import six from cinder import exception from cinder.i18n import _ +from cinder import interface from cinder import utils from cinder.volume.drivers.netapp.dataontap.client import client_7mode from cinder.volume.drivers.netapp.dataontap import nfs_base @@ -40,6 +41,7 @@ LOG = logging.getLogger(__name__) @six.add_metaclass(utils.TraceWrapperWithABCMetaclass) +@interface.volumedriver class NetApp7modeNfsDriver(nfs_base.NetAppNfsDriver): """NetApp NFS driver for Data ONTAP (7-mode).""" diff --git a/cinder/volume/drivers/netapp/dataontap/nfs_cmode.py b/cinder/volume/drivers/netapp/dataontap/nfs_cmode.py index efd6d3e1479..84ebdd0fd84 100644 --- a/cinder/volume/drivers/netapp/dataontap/nfs_cmode.py +++ b/cinder/volume/drivers/netapp/dataontap/nfs_cmode.py @@ -32,6 +32,7 @@ import six from cinder import exception from cinder.i18n import _, _LE, _LI, _LW from cinder.image import image_utils +from cinder import interface from cinder import utils from cinder.volume.drivers.netapp.dataontap.client import client_cmode from cinder.volume.drivers.netapp.dataontap import nfs_base @@ -47,6 +48,7 @@ LOG = logging.getLogger(__name__) QOS_CLEANUP_INTERVAL_SECONDS = 60 +@interface.volumedriver @six.add_metaclass(utils.TraceWrapperWithABCMetaclass) class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver): """NetApp NFS driver for Data ONTAP (Cluster-mode).""" diff --git a/cinder/volume/drivers/netapp/eseries/fc_driver.py b/cinder/volume/drivers/netapp/eseries/fc_driver.py index 06085f2fca1..e06864b256a 100644 --- a/cinder/volume/drivers/netapp/eseries/fc_driver.py +++ b/cinder/volume/drivers/netapp/eseries/fc_driver.py @@ -16,12 +16,14 @@ Volume driver for NetApp E-Series FibreChannel storage systems. """ +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.netapp.eseries import library from cinder.volume.drivers.netapp import utils as na_utils from cinder.zonemanager import utils as fczm_utils +@interface.volumedriver class NetAppEseriesFibreChannelDriver(driver.BaseVD, driver.ManageableVD, driver.ExtendVD, diff --git a/cinder/volume/drivers/netapp/eseries/iscsi_driver.py b/cinder/volume/drivers/netapp/eseries/iscsi_driver.py index 8b67187eeec..5bee8da68e6 100644 --- a/cinder/volume/drivers/netapp/eseries/iscsi_driver.py +++ b/cinder/volume/drivers/netapp/eseries/iscsi_driver.py @@ -18,11 +18,13 @@ Volume driver for NetApp E-Series iSCSI storage systems. """ +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.netapp.eseries import library from cinder.volume.drivers.netapp import utils as na_utils +@interface.volumedriver class NetAppEseriesISCSIDriver(driver.BaseVD, driver.ManageableVD, driver.ExtendVD, diff --git a/cinder/volume/drivers/nexenta/iscsi.py b/cinder/volume/drivers/nexenta/iscsi.py index 19bcf741ad3..52e5440ea39 100644 --- a/cinder/volume/drivers/nexenta/iscsi.py +++ b/cinder/volume/drivers/nexenta/iscsi.py @@ -20,6 +20,7 @@ from oslo_utils import excutils from cinder import context from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.nexenta import jsonrpc from cinder.volume.drivers.nexenta import options @@ -29,6 +30,7 @@ VERSION = '1.3.0.1' LOG = logging.getLogger(__name__) +@interface.volumedriver class NexentaISCSIDriver(driver.ISCSIDriver): """Executes volume driver commands on Nexenta Appliance. diff --git a/cinder/volume/drivers/nexenta/nexentaedge/iscsi.py b/cinder/volume/drivers/nexenta/nexentaedge/iscsi.py index 26991b8c070..8d20fdef33f 100644 --- a/cinder/volume/drivers/nexenta/nexentaedge/iscsi.py +++ b/cinder/volume/drivers/nexenta/nexentaedge/iscsi.py @@ -21,6 +21,7 @@ from oslo_utils import units from cinder import exception from cinder.i18n import _, _LE, _LI +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.nexenta.nexentaedge import jsonrpc from cinder.volume.drivers.nexenta import options @@ -29,6 +30,7 @@ from cinder.volume.drivers.nexenta import options LOG = logging.getLogger(__name__) +@interface.volumedriver class NexentaEdgeISCSIDriver(driver.ISCSIDriver): """Executes volume driver commands on NexentaEdge cluster. diff --git a/cinder/volume/drivers/nexenta/nfs.py b/cinder/volume/drivers/nexenta/nfs.py index c9e56212bfc..71cfbb46fdb 100644 --- a/cinder/volume/drivers/nexenta/nfs.py +++ b/cinder/volume/drivers/nexenta/nfs.py @@ -26,6 +26,7 @@ from cinder import context from cinder import db from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder.volume.drivers.nexenta import jsonrpc from cinder.volume.drivers.nexenta import options from cinder.volume.drivers.nexenta import utils @@ -35,6 +36,7 @@ VERSION = '1.3.0' LOG = logging.getLogger(__name__) +@interface.volumedriver class NexentaNfsDriver(nfs.NfsDriver): # pylint: disable=R0921 """Executes volume driver commands on Nexenta Appliance. diff --git a/cinder/volume/drivers/nexenta/ns5/iscsi.py b/cinder/volume/drivers/nexenta/ns5/iscsi.py index e9ddb4b37a0..bf26ef4c6cf 100644 --- a/cinder/volume/drivers/nexenta/ns5/iscsi.py +++ b/cinder/volume/drivers/nexenta/ns5/iscsi.py @@ -20,6 +20,7 @@ from cinder import context from cinder import db from cinder import exception from cinder.i18n import _, _LI, _LE, _LW +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.nexenta.ns5 import jsonrpc from cinder.volume.drivers.nexenta import options @@ -29,6 +30,7 @@ VERSION = '1.0.0' LOG = logging.getLogger(__name__) +@interface.volumedriver class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 """Executes volume driver commands on Nexenta Appliance. diff --git a/cinder/volume/drivers/nexenta/ns5/nfs.py b/cinder/volume/drivers/nexenta/ns5/nfs.py index 12f1c13922d..5db46a250ef 100644 --- a/cinder/volume/drivers/nexenta/ns5/nfs.py +++ b/cinder/volume/drivers/nexenta/ns5/nfs.py @@ -22,6 +22,7 @@ from cinder import context from cinder import db from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder.volume.drivers.nexenta.ns5 import jsonrpc from cinder.volume.drivers.nexenta import options from cinder.volume.drivers.nexenta import utils @@ -31,6 +32,7 @@ VERSION = '1.0.0' LOG = logging.getLogger(__name__) +@interface.volumedriver class NexentaNfsDriver(nfs.NfsDriver): # pylint: disable=R0921 """Executes volume driver commands on Nexenta Appliance. diff --git a/cinder/volume/drivers/nfs.py b/cinder/volume/drivers/nfs.py index c1ac655e151..70b598f67a0 100644 --- a/cinder/volume/drivers/nfs.py +++ b/cinder/volume/drivers/nfs.py @@ -27,6 +27,7 @@ import six from cinder import exception from cinder.i18n import _, _LE, _LI, _LW from cinder.image import image_utils +from cinder import interface from cinder import utils from cinder.volume import driver from cinder.volume.drivers import remotefs @@ -63,6 +64,7 @@ CONF = cfg.CONF CONF.register_opts(nfs_opts) +@interface.volumedriver class NfsDriver(driver.ExtendVD, remotefs.RemoteFSDriver): """NFS based cinder driver. diff --git a/cinder/volume/drivers/nimble.py b/cinder/volume/drivers/nimble.py index 285908fc7f9..fb3d3592116 100644 --- a/cinder/volume/drivers/nimble.py +++ b/cinder/volume/drivers/nimble.py @@ -33,6 +33,7 @@ from suds import client from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder.objects import volume from cinder.volume.drivers.san import san from cinder.volume import volume_types @@ -84,6 +85,7 @@ class NimbleAPIException(exception.VolumeBackendAPIException): message = _("Unexpected response from Nimble API") +@interface.volumedriver class NimbleISCSIDriver(san.SanISCSIDriver): """OpenStack driver to enable Nimble Controller. diff --git a/cinder/volume/drivers/prophetstor/dpl_fc.py b/cinder/volume/drivers/prophetstor/dpl_fc.py index d673d8b1007..b9776585cd9 100644 --- a/cinder/volume/drivers/prophetstor/dpl_fc.py +++ b/cinder/volume/drivers/prophetstor/dpl_fc.py @@ -19,6 +19,7 @@ from oslo_log import log as logging from cinder import exception from cinder.i18n import _, _LE, _LI +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.prophetstor import dplcommon from cinder.zonemanager import utils as fczm_utils @@ -26,6 +27,7 @@ from cinder.zonemanager import utils as fczm_utils LOG = logging.getLogger(__name__) +@interface.volumedriver class DPLFCDriver(dplcommon.DPLCOMMONDriver, driver.FibreChannelDriver): def __init__(self, *args, **kwargs): diff --git a/cinder/volume/drivers/prophetstor/dpl_iscsi.py b/cinder/volume/drivers/prophetstor/dpl_iscsi.py index 176569d95d8..a726f3b581d 100644 --- a/cinder/volume/drivers/prophetstor/dpl_iscsi.py +++ b/cinder/volume/drivers/prophetstor/dpl_iscsi.py @@ -19,12 +19,14 @@ from oslo_log import log as logging from cinder import exception from cinder.i18n import _, _LI, _LW +from cinder import interface import cinder.volume.driver from cinder.volume.drivers.prophetstor import dplcommon LOG = logging.getLogger(__name__) +@interface.volumedriver class DPLISCSIDriver(dplcommon.DPLCOMMONDriver, cinder.volume.driver.ISCSIDriver): def __init__(self, *args, **kwargs): diff --git a/cinder/volume/drivers/pure.py b/cinder/volume/drivers/pure.py index 7ecccd8f895..b6c27f3df9e 100644 --- a/cinder/volume/drivers/pure.py +++ b/cinder/volume/drivers/pure.py @@ -18,6 +18,7 @@ Volume driver for Pure Storage FlashArray storage system. This driver requires Purity version 4.0.0 or later. """ +import functools import math import platform import re @@ -32,6 +33,7 @@ import six from cinder import context from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder.objects import fields from cinder import utils from cinder.volume import driver @@ -119,6 +121,7 @@ def pure_driver_debug_trace(f): This should only be used on VolumeDriver class methods. It depends on having a 'self' argument that is a PureBaseVolumeDriver. """ + @functools.wraps(f) def wrapper(*args, **kwargs): driver = args[0] # self cls_name = driver.__class__.__name__ @@ -1470,6 +1473,7 @@ class PureBaseVolumeDriver(san.SanDriver): self._array = array +@interface.volumedriver class PureISCSIDriver(PureBaseVolumeDriver, san.SanISCSIDriver): VERSION = "4.0.0" @@ -1632,6 +1636,7 @@ class PureISCSIDriver(PureBaseVolumeDriver, san.SanISCSIDriver): return connection +@interface.volumedriver class PureFCDriver(PureBaseVolumeDriver, driver.FibreChannelDriver): VERSION = "2.0.0" diff --git a/cinder/volume/drivers/quobyte.py b/cinder/volume/drivers/quobyte.py index d6d8c825efd..3be0de22329 100644 --- a/cinder/volume/drivers/quobyte.py +++ b/cinder/volume/drivers/quobyte.py @@ -26,6 +26,7 @@ from cinder import compute from cinder import exception from cinder.i18n import _, _LI, _LW from cinder.image import image_utils +from cinder import interface from cinder import utils from cinder.volume.drivers import remotefs as remotefs_drv @@ -57,6 +58,7 @@ CONF = cfg.CONF CONF.register_opts(volume_opts) +@interface.volumedriver class QuobyteDriver(remotefs_drv.RemoteFSSnapDriver): """Cinder driver for Quobyte USP. diff --git a/cinder/volume/drivers/rbd.py b/cinder/volume/drivers/rbd.py index 75ef6471408..6f26659a73f 100644 --- a/cinder/volume/drivers/rbd.py +++ b/cinder/volume/drivers/rbd.py @@ -30,6 +30,7 @@ from six.moves import urllib from cinder import exception from cinder.i18n import _, _LE, _LI, _LW from cinder.image import image_utils +from cinder import interface from cinder import utils from cinder.volume import driver @@ -261,6 +262,7 @@ class RADOSClient(object): return int(features) +@interface.volumedriver class RBDDriver(driver.TransferVD, driver.ExtendVD, driver.CloneableImageVD, driver.SnapshotVD, driver.MigrateVD, driver.ManageableVD, driver.BaseVD): diff --git a/cinder/volume/drivers/san/hp/hpmsa_fc.py b/cinder/volume/drivers/san/hp/hpmsa_fc.py index 2c1da28694f..efa9e83961d 100644 --- a/cinder/volume/drivers/san/hp/hpmsa_fc.py +++ b/cinder/volume/drivers/san/hp/hpmsa_fc.py @@ -14,10 +14,12 @@ # under the License. # +from cinder import interface from cinder.volume.drivers.dothill import dothill_fc from cinder.volume.drivers.san.hp import hpmsa_common +@interface.volumedriver class HPMSAFCDriver(dothill_fc.DotHillFCDriver): """OpenStack Fibre Channel cinder drivers for HPMSA arrays. diff --git a/cinder/volume/drivers/san/hp/hpmsa_iscsi.py b/cinder/volume/drivers/san/hp/hpmsa_iscsi.py index 258b05083ed..d24dd3ad311 100644 --- a/cinder/volume/drivers/san/hp/hpmsa_iscsi.py +++ b/cinder/volume/drivers/san/hp/hpmsa_iscsi.py @@ -14,10 +14,12 @@ # under the License. # +from cinder import interface from cinder.volume.drivers.dothill import dothill_iscsi from cinder.volume.drivers.san.hp import hpmsa_common +@interface.volumedriver class HPMSAISCSIDriver(dothill_iscsi.DotHillISCSIDriver): """OpenStack iSCSI cinder drivers for HPMSA arrays. diff --git a/cinder/volume/drivers/scality.py b/cinder/volume/drivers/scality.py index 64e4c93483f..2ea23956765 100644 --- a/cinder/volume/drivers/scality.py +++ b/cinder/volume/drivers/scality.py @@ -31,6 +31,7 @@ from six.moves import urllib from cinder import exception from cinder.i18n import _, _LI from cinder.image import image_utils +from cinder import interface from cinder import utils from cinder.volume.drivers import remotefs as remotefs_drv from cinder.volume import utils as volume_utils @@ -53,6 +54,7 @@ CONF = cfg.CONF CONF.register_opts(volume_opts) +@interface.volumedriver class ScalityDriver(remotefs_drv.RemoteFSSnapDriver): """Scality SOFS cinder driver. diff --git a/cinder/volume/drivers/sheepdog.py b/cinder/volume/drivers/sheepdog.py index 162fb1850ff..327269d961f 100644 --- a/cinder/volume/drivers/sheepdog.py +++ b/cinder/volume/drivers/sheepdog.py @@ -34,6 +34,7 @@ from oslo_utils import units from cinder import exception from cinder.i18n import _, _LE, _LW from cinder.image import image_utils +from cinder import interface from cinder import utils from cinder.volume import driver @@ -423,6 +424,7 @@ class SheepdogIOWrapper(io.RawIOBase): raise IOError(_("fileno is not supported by SheepdogIOWrapper")) +@interface.volumedriver class SheepdogDriver(driver.VolumeDriver): """Executes commands relating to Sheepdog Volumes.""" diff --git a/cinder/volume/drivers/smbfs.py b/cinder/volume/drivers/smbfs.py index 29bc6ca2e1d..e5c06bd2ddb 100644 --- a/cinder/volume/drivers/smbfs.py +++ b/cinder/volume/drivers/smbfs.py @@ -29,6 +29,7 @@ from oslo_utils import units from cinder import exception from cinder.i18n import _, _LI, _LW from cinder.image import image_utils +from cinder import interface from cinder import utils from cinder.volume.drivers import remotefs as remotefs_drv @@ -97,6 +98,7 @@ def update_allocation_data(delete=False): return wrapper +@interface.volumedriver class SmbfsDriver(remotefs_drv.RemoteFSSnapDriver): """SMBFS based cinder volume driver.""" diff --git a/cinder/volume/drivers/solidfire.py b/cinder/volume/drivers/solidfire.py index e8d45061dbd..0bf93da8a1b 100644 --- a/cinder/volume/drivers/solidfire.py +++ b/cinder/volume/drivers/solidfire.py @@ -35,6 +35,7 @@ from cinder import context from cinder import exception from cinder.i18n import _, _LE, _LW from cinder.image import image_utils +from cinder import interface from cinder.objects import fields from cinder.volume.drivers.san import san from cinder.volume import qos_specs @@ -133,6 +134,7 @@ def retry(exc_tuple, tries=5, delay=1, backoff=2): return retry_dec +@interface.volumedriver class SolidFireDriver(san.SanISCSIDriver): """OpenStack driver to enable SolidFire cluster. diff --git a/cinder/volume/drivers/tegile.py b/cinder/volume/drivers/tegile.py index 7dd928e0a7f..5b84fc79ecf 100644 --- a/cinder/volume/drivers/tegile.py +++ b/cinder/volume/drivers/tegile.py @@ -28,6 +28,7 @@ import six from cinder import exception from cinder import utils from cinder.i18n import _, _LI, _LW +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.san import san from cinder.volume import utils as volume_utils @@ -472,6 +473,7 @@ class TegileIntelliFlashVolumeDriver(san.SanDriver): 'set.') % {'attr': attr}) +@interface.volumedriver class TegileISCSIDriver(TegileIntelliFlashVolumeDriver, san.SanISCSIDriver): """Tegile ISCSI Driver.""" @@ -584,6 +586,7 @@ class TegileISCSIDriver(TegileIntelliFlashVolumeDriver, san.SanISCSIDriver): 'provider_auth': provider_auth}) +@interface.volumedriver class TegileFCDriver(TegileIntelliFlashVolumeDriver, driver.FibreChannelDriver): """Tegile FC driver.""" diff --git a/cinder/volume/drivers/tintri.py b/cinder/volume/drivers/tintri.py index 1ecf02ec8ec..1dd492c562d 100644 --- a/cinder/volume/drivers/tintri.py +++ b/cinder/volume/drivers/tintri.py @@ -34,6 +34,7 @@ from cinder import exception from cinder import utils from cinder.i18n import _, _LE, _LI, _LW from cinder.image import image_utils +from cinder import interface from cinder.volume import driver from cinder.volume.drivers import nfs @@ -65,6 +66,7 @@ CONF = cfg.CONF CONF.register_opts(tintri_opts) +@interface.volumedriver class TintriDriver(driver.ManageableVD, driver.CloneableImageVD, driver.SnapshotVD, diff --git a/cinder/volume/drivers/violin/v7000_fcp.py b/cinder/volume/drivers/violin/v7000_fcp.py index 9f12c10d83f..026bb331f8a 100644 --- a/cinder/volume/drivers/violin/v7000_fcp.py +++ b/cinder/volume/drivers/violin/v7000_fcp.py @@ -39,6 +39,7 @@ from oslo_log import log as logging from cinder import exception from cinder.i18n import _, _LE, _LI +from cinder import interface from cinder import utils from cinder.volume import driver from cinder.volume.drivers.san import san @@ -50,6 +51,7 @@ import socket LOG = logging.getLogger(__name__) +@interface.volumedriver class V7000FCPDriver(driver.FibreChannelDriver): """Executes commands relating to fibre channel based Violin Memory arrays. diff --git a/cinder/volume/drivers/vmware/vmdk.py b/cinder/volume/drivers/vmware/vmdk.py index 3a8f7328fa3..f04e6cfa844 100644 --- a/cinder/volume/drivers/vmware/vmdk.py +++ b/cinder/volume/drivers/vmware/vmdk.py @@ -43,6 +43,7 @@ import six from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.vmware import datastore as hub from cinder.volume.drivers.vmware import exceptions as vmdk_exceptions @@ -207,6 +208,7 @@ class ImageDiskType(object): extra_spec_disk_type) +@interface.volumedriver class VMwareVcVmdkDriver(driver.VolumeDriver): """Manage volumes on VMware vCenter server.""" diff --git a/cinder/volume/drivers/vzstorage.py b/cinder/volume/drivers/vzstorage.py index aaa304e0fed..71af06d9985 100644 --- a/cinder/volume/drivers/vzstorage.py +++ b/cinder/volume/drivers/vzstorage.py @@ -27,6 +27,7 @@ from oslo_utils import units from cinder import exception from cinder.i18n import _, _LI from cinder.image import image_utils +from cinder import interface from cinder import utils from cinder.volume.drivers import remotefs as remotefs_drv @@ -62,6 +63,7 @@ CONF = cfg.CONF CONF.register_opts(vzstorage_opts) +@interface.volumedriver class VZStorageDriver(remotefs_drv.RemoteFSSnapDriver): """Cinder driver for Virtuozzo Storage. diff --git a/cinder/volume/drivers/windows/smbfs.py b/cinder/volume/drivers/windows/smbfs.py index f979792156e..a2edc9f8665 100644 --- a/cinder/volume/drivers/windows/smbfs.py +++ b/cinder/volume/drivers/windows/smbfs.py @@ -26,6 +26,7 @@ from oslo_utils import units from cinder import exception from cinder.i18n import _, _LI from cinder.image import image_utils +from cinder import interface from cinder.volume.drivers import remotefs as remotefs_drv from cinder.volume.drivers import smbfs from cinder.volume.drivers.windows import remotefs @@ -42,6 +43,7 @@ CONF.set_default('smbfs_mount_point_base', r'C:\OpenStack\_mnt') CONF.set_default('smbfs_default_volume_format', 'vhd') +@interface.volumedriver class WindowsSmbfsDriver(smbfs.SmbfsDriver): VERSION = VERSION _MINIMUM_QEMU_IMG_VERSION = '1.6' diff --git a/cinder/volume/drivers/xio.py b/cinder/volume/drivers/xio.py index 95ce3a1a2a0..1e69a91acab 100644 --- a/cinder/volume/drivers/xio.py +++ b/cinder/volume/drivers/xio.py @@ -23,6 +23,7 @@ from six.moves import urllib from cinder import context from cinder import exception from cinder.i18n import _LE, _LI, _LW +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.san import san from cinder.volume import qos_specs @@ -1380,6 +1381,7 @@ class XIOISEDriver(object): # Protocol specific classes for entry. They are wrappers around base class # above and every external API resuslts in a call to common function in base # class. +@interface.volumedriver class XIOISEISCSIDriver(driver.ISCSIDriver): """Requires ISE Running FW version 3.1.0 or higher""" @@ -1506,6 +1508,7 @@ class XIOISEISCSIDriver(driver.ISCSIDriver): return self.driver.remove_export(context, volume) +@interface.volumedriver class XIOISEFCDriver(driver.FibreChannelDriver): """Requires ISE Running FW version 2.8.0 or higher""" diff --git a/cinder/volume/drivers/zfssa/zfssaiscsi.py b/cinder/volume/drivers/zfssa/zfssaiscsi.py index 60753180f12..414916f513f 100644 --- a/cinder/volume/drivers/zfssa/zfssaiscsi.py +++ b/cinder/volume/drivers/zfssa/zfssaiscsi.py @@ -28,6 +28,7 @@ from cinder import exception from cinder import utils from cinder.i18n import _, _LE, _LI, _LW from cinder.image import image_utils +from cinder import interface from cinder.volume import driver from cinder.volume.drivers.san import san from cinder.volume.drivers.zfssa import zfssarest @@ -105,6 +106,7 @@ def factory_zfssa(): return zfssarest.ZFSSAApi() +@interface.volumedriver class ZFSSAISCSIDriver(driver.ISCSIDriver): """ZFSSA Cinder iSCSI volume driver. @@ -224,10 +226,10 @@ class ZFSSAISCSIDriver(driver.ISCSIDriver): # Parse interfaces interfaces = [] - for interface in lcfg.zfssa_target_interfaces.split(','): - if interface == '': + for intrface in lcfg.zfssa_target_interfaces.split(','): + if intrface == '': continue - interfaces.append(interface) + interfaces.append(intrface) # Setup target and target group iqn = self.zfssa.create_target( diff --git a/cinder/volume/drivers/zfssa/zfssanfs.py b/cinder/volume/drivers/zfssa/zfssanfs.py index 847629d5670..66f6781b1fd 100644 --- a/cinder/volume/drivers/zfssa/zfssanfs.py +++ b/cinder/volume/drivers/zfssa/zfssanfs.py @@ -30,6 +30,7 @@ from cinder import exception from cinder import utils from cinder.i18n import _, _LE, _LI from cinder.image import image_utils +from cinder import interface from cinder.volume.drivers import nfs from cinder.volume.drivers.san import san from cinder.volume.drivers.zfssa import zfssarest @@ -76,6 +77,7 @@ def factory_zfssa(): return zfssarest.ZFSSANfsApi() +@interface.volumedriver class ZFSSANFSDriver(nfs.NfsDriver): """ZFSSA Cinder NFS volume driver. diff --git a/cinder/zonemanager/drivers/brocade/brcd_fc_zone_driver.py b/cinder/zonemanager/drivers/brocade/brcd_fc_zone_driver.py index d42990200e1..64fe378fea5 100644 --- a/cinder/zonemanager/drivers/brocade/brcd_fc_zone_driver.py +++ b/cinder/zonemanager/drivers/brocade/brcd_fc_zone_driver.py @@ -38,6 +38,7 @@ import string from cinder import exception from cinder.i18n import _, _LE, _LI, _LW +from cinder import interface from cinder.zonemanager.drivers.brocade import brcd_fabric_opts as fabric_opts from cinder.zonemanager.drivers.brocade import fc_zone_constants from cinder.zonemanager.drivers import driver_utils @@ -57,6 +58,7 @@ CONF = cfg.CONF CONF.register_opts(brcd_opts, group='fc-zone-manager') +@interface.fczmdriver class BrcdFCZoneDriver(fc_zone_driver.FCZoneDriver): """Brocade FC zone driver implementation. diff --git a/cinder/zonemanager/drivers/cisco/cisco_fc_zone_driver.py b/cinder/zonemanager/drivers/cisco/cisco_fc_zone_driver.py index 597d66b2b3e..289fd7f0e73 100644 --- a/cinder/zonemanager/drivers/cisco/cisco_fc_zone_driver.py +++ b/cinder/zonemanager/drivers/cisco/cisco_fc_zone_driver.py @@ -37,6 +37,7 @@ import string from cinder import exception from cinder.i18n import _, _LE, _LI +from cinder import interface from cinder.zonemanager.drivers.cisco import cisco_fabric_opts as fabric_opts from cinder.zonemanager.drivers import driver_utils from cinder.zonemanager.drivers import fc_zone_driver @@ -56,6 +57,7 @@ CONF = cfg.CONF CONF.register_opts(cisco_opts, group='fc-zone-manager') +@interface.fczmdriver class CiscoFCZoneDriver(fc_zone_driver.FCZoneDriver): """Cisco FC zone driver implementation. diff --git a/cinder/zonemanager/drivers/fc_zone_driver.py b/cinder/zonemanager/drivers/fc_zone_driver.py index 236e7698eef..9417c965b80 100644 --- a/cinder/zonemanager/drivers/fc_zone_driver.py +++ b/cinder/zonemanager/drivers/fc_zone_driver.py @@ -31,76 +31,16 @@ interfaces. from oslo_log import log as logging +from cinder.interface import fczm_driver from cinder.zonemanager import fc_common LOG = logging.getLogger(__name__) -class FCZoneDriver(fc_common.FCCommon): +class FCZoneDriver( + fc_common.FCCommon, fczm_driver.FibreChannelZoneManagerDriver): """Interface to manage Connection control during attach/detach.""" def __init__(self, **kwargs): super(FCZoneDriver, self).__init__(**kwargs) LOG.debug("Initializing FCZoneDriver") - - def add_connection(self, fabric, initiator_target_map, host_name=None, - storage_system=None): - """Add connection control. - - Abstract method to add connection control. - All implementing drivers should provide concrete implementation - for this API. - - :param fabric: Fabric name from cinder.conf file - :param initiator_target_map: Mapping of initiator to list of targets - - .. code-block:: python - - Example initiator_target_map: - - { - '10008c7cff523b01': ['20240002ac000a50', '20240002ac000a40'] - } - - Note that WWPN can be in lower or upper case and can be ':' - separated strings - """ - raise NotImplementedError() - - def delete_connection(self, fabric, initiator_target_map, host_name=None, - storage_system=None): - """Delete connection control. - - Abstract method to remove connection control. - All implementing drivers should provide concrete implementation - for this API. - - :param fabric: Fabric name from cinder.conf file - :param initiator_target_map: Mapping of initiator to list of targets - - .. code-block:: python - - Example initiator_target_map: - - { - '10008c7cff523b01': ['20240002ac000a50', '20240002ac000a40'] - } - - Note that WWPN can be in lower or upper case and can be ':' - separated strings - """ - raise NotImplementedError() - - def get_san_context(self, target_wwn_list): - """Get SAN context for end devices. - - Abstract method to get SAN contexts for given list of end devices - All implementing drivers should provide concrete implementation - for this API. - :param fabric: Fabric name from cinder.conf file - :param initiator_target_map: Mapping of initiator to list of targets - Example initiator_target_map: ['20240002ac000a50', '20240002ac000a40'] - Note that WWPN can be in lower or upper case and can be - ':' separated strings - """ - raise NotImplementedError() diff --git a/doc/source/devref/drivers.rst b/doc/source/devref/drivers.rst index 8eeca2e095c..ddd9f08ce23 100644 --- a/doc/source/devref/drivers.rst +++ b/doc/source/devref/drivers.rst @@ -28,8 +28,8 @@ Minimum features are enforced to avoid having a grid of what features are supported by which drivers and which releases. Cinder Core requires that all drivers implement the following minimum features. -Havana ------- +Core Functionality +------------------ * Volume Create/Delete * Volume Attach/Detach @@ -39,11 +39,6 @@ Havana * Copy Image to Volume * Copy Volume to Image * Clone Volume - -Icehouse --------- - -* All of the above plus * Extend Volume Volume Stats @@ -65,3 +60,25 @@ provided by a driver. total_capacity_gb, keywords can be provided instead. Please use 'unknown' if the array cannot report the value or 'infinite' if the array has no upper limit. + +Feature Enforcement +------------------- + +All concrete driver implementations should use the +``cinder.interface.volumedriver`` decorator on the driver class:: + + @interface.volumedriver + class LVMVolumeDriver(driver.VolumeDriver): + +This will register the driver and allow automated compliance tests to run +against and verify the compliance of the driver against the required interface +to support the `Core Functionality`_ listed above. + +Running ``tox -e compliance`` will verify all registered drivers comply to +this interface. This can be used during development to perform self checks +along the way. Any missing method calls will be identified by the compliance +tests. + +The details for the required volume driver interfaces can be found in the +``cinder/interface/volume_*_driver.py`` source. + diff --git a/tools/generate_driver_list.py b/tools/generate_driver_list.py index 72d4d5af0ec..d7531b46f8a 100755 --- a/tools/generate_driver_list.py +++ b/tools/generate_driver_list.py @@ -14,38 +14,42 @@ """Generate list of cinder drivers""" -import importlib -import inspect -import pkgutil -import pprint - -from cinder.volume import drivers -from cinder.volume import driver - -package = drivers +from cinder.interface import util -def get_driver_list(): - dr_list = [] - for _loader, modname, _ispkg in pkgutil.walk_packages( - path=package.__path__, - prefix=package.__name__ + '.', - onerror=lambda x: None): - try: - mod = importlib.import_module(modname) - list_classes = inspect.getmembers(mod, inspect.isclass) - dr_list += [ - modname + '.' + dr_name for dr_name, dr in list_classes - if driver.BaseVD in inspect.getmro(dr)] - except ImportError: - print("%s module ignored!!" % modname) - return dr_list +def format_description(desc): + desc = desc or '' + lines = desc.rstrip('\n').split('\n') + for line in lines: + print(' %s' % line) + + +def print_drivers(drivers, config_name): + # for driver in drivers.sort(key=lambda x: x.class_fqn): + for driver in sorted(drivers, key=lambda x: x.class_fqn): + print(driver.class_name) + print('-' * len(driver.class_name)) + if driver.version: + print('* Version: %s' % driver.version) + print('* %s=%s' % (config_name, driver.class_fqn)) + print('* Description:') + format_description(driver.desc) + print('') + print('') def main(): - dr_list = get_driver_list() - print("Drivers list:") - pprint.pprint(dr_list) + print('VOLUME DRIVERS') + print('==============') + print_drivers(util.get_volume_drivers(), 'volume_driver') + + print('BACKUP DRIVERS') + print('==============') + print_drivers(util.get_backup_drivers(), 'backup_driver') + + print('FC ZONE MANAGER DRIVERS') + print('=======================') + print_drivers(util.get_fczm_drivers(), 'zone_driver') if __name__ == '__main__': diff --git a/tox.ini b/tox.ini index d0840097ec5..d3a8c5103a9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] minversion = 1.8 skipsdist = True -envlist = py34,py27,pep8 +envlist = py34,py27,compliance,pep8 [testenv] # Note the hash seed is set to 0 until cinder can be tested with a @@ -47,6 +47,11 @@ commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasen setenv = OS_TEST_PATH = ./cinder/tests/functional +[testenv:compliance] +envdir = {toxworkdir}/pep8 +setenv = + OS_TEST_PATH = ./cinder/tests/compliance + [testenv:pep8] commands = flake8 {posargs} .