Nexenta iSCSI driver: Refactor create_cloned_volume

Optimize volume cloning of Nexenta iSCSI driver, current implementation
with zfs send recv may take long time, because it requires data
transferring.

Change-Id: Iaa2eea608e09d0a4ac155b74f964b153de561f4c
This commit is contained in:
Victor Rodionov 2013-09-07 03:07:11 +04:00
parent 727175b339
commit 4b386ad5cf
2 changed files with 61 additions and 44 deletions

View File

@ -111,29 +111,41 @@ class TestNexentaISCSIDriver(test.TestCase):
self.drv.create_volume(self.TEST_VOLUME_REF)
def test_delete_volume(self):
self.nms_mock.zvol.get_child_props('cinder/volume1',
'origin').AndReturn({})
self.nms_mock.zvol.destroy('cinder/volume1', '')
self.mox.ReplayAll()
self.drv.delete_volume(self.TEST_VOLUME_REF)
self.mox.ResetAll()
c = self.nms_mock.zvol.get_child_props('cinder/volume1', 'origin')
c.AndReturn({'origin': 'cinder/volume0@snapshot'})
self.nms_mock.zvol.destroy('cinder/volume1', '')
self.mox.ReplayAll()
self.drv.delete_volume(self.TEST_VOLUME_REF)
self.mox.ResetAll()
c = self.nms_mock.zvol.get_child_props('cinder/volume1', 'origin')
c.AndReturn({'origin': 'cinder/volume0@cinder-clone-snapshot-1'})
self.nms_mock.zvol.destroy('cinder/volume1', '')
self.nms_mock.snapshot.destroy(
'cinder/volume0@cinder-clone-snapshot-1', '')
self.mox.ReplayAll()
self.drv.delete_volume(self.TEST_VOLUME_REF)
self.mox.ResetAll()
def test_create_cloned_volume(self):
vol = self.TEST_VOLUME_REF2
src_vref = self.TEST_VOLUME_REF
snapshot = {
'volume_name': src_vref['name'],
'name': 'cinder-clone-snap-%s' % vol['id'],
'name': 'cinder-clone-snapshot-%s' % vol['id'],
}
self.nms_mock.zvol.create_snapshot('cinder/%s' % src_vref['name'],
snapshot['name'], '')
cmd = 'zfs send %(src_vol)s@%(src_snap)s | zfs recv %(volume)s' % {
'src_vol': 'cinder/%s' % src_vref['name'],
'src_snap': snapshot['name'],
'volume': 'cinder/%s' % vol['name']
}
self.nms_mock.appliance.execute(cmd)
self.nms_mock.snapshot.destroy('cinder/%s@%s' % (src_vref['name'],
snapshot['name']), '')
self.nms_mock.snapshot.destroy('cinder/%s@%s' % (vol['name'],
snapshot['name']), '')
self.nms_mock.zvol.clone('cinder/%s@%s' % (src_vref['name'],
snapshot['name']),
'cinder/%s' % vol['name'])
self.mox.ReplayAll()
self.drv.create_cloned_volume(vol, src_vref)

View File

@ -51,9 +51,11 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921
lu_exists.
1.1.0 - Changed class name to NexentaISCSIDriver.
1.1.1 - Ignore "does not exist" exception of nms.snapshot.destroy.
1.1.2 - Optimized create_cloned_volume, replaced zfs send recv with zfs
clone.
"""
VERSION = '1.1.1'
VERSION = '1.1.2'
def __init__(self, *args, **kwargs):
super(NexentaISCSIDriver, self).__init__(*args, **kwargs)
@ -100,9 +102,14 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921
return '%s%s' % (self.configuration.nexenta_target_group_prefix,
volume_name)
def _get_clone_snap_name(self, volume):
def _get_clone_snapshot_name(self, volume):
"""Return name for snapshot that will be used to clone the volume."""
return 'cinder-clone-snap-%(id)s' % volume
return 'cinder-clone-snapshot-%(id)s' % volume
def _is_clone_snapshot_name(self, snapshot):
"""Check if snapshot is created for cloning."""
name = snapshot.split('@')[-1]
return name.startswith('cinder-clone-snapshot-')
def create_volume(self, volume):
"""Create a zvol on appliance.
@ -133,16 +140,27 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921
:param volume: volume reference
"""
volume_name = self._get_zvol_name(volume['name'])
props = self.nms.zvol.get_child_props(volume_name, 'origin') or {}
try:
self.nms.zvol.destroy(self._get_zvol_name(volume['name']), '')
self.nms.zvol.destroy(volume_name, '')
except nexenta.NexentaException as exc:
if "does not exist" in exc.args[0]:
if 'does not exist' in exc.args[0]:
LOG.info(_('Volume %s does not exist, it seems it was already '
'deleted.'), volume['name'])
'deleted.'), volume_name)
return
if "zvol has children" in exc.args[0]:
raise exception.VolumeIsBusy(volume_name=volume['name'])
if 'zvol has children' in exc.args[0]:
raise exception.VolumeIsBusy(volume_name=volume_name)
raise
origin = props.get('origin')
if origin and self._is_clone_snapshot_name(origin):
volume, snapshot = origin.split('@')
volume = volume.lstrip('%s/' % self.configuration.nexenta_volume)
try:
self.delete_snapshot({'volume_name': volume, 'name': snapshot})
except nexenta.NexentaException as exc:
LOG.warning(_('Cannot delete snapshot %(origin): %(exc)s'),
{'origin': origin, 'exc': exc})
def create_cloned_volume(self, volume, src_vref):
"""Creates a clone of the specified volume.
@ -151,38 +169,25 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921
:param src_vref: source volume reference
"""
snapshot = {'volume_name': src_vref['name'],
'name': self._get_clone_snap_name(volume)}
'name': self._get_clone_snapshot_name(volume)}
LOG.debug(_('Creating temp snapshot of the original volume: '
'%(volume_name)s@%(name)s'), snapshot)
# We don't delete this snapshot, because this snapshot will be origin
# of new volume. This snapshot will be automatically promoted by NMS
# when user will delete origin volume. But when cloned volume deleted
# we check its origin property and delete source snapshot if needed.
self.create_snapshot(snapshot)
try:
cmd = 'zfs send %(src_vol)s@%(src_snap)s | zfs recv %(volume)s' % {
'src_vol': self._get_zvol_name(src_vref['name']),
'src_snap': snapshot['name'],
'volume': self._get_zvol_name(volume['name'])
}
LOG.debug(_('Executing zfs send/recv on the appliance'))
self.nms.appliance.execute(cmd)
LOG.debug(_('zfs send/recv done, new volume %s created'),
volume['name'])
finally:
self.create_volume_from_snapshot(volume, snapshot)
except nexenta.NexentaException:
LOG.error(_('Volume creation failed, deleting created snapshot '
'%(volume_name)s@%(name)s'), snapshot)
try:
# deleting temp snapshot of the original volume
self.delete_snapshot(snapshot)
except (nexenta.NexentaException, exception.SnapshotIsBusy):
LOG.warning(_('Failed to delete temp snapshot '
'%(volume)s@%(snapshot)s'),
{'volume': src_vref['name'],
'snapshot': snapshot['name']})
try:
# deleting snapshot resulting from zfs recv
self.delete_snapshot({'volume_name': volume['name'],
'name': snapshot['name']})
except (nexenta.NexentaException, exception.SnapshotIsBusy):
LOG.warning(_('Failed to delete zfs recv snapshot '
'%(volume)s@%(snapshot)s'),
{'volume': volume['name'],
'snapshot': snapshot['name']})
LOG.warning(_('Failed to delete zfs snapshot '
'%(volume_name)s@%(name)s'), snapshot)
raise
def create_snapshot(self, snapshot):
"""Create snapshot of existing zvol on appliance.