From 65c635d6a5be0b4afaa9289c901f90275a0be102 Mon Sep 17 00:00:00 2001 From: Victor Rodionov Date: Mon, 23 Dec 2013 13:24:41 -0800 Subject: [PATCH] Nexenta iSCSI driver: fixed volume_migration Fixed volume_migration updating volume provider_location. Fixed temporal snapshot deleting after volume migrated. Change-Id: Id23749cb3724e1510865425232b38b9fc32690f8 Closes-Bug: #1263258 --- cinder/tests/test_nexenta.py | 19 +++++-- cinder/volume/drivers/nexenta/iscsi.py | 73 ++++++++++++++++++++------ cinder/volume/drivers/nexenta/utils.py | 5 ++ 3 files changed, 75 insertions(+), 22 deletions(-) diff --git a/cinder/tests/test_nexenta.py b/cinder/tests/test_nexenta.py index cfc014609..824f89812 100644 --- a/cinder/tests/test_nexenta.py +++ b/cinder/tests/test_nexenta.py @@ -160,7 +160,9 @@ class TestNexentaISCSIDriver(test.TestCase): 'capabilities': { 'vendor_name': 'Nexenta', 'location_info': 'NexentaISCSIDriver:1.1.1.1:cinder', - 'free_capacity_gb': 1 + 'free_capacity_gb': 1, + 'iscsi_target_portal_port': 3260, + 'nms_url': 'http://admin:password@1.1.1.1:2000' } } snapshot = { @@ -174,19 +176,26 @@ class TestNexentaISCSIDriver(test.TestCase): src = '%(volume)s/%(zvol)s@%(snapshot)s' % { 'volume': 'cinder', 'zvol': volume['name'], - 'snapshot': snapshot['name']} + 'snapshot': snapshot['name'] + } dst = '1.1.1.1:cinder' cmd = ' '.join(['rrmgr -s zfs -c 1 -q -e -w 1024 -n 2', src, dst]) self.nms_mock.appliance.execute(cmd) - self.nms_mock.snapshot.destroy('cinder/%(volume)s@%(snapshot)s' % { - 'volume': volume['name'], - 'snapshot': snapshot['name']}, '') + snapshot_name = 'cinder/%(volume)s@%(snapshot)s' % { + 'volume': volume['name'], + 'snapshot': snapshot['name'] + } + self.nms_mock.snapshot.destroy(snapshot_name, '') volume_name = 'cinder/%s' % volume['name'] self.nms_mock.zvol.get_child_props(volume_name, 'origin').AndReturn(None) self.nms_mock.zvol.destroy(volume_name, '') + self.nms_mock.snapshot.destroy('cinder/%(volume)s@%(snapshot)s' % { + 'volume': volume['name'], + 'snapshot': snapshot['name'] + }, '') self.mox.ReplayAll() self.drv.migrate_volume(None, volume, host) diff --git a/cinder/volume/drivers/nexenta/iscsi.py b/cinder/volume/drivers/nexenta/iscsi.py index 81d1c84f6..5e4c9c817 100644 --- a/cinder/volume/drivers/nexenta/iscsi.py +++ b/cinder/volume/drivers/nexenta/iscsi.py @@ -32,7 +32,7 @@ from cinder.volume.drivers.nexenta import jsonrpc from cinder.volume.drivers.nexenta import options from cinder.volume.drivers.nexenta import utils -VERSION = '1.1.3' +VERSION = '1.2.1' LOG = logging.getLogger(__name__) @@ -48,6 +48,10 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 1.1.2 - Optimized create_cloned_volume, replaced zfs send recv with zfs clone. 1.1.3 - Extended volume stats provided by _update_volume_stats method. + 1.2.0 - Added volume migration with storage assist method. + 1.2.1 - Fixed bug #1263258: now migrate_volume update provider_location + of migrated volume; after migrating volume migrate_volume + destroy snapshot on migration destination. """ VERSION = VERSION @@ -73,6 +77,8 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 self.rrmgr_compression = self.configuration.nexenta_rrmgr_compression self.rrmgr_tcp_buf_size = self.configuration.nexenta_rrmgr_tcp_buf_size self.rrmgr_connections = self.configuration.nexenta_rrmgr_connections + self.iscsi_target_portal_port = \ + self.configuration.nexenta_iscsi_target_portal_port @property def backend_name(self): @@ -125,11 +131,6 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 name = snapshot.split('@')[-1] return name.startswith('cinder-clone-snapshot-') - @staticmethod - def _get_migrate_snapshot_name(volume): - """Return name for snapshot that will be used to migrate the volume.""" - return 'cinder-migrate-snapshot-%(id)s' % volume - def create_volume(self, volume): """Create a zvol on appliance. @@ -215,6 +216,14 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 tcp_buf_size=self.rrmgr_tcp_buf_size, connections=self.rrmgr_connections) + @staticmethod + def get_nms_for_url(url): + """Returns initialized nms object for url.""" + auto, scheme, user, password, host, port, path =\ + utils.parse_nms_url(url) + return jsonrpc.NexentaJSONProxy(scheme, host, port, path, user, + password, auto=auto) + def migrate_volume(self, ctxt, volume, host): """Migrate if volume and host are managed by Nexenta appliance. @@ -230,14 +239,23 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 if volume['status'] != 'available': return false_ret - if 'location_info' not in host['capabilities']: + if 'capabilities' not in host: return false_ret - dst_parts = host['capabilities']['location_info'].split(':') + capabilities = host['capabilities'] - if host['capabilities']['vendor_name'] != 'Nexenta' or \ - dst_parts[0] != self.__class__.__name__ or \ - host['capabilities']['free_capacity_gb'] < volume['size']: + if 'location_info' not in capabilities or \ + 'iscsi_target_portal_port' not in capabilities or \ + 'nms_url' not in capabilities: + return false_ret + + iscsi_target_portal_port = capabilities['iscsi_target_portal_port'] + nms_url = capabilities['nms_url'] + dst_parts = capabilities['location_info'].split(':') + + if capabilities.get('vendor_name') != 'Nexenta' or \ + dst_parts[0] != self.__class__.__name__ or \ + capabilities['free_capacity_gb'] < volume['size']: return false_ret dst_host, dst_volume = dst_parts[1:] @@ -248,19 +266,22 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 if bind.index(dst_host) != -1: ssh_bound = True break - if not(ssh_bound): + if not ssh_bound: LOG.warning(_("Remote NexentaStor appliance at %s should be " "SSH-bound."), dst_host) # Create temporary snapshot of volume on NexentaStor Appliance. - snapshot = {'volume_name': volume['name'], - 'name': self._get_migrate_snapshot_name(volume)} + snapshot = { + 'volume_name': volume['name'], + 'name': utils.get_migrate_snapshot_name(volume) + } self.create_snapshot(snapshot) src = '%(volume)s/%(zvol)s@%(snapshot)s' % { 'volume': self.volume, 'zvol': volume['name'], - 'snapshot': snapshot['name']} + 'snapshot': snapshot['name'] + } dst = ':'.join([dst_host, dst_volume]) try: @@ -284,7 +305,23 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 "NexentaStor Appliance: %(exc)s"), {'volume': volume['name'], 'exc': exc}) - return (True, None) + dst_nms = self.get_nms_for_url(nms_url) + dst_snapshot = '%s/%s@%s' % (dst_volume, volume['name'], + snapshot['name']) + try: + dst_nms.snapshot.destroy(dst_snapshot, '') + except nexenta.NexentaException as exc: + LOG.warning(_("Cannot delete temporary destination snapshot " + "%(dst)s on NexentaStor Appliance: %(exc)s"), + {'dst': dst_snapshot, 'exc': exc}) + + provider_location = '%(host)s:%(port)s,1 %(name)s 0' % { + 'host': dst_host, + 'port': iscsi_target_portal_port, + 'name': self._get_target_name(volume['name']) + } + + return True, {'provider_location': provider_location} def create_snapshot(self, snapshot): """Create snapshot of existing zvol on appliance. @@ -560,5 +597,7 @@ class NexentaISCSIDriver(driver.ISCSIDriver): # pylint: disable=R0921 'reserved_percentage': 0, 'QoS_support': False, 'volume_backend_name': self.backend_name, - 'location_info': location_info + 'location_info': location_info, + 'iscsi_target_portal_port': self.iscsi_target_portal_port, + 'nms_url': self.nms.url } diff --git a/cinder/volume/drivers/nexenta/utils.py b/cinder/volume/drivers/nexenta/utils.py index 4fa9f22fb..c724a530c 100644 --- a/cinder/volume/drivers/nexenta/utils.py +++ b/cinder/volume/drivers/nexenta/utils.py @@ -118,3 +118,8 @@ def parse_nms_url(url): else: host, port = host_and_port, '2000' return auto, scheme, user, password, host, port, '/rest/nms/' + + +def get_migrate_snapshot_name(volume): + """Return name for snapshot that will be used to migrate the volume.""" + return 'cinder-migrate-snapshot-%(id)s' % volume -- 2.45.2