def _device_is_multipathed(devpath): devpath = os.path.realpath(devpath) info = udevadm_info(devpath) if multipath.is_mpath_device(devpath, info=info): return True if multipath.is_mpath_partition(devpath, info=info): return True if devpath.startswith('/dev/dm-'): # check members of composed devices (LVM, dm-crypt) if 'DM_LV_NAME' in info: volgroup = info.get('DM_VG_NAME') if volgroup: if any((multipath.is_mpath_member(pv) for pv in lvm.get_pvols_in_volgroup(volgroup))): return True elif devpath.startswith('/dev/md'): if any((multipath.is_mpath_member(md) for md in md_get_devices_list(devpath) + md_get_spares_list(devpath))): return True result = multipath.is_mpath_member(devpath) return result
def test_is_mpath_member_true(self): """is_mpath_member returns true if DM_MULTIPATH_DEVICE_PATH is '1'""" self.m_udev.udevadm_info.return_value = { "DM_MULTIPATH_DEVICE_PATH": "1", } self.assertTrue(multipath.is_mpath_member(self.random_string()))
def test_is_mpath_member_false_2(self): """is_mpath_member returns false if DM_MULTIPATH_DEVICE_PATH is not '1'""" self.m_udev.udevadm_info.return_value = { "DM_MULTIPATH_DEVICE_PATH": "2", } self.assertFalse(multipath.is_mpath_member(self.random_string()))
def lookup_disk(serial): """ Search for a disk by its serial number using /dev/disk/by-id/ """ # Get all volumes in /dev/disk/by-id/ containing the serial string. The # string specified can be either in the short or long serial format # hack, some serials have spaces, udev usually converts ' ' -> '_' serial_udev = serial.replace(' ', '_') LOG.info('Processing serial %s via udev to %s', serial, serial_udev) disks = list( filter(lambda x: serial_udev in x, os.listdir("/dev/disk/by-id/"))) if not disks or len(disks) < 1: raise ValueError("no disk with serial '%s' found" % serial_udev) # Sort by length and take the shortest path name, as the longer path names # will be the partitions on the disk. Then use os.path.realpath to # determine the path to the block device in /dev/ disks.sort(key=lambda x: len(x)) LOG.debug('lookup_disks found: %s', disks) path = os.path.realpath("/dev/disk/by-id/%s" % disks[0]) # /dev/dm-X if multipath.is_mpath_device(path): info = udevadm_info(path) path = os.path.join('/dev/mapper', info['DM_NAME']) # /dev/sdX elif multipath.is_mpath_member(path): mp_name = multipath.find_mpath_id_by_path(path) path = os.path.join('/dev/mapper', mp_name) if not os.path.exists(path): raise ValueError("path '%s' to block device for disk with serial '%s' \ does not exist" % (path, serial_udev)) LOG.debug('block.lookup_disk() returning path %s', path) return path
def test_is_mpath_member_false(self): """is_mpath_member returns false if 'multipath -c <dev>' exits err.""" self.m_subp.side_effect = raise_pexec_error self.assertFalse(multipath.is_mpath_member(self.random_string()))
def test_is_mpath_member_true(self): """is_mpath_device returns false when DM_UUID doesnt start w/ mpath-""" self.assertTrue(multipath.is_mpath_member(self.random_string()))
def wipe_superblock(device): """ Wrapper for block.wipe_volume compatible with shutdown function interface """ blockdev = block.sysfs_to_devpath(device) # when operating on a disk that used to have a dos part table with an # extended partition, attempting to wipe the extended partition will fail try: if not block.is_online(blockdev): LOG.debug("Device is not online (size=0), so skipping:" " '%s'", blockdev) return if block.is_extended_partition(blockdev): LOG.info("extended partitions do not need wiping, so skipping:" " '%s'", blockdev) return except OSError as e: if util.is_file_not_found_exc(e): LOG.debug('Device to wipe disappeared: %s', e) LOG.debug('/proc/partitions says: %s', util.load_file('/proc/partitions')) (parent, partnum) = block.get_blockdev_for_partition(blockdev) out, _e = util.subp(['sfdisk', '-d', parent], capture=True, combine_capture=True) LOG.debug('Disk partition info:\n%s', out) return else: raise e # gather any partitions partitions = block.get_sysfs_partitions(device) # release zfs member by exporting the pool if zfs.zfs_supported() and block.is_zfs_member(blockdev): poolname = zfs.device_to_poolname(blockdev) # only export pools that have been imported if poolname in zfs.zpool_list(): try: zfs.zpool_export(poolname) except util.ProcessExecutionError as e: LOG.warning('Failed to export zpool "%s": %s', poolname, e) if is_swap_device(blockdev): shutdown_swap(blockdev) # some volumes will be claimed by the bcache layer but do not surface # an actual /dev/bcacheN device which owns the parts (backing, cache) # The result is that some volumes cannot be wiped while bcache claims # the device. Resolve this by stopping bcache layer on those volumes # if present. for bcache_path in ['bcache', 'bcache/set']: stop_path = os.path.join(device, bcache_path) if os.path.exists(stop_path): LOG.debug('Attempting to release bcache layer from device: %s:%s', device, stop_path) if stop_path.endswith('set'): rp = os.path.realpath(stop_path) bcache.stop_cacheset(rp) else: bcache._stop_device(stop_path) # the blockdev (e.g. /dev/sda2) may be a multipath partition which can # only be wiped via its device mapper device (e.g. /dev/dm-4) # check for this and determine the correct device mapper value to use. if multipath.multipath_supported(): # handle /dev/mapper/mpatha , base mp device if multipath.is_mpath_device(blockdev): # if mpath device has "partitions" those need to be removed. # clear-holders will have already wiped these devices as they # are higher up in the dependency tree. mpath_id = multipath.find_mpath_id(blockdev) for mp_part_id in multipath.find_mpath_partitions(mpath_id): multipath.remove_partition(mp_part_id) # handle /dev/sdX which are held by multipath layer if multipath.is_mpath_member(blockdev): LOG.debug('Skipping multipath partition path member: %s', blockdev) return _wipe_superblock(blockdev) # if we had partitions, make sure they've been removed if partitions: LOG.debug('%s had partitions, issuing partition reread', device) retries = [.5, .5, 1, 2, 5, 7] for attempt, wait in enumerate(retries): try: # only rereadpt on wiped device block.rescan_block_devices(devices=[blockdev]) # may raise IOError, OSError due to wiped partition table curparts = block.get_sysfs_partitions(device) if len(curparts) == 0: return except (IOError, OSError): if attempt + 1 >= len(retries): raise LOG.debug("%s partitions still present, rereading pt" " (%s/%s). sleeping %ss before retry", device, attempt + 1, len(retries), wait) time.sleep(wait)
def is_mpath_member(self, blockdev): return multipath.is_mpath_member(blockdev.get('DEVNAME', ''), blockdev)
def test_is_mpath_member_false(self): self.m_subp.side_effect = raise_pexec_error self.assertFalse(multipath.is_mpath_member(self.random_string()))
def test_is_mpath_member_true(self): self.assertTrue(multipath.is_mpath_member(self.random_string()))