From e7b40242f8aa3a99aa2d6bb5f0a80a2d8bd19aea Mon Sep 17 00:00:00 2001 From: Sean McGinnis Date: Wed, 23 Mar 2016 16:39:07 -0500 Subject: [PATCH] Add driver interface checks This is the start of an effort to both validate that drivers fully implement the expected minimum requirements as well as to create a clear place for driver developers to learn what needs to be implemented and get documentation explaining what is expected for each method. This also enables us to create tooling for documenting the available drivers and their capabilities, to some degree. A follow up patch will show some of what I'm thinking there, but it will make it possible to write scripts for different needs. This is somewhat a cleanup attempt to the ABC work that was started a while back. This does not aim to replace that effort, but give a mechanism for some of the things expected out of that effort that ended up not being possible with how it evolved. In most cases we do not really care if a driver is inherited from a certain base class, just that it conforms to the given interface. The interface/inheritance work really centers around two separate things: * Ensuring drivers conform to an expected interface * Allowing code reuse and common implementation This is really for the first item. Additional work is needed to complete the ABC work we've done, but that really focuses on the second item, and is out of scope for the intent of this patch. Change-Id: I4168225126fe88c31712d94f0a130e9e7ede3446 --- cinder/backup/drivers/ceph.py | 2 + cinder/backup/drivers/glusterfs.py | 2 + cinder/backup/drivers/google.py | 2 + cinder/backup/drivers/nfs.py | 2 + cinder/backup/drivers/posix.py | 2 + cinder/backup/drivers/swift.py | 2 + cinder/backup/drivers/tsm.py | 2 + cinder/interface/__init__.py | 37 +++ cinder/interface/backup_chunked_driver.py | 79 ++++++ cinder/interface/backup_driver.py | 111 ++++++++ cinder/interface/backup_verify_driver.py | 38 +++ cinder/interface/base.py | 105 ++++++++ cinder/interface/fczm_driver.py | 78 ++++++ cinder/interface/util.py | 78 ++++++ .../volume_consistencygroup_driver.py | 231 ++++++++++++++++ cinder/interface/volume_driver.py | 247 ++++++++++++++++++ cinder/interface/volume_management_driver.py | 81 ++++++ cinder/interface/volume_snapshot_driver.py | 57 ++++ .../volume_snapshotmanagement_driver.py | 73 ++++++ cinder/tests/compliance/__init__.py | 0 .../tests/compliance/test_backup_drivers.py | 45 ++++ cinder/tests/compliance/test_fczm_drivers.py | 45 ++++ .../tests/compliance/test_volume_drivers.py | 44 ++++ cinder/volume/drivers/block_device.py | 2 + cinder/volume/drivers/blockbridge.py | 2 + cinder/volume/drivers/cloudbyte/cloudbyte.py | 2 + cinder/volume/drivers/coho.py | 2 + cinder/volume/drivers/datera.py | 2 + .../drivers/dell/dell_storagecenter_fc.py | 2 + .../drivers/dell/dell_storagecenter_iscsi.py | 2 + cinder/volume/drivers/disco/disco.py | 2 + cinder/volume/drivers/dothill/dothill_fc.py | 2 + .../volume/drivers/dothill/dothill_iscsi.py | 2 + cinder/volume/drivers/drbdmanagedrv.py | 4 +- cinder/volume/drivers/emc/emc_cli_fc.py | 2 + cinder/volume/drivers/emc/emc_cli_iscsi.py | 2 + cinder/volume/drivers/emc/emc_vmax_fc.py | 2 + cinder/volume/drivers/emc/emc_vmax_iscsi.py | 2 + cinder/volume/drivers/emc/scaleio.py | 2 + cinder/volume/drivers/emc/xtremio.py | 3 + cinder/volume/drivers/eqlx.py | 2 + .../volume/drivers/fujitsu/eternus_dx_fc.py | 2 + .../drivers/fujitsu/eternus_dx_iscsi.py | 2 + cinder/volume/drivers/glusterfs.py | 2 + cinder/volume/drivers/hgst.py | 2 + cinder/volume/drivers/hitachi/hbsd_fc.py | 2 + cinder/volume/drivers/hitachi/hbsd_iscsi.py | 2 + cinder/volume/drivers/hitachi/hnas_iscsi.py | 2 + cinder/volume/drivers/hitachi/hnas_nfs.py | 2 + cinder/volume/drivers/hpe/hpe_3par_fc.py | 2 + cinder/volume/drivers/hpe/hpe_3par_iscsi.py | 2 + .../volume/drivers/hpe/hpe_lefthand_iscsi.py | 2 + cinder/volume/drivers/hpe/hpe_xp_fc.py | 2 + cinder/volume/drivers/huawei/huawei_driver.py | 3 + cinder/volume/drivers/ibm/flashsystem_fc.py | 2 + .../volume/drivers/ibm/flashsystem_iscsi.py | 2 + cinder/volume/drivers/ibm/gpfs.py | 3 + .../ibm/storwize_svc/storwize_svc_fc.py | 2 + .../ibm/storwize_svc/storwize_svc_iscsi.py | 2 + cinder/volume/drivers/ibm/xiv_ds8k.py | 2 + .../drivers/infortrend/infortrend_fc_cli.py | 2 + .../infortrend/infortrend_iscsi_cli.py | 2 + cinder/volume/drivers/lenovo/lenovo_fc.py | 2 + cinder/volume/drivers/lenovo/lenovo_iscsi.py | 2 + cinder/volume/drivers/lvm.py | 2 + .../drivers/netapp/dataontap/fc_7mode.py | 2 + .../drivers/netapp/dataontap/fc_cmode.py | 2 + .../drivers/netapp/dataontap/iscsi_7mode.py | 2 + .../drivers/netapp/dataontap/iscsi_cmode.py | 2 + .../drivers/netapp/dataontap/nfs_7mode.py | 2 + .../drivers/netapp/dataontap/nfs_cmode.py | 2 + .../drivers/netapp/eseries/fc_driver.py | 2 + .../drivers/netapp/eseries/iscsi_driver.py | 2 + cinder/volume/drivers/nexenta/iscsi.py | 2 + .../drivers/nexenta/nexentaedge/iscsi.py | 2 + cinder/volume/drivers/nexenta/nfs.py | 2 + cinder/volume/drivers/nexenta/ns5/iscsi.py | 2 + cinder/volume/drivers/nexenta/ns5/nfs.py | 2 + cinder/volume/drivers/nfs.py | 2 + cinder/volume/drivers/nimble.py | 2 + cinder/volume/drivers/prophetstor/dpl_fc.py | 2 + .../volume/drivers/prophetstor/dpl_iscsi.py | 2 + cinder/volume/drivers/pure.py | 5 + cinder/volume/drivers/quobyte.py | 2 + cinder/volume/drivers/rbd.py | 2 + cinder/volume/drivers/san/hp/hpmsa_fc.py | 2 + cinder/volume/drivers/san/hp/hpmsa_iscsi.py | 2 + cinder/volume/drivers/scality.py | 2 + cinder/volume/drivers/sheepdog.py | 2 + cinder/volume/drivers/smbfs.py | 2 + cinder/volume/drivers/solidfire.py | 2 + cinder/volume/drivers/tegile.py | 3 + cinder/volume/drivers/tintri.py | 2 + cinder/volume/drivers/violin/v7000_fcp.py | 2 + cinder/volume/drivers/vmware/vmdk.py | 2 + cinder/volume/drivers/vzstorage.py | 2 + cinder/volume/drivers/windows/smbfs.py | 2 + cinder/volume/drivers/xio.py | 3 + cinder/volume/drivers/zfssa/zfssaiscsi.py | 8 +- cinder/volume/drivers/zfssa/zfssanfs.py | 2 + .../drivers/brocade/brcd_fc_zone_driver.py | 2 + .../drivers/cisco/cisco_fc_zone_driver.py | 2 + cinder/zonemanager/drivers/fc_zone_driver.py | 66 +---- doc/source/devref/drivers.rst | 31 ++- tools/generate_driver_list.py | 58 ++-- tox.ini | 7 +- 106 files changed, 1597 insertions(+), 102 deletions(-) create mode 100644 cinder/interface/__init__.py create mode 100644 cinder/interface/backup_chunked_driver.py create mode 100644 cinder/interface/backup_driver.py create mode 100644 cinder/interface/backup_verify_driver.py create mode 100644 cinder/interface/base.py create mode 100644 cinder/interface/fczm_driver.py create mode 100644 cinder/interface/util.py create mode 100644 cinder/interface/volume_consistencygroup_driver.py create mode 100644 cinder/interface/volume_driver.py create mode 100644 cinder/interface/volume_management_driver.py create mode 100644 cinder/interface/volume_snapshot_driver.py create mode 100644 cinder/interface/volume_snapshotmanagement_driver.py create mode 100644 cinder/tests/compliance/__init__.py create mode 100644 cinder/tests/compliance/test_backup_drivers.py create mode 100644 cinder/tests/compliance/test_fczm_drivers.py create mode 100644 cinder/tests/compliance/test_volume_drivers.py 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} .