--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/cinder/files/zfssa/zfssaiscsi.py Fri Jun 13 09:10:23 2014 -0700
@@ -0,0 +1,389 @@
+# Copyright (c) 2014, Oracle and/or its affiliates. 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.
+"""
+ZFS Storage Appliance Cinder Volume Driver
+"""
+import base64
+
+from cinder import exception
+from cinder.openstack.common import log
+from cinder.volume import driver
+from oslo.config import cfg
+
+from cinder.volume.drivers.zfssa import zfssarest
+
+
+CONF = cfg.CONF
+LOG = log.getLogger(__name__)
+
+ZFSSA_OPTS = [
+ cfg.StrOpt('zfssa_host', required=True,
+ help='ZFSSA management IP address'),
+ cfg.StrOpt('zfssa_auth_user', required=True, secret=True,
+ help='ZFSSA management authorized user\'s name'),
+ cfg.StrOpt('zfssa_auth_password', required=True, secret=True,
+ help='ZFSSA management authorized user\'s password'),
+ cfg.StrOpt('zfssa_pool', required=True,
+ help='ZFSSA storage pool name'),
+ cfg.StrOpt('zfssa_project', required=True,
+ help='ZFSSA project name'),
+ cfg.StrOpt('zfssa_lun_volblocksize', default='8k',
+ help='Block size: 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, 128k'),
+ cfg.BoolOpt('zfssa_lun_sparse', default=False,
+ help='Flag to enable sparse (thin-provisioned): True, False'),
+ cfg.StrOpt('zfssa_lun_compression', default='',
+ help='Data compression-off, lzjb, gzip-2, gzip, gzip-9'),
+ cfg.StrOpt('zfssa_lun_logbias', default='',
+ help='Synchronous write bias-latency, throughput'),
+ cfg.StrOpt('zfssa_initiator_group', default='',
+ help='iSCSI initiator group'),
+ cfg.StrOpt('zfssa_initiator', default='',
+ help='iSCSI initiator IQNs (comma separated)'),
+ cfg.StrOpt('zfssa_initiator_user', default='',
+ help='iSCSI initiator CHAP user'),
+ cfg.StrOpt('zfssa_initiator_password', default='',
+ help='iSCSI initiator CHAP password'),
+ cfg.StrOpt('zfssa_target_group', default='tgt-grp',
+ help='iSCSI target group name'),
+ cfg.StrOpt('zfssa_target_user', default='',
+ help='iSCSI target CHAP user'),
+ cfg.StrOpt('zfssa_target_password', default='',
+ help='iSCSI target CHAP password'),
+ cfg.StrOpt('zfssa_target_portal', required=True,
+ help='iSCSI target portal (Data-IP:Port, w.x.y.z:3260)'),
+ cfg.StrOpt('zfssa_target_interfaces', required=True,
+ help='Network interfaces of iSCSI targets (comma separated)')
+]
+
+CONF.register_opts(ZFSSA_OPTS)
+
+SIZE_GB = 1073741824
+
+
+#pylint: disable=R0904
+class ZFSSAISCSIDriver(driver.ISCSIDriver):
+ """ZFSSA Cinder volume driver"""
+
+ VERSION = '1.0.0'
+ protocol = 'iSCSI'
+
+ def __init__(self, *args, **kwargs):
+ super(ZFSSAISCSIDriver, self).__init__(*args, **kwargs)
+ self.configuration.append_config_values(ZFSSA_OPTS)
+ self.zfssa = None
+ self._stats = None
+
+ def _get_target_alias(self):
+ """return target alias"""
+ return self.configuration.zfssa_target_group
+
+ def do_setup(self, context):
+ """Setup - create project, initiators, initiatorgroup, target,
+ targetgroup
+ """
+ self.configuration._check_required_opts()
+ lcfg = self.configuration
+
+ LOG.info('Connecting to host: %s' % lcfg.zfssa_host)
+ self.zfssa = zfssarest.ZFSSAApi(lcfg.zfssa_host)
+ auth_str = base64.encodestring('%s:%s' %
+ (lcfg.zfssa_auth_user,
+ lcfg.zfssa_auth_password))[:-1]
+ self.zfssa.login(auth_str)
+ self.zfssa.create_project(lcfg.zfssa_pool, lcfg.zfssa_project,
+ compression=lcfg.zfssa_lun_compression,
+ logbias=lcfg.zfssa_lun_logbias)
+
+ if (lcfg.zfssa_initiator != '' and
+ (lcfg.zfssa_initiator_group == '' or
+ lcfg.zfssa_initiator_group == 'default')):
+ LOG.warning('zfssa_initiator= %s wont be used on \
+ zfssa_initiator_group= %s' %
+ (lcfg.zfssa_initiator,
+ lcfg.zfssa_initiator_group))
+
+ # Setup initiator and initiator group
+ if lcfg.zfssa_initiator != '' and \
+ lcfg.zfssa_initiator_group != '' and \
+ lcfg.zfssa_initiator_group != 'default':
+ for initiator in lcfg.zfssa_initiator.split(','):
+ self.zfssa.create_initiator(initiator,
+ lcfg.zfssa_initiator_group + '-' +
+ initiator,
+ chapuser=
+ lcfg.zfssa_initiator_user,
+ chapsecret=
+ lcfg.zfssa_initiator_password)
+ self.zfssa.add_to_initiatorgroup(initiator,
+ lcfg.zfssa_initiator_group)
+ # Parse interfaces
+ interfaces = []
+ for interface in lcfg.zfssa_target_interfaces.split(','):
+ if interface == '':
+ continue
+ interfaces.append(interface)
+
+ # Setup target and target group
+ iqn = self.zfssa.create_target(
+ self._get_target_alias(),
+ interfaces,
+ tchapuser=lcfg.zfssa_target_user,
+ tchapsecret=lcfg.zfssa_target_password)
+
+ self.zfssa.add_to_targetgroup(iqn, lcfg.zfssa_target_group)
+
+ def check_for_setup_error(self):
+ """Check that driver can login and pool, project, initiators,
+ initiatorgroup, target, targetgroup exist
+ """
+ lcfg = self.configuration
+
+ self.zfssa.verify_pool(lcfg.zfssa_pool)
+ self.zfssa.verify_project(lcfg.zfssa_pool, lcfg.zfssa_project)
+
+ if lcfg.zfssa_initiator != '' and \
+ lcfg.zfssa_initiator_group != '' and \
+ lcfg.zfssa_initiator_group != 'default':
+ for initiator in lcfg.zfssa_initiator.split(','):
+ self.zfssa.verify_initiator(initiator)
+
+ self.zfssa.verify_target(self._get_target_alias())
+
+ def _get_provider_info(self, volume):
+ """return provider information"""
+ lcfg = self.configuration
+ lun = self.zfssa.get_lun(lcfg.zfssa_pool,
+ lcfg.zfssa_project, volume['name'])
+ iqn = self.zfssa.get_target(self._get_target_alias())
+ loc = "%s %s %s" % (lcfg.zfssa_target_portal, iqn, lun['number'])
+ LOG.debug('_export_volume: provider_location: %s' % loc)
+ provider = {'provider_location': loc}
+ if lcfg.zfssa_target_user != '' and lcfg.zfssa_target_password != '':
+ provider['provider_auth'] = 'CHAP %s %s' % \
+ (lcfg.zfssa_target_user,
+ lcfg.zfssa_target_password)
+ return provider
+
+ def create_volume(self, volume):
+ """Create a volume on ZFSSA"""
+ LOG.debug('zfssa.create_volume: volume=' + volume['name'])
+ lcfg = self.configuration
+ volsize = str(volume['size']) + 'g'
+ self.zfssa.create_lun(lcfg.zfssa_pool,
+ lcfg.zfssa_project,
+ volume['name'],
+ volsize,
+ targetgroup=lcfg.zfssa_target_group,
+ volblocksize=lcfg.zfssa_lun_volblocksize,
+ sparse=lcfg.zfssa_lun_sparse,
+ compression=lcfg.zfssa_lun_compression,
+ logbias=lcfg.zfssa_lun_logbias)
+
+ return self._get_provider_info(volume)
+
+ def delete_volume(self, volume):
+ """Deletes a volume with the given volume['name']."""
+ LOG.debug('zfssa.delete_volume: name=' + volume['name'])
+ lcfg = self.configuration
+ lun2del = self.zfssa.get_lun(lcfg.zfssa_pool,
+ lcfg.zfssa_project,
+ volume['name'])
+ """Delete clone's temp snapshot. see create_cloned_volume()"""
+ """clone is deleted as part of the snapshot delete."""
+ tmpsnap = 'tmp-snapshot-%s' % volume['id']
+ if 'origin' in lun2del and lun2del['origin']['snapshot'] == tmpsnap:
+ self.zfssa.delete_snapshot(lcfg.zfssa_pool,
+ lcfg.zfssa_project,
+ lun2del['origin']['share'],
+ lun2del['origin']['snapshot'])
+ return
+
+ self.zfssa.delete_lun(pool=lcfg.zfssa_pool,
+ project=lcfg.zfssa_project,
+ lun=volume['name'])
+
+ def create_snapshot(self, snapshot):
+ """Creates a snapshot with the given snapshot['name'] of the
+ snapshot['volume_name']
+ """
+ LOG.debug('zfssa.create_snapshot: snapshot=' + snapshot['name'])
+ lcfg = self.configuration
+ self.zfssa.create_snapshot(lcfg.zfssa_pool,
+ lcfg.zfssa_project,
+ snapshot['volume_name'],
+ snapshot['name'])
+
+ def delete_snapshot(self, snapshot):
+ """Deletes a snapshot."""
+ LOG.debug('zfssa.delete_snapshot: snapshot=' + snapshot['name'])
+ lcfg = self.configuration
+ has_clones = self.zfssa.has_clones(lcfg.zfssa_pool,
+ lcfg.zfssa_project,
+ snapshot['volume_name'],
+ snapshot['name'])
+ if has_clones:
+ LOG.error('snapshot %s: has clones' % snapshot['name'])
+ raise exception.SnapshotIsBusy(snapshot_name=snapshot['name'])
+
+ self.zfssa.delete_snapshot(lcfg.zfssa_pool,
+ lcfg.zfssa_project,
+ snapshot['volume_name'],
+ snapshot['name'])
+
+ def create_volume_from_snapshot(self, volume, snapshot):
+ """Creates a volume from a snapshot - clone a snapshot"""
+ LOG.debug('zfssa.create_volume_from_snapshot: volume=' +
+ volume['name'])
+ LOG.debug('zfssa.create_volume_from_snapshot: snapshot=' +
+ snapshot['name'])
+ if not self._verify_clone_size(snapshot, volume['size'] * SIZE_GB):
+ exception_msg = (_('Error verifying clone size on '
+ 'Volume clone: %(clone)s '
+ 'Size: %(size)d on'
+ 'Snapshot: %(snapshot)s')
+ % {'clone': volume['name'],
+ 'size': volume['size'],
+ 'snapshot': snapshot['name']})
+ LOG.error(exception_msg)
+ raise exception.InvalidInput(reason=exception_msg)
+
+ lcfg = self.configuration
+ self.zfssa.clone_snapshot(lcfg.zfssa_pool,
+ lcfg.zfssa_project,
+ snapshot['volume_name'],
+ snapshot['name'],
+ volume['name'])
+
+ def _update_volume_status(self):
+ """Retrieve status info from volume group."""
+ LOG.debug("Updating volume status")
+ self._stats = None
+ data = {}
+ data["volume_backend_name"] = self.__class__.__name__
+ data["vendor_name"] = 'Oracle'
+ data["driver_version"] = self.VERSION
+ data["storage_protocol"] = self.protocol
+
+ lcfg = self.configuration
+ (avail, used) = self.zfssa.get_pool_stats(lcfg.zfssa_pool)
+ if avail is None or used is None:
+ return
+ total = int(avail) + int(used)
+
+ if lcfg.zfssa_lun_sparse:
+ data['total_capacity_gb'] = 'infinite'
+ else:
+ data['total_capacity_gb'] = total / SIZE_GB
+ data['free_capacity_gb'] = int(avail) / SIZE_GB
+ data['reserved_percentage'] = 0
+ data['QoS_support'] = False
+ self._stats = data
+
+ def get_volume_stats(self, refresh=False):
+ """Get volume status.
+ If 'refresh' is True, run update the stats first.
+ """
+ if refresh:
+ self._update_volume_status()
+ return self._stats
+
+ def _export_volume(self, volume):
+ """Export the volume - set the initiatorgroup property."""
+ LOG.debug('_export_volume: volume name: %s' % volume['name'])
+ lcfg = self.configuration
+
+ self.zfssa.set_lun_initiatorgroup(lcfg.zfssa_pool,
+ lcfg.zfssa_project,
+ volume['name'],
+ lcfg.zfssa_initiator_group)
+ return self._get_provider_info(volume)
+
+ def create_export(self, context, volume):
+ """Driver entry point to get the export info for a new volume."""
+ LOG.debug('create_export: volume name: %s' % volume['name'])
+ return self._export_volume(volume)
+
+ def remove_export(self, context, volume):
+ """Driver entry point to remove an export for a volume."""
+ LOG.debug('remove_export: volume name: %s' % volume['name'])
+ lcfg = self.configuration
+ self.zfssa.set_lun_initiatorgroup(lcfg.zfssa_pool,
+ lcfg.zfssa_project,
+ volume['name'],
+ '')
+
+ def ensure_export(self, context, volume):
+ """Driver entry point to get the export info for an existing volume."""
+ LOG.debug('ensure_export: volume name: %s' % volume['name'])
+ return self._export_volume(volume)
+
+ def copy_image_to_volume(self, context, volume, image_service, image_id):
+ self.ensure_export(context, volume)
+ super(ZFSSAISCSIDriver, self).copy_image_to_volume(
+ context, volume, image_service, image_id)
+
+ def extend_volume(self, volume, new_size):
+ """Driver entry point to extent volume size."""
+ LOG.debug('extend_volume: volume name: %s' % volume['name'])
+ lcfg = self.configuration
+ self.zfssa.set_lun_size(lcfg.zfssa_pool,
+ lcfg.zfssa_project,
+ volume['name'],
+ new_size * SIZE_GB)
+
+ def _get_iscsi_properties(self, volume):
+ lcfg = self.configuration
+ lun = self.zfssa.get_lun(lcfg.zfssa_pool,
+ lcfg.zfssa_project,
+ volume['name'])
+ iqn = self.zfssa.get_target(self._get_target_alias())
+
+ return {'target_discovered': True,
+ 'target_iqn': iqn,
+ 'target_portal': lcfg.zfssa_target_portal,
+ 'volume_id': lun['number'],
+ 'access_mode': 'rw'}
+
+ def create_cloned_volume(self, volume, src_vref):
+ """Create a clone of the specified volume."""
+ zfssa_snapshot = {'volume_name': src_vref['name'],
+ 'name': 'tmp-snapshot-%s' % volume['id']}
+ self.create_snapshot(zfssa_snapshot)
+ try:
+ self.create_volume_from_snapshot(volume, zfssa_snapshot)
+ except exception.VolumeBackendAPIException:
+ LOG.error("Clone Volume '%s' failed from source volume '%s'"
+ % (volume['name'], src_vref['name']))
+ # Cleanup snapshot
+ self.delete_snapshot(zfssa_snapshot)
+
+ def local_path(self, volume):
+ """Not implemented"""
+ pass
+
+ def backup_volume(self, context, backup, backup_service):
+ """Not implemented"""
+ pass
+
+ def restore_backup(self, context, backup, volume, backup_service):
+ """Not implemented"""
+ pass
+
+ def _verify_clone_size(self, snapshot, size):
+ """Check whether the clone size is the same as the parent volume"""
+ lcfg = self.configuration
+ lun = self.zfssa.get_lun(lcfg.zfssa_pool,
+ lcfg.zfssa_project,
+ snapshot['volume_name'])
+ return (lun['size'] == size)