Esempio n. 1
0
def gen_holders_tree(device):
    """
    generate a tree representing the current storage hirearchy above 'device'
    """
    device = block.sys_block_path(device)
    dev_name = block.path_to_kname(device)
    # the holders for a device should consist of the devices in the holders/
    # dir in sysfs and any partitions on the device. this ensures that a
    # storage tree starting from a disk will include all devices holding the
    # disk's partitions
    holder_paths = ([block.sys_block_path(h) for h in get_holders(device)] +
                    block.get_sysfs_partitions(device))
    # the DEV_TYPE registry contains a function under the key 'ident' for each
    # device type entry that returns true if the device passed to it is of the
    # correct type. there should never be a situation in which multiple
    # identify functions return true. therefore, it will always work to take
    # the device type with the first identify function that returns true as the
    # device type for the current device. in the event that no identify
    # functions return true, the device will be treated as a disk
    # (DEFAULT_DEV_TYPE). the identify function for disk never returns true.
    # the next() builtin in python will not raise a StopIteration exception if
    # there is a default value defined
    dev_type = next((k for k, v in DEV_TYPES.items() if v['ident'](device)),
                    DEFAULT_DEV_TYPE)
    return {
        'device': device,
        'dev_type': dev_type,
        'name': dev_name,
        'holders': [gen_holders_tree(h) for h in holder_paths],
    }
Esempio n. 2
0
def identify_bcache(device):
    """
    determine if specified device is a bcache device
    """
    # bcache devices can be partitioned and the partitions are *not*
    # bcache devices with a sysfs 'slaves' subdirectory
    partition = identify_partition(device)
    return block.path_to_kname(device).startswith('bcache') and not partition
Esempio n. 3
0
def shutdown_mdadm(device):
    """
    Shutdown specified mdadm device.
    """

    blockdev = block.sysfs_to_devpath(device)

    LOG.info('Wiping superblock on raid device: %s', device)
    _wipe_superblock(blockdev, exclusive=False)

    md_devs = (mdadm.md_get_devices_list(blockdev) +
               mdadm.md_get_spares_list(blockdev))
    mdadm.set_sync_action(blockdev, action="idle")
    mdadm.set_sync_action(blockdev, action="frozen")
    for mddev in md_devs:
        try:
            mdadm.fail_device(blockdev, mddev)
            mdadm.remove_device(blockdev, mddev)
        except util.ProcessExecutionError as e:
            LOG.debug('Non-fatal error clearing raid array: %s', e.stderr)
            pass

    LOG.debug('using mdadm.mdadm_stop on dev: %s', blockdev)
    mdadm.mdadm_stop(blockdev)

    for mddev in md_devs:
        mdadm.zero_device(mddev)

    # mdadm stop operation is asynchronous so we must wait for the kernel to
    # release resources. For more details see  LP: #1682456
    try:
        for wait in MDADM_RELEASE_RETRIES:
            if mdadm.md_present(block.path_to_kname(blockdev)):
                time.sleep(wait)
            else:
                LOG.debug('%s has been removed', blockdev)
                break

        if mdadm.md_present(block.path_to_kname(blockdev)):
            raise OSError('Timeout exceeded for removal of %s', blockdev)

    except OSError:
        LOG.critical('Failed to stop mdadm device %s', device)
        if os.path.exists('/proc/mdstat'):
            LOG.critical("/proc/mdstat:\n%s", util.load_file('/proc/mdstat'))
        raise
Esempio n. 4
0
def identify_mdadm(device):
    """
    determine if specified device is a mdadm device
    """
    # RAID0 and 1 devices can be partitioned and the partitions are *not*
    # raid devices with a sysfs 'md' subdirectory
    partition = identify_partition(device)
    return block.path_to_kname(device).startswith('md') and not partition
Esempio n. 5
0
def volpath_is_iscsi(volume_path):
    """ Determine if the volume_path's kname is backed by iSCSI.
        Recursively check volume_path's slave devices as well in
        case volume_path is a stacked block device (like LVM/MD)

        returns a boolean
    """
    if not volume_path:
        raise ValueError("Invalid input for volume_path: '%s'", volume_path)

    volume_path_slaves = get_device_slave_knames(volume_path)
    LOG.debug('volume_path=%s found slaves: %s', volume_path,
              volume_path_slaves)
    knames = [path_to_kname(volume_path)] + volume_path_slaves
    return any([kname_is_iscsi(kname) for kname in knames])
Esempio n. 6
0
 def test_path_to_kname(self, mock_os_realpath):
     mock_os_realpath.side_effect = lambda x: os.path.normpath(x)
     path_knames = [('/dev/sda', 'sda'), ('/dev/sda1', 'sda1'),
                    ('/dev////dm-0/', 'dm-0'), ('/dev/md0p1', 'md0p1'),
                    ('vdb', 'vdb'), ('/dev/mmcblk0p1', 'mmcblk0p1'),
                    ('/dev/nvme0n0p1', 'nvme0n0p1'),
                    ('/sys/block/vdb', 'vdb'),
                    ('/sys/block/vdb/vdb2/', 'vdb2'),
                    ('/dev/cciss/c0d0', 'cciss!c0d0'),
                    ('/dev/cciss/c0d0p1/', 'cciss!c0d0p1'),
                    ('/sys/class/block/cciss!c0d0p1', 'cciss!c0d0p1'),
                    ('nvme0n1p4', 'nvme0n1p4')]
     for (path, expected_kname) in path_knames:
         self.assertEqual(block.path_to_kname(path), expected_kname)
         if os.path.sep in path:
             mock_os_realpath.assert_called_with(path)
Esempio n. 7
0
def shutdown_bcache(device):
    """
    Shut down bcache for specified bcache device

    1. wipe the bcache device contents
    2. extract the cacheset uuid (if cached)
    3. extract the backing device
    4. stop cacheset (if present)
    5. stop the bcacheN device
    6. wait for removal of sysfs path to bcacheN, bcacheN/bcache and
       backing/bcache to go away
    """
    if not device.startswith('/sys/class/block'):
        raise ValueError(
            'Invalid Device (%s): '
            'Device path must start with /sys/class/block/', device)

    # bcache device removal should be fast but in an extreme
    # case, might require the cache device to flush large
    # amounts of data to a backing device.  The strategy here
    # is to wait for approximately 30 seconds but to check
    # frequently since curtin cannot proceed until devices
    # cleared.
    bcache_shutdown_message = ('shutdown_bcache running on {} has determined '
                               'that the device has already been shut down '
                               'during handling of another bcache dev. '
                               'skipping'.format(device))

    if not os.path.exists(device):
        LOG.info(bcache_shutdown_message)
        return

    LOG.info('Wiping superblock on bcache device: %s', device)
    _wipe_superblock(block.sysfs_to_devpath(device), exclusive=False)

    # collect required information before stopping bcache device
    # UUID from /sys/fs/cache/UUID
    cset_uuid = bcache.get_attached_cacheset(device)
    # /sys/class/block/vdX which is a backing dev of device (bcacheN)
    backing_sysfs = bcache.get_backing_device(block.path_to_kname(device))
    # /sys/class/block/bcacheN/bache
    bcache_sysfs = bcache.sysfs_path(device, strict=False)

    # stop cacheset if one is presennt
    if cset_uuid:
        LOG.info('%s was attached to cacheset %s, stopping cacheset', device,
                 cset_uuid)
        bcache.stop_cacheset(cset_uuid)

        # let kernel settle before the next remove
        udev.udevadm_settle()
        LOG.info('bcache cacheset stopped: %s', cset_uuid)

    # test and log whether the device paths are still present
    to_check = [bcache_sysfs, backing_sysfs]
    found_devs = [os.path.exists(p) for p in to_check]
    LOG.debug('os.path.exists on blockdevs:\n%s',
              list(zip(to_check, found_devs)))
    if not any(found_devs):
        LOG.info('bcache backing device already removed: %s (%s)',
                 bcache_sysfs, device)
        LOG.debug('bcache backing device checked: %s', backing_sysfs)
    else:
        LOG.info('stopping bcache backing device at: %s', bcache_sysfs)
        bcache.stop_device(bcache_sysfs)
    return
Esempio n. 8
0
def identify_crypt(device):
    """
    determine if specified device is dm-crypt device
    """
    return (block.path_to_kname(device).startswith('dm')
            and get_dmsetup_uuid(device).startswith('CRYPT'))
Esempio n. 9
0
def identify_lvm(device):
    """
    determine if specified device is a lvm device
    """
    return (block.path_to_kname(device).startswith('dm')
            and get_dmsetup_uuid(device).startswith('LVM'))
Esempio n. 10
0
def shutdown_mdadm(device):
    """
    Shutdown specified mdadm device.
    """

    blockdev = block.sysfs_to_devpath(device)

    LOG.info('Discovering raid devices and spares for %s', device)
    md_devs = (mdadm.md_get_devices_list(blockdev) +
               mdadm.md_get_spares_list(blockdev))
    mdadm.set_sync_action(blockdev, action="idle")
    mdadm.set_sync_action(blockdev, action="frozen")

    LOG.info('Wiping superblock on raid device: %s', device)
    try:
        _wipe_superblock(blockdev, exclusive=False)
    except ValueError as e:
        # if the array is not functional, writes to the device may fail
        # and _wipe_superblock will raise ValueError for short writes
        # which happens on inactive raid volumes.  In that case we
        # shouldn't give up yet as we still want to disassemble
        # array and wipe members.  Other errors such as IOError or OSError
        # are unwelcome and will stop deployment.
        LOG.debug(
            'Non-fatal error writing to array device %s, '
            'proceeding with shutdown: %s', blockdev, e)

    LOG.info('Removing raid array members: %s', md_devs)
    for mddev in md_devs:
        try:
            mdadm.fail_device(blockdev, mddev)
            mdadm.remove_device(blockdev, mddev)
        except util.ProcessExecutionError as e:
            LOG.debug('Non-fatal error clearing raid array: %s', e.stderr)
            pass

    LOG.debug('using mdadm.mdadm_stop on dev: %s', blockdev)
    mdadm.mdadm_stop(blockdev)

    LOG.debug('Wiping mdadm member devices: %s' % md_devs)
    for mddev in md_devs:
        mdadm.zero_device(mddev, force=True)

    # mdadm stop operation is asynchronous so we must wait for the kernel to
    # release resources. For more details see  LP: #1682456
    try:
        for wait in MDADM_RELEASE_RETRIES:
            if mdadm.md_present(block.path_to_kname(blockdev)):
                time.sleep(wait)
            else:
                LOG.debug('%s has been removed', blockdev)
                break

        if mdadm.md_present(block.path_to_kname(blockdev)):
            raise OSError('Timeout exceeded for removal of %s', blockdev)

    except OSError:
        LOG.critical('Failed to stop mdadm device %s', device)
        if os.path.exists('/proc/mdstat'):
            LOG.critical("/proc/mdstat:\n%s", util.load_file('/proc/mdstat'))
        raise
Esempio n. 11
0
def identify_bcache(device):
    """
    determine if specified device is a bcache device
    """
    return block.path_to_kname(device).startswith('bcache')