def _verify_rtstool(self): try: # This call doesn't need locking utils.execute('storage-rtstool', 'verify') except (OSError, putils.ProcessExecutionError): LOG.error(_LE('storage-rtstool is not installed correctly')) raise
def _make_link(volume_path, backup_path, vol_id): """Create a hard link for the volume block device. The IBM TSM client performs an image backup on a block device. The name of the block device is the backup prefix plus the backup id :param volume_path: real device path name for volume :param backup_path: path name TSM will use as volume to backup :param vol_id: id of volume to backup (for reporting) :raises: InvalidBackup """ try: utils.execute('ln', volume_path, backup_path, run_as_root=True, check_exit_code=True) except processutils.ProcessExecutionError as exc: err = (_('backup: %(vol_id)s failed to create device hardlink ' 'from %(vpath)s to %(bpath)s.\n' 'stdout: %(out)s\n stderr: %(err)s') % { 'vol_id': vol_id, 'vpath': volume_path, 'bpath': backup_path, 'out': exc.stdout, 'err': exc.stderr }) LOG.error(err) raise exception.InvalidBackup(reason=err)
def _make_link(volume_path, backup_path, vol_id): """Create a hard link for the volume block device. The IBM TSM client performs an image backup on a block device. The name of the block device is the backup prefix plus the backup id :param volume_path: real device path name for volume :param backup_path: path name TSM will use as volume to backup :param vol_id: id of volume to backup (for reporting) :raises: InvalidBackup """ try: utils.execute('ln', volume_path, backup_path, run_as_root=True, check_exit_code=True) except processutils.ProcessExecutionError as exc: err = (_('backup: %(vol_id)s failed to create device hardlink ' 'from %(vpath)s to %(bpath)s.\n' 'stdout: %(out)s\n stderr: %(err)s') % {'vol_id': vol_id, 'vpath': volume_path, 'bpath': backup_path, 'out': exc.stdout, 'err': exc.stderr}) LOG.error(err) raise exception.InvalidBackup(reason=err)
def _verify_rtstool(self): try: # This call doesn't need locking utils.execute('storage-rtstool', 'verify') except (OSError, putils.ProcessExecutionError): LOG.error(_LE('storage-rtstool is not installed correctly')) raise
def update_config_file(self, name, tid, path, config_auth): conf_file = self.iet_conf vol_id = name.split(':')[1] # If config file does not exist, create a blank conf file and # add configuration for the volume on the new file. if not os.path.exists(conf_file): try: utils.execute("truncate", conf_file, "--size=0", run_as_root=True) except putils.ProcessExecutionError: LOG.exception(_LE("Failed to create %(conf)s for volume " "id:%(vol_id)s"), {'conf': conf_file, 'vol_id': vol_id}) raise exception.ISCSITargetCreateFailed(volume_id=vol_id) try: volume_conf = """ Target %s %s Lun 0 Path=%s,Type=%s """ % (name, config_auth, path, self._iotype(path)) with utils.temporary_chown(conf_file): with open(conf_file, 'a+') as f: f.write(volume_conf) except Exception: LOG.exception(_LE("Failed to update %(conf)s for volume " "id:%(vol_id)s"), {'conf': conf_file, 'vol_id': vol_id}) raise exception.ISCSITargetCreateFailed(volume_id=vol_id)
def _delete_logicalunit(self, tid, lun): utils.execute('ietadm', '--op', 'delete', '--tid=%s' % tid, '--lun=%d' % lun, run_as_root=True)
def _new_auth(self, tid, type, username, password): utils.execute('ietadm', '--op', 'new', '--tid=%s' % tid, '--user', '--params=%s=%s,Password=%s' % (type, username, password), run_as_root=True)
def _force_delete_target(self, tid, sid, cid): utils.execute('ietadm', '--op', 'delete', '--tid=%s' % tid, '--sid=%s' % sid, '--cid=%s' % cid, run_as_root=True)
def _new_auth(self, tid, type, username, password): utils.execute('ietadm', '--op', 'new', '--tid=%s' % tid, '--user', '--params=%s=%s,Password=%s' % (type, username, password), run_as_root=True)
def _new_target(self, name, tid): """Create new scsi target using specified parameters. If the target already exists, ietadm returns 'Invalid argument' and error code '234'. This should be ignored for ensure export case. """ utils.execute('ietadm', '--op', 'new', '--tid=%s' % tid, '--params', 'Name=%s' % name, run_as_root=True, check_exit_code=[0, 234])
def _limit_bps(self, rw, dev, bps): try: utils.execute('cgset', '-r', 'blkio.throttle.%s_bps_device=%s %d' % (rw, dev, bps), self.cgroup, run_as_root=True) except processutils.ProcessExecutionError: LOG.warning( _LW('Failed to setup blkio cgroup to throttle the ' 'device \'%(device)s\'.'), {'device': dev})
def clear_volume(volume_size, volume_path, volume_clear=None, volume_clear_size=None, volume_clear_ionice=None, throttle=None): """Unprovision old volumes to prevent data leaking between users.""" if volume_clear is None: volume_clear = CONF.volume_clear if volume_clear_size is None: volume_clear_size = CONF.volume_clear_size if volume_clear_size == 0: volume_clear_size = volume_size if volume_clear_ionice is None: volume_clear_ionice = CONF.volume_clear_ionice LOG.info(_LI("Performing secure delete on volume: %s"), volume_path) # We pass sparse=False explicitly here so that zero blocks are not # skipped in order to clear the volume. if volume_clear == 'zero': return copy_volume('/dev/zero', volume_path, volume_clear_size, CONF.volume_dd_blocksize, sync=True, execute=utils.execute, ionice=volume_clear_ionice, throttle=throttle, sparse=False) elif volume_clear == 'shred': clear_cmd = ['shred', '-n3'] if volume_clear_size: clear_cmd.append('-s%dMiB' % volume_clear_size) else: raise exception.InvalidConfigurationValue(option='volume_clear', value=volume_clear) clear_cmd.append(volume_path) start_time = timeutils.utcnow() utils.execute(*clear_cmd, run_as_root=True) duration = timeutils.delta_seconds(start_time, timeutils.utcnow()) # NOTE(jdg): use a default of 1, mostly for unit test, but in # some incredible event this is 0 (cirros image?) don't barf if duration < 1: duration = 1 LOG.info(_LI('Elapsed time for clear volume: %.2f sec'), duration)
def _new_logicalunit(self, tid, lun, path): """Attach a new volume to scsi target as a logical unit. If a logical unit exists on the specified target lun, ietadm returns 'File exists' and error code '239'. This should be ignored for ensure export case. """ utils.execute('ietadm', '--op', 'new', '--tid=%s' % tid, '--lun=%d' % lun, '--params', 'Path=%s,Type=%s' % (path, self._iotype(path)), run_as_root=True, check_exit_code=[0, 239])
def __init__(self, bps_limit, cgroup_name): self.bps_limit = bps_limit self.cgroup = cgroup_name self.srcdevs = {} self.dstdevs = {} try: utils.execute('cgcreate', '-g', 'blkio:%s' % self.cgroup, run_as_root=True) except processutils.ProcessExecutionError: LOG.error(_LE('Failed to create blkio cgroup \'%(name)s\'.'), {'name': cgroup_name}) raise
def _new_target(self, name, tid): """Create new scsi target using specified parameters. If the target already exists, ietadm returns 'Invalid argument' and error code '234'. This should be ignored for ensure export case. """ utils.execute('ietadm', '--op', 'new', '--tid=%s' % tid, '--params', 'Name=%s' % name, run_as_root=True, check_exit_code=[0, 234])
def check_for_odirect_support(src, dest, flag='oflag=direct'): # Check whether O_DIRECT is supported try: # iflag=direct and if=/dev/zero combination does not work # error: dd: failed to open '/dev/zero': Invalid argument if (src == '/dev/zero' and flag == 'iflag=direct'): return False else: utils.execute('dd', 'count=0', 'if=%s' % src, 'of=%s' % dest, flag, run_as_root=True) return True except processutils.ProcessExecutionError: return False
def _convert_image(prefix, source, dest, out_format, run_as_root=True): """Convert image to other format.""" cmd = prefix + ('qemu-img', 'convert', '-O', out_format, source, dest) # Check whether O_DIRECT is supported and set '-t none' if it is # This is needed to ensure that all data hit the device before # it gets unmapped remotely from the host for some backends # Reference Bug: #1363016 # NOTE(jdg): In the case of file devices qemu does the # flush properly and more efficiently than would be done # setting O_DIRECT, so check for that and skip the # setting for non BLK devs if (utils.is_blk_device(dest) and volume_utils.check_for_odirect_support( source, dest, 'oflag=direct')): cmd = prefix + ('qemu-img', 'convert', '-t', 'none', '-O', out_format, source, dest) start_time = timeutils.utcnow() utils.execute(*cmd, run_as_root=run_as_root) duration = timeutils.delta_seconds(start_time, timeutils.utcnow()) # NOTE(jdg): use a default of 1, mostly for unit test, but in # some incredible event this is 0 (cirros image?) don't barf if duration < 1: duration = 1 try: image_size = qemu_img_info(source, run_as_root=True).virtual_size except ValueError as e: msg = _LI("The image was successfully converted, but image size " "is unavailable. src %(src)s, dest %(dest)s. %(error)s") LOG.info(msg, {"src": source, "dest": dest, "error": e}) return fsz_mb = image_size / units.Mi mbps = (fsz_mb / duration) msg = ("Image conversion details: src %(src)s, size %(sz).2f MB, " "duration %(duration).2f sec, destination %(dest)s") LOG.debug(msg, { "src": source, "sz": fsz_mb, "duration": duration, "dest": dest }) msg = _LI("Converted %(sz).2f MB image at %(mbps).2f MB/s") LOG.info(msg, {"sz": fsz_mb, "mbps": mbps})
def qemu_img_info(path, run_as_root=True): """Return an object containing the parsed output from qemu-img info.""" cmd = ('env', 'LC_ALL=C', 'qemu-img', 'info', path) if os.name == 'nt': cmd = cmd[2:] out, _err = utils.execute(*cmd, run_as_root=run_as_root) return imageutils.QemuImgInfo(out)
def _recreate_backing_lun(self, iqn, tid, name, path): LOG.warning(_LW('Attempting recreate of backing lun...')) # Since we think the most common case of this is a dev busy # (create vol from snapshot) we're going to add a sleep here # this will hopefully give things enough time to stabilize # how long should we wait?? I have no idea, let's go big # and error on the side of caution time.sleep(10) (out, err) = (None, None) try: (out, err) = utils.execute('tgtadm', '--lld', 'iscsi', '--op', 'new', '--mode', 'logicalunit', '--tid', tid, '--lun', '1', '-b', path, run_as_root=True) except putils.ProcessExecutionError as e: LOG.error(_LE("Failed recovery attempt to create " "iscsi backing lun for Volume " "ID:%(vol_id)s: %(e)s"), {'vol_id': name, 'e': e}) finally: LOG.debug('StdOut from recreate backing lun: %s', out) LOG.debug('StdErr from recreate backing lun: %s', err)
def _do_backup(self, backup_path, vol_id, backup_mode): """Perform the actual backup operation. :param backup_path: volume path :param vol_id: volume id :param backup_mode: file mode of source volume; 'image' or 'file' :raises: InvalidBackup """ backup_attrs = {'Total number of objects backed up': '1'} compr_flag = 'yes' if CONF.backup_tsm_compression else 'no' backup_cmd = ['dsmc', 'backup'] if _image_mode(backup_mode): backup_cmd.append('image') backup_cmd.extend(['-quiet', '-compression=%s' % compr_flag, '-password=%s' % self.tsm_password, backup_path]) out, err = utils.execute(*backup_cmd, run_as_root=True, check_exit_code=False) success = _check_dsmc_output(out, backup_attrs, exact_match=False) if not success: err = (_('backup: %(vol_id)s failed to obtain backup ' 'success notification from server.\n' 'stdout: %(out)s\n stderr: %(err)s') % {'vol_id': vol_id, 'out': out, 'err': err}) LOG.error(err) raise exception.InvalidBackup(reason=err)
def _do_iscsi_discovery(self, volume): # TODO(justinsb): Deprecate discovery and use stored info # NOTE(justinsb): Discovery won't work with CHAP-secured targets (?) LOG.warning(_LW("ISCSI provider_location not stored, using discovery")) volume_id = volume['id'] try: # NOTE(griff) We're doing the split straight away which should be # safe since using '@' in hostname is considered invalid (out, _err) = utils.execute('iscsiadm', '-m', 'discovery', '-t', 'sendtargets', '-p', volume['host'].split('@')[0], run_as_root=True) except processutils.ProcessExecutionError as ex: LOG.error(_LE("ISCSI discovery attempt failed for:%s") % volume['host'].split('@')[0]) LOG.debug(("Error from iscsiadm -m discovery: %s") % ex.stderr) return None for target in out.splitlines(): if (self.configuration.safe_get('iscsi_ip_address') in target and volume_id in target): return target return None
def _execute(*args, **kwargs): """Locked execution to prevent racing issues. Racing issues are derived from a bug in RTSLib: https://github.com/agrover/rtslib-fb/issues/36 """ return utils.execute(*args, **kwargs)
def _execute(*args, **kwargs): """Locked execution to prevent racing issues. Racing issues are derived from a bug in RTSLib: https://github.com/agrover/rtslib-fb/issues/36 """ return utils.execute(*args, **kwargs)
def _do_tgt_update(self, name): (out, err) = utils.execute('tgt-admin', '--update', name, run_as_root=True) LOG.debug("StdOut from tgt-admin --update: %s", out) LOG.debug("StdErr from tgt-admin --update: %s", err)
def get_qemu_img_version(): info = utils.execute('qemu-img', '--help', check_exit_code=False)[0] pattern = r"qemu-img version ([0-9\.]*)" version = re.match(pattern, info) if not version: LOG.warning(_LW("qemu-img is not installed.")) return None return _get_version_from_string(version.groups()[0])
def _new_logicalunit(self, tid, lun, path): """Attach a new volume to scsi target as a logical unit. If a logical unit exists on the specified target lun, ietadm returns 'File exists' and error code '239'. This should be ignored for ensure export case. """ utils.execute('ietadm', '--op', 'new', '--tid=%s' % tid, '--lun=%d' % lun, '--params', 'Path=%s,Type=%s' % (path, self._iotype(path)), run_as_root=True, check_exit_code=[0, 239])
def clear_volume(volume_size, volume_path, volume_clear=None, volume_clear_size=None, volume_clear_ionice=None, throttle=None): """Unprovision old volumes to prevent data leaking between users.""" if volume_clear is None: volume_clear = CONF.volume_clear if volume_clear_size is None: volume_clear_size = CONF.volume_clear_size if volume_clear_size == 0: volume_clear_size = volume_size if volume_clear_ionice is None: volume_clear_ionice = CONF.volume_clear_ionice LOG.info(_LI("Performing secure delete on volume: %s"), volume_path) # We pass sparse=False explicitly here so that zero blocks are not # skipped in order to clear the volume. if volume_clear == 'zero': return copy_volume('/dev/zero', volume_path, volume_clear_size, CONF.volume_dd_blocksize, sync=True, execute=utils.execute, ionice=volume_clear_ionice, throttle=throttle, sparse=False) elif volume_clear == 'shred': clear_cmd = ['shred', '-n3'] if volume_clear_size: clear_cmd.append('-s%dMiB' % volume_clear_size) else: raise exception.InvalidConfigurationValue( option='volume_clear', value=volume_clear) clear_cmd.append(volume_path) start_time = timeutils.utcnow() utils.execute(*clear_cmd, run_as_root=True) duration = timeutils.delta_seconds(start_time, timeutils.utcnow()) # NOTE(jdg): use a default of 1, mostly for unit test, but in # some incredible event this is 0 (cirros image?) don't barf if duration < 1: duration = 1 LOG.info(_LI('Elapsed time for clear volume: %.2f sec'), duration)
def check_for_odirect_support(src, dest, flag='oflag=direct'): # Check whether O_DIRECT is supported try: # iflag=direct and if=/dev/zero combination does not work # error: dd: failed to open '/dev/zero': Invalid argument if (src == '/dev/zero' and flag == 'iflag=direct'): return False else: utils.execute('dd', 'count=0', 'if=%s' % src, 'of=%s' % dest, flag, run_as_root=True) return True except processutils.ProcessExecutionError: return False
def delete(self, backup): """Delete the given backup from TSM server. :param backup: backup information for volume :raises: InvalidBackup """ delete_attrs = {'Total number of objects deleted': '1'} delete_path, backup_mode = _get_backup_metadata(backup, 'restore') LOG.debug('Delete started for backup: %(backup)s, mode: %(mode)s.', {'backup': backup.id, 'mode': backup_mode}) try: out, err = utils.execute('dsmc', 'delete', 'backup', '-quiet', '-noprompt', '-objtype=%s' % backup_mode, '-password=%s' % self.tsm_password, delete_path, run_as_root=True, check_exit_code=False) except processutils.ProcessExecutionError as exc: err = (_('delete: %(vol_id)s failed to run dsmc with ' 'stdout: %(out)s\n stderr: %(err)s') % {'vol_id': backup.volume_id, 'out': exc.stdout, 'err': exc.stderr}) LOG.error(err) raise exception.InvalidBackup(reason=err) except exception.Error as exc: err = (_('delete: %(vol_id)s failed to run dsmc ' 'due to invalid arguments with ' 'stdout: %(out)s\n stderr: %(err)s') % {'vol_id': backup.volume_id, 'out': exc.stdout, 'err': exc.stderr}) LOG.error(err) raise exception.InvalidBackup(reason=err) success = _check_dsmc_output(out, delete_attrs) if not success: # log error if tsm cannot delete the backup object # but do not raise exception so that storage backup # object can be removed. LOG.error(_LE('delete: %(vol_id)s failed with ' 'stdout: %(out)s\n stderr: %(err)s'), {'vol_id': backup.volume_id, 'out': out, 'err': err}) LOG.debug('Delete %s finished.', backup['id'])
def _get_target(self, iqn): (out, err) = utils.execute('tgt-admin', '--show', run_as_root=True) lines = out.split('\n') for line in lines: if iqn in line: parsed = line.split() tid = parsed[1] return tid[:-1] return None
def _get_target(self, iqn): (out, err) = utils.execute('tgt-admin', '--show', run_as_root=True) lines = out.split('\n') for line in lines: if iqn in line: parsed = line.split() tid = parsed[1] return tid[:-1] return None
def _cleanup_device_hardlink(hardlink_path, volume_path, volume_id): """Remove the hardlink for the volume block device. :param hardlink_path: hardlink to the volume block device :param volume_path: real path of the backup/restore device :param volume_id: Volume id for backup or as restore target """ try: utils.execute('rm', '-f', hardlink_path, run_as_root=True) except processutils.ProcessExecutionError as exc: LOG.error( _LE('backup: %(vol_id)s failed to remove backup hardlink ' 'from %(vpath)s to %(bpath)s.\n' 'stdout: %(out)s\n stderr: %(err)s.'), { 'vol_id': volume_id, 'vpath': volume_path, 'bpath': hardlink_path, 'out': exc.stdout, 'err': exc.stderr })
def update_config_file(self, name, tid, path, config_auth): conf_file = self.iet_conf vol_id = name.split(':')[1] # If config file does not exist, create a blank conf file and # add configuration for the volume on the new file. if not os.path.exists(conf_file): try: utils.execute("truncate", conf_file, "--size=0", run_as_root=True) except putils.ProcessExecutionError: LOG.exception( _LE("Failed to create %(conf)s for volume " "id:%(vol_id)s"), { 'conf': conf_file, 'vol_id': vol_id }) raise exception.ISCSITargetCreateFailed(volume_id=vol_id) try: volume_conf = """ Target %s %s Lun 0 Path=%s,Type=%s """ % (name, config_auth, path, self._iotype(path)) with utils.temporary_chown(conf_file): with open(conf_file, 'a+') as f: f.write(volume_conf) except Exception: LOG.exception( _LE("Failed to update %(conf)s for volume " "id:%(vol_id)s"), { 'conf': conf_file, 'vol_id': vol_id }) raise exception.ISCSITargetCreateFailed(volume_id=vol_id)
def _cleanup_device_hardlink(hardlink_path, volume_path, volume_id): """Remove the hardlink for the volume block device. :param hardlink_path: hardlink to the volume block device :param volume_path: real path of the backup/restore device :param volume_id: Volume id for backup or as restore target """ try: utils.execute('rm', '-f', hardlink_path, run_as_root=True) except processutils.ProcessExecutionError as exc: LOG.error(_LE('backup: %(vol_id)s failed to remove backup hardlink ' 'from %(vpath)s to %(bpath)s.\n' 'stdout: %(out)s\n stderr: %(err)s.'), {'vol_id': volume_id, 'vpath': volume_path, 'bpath': hardlink_path, 'out': exc.stdout, 'err': exc.stderr})
def _get_target(self, iqn): # We can use target=iqn here, but iscsictl has no --brief mode, and # this way we save on a lot of unnecessary parsing (out, err) = utils.execute('iscsictl', '-c', 'target=ALL', run_as_root=True) lines = out.split('\n') for line in lines: if iqn in line: parsed = line.split() tid = parsed[2] return tid[3:].rstrip(',') return None
def _verify_backing_lun(self, iqn, tid): backing_lun = True capture = False target_info = [] (out, err) = utils.execute('tgt-admin', '--show', run_as_root=True) lines = out.split('\n') for line in lines: if iqn in line and "Target %s" % tid in line: capture = True if capture: target_info.append(line) if iqn not in line and 'Target ' in line: capture = False if ' LUN: 1' not in target_info: backing_lun = False return backing_lun
def _verify_backing_lun(self, iqn, tid): backing_lun = True capture = False target_info = [] (out, err) = utils.execute('tgt-admin', '--show', run_as_root=True) lines = out.split('\n') for line in lines: if iqn in line and "Target %s" % tid in line: capture = True if capture: target_info.append(line) if iqn not in line and 'Target ' in line: capture = False if ' LUN: 1' not in target_info: backing_lun = False return backing_lun
def _do_restore(self, backup_path, restore_path, vol_id, backup_mode): """Perform the actual restore operation. :param backup_path: the path the backup was created from, this identifies the backup to tsm :param restore_path: volume path to restore into :param vol_id: volume id :param backup_mode: mode used to create the backup ('image' or 'file') :raises: InvalidBackup """ restore_attrs = {'Total number of objects restored': '1'} restore_cmd = ['dsmc', 'restore'] if _image_mode(backup_mode): restore_cmd.append('image') restore_cmd.append('-noprompt') # suppress prompt else: restore_cmd.append('-replace=yes') # suppress prompt restore_cmd.extend( ['-quiet', '-password=%s' % self.tsm_password, backup_path]) if restore_path != backup_path: restore_cmd.append(restore_path) out, err = utils.execute(*restore_cmd, run_as_root=True, check_exit_code=False) success = _check_dsmc_output(out, restore_attrs) if not success: err = (_('restore: %(vol_id)s failed.\n' 'stdout: %(out)s\n stderr: %(err)s.') % { 'vol_id': vol_id, 'out': out, 'err': err }) LOG.error(err) raise exception.InvalidBackup(reason=err)
def _do_restore(self, backup_path, restore_path, vol_id, backup_mode): """Perform the actual restore operation. :param backup_path: the path the backup was created from, this identifies the backup to tsm :param restore_path: volume path to restore into :param vol_id: volume id :param backup_mode: mode used to create the backup ('image' or 'file') :raises: InvalidBackup """ restore_attrs = {'Total number of objects restored': '1'} restore_cmd = ['dsmc', 'restore'] if _image_mode(backup_mode): restore_cmd.append('image') restore_cmd.append('-noprompt') # suppress prompt else: restore_cmd.append('-replace=yes') # suppress prompt restore_cmd.extend(['-quiet', '-password=%s' % self.tsm_password, backup_path]) if restore_path != backup_path: restore_cmd.append(restore_path) out, err = utils.execute(*restore_cmd, run_as_root=True, check_exit_code=False) success = _check_dsmc_output(out, restore_attrs) if not success: err = (_('restore: %(vol_id)s failed.\n' 'stdout: %(out)s\n stderr: %(err)s.') % {'vol_id': vol_id, 'out': out, 'err': err}) LOG.error(err) raise exception.InvalidBackup(reason=err)
def _recreate_backing_lun(self, iqn, tid, name, path): LOG.warning(_LW('Attempting recreate of backing lun...')) # Since we think the most common case of this is a dev busy # (create vol from snapshot) we're going to add a sleep here # this will hopefully give things enough time to stabilize # how long should we wait?? I have no idea, let's go big # and error on the side of caution time.sleep(10) (out, err) = (None, None) try: (out, err) = utils.execute('tgtadm', '--lld', 'iscsi', '--op', 'new', '--mode', 'logicalunit', '--tid', tid, '--lun', '1', '-b', path, run_as_root=True) except putils.ProcessExecutionError as e: LOG.error( _LE("Failed recovery attempt to create " "iscsi backing lun for Volume " "ID:%(vol_id)s: %(e)s"), { 'vol_id': name, 'e': e }) finally: LOG.debug('StdOut from recreate backing lun: %s', out) LOG.debug('StdErr from recreate backing lun: %s', err)
def _do_backup(self, backup_path, vol_id, backup_mode): """Perform the actual backup operation. :param backup_path: volume path :param vol_id: volume id :param backup_mode: file mode of source volume; 'image' or 'file' :raises: InvalidBackup """ backup_attrs = {'Total number of objects backed up': '1'} compr_flag = 'yes' if CONF.backup_tsm_compression else 'no' backup_cmd = ['dsmc', 'backup'] if _image_mode(backup_mode): backup_cmd.append('image') backup_cmd.extend([ '-quiet', '-compression=%s' % compr_flag, '-password=%s' % self.tsm_password, backup_path ]) out, err = utils.execute(*backup_cmd, run_as_root=True, check_exit_code=False) success = _check_dsmc_output(out, backup_attrs, exact_match=False) if not success: err = (_('backup: %(vol_id)s failed to obtain backup ' 'success notification from server.\n' 'stdout: %(out)s\n stderr: %(err)s') % { 'vol_id': vol_id, 'out': out, 'err': err }) LOG.error(err) raise exception.InvalidBackup(reason=err)
def create_iscsi_target(self, name, tid, lun, path, chap_auth=None, **kwargs): (out, err) = utils.execute('iscsictl', '-c', 'target=ALL', run_as_root=True) LOG.debug("Targets prior to update: %s", out) volumes_dir = self._get_volumes_dir() fileutils.ensure_tree(volumes_dir) vol_id = name.split(':')[1] cfg_port = kwargs.get('portals_port') cfg_ips = kwargs.get('portals_ips') portals = ','.join(map(lambda ip: self._get_portal(ip, cfg_port), cfg_ips)) if chap_auth is None: volume_conf = self.TARGET_FMT % (name, path, portals) else: volume_conf = self.TARGET_FMT_WITH_CHAP % (name, path, portals, '"%s":"%s"' % chap_auth) LOG.debug('Creating iscsi_target for: %s', vol_id) volume_path = os.path.join(volumes_dir, vol_id) if os.path.exists(volume_path): LOG.warning(_LW('Persistence file already exists for volume, ' 'found file at: %s'), volume_path) utils.robust_file_write(volumes_dir, vol_id, volume_conf) LOG.debug('Created volume path %(vp)s,\n' 'content: %(vc)s', {'vp': volume_path, 'vc': volume_conf}) old_persist_file = None old_name = kwargs.get('old_name', None) if old_name: LOG.debug('Detected old persistence file for volume ' '%{vol}s at %{old_name}s', {'vol': vol_id, 'old_name': old_name}) old_persist_file = os.path.join(volumes_dir, old_name) try: # With the persistent tgts we create them # by creating the entry in the persist file # and then doing an update to get the target # created. (out, err) = utils.execute('iscsictl', '-S', 'target=%s' % name, '-f', volume_path, '-x', self.config, run_as_root=True) except putils.ProcessExecutionError as e: LOG.error(_LE("Failed to create iscsi target for volume " "id:%(vol_id)s: %(e)s"), {'vol_id': vol_id, 'e': e}) # Don't forget to remove the persistent file we created os.unlink(volume_path) raise exception.ISCSITargetCreateFailed(volume_id=vol_id) finally: LOG.debug("StdOut from iscsictl -S: %s", out) LOG.debug("StdErr from iscsictl -S: %s", err) # Grab targets list for debug (out, err) = utils.execute('iscsictl', '-c', 'target=ALL', run_as_root=True) LOG.debug("Targets after update: %s", out) iqn = '%s%s' % (self.iscsi_target_prefix, vol_id) tid = self._get_target(iqn) if tid is None: LOG.error(_LE("Failed to create iscsi target for volume " "id:%(vol_id)s. Please verify your configuration " "in %(volumes_dir)s'"), { 'vol_id': vol_id, 'volumes_dir': volumes_dir, }) raise exception.NotFound() if old_persist_file is not None and os.path.exists(old_persist_file): os.unlink(old_persist_file) return tid
def coalesce_vhd(vhd_path): utils.execute('vhd-util', 'coalesce', '-n', vhd_path)
def get_vhd_size(vhd_path): out, _err = utils.execute('vhd-util', 'query', '-n', vhd_path, '-v') return int(out)
def resize_vhd(vhd_path, size, journal): utils.execute('vhd-util', 'resize', '-n', vhd_path, '-s', '%d' % size, '-j', journal)
def set_vhd_parent(vhd_path, parentpath): utils.execute('vhd-util', 'modify', '-n', vhd_path, '-p', parentpath)
def extract_targz(archive_name, target): utils.execute('tar', '-xzf', archive_name, '-C', target)
def remove_iscsi_target(self, tid, lun, vol_id, vol_name, **kwargs): LOG.info(_LI('Removing iscsi_target for Volume ID: %s'), vol_id) vol_uuid_file = vol_name volume_path = os.path.join(self.volumes_dir, vol_uuid_file) if not os.path.exists(volume_path): LOG.warning(_LW('Volume path %s does not exist, ' 'nothing to remove.'), volume_path) return if os.path.isfile(volume_path): iqn = '%s%s' % (self.iscsi_target_prefix, vol_uuid_file) else: raise exception.ISCSITargetRemoveFailed(volume_id=vol_id) try: # NOTE(vish): --force is a workaround for bug: # https://bugs.launchpad.net/storage/+bug/1159948 utils.execute('tgt-admin', '--force', '--delete', iqn, run_as_root=True) except putils.ProcessExecutionError as e: non_fatal_errors = ("can't find the target", "access control rule does not exist") if any(error in e.stderr for error in non_fatal_errors): LOG.warning(_LW("Failed target removal because target or " "ACL's couldn't be found for iqn: %s."), iqn) else: LOG.error(_LE("Failed to remove iscsi target for Volume " "ID: %(vol_id)s: %(e)s"), {'vol_id': vol_id, 'e': e}) raise exception.ISCSITargetRemoveFailed(volume_id=vol_id) # NOTE(jdg): There's a bug in some versions of tgt that # will sometimes fail silently when using the force flag # https://bugs.launchpad.net/ubuntu/+source/tgt/+bug/1305343 # For now work-around by checking if the target was deleted, # if it wasn't, try again without the force. # This will NOT do any good for the case of mutliple sessions # which the force was aded for but it will however address # the cases pointed out in bug: # https://bugs.launchpad.net/storage/+bug/1304122 if self._get_target(iqn): try: LOG.warning(_LW('Silent failure of target removal ' 'detected, retry....')) utils.execute('tgt-admin', '--delete', iqn, run_as_root=True) except putils.ProcessExecutionError as e: LOG.error(_LE("Failed to remove iscsi target for Volume " "ID: %(vol_id)s: %(e)s"), {'vol_id': vol_id, 'e': e}) raise exception.ISCSITargetRemoveFailed(volume_id=vol_id) # NOTE(jdg): This *should* be there still but incase # it's not we don't care, so just ignore it if was # somehow deleted between entry of this method # and here if os.path.exists(volume_path): os.unlink(volume_path) else: LOG.debug('Volume path %s not found at end, ' 'of remove_iscsi_target.', volume_path)
def remove_iscsi_target(self, tid, lun, vol_id, vol_name, **kwargs): LOG.info(_LI('Removing iscsi_target for: %s'), vol_id) vol_uuid_file = vol_name volume_path = os.path.join(self._get_volumes_dir(), vol_uuid_file) if not os.path.exists(volume_path): LOG.warning(_LW('Volume path %s does not exist, ' 'nothing to remove.'), volume_path) return if os.path.isfile(volume_path): iqn = '%s%s' % (self.iscsi_target_prefix, vol_uuid_file) else: raise exception.ISCSITargetRemoveFailed(volume_id=vol_id) target_exists = False try: (out, err) = utils.execute('iscsictl', '-c', 'target=%s' % iqn, run_as_root=True) LOG.debug("StdOut from iscsictl -c: %s", out) LOG.debug("StdErr from iscsictl -c: %s", err) except putils.ProcessExecutionError as e: if "NOT found" in e.stdout: LOG.info(_LI("No iscsi target present for volume " "id:%(vol_id)s: %(e)s"), {'vol_id': vol_id, 'e': e}) return else: raise exception.ISCSITargetRemoveFailed(volume_id=vol_id) else: target_exists = True try: utils.execute('iscsictl', '-s', 'target=%s' % iqn, run_as_root=True) except putils.ProcessExecutionError as e: # There exists a race condition where multiple calls to # remove_iscsi_target come in simultaneously. If we can poll # for a target successfully but it is gone before we can remove # it, fail silently if "is not found" in e.stderr and target_exists: LOG.info(_LI("No iscsi target present for volume " "id:%(vol_id)s: %(e)s"), {'vol_id': vol_id, 'e': e}) return else: LOG.error(_LE("Failed to remove iscsi target for volume " "id:%(vol_id)s: %(e)s"), {'vol_id': vol_id, 'e': e}) raise exception.ISCSITargetRemoveFailed(volume_id=vol_id) # Carried over from tgt # NOTE(jdg): This *should* be there still but incase # it's not we don't care, so just ignore it if was # somehow deleted between entry of this method # and here if os.path.exists(volume_path): os.unlink(volume_path) else: LOG.debug('Volume path %s not found at end, ' 'of remove_iscsi_target.', volume_path)
def _delete_target(self, tid): utils.execute('ietadm', '--op', 'delete', '--tid=%s' % tid, run_as_root=True)
def create_iscsi_target(self, name, tid, lun, path, chap_auth=None, **kwargs): # Note(jdg) tid and lun aren't used by TgtAdm but remain for # compatibility # NOTE(jdg): Remove this when we get to the bottom of bug: #1398078 # for now, since we intermittently hit target already exists we're # adding some debug info to try and pinpoint what's going on (out, err) = utils.execute('tgtadm', '--lld', 'iscsi', '--op', 'show', '--mode', 'target', run_as_root=True) LOG.debug("Targets prior to update: %s", out) fileutils.ensure_tree(self.volumes_dir) vol_id = name.split(':')[1] write_cache = self.configuration.get('iscsi_write_cache', 'on') driver = self.iscsi_protocol chap_str = '' if chap_auth is not None: chap_str = 'incominguser %s %s' % chap_auth target_flags = self.configuration.get('iscsi_target_flags', '') if target_flags: target_flags = 'bsoflags ' + target_flags volume_conf = self.VOLUME_CONF % { 'name': name, 'path': path, 'driver': driver, 'chap_auth': chap_str, 'target_flags': target_flags, 'write_cache': write_cache} LOG.debug('Creating iscsi_target for Volume ID: %s', vol_id) volumes_dir = self.volumes_dir volume_path = os.path.join(volumes_dir, vol_id) if os.path.exists(volume_path): LOG.warning(_LW('Persistence file already exists for volume, ' 'found file at: %s'), volume_path) utils.robust_file_write(volumes_dir, vol_id, volume_conf) LOG.debug(('Created volume path %(vp)s,\n' 'content: %(vc)s'), {'vp': volume_path, 'vc': volume_conf}) old_persist_file = None old_name = kwargs.get('old_name', None) if old_name is not None: LOG.debug('Detected old persistence file for volume ' '%{vol}s at %{old_name}s', {'vol': vol_id, 'old_name': old_name}) old_persist_file = os.path.join(volumes_dir, old_name) try: # With the persistent tgts we create them # by creating the entry in the persist file # and then doing an update to get the target # created. self._do_tgt_update(name) except putils.ProcessExecutionError as e: if "target already exists" in e.stderr: # Adding the additional Warning message below for a clear # ER marker (Ref bug: #1398078). LOG.warning(_LW('Could not create target because ' 'it already exists for volume: %s'), vol_id) LOG.debug('Exception was: %s', e) else: LOG.error(_LE("Failed to create iscsi target for Volume " "ID: %(vol_id)s: %(e)s"), {'vol_id': vol_id, 'e': e}) # Don't forget to remove the persistent file we created os.unlink(volume_path) raise exception.ISCSITargetCreateFailed(volume_id=vol_id) # Grab targets list for debug # Consider adding a check for lun 0 and 1 for tgtadm # before considering this as valid (out, err) = utils.execute('tgtadm', '--lld', 'iscsi', '--op', 'show', '--mode', 'target', run_as_root=True) LOG.debug("Targets after update: %s", out) iqn = '%s%s' % (self.iscsi_target_prefix, vol_id) tid = self._get_target(iqn) if tid is None: LOG.error(_LE("Failed to create iscsi target for Volume " "ID: %(vol_id)s. Please ensure your tgtd config " "file contains 'include %(volumes_dir)s/*'"), { 'vol_id': vol_id, 'volumes_dir': volumes_dir, }) raise exception.NotFound() # NOTE(jdg): Sometimes we have some issues with the backing lun # not being created, believe this is due to a device busy # or something related, so we're going to add some code # here that verifies the backing lun (lun 1) was created # and we'll try and recreate it if it's not there if not self._verify_backing_lun(iqn, tid): try: self._recreate_backing_lun(iqn, tid, name, path) except putils.ProcessExecutionError: os.unlink(volume_path) raise exception.ISCSITargetCreateFailed(volume_id=vol_id) # Finally check once more and if no go, fail and punt if not self._verify_backing_lun(iqn, tid): os.unlink(volume_path) raise exception.ISCSITargetCreateFailed(volume_id=vol_id) if old_persist_file is not None and os.path.exists(old_persist_file): os.unlink(old_persist_file) return tid
def _force_delete_target(self, tid, sid, cid): utils.execute('ietadm', '--op', 'delete', '--tid=%s' % tid, '--sid=%s' % sid, '--cid=%s' % cid, run_as_root=True)
def scst_execute(self, *args): return utils.execute('scstadmin', *args, run_as_root=True)
def _do_tgt_update(self, name): (out, err) = utils.execute('tgt-admin', '--update', name, run_as_root=True) LOG.debug("StdOut from tgt-admin --update: %s", out) LOG.debug("StdErr from tgt-admin --update: %s", err)
def show_target(self, tid, iqn=None): utils.execute('ietadm', '--op', 'show', '--tid=%s' % tid, run_as_root=True)
def _delete_logicalunit(self, tid, lun): utils.execute('ietadm', '--op', 'delete', '--tid=%s' % tid, '--lun=%d' % lun, run_as_root=True)