def get_device_path(self, blockdevice_id): LOG.debug('Entering EMCVNXBlockAPI.get_device_path blockdevice_id=%s', blockdevice_id) lun_name = self._build_volume_name_from_blockdevice_id(blockdevice_id) lun = self.client.get_lun(lun_name) if self.client.is_lun_destroyed(lun): raise blockdevice.UnknownVolume(blockdevice_id) storage_groups = self.client.get_storage_group() for storage_group in storage_groups: if storage_group.has_alu(lun): hlu = storage_group.get_hlu(lun) break else: raise blockdevice.UnattachedVolume(blockdevice_id) # XXX This will only operate on one of the resulting device paths. # /sys/class/scsi_disk/x:x:x:HLU/device/block/sdvb for example. connection_properties = self.connector.build_connection_properties( hlu, storage_group=storage_group) device_info = self.connector.get_device_info( connection_properties['data']) LOG.info('Exiting EMCVNXBlockAPI.get_device_path device_info=%s', device_info) return filepath.FilePath(device_info['path'])
def _get_target_info(self, vol_name): """Build a dictionary of information about the target. :param vol_name: :return: dictionary containing the following info: iSCSI: target_portal(s) - ip and optional port target_iqn(s) - iSCSI Qualified Name target_lun(s) - LUN id of the volume FC: target_wwn - World Wide Name target_lun - LUN id of the volume ALL: volume - A dictionary representation of the Purity volume object """ conn_info = {} try: connected_hosts = self._array.list_volume_private_connections( vol_name) except purestorage.PureHTTPError as err: if err.code == 400 and ERR_MSG_NOT_EXIST in err.text: raise blockdevice.UnknownVolume(vol_name) else: raise for host_info in connected_hosts: if host_info["host"] == self._purity_hostname: conn_info = host_info break if not conn_info: raise blockdevice.UnattachedVolume(vol_name) return self._format_connection_info(conn_info)
def detach_volume(self, blockdevice_id): LOG.debug('Entering EMCVNXBlockAPI.detach_volume: blockdevice_id=%s', blockdevice_id) volume_name = self._build_volume_name_from_blockdevice_id( blockdevice_id) lun = self.client.get_lun(volume_name) if self.client.is_lun_destroyed(lun): raise blockdevice.UnknownVolume(blockdevice_id) storage_groups = self.client.get_storage_group() for storage_group in storage_groups: if storage_group.has_alu(lun): hlu = storage_group.get_hlu(lun) break else: raise blockdevice.UnattachedVolume(blockdevice_id) connection_properties = self.connector.build_connection_properties( hlu, storage_group=storage_group) self.connector.disconnect_volume(connection_properties['data']) storage_group.detach_alu(lun) LOG.debug('Exiting EMCVNXBlockAPI.detach_volume: ' 'storage_group=%s, volume_name=%s, volume_id=%s, hlu=%s, ' 'target_prop=%s', storage_group.name, volume_name, lun.lun_id, hlu, connection_properties)
def destroy_volume(self, blockdevice_id): """ Destroy an existing volume. :param unicode blockdevice_id: The unique identifier for the volume to destroy. :raises UnknownVolume: If the supplied ``blockdevice_id`` does not exist. :return: ``None`` """ try: eliot.Message.new(Info="Destroying Volume" + str(blockdevice_id)).write(_logger) try: self._disconnect_volume(blockdevice_id) except blockdevice.UnattachedVolume: # Don't worry if it is not connected, normally it won't be pass self._array.destroy_volume(blockdevice_id) except purestorage.PureHTTPError as err: if (err.code == 400 and (ERR_MSG_NOT_EXIST in err.text or ERR_MSG_PENDING_ERADICATION in err.text)): raise blockdevice.UnknownVolume(blockdevice_id) else: eliot.Message.new( Error="Failed to delete volume for dataset " + str(blockdevice_id), Exception=err).write(_logger) raise
def detach_volume(self, blockdevice_id): LOG.debug('Entering EMCUnityBlockAPI.detach_volume: blockdevice_id=%s', blockdevice_id) volume_name = self._build_volume_name_from_blockdevice_id( blockdevice_id) lun = self.client.get_lun(volume_name) if self.client.is_lun_destroyed(lun): raise blockdevice.UnknownVolume(blockdevice_id) hosts = self.client.get_host() for host in hosts: if host.has_alu(lun): hlu = host.get_hlu(lun) break else: raise blockdevice.UnattachedVolume(blockdevice_id) connection_properties = self.connector.build_connection_properties( hlu, host=host) self.connector.disconnect_volume(connection_properties['data']) host.detach_alu(lun) LOG.debug('Exiting EMCUnityBlockAPI.detach_volume: ' 'host=%s, volume_name=%s, volume_id=%s, hlu=%s, ' 'target_prop=%s', host.name, volume_name, lun.id, hlu, connection_properties)
def get_device_path(self, blockdevice_id): """Return the device path. Returns the local device path that has been allocated to the block device on the host to which it is currently attached. :param unicode blockdevice_id: The unique identifier for the block device. :raises UnknownVolume: If the supplied ``blockdevice_id`` does not exist. :raises UnattachedVolume: If the supplied ``blockdevice_id`` is not attached to a host. :returns: A ``FilePath`` for the device. """ # Check for volume volume = self.krest.search("volumes", scsi_sn=blockdevice_id) if volume.total == 0: raise blockdevice.UnknownVolume(blockdevice_id) volume = volume.hits[0] # Check for volume is mapped or not # NOTE: The assumption right now is if we are mapped, # we are mapped to the instance host. mapped = self.krest.search("mappings", volume=volume) if mapped.total == 0: # if not mapped raise exception raise blockdevice.UnattachedVolume(blockdevice_id) # Get devices path paths = self.api_client.find_paths(blockdevice_id) if paths: # return the first path LOG.info('%s path', paths[0]) return filepath.FilePath(paths[0]) return None
def detach_volume(self, blockdevice_id): """Detach ``blockdevice_id`` from whatever host it is attached to. :param unicode blockdevice_id: The unique identifier for the block device being detached. :raises UnknownVolume: If the supplied ``blockdevice_id`` does not exist. :raises UnattachedVolume: If the supplied ``blockdevice_id`` is not attached to anything. :returns: ``None`` """ LOG.info('Detaching %s', blockdevice_id) # Check for volume by block device id(scsi_sn) volume = self.krest.search("volumes", scsi_sn=blockdevice_id) if volume.total == 0: raise blockdevice.UnknownVolume(blockdevice_id) # First check if we are mapped. mapped = self.krest.search("mappings", volume=volume) if mapped.total == 0: raise blockdevice.UnattachedVolume(blockdevice_id) # executing sync cmd for synchronize data on disk with memory self.api_client.sync_device() paths = self.api_client.find_paths(blockdevice_id) for path in paths: if "/dev/mapper/" in path: self.api_client.remove_multipath(path) break # Make sure iqn is mapped with host. iqn = self.api_client.get_initiator_name() host_iqns = self.krest.search("host_iqns", iqn=iqn) if host_iqns.total > 0: # find host which is associate with iqn. host_iqns = self.api_client.rgetattr(host_iqns.hits[0], "host", None) # Get the mapped host host = self.api_client.rgetattr(mapped.hits[0], "host", None) # Make sure host should be exists for volume if not host: raise StorageDriverAPIException('Unable to locate server.') # Make sure both host have same name which is to be unmapped if host.name == host_iqns.name: mapped.hits[0].delete() LOG.info("Removed mapped host %s", host.name) if self.destroy_host: try: host.delete() except Exception as e: LOG.exception("Unable to delete host due to %s", e.message) pass # start iscsi rescan self._iscsi_rescan('detach') return None
def get_device_path(self, blockdevice_id): """Return the device path. Returns the local device path that has been allocated to the block device on the host to which it is currently attached. :param unicode blockdevice_id: The unique identifier for the block device. :raises UnknownVolume: If the supplied ``blockdevice_id`` does not exist. :raises UnattachedVolume: If the supplied ``blockdevice_id`` is not attached to a host. :returns: A ``FilePath`` for the device. """ device_id = None with self._client.open_connection() as api: # Check that we have that volume volume = api.find_volume(blockdevice_id) if not volume: raise blockdevice.UnknownVolume(blockdevice_id) scvolume = api.find_volume(blockdevice_id) device_id = scvolume['deviceId'] # First check if we are mapped # NOTE: The assumption right now is if we are mapped, # we are mapped to the local compute host. mappings = api.find_mapping_profiles(scvolume) if not mappings: raise blockdevice.UnattachedVolume(blockdevice_id) if not device_id: raise blockdevice.UnknownVolume(blockdevice_id) # Look for any new devices retries = 0 while retries < 4: paths = iscsi_utils.find_paths(device_id) if paths: # Just return the first path return filepath.FilePath(paths[0]).realpath() retries += 1 LOG.info('%s not found, attempt %d', device_id, retries) time.sleep(5) return None
def _disconnect_volume(self, vol_name): try: self._array.disconnect_host(self._purity_hostname, vol_name) except purestorage.PureHTTPError as err: if err.code == 400 and ERR_MSG_NOT_CONNECTED in err.text: raise blockdevice.UnattachedVolume(vol_name) elif err.code == 400 and ERR_MSG_NOT_EXIST in err.text: raise blockdevice.UnknownVolume(vol_name) else: raise
def destroy_volume(self, blockdevice_id): LOG.debug('Entering EMCVNXBlockAPI.destroy_volume: blockdevice_id=%s', blockdevice_id) volume_name = self._build_volume_name_from_blockdevice_id( blockdevice_id) lun = self.client.get_lun(name=volume_name) if self.client.is_lun_destroyed(lun): raise blockdevice.UnknownVolume(blockdevice_id) lun.delete() LOG.debug('Exiting EMCVNXBlockAPI.destroy_volume: volume_name=%s', volume_name)
def _connect_volume(self, vol_name): """Connect the volume object to our Purity host. We need to return a dictionary with target information that will be consumed by os-brick. """ try: connection = self._array.connect_host(self._purity_hostname, vol_name) except purestorage.PureHTTPError as err: if err.code == 400 and ERR_MSG_ALREADY_EXISTS in err.text: raise blockdevice.AlreadyAttachedVolume(vol_name) elif err.code == 400 and ERR_MSG_NOT_EXIST in err.text: raise blockdevice.UnknownVolume(vol_name) else: raise return self._format_connection_info(connection)
def detach_volume(self, blockdevice_id): """Detach ``blockdevice_id`` from whatever host it is attached to. :param unicode blockdevice_id: The unique identifier for the block device being detached. :raises UnknownVolume: If the supplied ``blockdevice_id`` does not exist. :raises UnattachedVolume: If the supplied ``blockdevice_id`` is not attached to anything. :returns: ``None`` """ LOG.info('Detaching %s', blockdevice_id) with self._client.open_connection() as api: # Check that we have that volume scvolume = api.find_volume(blockdevice_id) if not scvolume: raise blockdevice.UnknownVolume(blockdevice_id) # First check if we are mapped mappings = api.find_mapping_profiles(scvolume) if not mappings: raise blockdevice.UnattachedVolume(blockdevice_id) device_id = scvolume['deviceId'] paths = iscsi_utils.find_paths(device_id) paths.reverse() for path in paths: iscsi_utils.remove_device(path) # Make sure we have a server defined for this host iqn = iscsi_utils.get_initiator_name() host = api.find_server(iqn) LOG.info("Search for server returned: %s", host) if not host: # Try to create a new host host = api.create_server(self.compute_instance_id(), iqn) LOG.info("Created server %s", host) # Make sure we were able to find something if not host: raise BlockDriverAPIException('Unable to locate server.') api.unmap_volume(scvolume, host) self._do_rescan('detach')
def resize_volume(self, blockdevice_id, size): """Resize an existing volume. :param blockdevice_id: The unique identifier for the device. :param size: The new requested size. :raises UnknownVolume: If the supplied ``blockdevice_id`` does not exist. :returns: ``None`` """ with self._client.open_connection() as api: # Check that we have that volume scvolume = api.find_volume(blockdevice_id) if not scvolume: raise blockdevice.UnknownVolume(blockdevice_id) volume_size = self._bytes_to_gig(size) if not api.expand_volume(scvolume, volume_size): raise blockdevice.VolumeException(blockdevice_id)
def attach_volume(self, blockdevice_id, attach_to): LOG.debug('Entering EMCVNXBlockAPI.attach_volume: ' 'blockdevice_id=%s, attach_to=%s', blockdevice_id, attach_to) volume_name = self._build_volume_name_from_blockdevice_id( blockdevice_id) lun = self.client.get_lun(name=volume_name) if self.client.is_lun_destroyed(lun): raise blockdevice.UnknownVolume(blockdevice_id) if self._lun_already_in_sg(lun): raise blockdevice.AlreadyAttachedVolume(blockdevice_id) host_info = self._build_host_info(self.host_ip, attach_to) storage_group = self._assure_storage_group(host_info) try: hlu = self._assure_host_access(storage_group, lun) except exception.VNXAluAlreadyAttachedError: raise blockdevice.AlreadyAttachedVolume(blockdevice_id) connection_properties = self.connector.build_connection_properties( hlu, storage_group=storage_group) self.connector.connect_volume(connection_properties['data']) volume_size = self._gib_to_bytes(lun.total_capacity_gb) volume = loopback._blockdevicevolume_from_blockdevice_id( blockdevice_id=blockdevice_id, size=volume_size, attached_to=unicode(attach_to)) LOG.debug('Exiting EMCVNXBlockAPI.attach_volume: ' 'storage_group=%s, volume_name=%s, volume_size=%s, hlu=%s, ' 'target_prop=%s', attach_to, volume_name, volume_size, hlu, connection_properties) return volume
def attach_volume(self, blockdevice_id, attach_to): LOG.debug('Entering EMCUnityBlockAPI.attach_volume: ' 'blockdevice_id=%s, attach_to=%s', blockdevice_id, attach_to) volume_name = self._build_volume_name_from_blockdevice_id( blockdevice_id) lun = self.client.get_lun(name=volume_name) if self.client.is_lun_destroyed(lun): raise blockdevice.UnknownVolume(blockdevice_id) if self._lun_already_in_host(lun): raise blockdevice.AlreadyAttachedVolume(blockdevice_id) host_info = self._build_host_info(attach_to) host = self._assure_host(host_info) try: hlu = self._assure_host_access(host, lun) except exception.UnityAluAlreadyAttachedError: raise blockdevice.AlreadyAttachedVolume(blockdevice_id) connection_properties = self.connector.build_connection_properties( hlu, host=host) self.connector.connect_volume(connection_properties['data']) volume = loopback._blockdevicevolume_from_blockdevice_id( blockdevice_id=blockdevice_id, size=lun.size_total, attached_to=unicode(attach_to)) LOG.debug('Exiting EMCUnityBlockAPI.attach_volume: ' 'host=%s, volume_name=%s, volume_size=%s, hlu=%s, ' 'target_prop=%s', attach_to, volume_name, lun.size_total, hlu, connection_properties) return volume
def destroy_volume(self, blockdevice_id): """Destroy an existing volume from an initiator (host). :param blockdevice_id: The volume unique ID. """ LOG.info('Destroying volume %s', blockdevice_id) try: volume = self.krest.search("volumes", scsi_sn=blockdevice_id) if volume.total == 0: raise blockdevice.UnknownVolume(blockdevice_id) volume = volume.hits[0] volume_group = self.api_client.rgetattr(volume, "volume_group", None) volume.delete() volume_group.delete() except Exception: raise StorageDriverAPIException( 'Error destroying volume blockdevice_id:{}'.format( blockdevice_id)) return None
def destroy_volume(self, blockdevice_id): """Destroy an existing volume. :param blockdevice_id: The volume unique ID. """ deleted = False LOG.info('Destroying volume %s', blockdevice_id) with self._client.open_connection() as api: try: volume = api.find_volume(blockdevice_id) if not volume: raise blockdevice.UnknownVolume(blockdevice_id) deleted = api.delete_volume(blockdevice_id) except Exception: # TODO(smcginnis) Catch more specific exception LOG.exception('Error destroying volume.') raise if not deleted: # Something happened raise BlockDriverAPIException('Unable to delete volume.')
def attach_volume(self, blockdevice_id, attach_to): """Attach an existing volume to an initiator (host). :param blockdevice_id: The unique identifier(scsi_sn of k2) for the volume. :param attach_to: It is a hostname of node which is returned by "compute_instance_id" method. :raises UnknownVolume: If the supplied "blockdevice_id" does not exist. :returns: A "BlockDeviceVolume" with a "attached_to" attribute set to "attach_to". """ LOG.info('attaching to blockdevice_id %s and host is %s', blockdevice_id, attach_to) # Searching for volume by scsi_sn via krest volume = self.krest.search("volumes", scsi_sn=blockdevice_id) if volume.total == 0: raise blockdevice.UnknownVolume(blockdevice_id) # Check for host which is associate with iqn(iSCSI Qualified Name) iqn = self.api_client.get_initiator_name() host_iqns = self.krest.search("host_iqns", iqn=iqn) host = self.api_client.rgetattr(host_iqns.hits[0], "host", None) \ if host_iqns.total > 0 else None # if iqn is not associate with any host if not host: # searching instance or node host which is return # by compute_instance_id method. host = self.krest.search("hosts", name=attach_to) if host.total > 0: raise InvalidDataException( 'Present host is not mapped with iqn') else: host = self._create_new_host(attach_to) self._map_host_with_iqn(host_iqns.hits[0], host) # Make sure the server is logged in to the array ips = self.krest.search("system/net_ips") for ip in ips.hits: self.api_client.iscsi_login( self.api_client.rgetattr(ip, 'ip_address', None), 3260) # Make sure we were able to find host if not host: raise InvalidDataException('Host does not exits') volume = volume.hits[0] # First check if we are already mapped mapped = self.krest.search('mappings', volume=volume) if mapped.total > 0: # Get the mapped host mapped_host = self.api_client.rgetattr(mapped.hits[0], "host", None) if mapped_host != host: LOG.info("Mapped server %s", mapped_host) # raise exception for attached volume raise blockdevice.AlreadyAttachedVolume(blockdevice_id) # Make sure host should not be associate with host group # Note: Currently host groups not supported. try: mapping = self.krest.new("mappings", volume=volume, host=host) mapping.save() LOG.info("Mapping is done- %s", mapping) except Exception: raise StorageDriverAPIException('Unable to map volume to server.') # start iscsi rescan self._iscsi_rescan('attach') return self._return_to_block_device_volume(volume, attach_to)
def attach_volume(self, blockdevice_id, attach_to): """Attach an existing volume to an initiator. :param blockdevice_id: The unique identifier for the volume. :param attach_to: An identifier like the one returned by the ``compute_instance_id`` method indicating the node to which to attach the volume. :raises UnknownVolume: If the supplied ``blockdevice_id`` does not exist. :returns: A ``BlockDeviceVolume`` with a ``attached_to`` attribute set to ``attach_to``. """ LOG.info('Attaching %s to %s', blockdevice_id, attach_to) # Functional tests expect a failure if it's already # attached, even if we're being asked to attach to # the same host. # not_local = attach_to != self.compute_instance_id() not_local = True with self._client.open_connection() as api: # Check that we have that volume scvolume = api.find_volume(blockdevice_id) if not scvolume: raise blockdevice.UnknownVolume(blockdevice_id) # Make sure we have a server defined for this host iqn = iscsi_utils.get_initiator_name() host = api.find_server(iqn) LOG.info("Search for server returned: %s", host) if not host: # Try to create a new host host = api.create_server(attach_to, iqn) LOG.info("Created server %s", host) # Make sure the server is logged in to the array ports = api.get_iscsi_ports() for port in ports: iscsi_utils.iscsi_login(port[0], port[1]) # Make sure we were able to find something if not host: raise BlockDriverAPIException() # First check if we are already mapped mappings = api.find_mapping_profiles(scvolume) if mappings: # See if it is to this server if not_local: raise blockdevice.AlreadyAttachedVolume(blockdevice_id) for mapping in mappings: if (mapping['server']['instanceName'] != host['instanceName']): raise blockdevice.AlreadyAttachedVolume(blockdevice_id) mapping = api.map_volume(scvolume, host) if not mapping: raise BlockDriverAPIException( 'Unable to map volume to server.') self._do_rescan('attach') return self._to_blockdevicevolume(scvolume, attach_to)