From 9440e2eb6cc78645f0a09d005b477c20e7624993 Mon Sep 17 00:00:00 2001 From: Victor Rodionov Date: Thu, 1 Aug 2013 18:43:37 +0400 Subject: [PATCH] Add support for volume cloning to Nexenta driver This patch implements missing functionality for Havana. Change-Id: I39d838e955f25b8438a24f6aff5ec97fb3c14e4b --- cinder/tests/test_nexenta.py | 26 +++++++++++- cinder/volume/drivers/nexenta/volume.py | 55 +++++++++++++++++++++---- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/cinder/tests/test_nexenta.py b/cinder/tests/test_nexenta.py index 08f9e1d0a..6275b11b3 100644 --- a/cinder/tests/test_nexenta.py +++ b/cinder/tests/test_nexenta.py @@ -40,10 +40,12 @@ class TestNexentaDriver(test.TestCase): TEST_VOLUME_REF = { 'name': TEST_VOLUME_NAME, 'size': 1, + 'id': '1' } TEST_VOLUME_REF2 = { 'name': TEST_VOLUME_NAME2, 'size': 1, + 'id': '2' } TEST_SNAPSHOT_REF = { 'name': TEST_SNAPSHOT_NAME, @@ -64,7 +66,7 @@ class TestNexentaDriver(test.TestCase): nexenta_sparse=True, ) self.nms_mock = self.mox.CreateMockAnything() - for mod in ['volume', 'zvol', 'iscsitarget', + for mod in ['volume', 'zvol', 'iscsitarget', 'appliance', 'stmf', 'scsidisk', 'snapshot']: setattr(self.nms_mock, mod, self.mox.CreateMockAnything()) self.stubs.Set(jsonrpc, 'NexentaJSONProxy', @@ -95,6 +97,28 @@ class TestNexentaDriver(test.TestCase): self.mox.ReplayAll() self.drv.delete_volume(self.TEST_VOLUME_REF) + 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'], + } + 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.mox.ReplayAll() + self.drv.create_cloned_volume(vol, src_vref) + def test_create_snapshot(self): self.nms_mock.zvol.create_snapshot('cinder/volume1', 'snapshot1', '') self.mox.ReplayAll() diff --git a/cinder/volume/drivers/nexenta/volume.py b/cinder/volume/drivers/nexenta/volume.py index fb1e94db0..f42378168 100644 --- a/cinder/volume/drivers/nexenta/volume.py +++ b/cinder/volume/drivers/nexenta/volume.py @@ -103,21 +103,22 @@ class NexentaDriver(driver.ISCSIDriver): # pylint: disable=R0921 raise LookupError(_("Volume %s does not exist in Nexenta SA"), CONF.nexenta_volume) - @staticmethod - def _get_zvol_name(volume_name): + def _get_zvol_name(self, volume_name): """Return zvol name that corresponds given volume name.""" return '%s/%s' % (CONF.nexenta_volume, volume_name) - @staticmethod - def _get_target_name(volume_name): + def _get_target_name(self, volume_name): """Return iSCSI target name to access volume.""" return '%s%s' % (CONF.nexenta_target_prefix, volume_name) - @staticmethod - def _get_target_group_name(volume_name): + def _get_target_group_name(self, volume_name): """Return Nexenta iSCSI target group name for volume.""" return '%s%s' % (CONF.nexenta_target_group_prefix, volume_name) + def _get_clone_snap_name(self, volume): + """Return name for snapshot that will be used to clone the volume.""" + return 'cinder-clone-snap-%(id)s' % volume + def create_volume(self, volume): """Create a zvol on appliance. @@ -144,6 +145,46 @@ class NexentaDriver(driver.ISCSIDriver): # pylint: disable=R0921 raise exception.VolumeIsBusy(volume_name=volume['name']) raise + def create_cloned_volume(self, volume, src_vref): + """Creates a clone of the specified volume. + + :param volume: new volume reference + :param src_vref: source volume reference + """ + snapshot = {'volume_name': src_vref['name'], + 'name': self._get_clone_snap_name(volume)} + LOG.debug(_('Creating temp snapshot of the original volume: ' + '%(volume_name)s@%(name)s'), snapshot) + 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: + 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']}) + def create_snapshot(self, snapshot): """Create snapshot of existing zvol on appliance. @@ -167,7 +208,7 @@ class NexentaDriver(driver.ISCSIDriver): # pylint: disable=R0921 def delete_snapshot(self, snapshot): """Delete volume's snapshot on appliance. - :param snapshot: shapshot reference + :param snapshot: snapshot reference """ try: self.nms.snapshot.destroy( -- 2.45.2