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 6f0b0c664..65970d2f1 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 7e274b505..c8bb548b0 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 f2a7c46aa..534ef72e8 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 b3d4d3651..da000fba2 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 5a00e2231..b7f151b25 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 7e1c661c6..f9469efb4 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 57b6cf620..aab738dc7 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 000000000..223cc4dec --- /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 000000000..3f32cc293 --- /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 000000000..f2de4167f --- /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 000000000..5f4d34b5b --- /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 000000000..3cea6df1c --- /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 000000000..208cb5856 --- /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 000000000..32adb0222 --- /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 000000000..ebb5c197d --- /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 000000000..05bd8f21a --- /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 000000000..966cb50bc --- /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 000000000..51ba4f08e --- /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 000000000..8bce8949c --- /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 000000000..e69de29bb diff --git a/cinder/tests/compliance/test_backup_drivers.py b/cinder/tests/compliance/test_backup_drivers.py new file mode 100644 index 000000000..e429b7b50 --- /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 000000000..3bc530abd --- /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 000000000..9e928cb10 --- /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 8c3ecd4fd..03b809bc5 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 f961e66ed..99d0aa98d 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 1a5aaea38..93c0ce4d2 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 a5fcb560d..255c92bf1 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 39e812f92..f360c2b8d 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 c91a4765d..9fe94392c 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 845c42a04..846b353d2 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 9ed70b4d9..6788b90d3 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 d088bf087..bc5da70d1 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 2b78c0ed9..b92a71b01 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 258ec4ee2..01d0b2403 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 6437cb07d..00236e855 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 aaf12392a..fd0d67261 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 66f6536cf..dd2559a55 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 e79c0c3eb..a6de60d7c 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 57ee3de77..346c6645c 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 6cd2b4e8e..5d7b503f6 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 c1d7956f3..4011452da 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 1c377ea32..2d9c3ebba 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 0070e47f2..45626960b 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 341598cbf..980c1f02b 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 19528f4cc..19865346d 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 b9146f2d1..afd2e0e6d 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 489252836..904290a81 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 bbbb1b48d..36e41fe43 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 0f85694f8..c79c89a92 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 853b70355..76972a042 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 149cc990a..9619d9f9d 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 134485378..9d6630c5f 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 1ec37b049..d0e76f9f3 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 704ec67a4..fd746473e 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 978ef9bfc..ef587c48c 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 cd3636983..29aa5030d 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 339156cfc..55f1fe7f0 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 2c1993fd4..f13d831f1 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 6265997a7..5cc7c0691 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 4e4a89988..de34e4657 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 c6ed860a7..6fda5dfca 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 6e363b813..461776cef 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 79573eecb..ba24af36c 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 e4387b2b4..77b7cbec4 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 4926e45bc..be7534d88 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 1b98faa19..bd5113c86 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 f5bdac313..391f42223 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 06a9be047..f523cb5fe 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 ce8b286b4..29e8d25d9 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 305fb5dac..e4bed3302 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 efd6d3e14..84ebdd0fd 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 06085f2fc..e06864b25 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 8b67187ee..5bee8da68 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 19bcf741a..52e5440ea 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 26991b8c0..8d20fdef3 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 c9e56212b..71cfbb46f 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 e9ddb4b37..bf26ef4c6 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 12f1c1392..5db46a250 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 c1ac655e1..70b598f67 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 285908fc7..fb3d35921 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 d673d8b10..b9776585c 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 176569d95..a726f3b58 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 7ecccd8f8..b6c27f3df 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 d6d8c825e..3be0de223 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 75ef64714..6f26659a7 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 2c1da2869..efa9e8396 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 258b05083..d24dd3ad3 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 64e4c9348..2ea239567 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 162fb1850..327269d96 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 29bc6ca2e..e5c06bd2d 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 e8d45061d..0bf93da8a 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 7dd928e0a..5b84fc79e 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 1ecf02ec8..1dd492c56 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 9f12c10d8..026bb331f 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 3a8f7328f..f04e6cfa8 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 aaa304e0f..71af06d99 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 f97979215..a2edc9f86 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 95ce3a1a2..1e69a91ac 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 60753180f..414916f51 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 847629d56..66f6781b1 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 d42990200..64fe378fe 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 597d66b2b..289fd7f0e 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 236e7698e..9417c965b 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 8eeca2e09..ddd9f08ce 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 72d4d5af0..d7531b46f 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 d0840097e..d3a8c5103 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} .