def create_lv_snapshot(self, name, source_lv_name, lv_type='default'): """Creates a snapshot of a logical volume. :param name: Name to assign to new snapshot :param source_lv_name: Name of Logical Volume to snapshot :param lv_type: Type of LV (default or thin) """ source_lvref = self.get_volume(source_lv_name) if source_lvref is None: LOG.error( _LE("Trying to create snapshot by non-existent LV: %s") % source_lv_name) raise exception.VolumeDeviceNotFound(device=source_lv_name) cmd = [ 'lvcreate', '--name', name, '--snapshot', '%s/%s' % (self.vg_name, source_lv_name) ] if lv_type != 'thin': size = source_lvref['size'] cmd.extend(['-L', '%sg' % (size)]) try: self._execute(*cmd, root_helper=self._root_helper, run_as_root=True) except putils.ProcessExecutionError as err: LOG.exception(_LE('Error creating snapshot')) LOG.error(_LE('Cmd :%s') % err.cmd) LOG.error(_LE('StdOut :%s') % err.stdout) LOG.error(_LE('StdErr :%s') % err.stderr) raise
def wait_for_path(self, volume_path): """Wait for a path to show up.""" LOG.debug("Checking to see if %s exists yet.", volume_path) if not os.path.exists(volume_path): LOG.debug("%(path)s doesn't exists yet.", {'path': volume_path}) raise exception.VolumeDeviceNotFound(device=volume_path) else: LOG.debug("%s has shown up.", volume_path)
def _wait_for_discovery(aoe_path): if os.path.exists(aoe_path): raise loopingcall.LoopingCallDone if waiting_status['tries'] >= self.device_scan_attempts: raise exception.VolumeDeviceNotFound(device=aoe_path) LOG.warn(_("AoE volume not yet found at: %(path)s. " "Try number: %(tries)s"), {'path': aoe_device, 'tries': waiting_status['tries']}) self._aoe_discover() waiting_status['tries'] += 1
def disconnect_volume(self, connection_properties, device_info): """Detach the volume from instance_name. connection_properties for iSCSI must include: target_portal(s) - IP and optional port target_iqn(s) - iSCSI Qualified Name target_lun(s) - LUN id of the volume """ # Moved _rescan_iscsi and _rescan_multipath # from _disconnect_volume_multipath_iscsi to here. # Otherwise, if we do rescan after _linuxscsi.remove_multipath_device # but before logging out, the removed devices under /dev/disk/by-path # will reappear after rescan. self._rescan_iscsi() if self.use_multipath: self._rescan_multipath() host_device = multipath_device = None host_devices = self._get_device_path(connection_properties) # Choose an accessible host device for dev in host_devices: if os.path.exists(dev): host_device = dev multipath_device = self._get_multipath_device_name(dev) if multipath_device: break if not host_device: LOG.error(_LE("No accessible volume device: %(host_devices)s"), {'host_devices': host_devices}) raise exception.VolumeDeviceNotFound(device=host_devices) if multipath_device: device_realpath = os.path.realpath(host_device) self._linuxscsi.remove_multipath_device(device_realpath) return self._disconnect_volume_multipath_iscsi( connection_properties, multipath_device) # When multiple portals/iqns/luns are specified, we need to remove # unused devices created by logging into other LUNs' session. ips_iqns_luns = self._multipath_targets(connection_properties) if not ips_iqns_luns: ips_iqns_luns = [[ connection_properties['target_portal'], connection_properties['target_iqn'], connection_properties.get('target_lun', 0) ]] for props in self._iterate_multiple_targets(connection_properties, ips_iqns_luns): self._disconnect_volume_iscsi(props)
def connect_volume(self, connection_properties): """Attach the volume to instance_name. connection_properties for iSCSI must include: target_portal - ip and optional port target_iqn - iSCSI Qualified Name target_lun - LUN id of the volume """ device_info = {'type': 'block'} if self.use_multipath: #multipath installed, discovering other targets if available target_portal = connection_properties['target_portal'] out = self._run_iscsiadm_bare(['-m', 'discovery', '-t', 'sendtargets', '-p', target_portal], check_exit_code=[0, 255])[0] \ or "" for ip, iqn in self._get_target_portals_from_iscsiadm_output(out): props = connection_properties.copy() props['target_portal'] = ip props['target_iqn'] = iqn self._connect_to_iscsi_portal(props) self._rescan_iscsi() else: self._connect_to_iscsi_portal(connection_properties) host_device = self._get_device_path(connection_properties) # The /dev/disk/by-path/... node is not always present immediately # TODO(justinsb): This retry-with-delay is a pattern, move to utils? tries = 0 while not os.path.exists(host_device): if tries >= self.device_scan_attempts: raise exception.VolumeDeviceNotFound(device=host_device) LOG.warn( _LW("ISCSI volume not yet found at: %(host_device)s. " "Will rescan & retry. Try number: %(tries)s"), { 'host_device': host_device, 'tries': tries }) # The rescan isn't documented as being necessary(?), but it helps self._run_iscsiadm(connection_properties, ("--rescan", )) tries = tries + 1 if not os.path.exists(host_device): time.sleep(tries**2) if tries != 0: LOG.debug( "Found iSCSI node %(host_device)s " "(after %(tries)s rescans)", { 'host_device': host_device, 'tries': tries }) if self.use_multipath: #we use the multipath device instead of the single path device self._rescan_multipath() multipath_device = self._get_multipath_device_name(host_device) if multipath_device is not None: host_device = multipath_device device_info['path'] = host_device return device_info
def connect_volume(self, connection_properties): """Attach the volume to instance_name. connection_properties for iSCSI must include: target_portal(s) - ip and optional port target_iqn(s) - iSCSI Qualified Name target_lun(s) - LUN id of the volume Note that plural keys may be used when use_multipath=True """ device_info = {'type': 'block'} if self.use_multipath: # multipath installed, discovering other targets if available for ip, iqn in self._discover_iscsi_portals(connection_properties): props = copy.deepcopy(connection_properties) props['target_portal'] = ip props['target_iqn'] = iqn self._connect_to_iscsi_portal(props) self._rescan_iscsi() host_devices = self._get_device_path(connection_properties) else: self._connect_to_iscsi_portal(connection_properties) host_devices = self._get_device_path(connection_properties) # The /dev/disk/by-path/... node is not always present immediately # TODO(justinsb): This retry-with-delay is a pattern, move to utils? tries = 0 # Loop until at least 1 path becomes available while all(map(lambda x: not os.path.exists(x), host_devices)): if tries >= self.device_scan_attempts: raise exception.VolumeDeviceNotFound(device=host_devices) LOG.warn( _LW("ISCSI volume not yet found at: %(host_devices)s. " "Will rescan & retry. Try number: %(tries)s"), { 'host_devices': host_devices, 'tries': tries }) # The rescan isn't documented as being necessary(?), but it helps if self.use_multipath: self._rescan_iscsi() else: self._run_iscsiadm(connection_properties, ("--rescan", )) tries = tries + 1 if all(map(lambda x: not os.path.exists(x), host_devices)): time.sleep(tries**2) else: break if tries != 0: LOG.debug( "Found iSCSI node %(host_devices)s " "(after %(tries)s rescans)", { 'host_devices': host_devices, 'tries': tries }) # Choose an accessible host device host_device = next(dev for dev in host_devices if os.path.exists(dev)) if self.use_multipath: # we use the multipath device instead of the single path device self._rescan_multipath() multipath_device = self._get_multipath_device_name(host_device) if multipath_device is not None: host_device = multipath_device device_info['path'] = host_device return device_info