'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 = {
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)
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__)
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
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):
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.
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.
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:]
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:
"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.
'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
}