Beispiel #1
0
def remove_partition(devpath, retries=10):
    LOG.debug('multipath: removing multipath partition: %s', devpath)
    for _ in range(0, retries):
        util.subp(['dmsetup', 'remove', devpath], rcs=[0, 1])
        udev.udevadm_settle()
        if not os.path.exists(devpath):
            return

    util.wait_for_removal(devpath)
Beispiel #2
0
def remove_partition(devpath, retries=10):
    """ Remove a multipath partition mapping. """
    LOG.debug('multipath: removing multipath partition: %s', devpath)
    for _ in range(0, retries):
        util.subp(['dmsetup', 'remove', '--force', '--retry', devpath])
        udev.udevadm_settle()
        if not os.path.exists(devpath):
            return

    util.wait_for_removal(devpath)
Beispiel #3
0
def remove_map(map_id, retries=10):
    LOG.debug('multipath: removing multipath map: %s', map_id)
    devpath = '/dev/mapper/%s' % map_id
    for _ in range(0, retries):
        util.subp(['multipath', '-f', map_id], rcs=[0, 1])
        udev.udevadm_settle()
        if not os.path.exists(devpath):
            return

    util.wait_for_removal(devpath)
Beispiel #4
0
    def test_wait_for_removal_timesout(self, mock_os, mock_time):
        path = "/file/to/remove"
        mock_os.path.exists.return_value = True

        with self.assertRaises(OSError):
            util.wait_for_removal(path)

        self.assertEqual(5, len(mock_os.path.exists.call_args_list))
        self.assertEqual(4, len(mock_time.sleep.call_args_list))
        mock_os.path.exists.assert_has_calls(5 * [mock.call(path)])
        mock_time.sleep.assert_has_calls([
            mock.call(1),
            mock.call(3),
            mock.call(5),
            mock.call(7),
        ])
Beispiel #5
0
def _stop_device(device):
    """  write to sysfs 'stop' and wait for path to be removed

    The caller needs to ensure that supplied path to the device
    is a 'bcache' sysfs path on a device.  This may be one of the
    following scenarios:

    Cacheset:
      /sys/fs/bcache/<uuid>/

    Bcache device:
     /sys/class/block/bcache0/bcache

    Backing device
     /sys/class/block/vdb/bcache

    Cached device
     /sys/class/block/nvme0n1p1/bcache/set

    To support all of these, we append 'stop' to the path
    and write '1' and then wait for the 'stop' path to
    be removed.
    """
    bcache_stop = os.path.join(device, 'stop')
    if not os.path.exists(bcache_stop):
        LOG.debug('bcache._stop_device: already removed %s', bcache_stop)
        return

    LOG.debug('bcache._stop_device: device=%s stop_path=%s', device,
              bcache_stop)
    try:
        util.write_file(bcache_stop, '1', mode=None)
    except (IOError, OSError) as e:
        # Note: if we get any exceptions in the above exception classes
        # it is a result of attempting to write "1" into the sysfs path
        # The range of errors changes depending on when we race with
        # the kernel asynchronously removing the sysfs path. Therefore
        # we log the exception errno we got, but do not re-raise as
        # the calling process is watching whether the same sysfs path
        # is being removed;  if it fails to go away then we'll have
        # a log of the exceptions to debug.
        LOG.debug('Error writing to bcache stop file %s, device removed: %s',
                  bcache_stop, e)
    finally:
        util.wait_for_removal(bcache_stop, retries=BCACHE_RETRIES)
Beispiel #6
0
    def test_wait_for_removal(self, mock_os, mock_time):
        path = "/file/to/remove"
        mock_os.path.exists.side_effect = iter([
            True,  # File is not yet removed
            False,  # File has  been removed
        ])

        util.wait_for_removal(path)

        self.assertEqual(2, len(mock_os.path.exists.call_args_list))
        self.assertEqual(1, len(mock_time.sleep.call_args_list))
        mock_os.path.exists.assert_has_calls([
            mock.call(path),
            mock.call(path),
        ])
        mock_time.sleep.assert_has_calls([
            mock.call(1),
        ])
Beispiel #7
0
def shutdown_bcache(device):
    """
    Shut down bcache for specified bcache device

    1. Stop the cacheset that `device` is connected to
    2. Stop the 'device'
    """
    if not device.startswith('/sys/class/block'):
        raise ValueError(
            'Invalid Device (%s): '
            'Device path must start with /sys/class/block/', device)

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

    # 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.
    removal_retries = [0.2] * 150  # 30 seconds total
    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

    # get slaves [vdb1, vdc], allow for slaves to not have bcache dir
    slave_paths = [
        get_bcache_sys_path(k, strict=False)
        for k in os.listdir(os.path.join(device, 'slaves'))
    ]

    # stop cacheset if it exists
    bcache_cache_sysfs = get_bcache_using_dev(device, strict=False)
    if not os.path.exists(bcache_cache_sysfs):
        LOG.info('bcache cacheset already removed: %s',
                 os.path.basename(bcache_cache_sysfs))
    else:
        LOG.info('stopping bcache cacheset at: %s', bcache_cache_sysfs)
        maybe_stop_bcache_device(bcache_cache_sysfs)
        try:
            util.wait_for_removal(bcache_cache_sysfs, retries=removal_retries)
        except OSError:
            LOG.info('Failed to stop bcache cacheset %s', bcache_cache_sysfs)
            raise

        # let kernel settle before the next remove
        udev.udevadm_settle()

    # after stopping cache set, we may need to stop the device
    # both the dev and sysfs entry should be gone.

    # we know the bcacheN device is really gone when we've removed:
    #  /sys/class/block/{bcacheN}
    #  /sys/class/block/slaveN1/bcache
    #  /sys/class/block/slaveN2/bcache
    bcache_block_sysfs = get_bcache_sys_path(device, strict=False)
    to_check = [device] + slave_paths
    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_block_sysfs, device)
        LOG.debug('bcache slave paths checked: %s', slave_paths)
        return
    else:
        LOG.info('stopping bcache backing device at: %s', bcache_block_sysfs)
        maybe_stop_bcache_device(bcache_block_sysfs)
        try:
            # wait for them all to go away
            for dev in [device, bcache_block_sysfs] + slave_paths:
                util.wait_for_removal(dev, retries=removal_retries)
        except OSError:
            LOG.info('Failed to stop bcache backing device %s',
                     bcache_block_sysfs)
            raise

    return
Beispiel #8
0
 def test_wait_for_removal_missing_path(self):
     with self.assertRaises(ValueError):
         util.wait_for_removal(None)