
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
297 lines
11 KiB
Python
297 lines
11 KiB
Python
# Copyright (c) 2012 - 2015 EMC Corporation, 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.
|
|
"""iSCSI Drivers for EMC VNX array based on CLI."""
|
|
|
|
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.
|
|
|
|
Version history:
|
|
|
|
.. code-block:: none
|
|
|
|
1.0.0 - Initial driver
|
|
2.0.0 - Thick/thin provisioning, robust enhancement
|
|
3.0.0 - Array-based Backend Support, FC Basic Support,
|
|
Target Port Selection for MPIO,
|
|
Initiator Auto Registration,
|
|
Storage Group Auto Deletion,
|
|
Multiple Authentication Type Support,
|
|
Storage-Assisted Volume Migration,
|
|
SP Toggle for HA
|
|
3.0.1 - Security File Support
|
|
4.0.0 - Advance LUN Features (Compression Support,
|
|
Deduplication Support, FAST VP Support,
|
|
FAST Cache Support), Storage-assisted Retype,
|
|
External Volume Management, Read-only Volume,
|
|
FC Auto Zoning
|
|
4.1.0 - Consistency group support
|
|
5.0.0 - Performance enhancement, LUN Number Threshold Support,
|
|
Initiator Auto Deregistration,
|
|
Force Deleting LUN in Storage Groups,
|
|
robust enhancement
|
|
5.1.0 - iSCSI multipath enhancement
|
|
5.2.0 - Pool-aware scheduler support
|
|
5.3.0 - Consistency group modification support
|
|
6.0.0 - Over subscription support
|
|
Create consistency group from cgsnapshot support
|
|
Multiple pools support enhancement
|
|
Manage/unmanage volume revise
|
|
White list target ports support
|
|
Snap copy support
|
|
Support efficient non-disruptive backup
|
|
7.0.0 - Clone consistency group support
|
|
Replication v2 support(managed)
|
|
Configurable migration rate support
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(EMCCLIISCSIDriver, self).__init__(*args, **kwargs)
|
|
self.cli = emc_vnx_cli.getEMCVnxCli(
|
|
'iSCSI',
|
|
configuration=self.configuration,
|
|
active_backend_id=kwargs.get('active_backend_id'))
|
|
self.VERSION = self.cli.VERSION
|
|
|
|
def check_for_setup_error(self):
|
|
pass
|
|
|
|
def create_volume(self, volume):
|
|
"""Creates a VNX volume."""
|
|
return self.cli.create_volume(volume)
|
|
|
|
def create_volume_from_snapshot(self, volume, snapshot):
|
|
"""Creates a volume from a snapshot."""
|
|
return self.cli.create_volume_from_snapshot(volume, snapshot)
|
|
|
|
def create_cloned_volume(self, volume, src_vref):
|
|
"""Creates a cloned volume."""
|
|
return self.cli.create_cloned_volume(volume, src_vref)
|
|
|
|
def extend_volume(self, volume, new_size):
|
|
"""Extend a volume."""
|
|
self.cli.extend_volume(volume, new_size)
|
|
|
|
def delete_volume(self, volume):
|
|
"""Deletes a VNX volume."""
|
|
self.cli.delete_volume(volume)
|
|
|
|
def migrate_volume(self, ctxt, volume, host):
|
|
return self.cli.migrate_volume(ctxt, volume, host)
|
|
|
|
def retype(self, ctxt, volume, new_type, diff, host):
|
|
"""Convert the volume to be of the new type."""
|
|
return self.cli.retype(ctxt, volume, new_type, diff, host)
|
|
|
|
def create_snapshot(self, snapshot):
|
|
"""Creates a snapshot."""
|
|
self.cli.create_snapshot(snapshot)
|
|
|
|
def delete_snapshot(self, snapshot):
|
|
"""Deletes a snapshot."""
|
|
self.cli.delete_snapshot(snapshot)
|
|
|
|
def ensure_export(self, context, volume):
|
|
"""Driver entry point to get the export info for an existing volume."""
|
|
pass
|
|
|
|
def create_export(self, context, volume, connector):
|
|
"""Driver entry point to get the export info for a new volume."""
|
|
pass
|
|
|
|
def remove_export(self, context, volume):
|
|
"""Driver entry point to remove an export for a volume."""
|
|
pass
|
|
|
|
def check_for_export(self, context, volume_id):
|
|
"""Make sure volume is exported."""
|
|
pass
|
|
|
|
def initialize_connection(self, volume, connector):
|
|
"""Initializes the connection and returns connection info.
|
|
|
|
The iscsi driver returns a driver_volume_type of 'iscsi'.
|
|
the format of the driver data is defined in vnx_get_iscsi_properties.
|
|
Example return value (multipath is not enabled)::
|
|
|
|
{
|
|
'driver_volume_type': 'iscsi'
|
|
'data': {
|
|
'target_discovered': True,
|
|
'target_iqn': 'iqn.2010-10.org.openstack:volume-00000001',
|
|
'target_portal': '127.0.0.0.1:3260',
|
|
'target_lun': 1,
|
|
}
|
|
}
|
|
|
|
Example return value (multipath is enabled)::
|
|
|
|
{
|
|
'driver_volume_type': 'iscsi'
|
|
'data': {
|
|
'target_discovered': True,
|
|
'target_iqns': ['iqn.2010-10.org.openstack:volume-00001',
|
|
'iqn.2010-10.org.openstack:volume-00002'],
|
|
'target_portals': ['127.0.0.1:3260', '127.0.1.1:3260'],
|
|
'target_luns': [1, 1],
|
|
}
|
|
}
|
|
|
|
"""
|
|
return self.cli.initialize_connection(volume, connector)
|
|
|
|
def terminate_connection(self, volume, connector, **kwargs):
|
|
"""Disallow connection from connector."""
|
|
self.cli.terminate_connection(volume, connector)
|
|
|
|
def get_volume_stats(self, refresh=False):
|
|
"""Get volume status.
|
|
|
|
If 'refresh' is True, run update the stats first.
|
|
"""
|
|
if refresh:
|
|
self.update_volume_stats()
|
|
|
|
return self._stats
|
|
|
|
def update_volume_stats(self):
|
|
"""Retrieve status info from volume group."""
|
|
LOG.debug("Updating volume status.")
|
|
# retrieving the volume update from the VNX
|
|
data = self.cli.update_volume_stats()
|
|
|
|
backend_name = self.configuration.safe_get('volume_backend_name')
|
|
data['volume_backend_name'] = backend_name or 'EMCCLIISCSIDriver'
|
|
data['storage_protocol'] = 'iSCSI'
|
|
|
|
self._stats = data
|
|
|
|
def manage_existing(self, volume, existing_ref):
|
|
"""Manage an existing lun in the array.
|
|
|
|
The lun should be in a manageable pool backend, otherwise
|
|
error would return.
|
|
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.
|
|
|
|
.. code-block:: none
|
|
|
|
manage_existing_ref:{
|
|
'source-id':<lun id in VNX>
|
|
}
|
|
|
|
or
|
|
|
|
manage_existing_ref:{
|
|
'source-name':<lun name in VNX>
|
|
}
|
|
|
|
"""
|
|
return self.cli.manage_existing(volume, existing_ref)
|
|
|
|
def manage_existing_get_size(self, volume, existing_ref):
|
|
"""Return size of volume to be managed by manage_existing."""
|
|
return self.cli.manage_existing_get_size(volume, existing_ref)
|
|
|
|
def create_consistencygroup(self, context, group):
|
|
"""Creates a consistencygroup."""
|
|
return self.cli.create_consistencygroup(context, group)
|
|
|
|
def delete_consistencygroup(self, context, group, volumes):
|
|
"""Deletes a consistency group."""
|
|
return self.cli.delete_consistencygroup(
|
|
context, group, volumes)
|
|
|
|
def create_cgsnapshot(self, context, cgsnapshot, snapshots):
|
|
"""Creates a cgsnapshot."""
|
|
return self.cli.create_cgsnapshot(
|
|
context, cgsnapshot, snapshots)
|
|
|
|
def delete_cgsnapshot(self, context, cgsnapshot, snapshots):
|
|
"""Deletes a cgsnapshot."""
|
|
return self.cli.delete_cgsnapshot(
|
|
context, cgsnapshot, snapshots)
|
|
|
|
def get_pool(self, volume):
|
|
"""Returns the pool name of a volume."""
|
|
return self.cli.get_pool(volume)
|
|
|
|
def update_consistencygroup(self, context, group,
|
|
add_volumes,
|
|
remove_volumes):
|
|
"""Updates LUNs in consistency group."""
|
|
return self.cli.update_consistencygroup(context, group,
|
|
add_volumes,
|
|
remove_volumes)
|
|
|
|
def unmanage(self, volume):
|
|
"""Unmanages a volume."""
|
|
self.cli.unmanage(volume)
|
|
|
|
def create_consistencygroup_from_src(self, context, group, volumes,
|
|
cgsnapshot=None, snapshots=None,
|
|
source_cg=None, source_vols=None):
|
|
"""Creates a consistency group from source."""
|
|
return self.cli.create_consistencygroup_from_src(context,
|
|
group,
|
|
volumes,
|
|
cgsnapshot,
|
|
snapshots,
|
|
source_cg,
|
|
source_vols)
|
|
|
|
def update_migrated_volume(self, context, volume, new_volume,
|
|
original_volume_status=None):
|
|
"""Returns model update for migrated volume."""
|
|
return self.cli.update_migrated_volume(context, volume, new_volume,
|
|
original_volume_status)
|
|
|
|
def create_export_snapshot(self, context, snapshot, connector):
|
|
"""Creates a snapshot mount point for snapshot."""
|
|
return self.cli.create_export_snapshot(context, snapshot, connector)
|
|
|
|
def remove_export_snapshot(self, context, snapshot):
|
|
"""Removes snapshot mount point for snapshot."""
|
|
return self.cli.remove_export_snapshot(context, snapshot)
|
|
|
|
def initialize_connection_snapshot(self, snapshot, connector, **kwargs):
|
|
"""Allows connection to snapshot."""
|
|
return self.cli.initialize_connection_snapshot(snapshot,
|
|
connector,
|
|
**kwargs)
|
|
|
|
def terminate_connection_snapshot(self, snapshot, connector, **kwargs):
|
|
"""Disallows connection to snapshot."""
|
|
return self.cli.terminate_connection_snapshot(snapshot,
|
|
connector,
|
|
**kwargs)
|
|
|
|
def backup_use_temp_snapshot(self):
|
|
return True
|
|
|
|
def failover_host(self, context, volumes, secondary_id=None):
|
|
"""Failovers volume from primary device to secondary."""
|
|
return self.cli.failover_host(context, volumes, secondary_id)
|