def migrate_volume(self, ctxt, volume, host): """Migrate if volume and host are managed by Nexenta appliance. :param ctxt: context :param volume: a dictionary describing the volume to migrate :param host: a dictionary describing the host to migrate to """ LOG.debug('Enter: migrate_volume: id=%(id)s, host=%(host)s', {'id': volume['id'], 'host': host}) false_ret = (False, None) if volume['status'] not in ('available', 'retyping'): LOG.warning(_LW("Volume status must be 'available' or 'retyping'." " Current volume status: %s"), volume['status']) return false_ret if 'capabilities' not in host: LOG.warning(_LW("Unsupported host. No capabilities found")) return false_ret capabilities = host['capabilities'] ns_shares = capabilities['ns_shares'] dst_parts = capabilities['location_info'].split(':') dst_host, dst_volume = dst_parts[1:] if (capabilities.get('vendor_name') != 'Nexenta' or dst_parts[0] != self.__class__.__name__ or capabilities['free_capacity_gb'] < volume['size']): return false_ret nms = self.share2nms[volume['provider_location']] ssh_bindings = nms.appliance.ssh_list_bindings() shares = [] for bind in ssh_bindings: for share in ns_shares: if (share.startswith(ssh_bindings[bind][3]) and ns_shares[share] >= volume['size']): shares.append(share) if len(shares) == 0: LOG.warning(_LW("Remote NexentaStor appliance at %s should be " "SSH-bound."), share) return false_ret share = sorted(shares, key=ns_shares.get, reverse=True)[0] snapshot = { 'volume_name': volume['name'], 'volume_id': volume['id'], 'name': utils.get_migrate_snapshot_name(volume) } self.create_snapshot(snapshot) location = volume['provider_location'] src = '%(share)s/%(volume)s@%(snapshot)s' % { 'share': location.split(':')[1].split('volumes/')[1], 'volume': volume['name'], 'snapshot': snapshot['name'] } dst = ':'.join([dst_host, dst_volume.split('/volumes/')[1]]) try: nms.appliance.execute(self._get_zfs_send_recv_cmd(src, dst)) except exception.NexentaException as exc: LOG.warning(_LW("Cannot send source snapshot %(src)s to " "destination %(dst)s. Reason: %(exc)s"), {'src': src, 'dst': dst, 'exc': exc}) return false_ret finally: try: self.delete_snapshot(snapshot) except exception.NexentaException as exc: LOG.warning(_LW("Cannot delete temporary source snapshot " "%(src)s on NexentaStor Appliance: %(exc)s"), {'src': src, 'exc': exc}) try: self.delete_volume(volume) except exception.NexentaException as exc: LOG.warning(_LW("Cannot delete source volume %(volume)s on " "NexentaStor Appliance: %(exc)s"), {'volume': volume['name'], 'exc': exc}) dst_nms = self._get_nms_for_url(capabilities['nms_url']) dst_snapshot = '%s/%s@%s' % (dst_volume.split('volumes/')[1], volume['name'], snapshot['name']) try: dst_nms.snapshot.destroy(dst_snapshot, '') except exception.NexentaException as exc: LOG.warning(_LW("Cannot delete temporary destination snapshot " "%(dst)s on NexentaStor Appliance: %(exc)s"), {'dst': dst_snapshot, 'exc': exc}) return True, {'provider_location': share}
def migrate_volume(self, ctxt, volume, host): """Migrate if volume and host are managed by Nexenta appliance. :param ctxt: context :param volume: a dictionary describing the volume to migrate :param host: a dictionary describing the host to migrate to """ LOG.debug('Enter: migrate_volume: id=%(id)s, host=%(host)s', {'id': volume['id'], 'host': host}) false_ret = (False, None) if volume['status'] not in ('available', 'retyping'): return false_ret if 'capabilities' not in host: return false_ret capabilities = host['capabilities'] if ('location_info' not in capabilities or 'iscsi_target_portal_port' not in capabilities or 'nms_url' not in capabilities): return false_ret 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:] ssh_bound = False ssh_bindings = self.nms.appliance.ssh_list_bindings() for bind in ssh_bindings: if dst_host.startswith(ssh_bindings[bind][3]): ssh_bound = True break 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': 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'] } dst = ':'.join([dst_host, dst_volume]) try: self.nms.appliance.execute(self._get_zfs_send_recv_cmd(src, dst)) except utils.NexentaException as exc: LOG.warning("Cannot send source snapshot %(src)s to " "destination %(dst)s. Reason: %(exc)s", {'src': src, 'dst': dst, 'exc': exc}) return false_ret finally: try: self.delete_snapshot(snapshot) except utils.NexentaException as exc: LOG.warning("Cannot delete temporary source snapshot " "%(src)s on NexentaStor Appliance: %(exc)s", {'src': src, 'exc': exc}) try: self.delete_volume(volume) except utils.NexentaException as exc: LOG.warning("Cannot delete source volume %(volume)s on " "NexentaStor Appliance: %(exc)s", {'volume': volume['name'], 'exc': exc}) 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 utils.NexentaException as exc: LOG.warning("Cannot delete temporary destination snapshot " "%(dst)s on NexentaStor Appliance: %(exc)s", {'dst': dst_snapshot, 'exc': exc}) return True, None
def migrate_volume(self, ctxt, volume, host): """Migrate if volume and host are managed by Nexenta appliance. :param ctxt: context :param volume: a dictionary describing the volume to migrate :param host: a dictionary describing the host to migrate to """ LOG.debug('Enter: migrate_volume: id=%(id)s, host=%(host)s', { 'id': volume['id'], 'host': host }) false_ret = (False, None) if volume['status'] not in ('available', 'retyping'): return false_ret if 'capabilities' not in host: return false_ret capabilities = host['capabilities'] if ('location_info' not in capabilities or 'iscsi_target_portal_port' not in capabilities or 'nms_url' not in capabilities): return false_ret 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:] ssh_bound = False ssh_bindings = self.nms.appliance.ssh_list_bindings() for bind in ssh_bindings: if dst_host.startswith(bind.split('@')[1].split(':')[0]): ssh_bound = True break 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': 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'] } dst = ':'.join([dst_host, dst_volume]) try: self.nms.appliance.execute(self._get_zfs_send_recv_cmd(src, dst)) except exception.NexentaException as exc: LOG.warning( "Cannot send source snapshot %(src)s to " "destination %(dst)s. Reason: %(exc)s", { 'src': src, 'dst': dst, 'exc': exc }) return false_ret finally: try: self.delete_snapshot(snapshot) except exception.NexentaException as exc: LOG.warning( "Cannot delete temporary source snapshot " "%(src)s on NexentaStor Appliance: %(exc)s", { 'src': src, 'exc': exc }) try: self.delete_volume(volume) except exception.NexentaException as exc: LOG.warning( "Cannot delete source volume %(volume)s on " "NexentaStor Appliance: %(exc)s", { 'volume': volume['name'], 'exc': exc }) 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 exception.NexentaException as exc: LOG.warning( "Cannot delete temporary destination snapshot " "%(dst)s on NexentaStor Appliance: %(exc)s", { 'dst': dst_snapshot, 'exc': exc }) return True, None
def migrate_volume(self, ctxt, volume, host): """Migrate if volume and host are managed by Nexenta appliance. :param ctxt: context :param volume: a dictionary describing the volume to migrate :param host: a dictionary describing the host to migrate to """ LOG.debug("Enter: migrate_volume: id=%(id)s, host=%(host)s", {"id": volume["id"], "host": host}) false_ret = (False, None) if volume["status"] not in ("available", "retyping"): LOG.warning( _LW("Volume status must be 'available' or 'retyping'." " Current volume status: %s"), volume["status"] ) return false_ret if "capabilities" not in host: LOG.warning(_LW("Unsupported host. No capabilities found")) return false_ret capabilities = host["capabilities"] ns_shares = capabilities["ns_shares"] dst_parts = capabilities["location_info"].split(":") dst_host, dst_volume = dst_parts[1:] if ( capabilities.get("vendor_name") != "Nexenta" or dst_parts[0] != self.__class__.__name__ or capabilities["free_capacity_gb"] < volume["size"] ): return false_ret nms = self.share2nms[volume["provider_location"]] ssh_bindings = nms.appliance.ssh_list_bindings() shares = [] for bind in ssh_bindings: for share in ns_shares: if share.startswith(ssh_bindings[bind][3]) and ns_shares[share] >= volume["size"]: shares.append(share) if len(shares) == 0: LOG.warning(_LW("Remote NexentaStor appliance at %s should be " "SSH-bound."), share) return false_ret share = sorted(shares, key=ns_shares.get, reverse=True)[0] snapshot = { "volume_name": volume["name"], "volume_id": volume["id"], "name": utils.get_migrate_snapshot_name(volume), } self.create_snapshot(snapshot) location = volume["provider_location"] src = "%(share)s/%(volume)s@%(snapshot)s" % { "share": location.split(":")[1].split("volumes/")[1], "volume": volume["name"], "snapshot": snapshot["name"], } dst = ":".join([dst_host, dst_volume.split("/volumes/")[1]]) try: nms.appliance.execute(self._get_zfs_send_recv_cmd(src, dst)) except exception.NexentaException as exc: LOG.warning( _LW("Cannot send source snapshot %(src)s to " "destination %(dst)s. Reason: %(exc)s"), {"src": src, "dst": dst, "exc": exc}, ) return false_ret finally: try: self.delete_snapshot(snapshot) except exception.NexentaException as exc: LOG.warning( _LW("Cannot delete temporary source snapshot " "%(src)s on NexentaStor Appliance: %(exc)s"), {"src": src, "exc": exc}, ) try: self.delete_volume(volume) except exception.NexentaException as exc: LOG.warning( _LW("Cannot delete source volume %(volume)s on " "NexentaStor Appliance: %(exc)s"), {"volume": volume["name"], "exc": exc}, ) dst_nms = self._get_nms_for_url(capabilities["nms_url"]) dst_snapshot = "%s/%s@%s" % (dst_volume.split("volumes/")[1], volume["name"], snapshot["name"]) try: dst_nms.snapshot.destroy(dst_snapshot, "") except exception.NexentaException as exc: LOG.warning( _LW("Cannot delete temporary destination snapshot " "%(dst)s on NexentaStor Appliance: %(exc)s"), {"dst": dst_snapshot, "exc": exc}, ) return True, {"provider_location": share}