Beispiel #1
0
def remove_block_dev(devpath, scan_timeout=10):
    """Remove a block device from the management partition.

    This method causes the operating system of the management partition to
    delete the device special files associated with the specified block device.

    :param devpath: Any path to the block special file associated with the
                    device to be removed.
    :param scan_timeout: The maximum number of seconds after scanning to wait
                         for the specified device to disappear.
    :raise InvalidDevicePath: If the specified device or its 'delete' special
                              file cannot be found.
    :raise DeviceDeletionException: If the deletion was attempted, but the
                                    device special file is still present
                                    afterward.
    """
    # Resolve symlinks, if any, to get to the /dev/sdX path
    devpath = os.path.realpath(devpath)
    try:
        os.stat(devpath)
    except OSError:
        raise exception.InvalidDevicePath(path=devpath)
    devname = devpath.rsplit('/', 1)[-1]
    delpath = '/sys/block/%s/device/delete' % devname
    try:
        os.stat(delpath)
    except OSError:
        raise exception.InvalidDevicePath(path=delpath)
    LOG.debug(
        "Deleting block device %(devpath)s from the management "
        "partition via special file %(delpath)s.", {
            'devpath': devpath,
            'delpath': delpath
        })
    # Writing '1' to this sysfs file deletes the block device and rescans.
    priv_path.writefile(delpath, 'a', '1')

    # The bus scan is asynchronous.  Need to poll, waiting for the device to
    # disappear.  Stop when stat raises OSError (dev file not found) - which is
    # success - or after the specified timeout (which is failure).  Sleep 1/4
    # second between polls.
    @retrying.retry(retry_on_result=lambda result: result,
                    wait_fixed=250,
                    stop_max_delay=scan_timeout * 1000)
    def _poll_for_del(statpath):
        try:
            os.stat(statpath)
            return True
        except OSError:
            # Device special file is absent, as expected
            return False

    try:
        _poll_for_del(devpath)
    except retrying.RetryError as re:
        # stat just kept returning (dev file continued to exist).
        raise npvmex.DeviceDeletionException(
            devpath=devpath,
            polls=re.last_attempt.attempt_number,
            timeout=scan_timeout)
Beispiel #2
0
def discover_vscsi_disk(mapping, scan_timeout=300):
    """Bring a mapped device into the management partition and find its name.

    Based on a VSCSIMapping, scan the appropriate virtual SCSI host bus,
    causing the operating system to discover the mapped device.  Find and
    return the path of the newly-discovered device based on its UDID in the
    mapping.

    Note: scanning the bus will cause the operating system to discover *all*
    devices on that bus.  However, this method will only return the path for
    the specific device from the input mapping, based on its UDID.

    :param mapping: The pypowervm.wrappers.virtual_io_server.VSCSIMapping
                    representing the mapping of the desired disk to the
                    management partition.
    :param scan_timeout: The maximum number of seconds after scanning to wait
                         for the specified device to appear.
    :return: The udev-generated ("/dev/sdX") name of the discovered disk.
    :raise NoDiskDiscoveryException: If the disk did not appear after the
                                     specified timeout.
    :raise UniqueDiskDiscoveryException: If more than one disk appears with the
                                         expected UDID.
    """
    # Calculate the Linux slot number from the client adapter slot number.
    lslot = 0x30000000 | mapping.client_adapter.lpar_slot_num
    # We'll match the device ID based on the UDID, which is actually the last
    # 32 chars of the field we get from PowerVM.
    udid = mapping.backing_storage.udid[-32:]

    LOG.debug(
        "Trying to discover VSCSI disk with UDID %(udid)s on slot "
        "%(slot)x.", {
            'udid': udid,
            'slot': lslot
        })

    # Find the special file to scan the bus, and scan it.
    # This glob should yield exactly one result, but use the loop just in case.
    for scanpath in glob.glob(
            '/sys/bus/vio/devices/%x/host*/scsi_host/host*/scan' % lslot):
        # Writing '- - -' to this sysfs file triggers bus rescan
        priv_path.writefile(scanpath, 'a', '- - -')

    # Now see if our device showed up.  If so, we can reliably match it based
    # on its Linux ID, which ends with the disk's UDID.
    dpathpat = '/dev/disk/by-id/*%s' % udid

    # The bus scan is asynchronous.  Need to poll, waiting for the device to
    # spring into existence.  Stop when glob finds at least one device, or
    # after the specified timeout.  Sleep 1/4 second between polls.
    @retrying.retry(retry_on_result=lambda result: not result,
                    wait_fixed=250,
                    stop_max_delay=scan_timeout * 1000)
    def _poll_for_dev(globpat):
        return glob.glob(globpat)

    try:
        disks = _poll_for_dev(dpathpat)
    except retrying.RetryError as re:
        raise npvmex.NoDiskDiscoveryException(
            bus=lslot,
            udid=udid,
            polls=re.last_attempt.attempt_number,
            timeout=scan_timeout)
    # If we get here, _poll_for_dev returned a nonempty list.  If not exactly
    # one entry, this is an error.
    if len(disks) != 1:
        raise npvmex.UniqueDiskDiscoveryException(path_pattern=dpathpat,
                                                  count=len(disks))

    # The by-id path is a symlink.  Resolve to the /dev/sdX path
    dpath = os.path.realpath(disks[0])
    LOG.debug(
        "Discovered VSCSI disk with UDID %(udid)s on slot %(slot)x at "
        "path %(devname)s.", {
            'udid': udid,
            'slot': lslot,
            'devname': dpath
        })
    return dpath