Esempio n. 1
0
def mdremove(mdname):
    # check if md exists
    if mdname not in get_mdnames():
        raise errors.MDNotFoundError(
            'Error while removing md: md %s not found' % mdname)
    # FIXME: The issue faced was quiet hard to reproduce and to figure out the
    #       root cause. For unknown reason already removed md device is
    #       unexpectedly returning back after a while from time to time making
    #       new md device creation to fail.
    #           Still the actual reason of its failure is unknown, but after a
    #       searching on a web a mention was found about a race in udev
    #       http://dev.bizo.com/2012/07/mdadm-device-or-resource-busy.html
    #       The article recommends to disable udev's queue entirely during md
    #       device manipulation which sounds rather unappropriate for our case.
    #       And the link to original post on mailing list suggests to execute
    #       `udevadm settle` before removing the md device.
    #       here -> http://permalink.gmane.org/gmane.linux.raid/34027
    #           So, what was done. `udevadm settle` calls were placed just
    #       before any of `mdadm` calls and the analizyng the logs was started.
    #       According to the manual `settle` is an option that "Watches the
    #       udev event queue, and exits if all current events are handled".
    #       That means it will wait for udev's finishing of processing the
    #       events. According to the logs noticeable delay had been recognized
    #       between `udevadm settle` and the next `mdadm` call.
    #           The delay was about 150-200ms or even bigger. It was appeared
    #       right before the `mdadm --stop` call. That just means that udev was
    #       too busy with events when we start to modifiy md devices hard.
    #           Thus `udevadm settle` is helping to avoid the later failure and
    #       to prevent strange behaviour of md device.
    utils.udevadm_settle()
    utils.execute('mdadm', '--stop', mdname, check_exit_code=[0])
    utils.execute('mdadm', '--remove', mdname, check_exit_code=[0, 1])
Esempio n. 2
0
def mdremove(mdname):
    # check if md exists
    if mdname not in get_mdnames():
        raise errors.MDNotFoundError(
            'Error while removing md: md %s not found' % mdname)
    # FIXME: The issue faced was quiet hard to reproduce and to figure out the
    #       root cause. For unknown reason already removed md device is
    #       unexpectedly returning back after a while from time to time making
    #       new md device creation to fail.
    #           Still the actual reason of its failure is unknown, but after a
    #       searching on a web a mention was found about a race in udev
    #       http://dev.bizo.com/2012/07/mdadm-device-or-resource-busy.html
    #       The article recommends to disable udev's queue entirely during md
    #       device manipulation which sounds rather unappropriate for our case.
    #       And the link to original post on mailing list suggests to execute
    #       `udevadm settle` before removing the md device.
    #       here -> http://permalink.gmane.org/gmane.linux.raid/34027
    #           So, what was done. `udevadm settle` calls were placed just
    #       before any of `mdadm` calls and the analizyng the logs was started.
    #       According to the manual `settle` is an option that "Watches the
    #       udev event queue, and exits if all current events are handled".
    #       That means it will wait for udev's finishing of processing the
    #       events. According to the logs noticeable delay had been recognized
    #       between `udevadm settle` and the next `mdadm` call.
    #           The delay was about 150-200ms or even bigger. It was appeared
    #       right before the `mdadm --stop` call. That just means that udev was
    #       too busy with events when we start to modifiy md devices hard.
    #           Thus `udevadm settle` is helping to avoid the later failure and
    #       to prevent strange behaviour of md device.
    utils.udevadm_settle()
    utils.execute('mdadm', '--stop', mdname, check_exit_code=[0])
    utils.execute('mdadm', '--remove', mdname, check_exit_code=[0, 1])
Esempio n. 3
0
def set_partition_flag(dev, num, flag, state='on'):
    """Sets flag on a partition

    :param dev: A device file, e.g. /dev/sda.
    :param num: Partition number
    :param flag: Flag name. Must be one of 'bios_grub', 'legacy_boot',
    'boot', 'raid', 'lvm'
    :param state: Desiable flag state. 'on' or 'off'. Default is 'on'.

    :returns: None
    """
    LOG.debug('Trying to set partition flag: dev=%s num=%s flag=%s state=%s' %
              (dev, num, flag, state))
    # parted supports more flags but we are interested in
    # setting only this subset of them.
    # not all of these flags are compatible with one another.
    if flag not in ('bios_grub', 'legacy_boot', 'boot', 'raid', 'lvm'):
        raise errors.WrongPartitionSchemeError(
            'Unsupported partition flag: %s' % flag)
    if state not in ('on', 'off'):
        raise errors.WrongPartitionSchemeError(
            'Wrong partition flag state: %s' % state)
    utils.udevadm_settle()
    out, err = utils.execute('parted', '-s', dev, 'set', str(num),
                             flag, state, check_exit_code=[0, 1])
    LOG.debug('Parted output: \n%s' % out)
    reread_partitions(dev, out=out)
Esempio n. 4
0
def make_partition(dev, begin, end, ptype):
    LOG.debug('Trying to create a partition: dev=%s begin=%s end=%s' %
              (dev, begin, end))
    if ptype not in ('primary', 'logical'):
        raise errors.WrongPartitionSchemeError(
            'Wrong partition type: %s' % ptype)

    # check begin >= end
    if begin >= end:
        raise errors.WrongPartitionSchemeError(
            'Wrong boundaries: begin >= end')

    # check if begin and end are inside one of free spaces available
    if not any(x['fstype'] == 'free' and begin >= x['begin'] and
               end <= x['end'] for x in info(dev)['parts']):
        raise errors.WrongPartitionSchemeError(
            'Invalid boundaries: begin and end '
            'are not inside available free space')

    utils.udevadm_settle()
    out, err = utils.execute(
        'parted', '-a', 'optimal', '-s', dev, 'unit', 'MiB',
        'mkpart', ptype, str(begin), str(end), check_exit_code=[0, 1])
    LOG.debug('Parted output: \n%s' % out)
    reread_partitions(dev, out=out)
Esempio n. 5
0
def set_partition_flag(dev, num, flag, state='on'):
    """Sets flag on a partition

    :param dev: A device file, e.g. /dev/sda.
    :param num: Partition number
    :param flag: Flag name. Must be one of 'bios_grub', 'legacy_boot',
    'boot', 'raid', 'lvm'
    :param state: Desiable flag state. 'on' or 'off'. Default is 'on'.

    :returns: None
    """
    LOG.debug('Trying to set partition flag: dev=%s num=%s flag=%s state=%s' %
              (dev, num, flag, state))
    # parted supports more flags but we are interested in
    # setting only this subset of them.
    # not all of these flags are compatible with one another.
    if flag not in ('bios_grub', 'legacy_boot', 'boot', 'raid', 'lvm'):
        raise errors.WrongPartitionSchemeError(
            'Unsupported partition flag: %s' % flag)
    if state not in ('on', 'off'):
        raise errors.WrongPartitionSchemeError(
            'Wrong partition flag state: %s' % state)
    utils.udevadm_settle()
    out, err = utils.execute('parted', '-s', dev, 'set', str(num),
                             flag, state, check_exit_code=[0, 1])
    LOG.debug('Parted output: \n%s' % out)
    reread_partitions(dev, out=out)
Esempio n. 6
0
def remove_partition(dev, num):
    LOG.debug('Trying to remove partition: dev=%s num=%s' % (dev, num))
    if not any(x['fstype'] != 'free' and x['num'] == num
               for x in info(dev)['parts']):
        raise errors.PartitionNotFoundError('Partition %s not found' % num)
    utils.udevadm_settle()
    out, err = utils.execute('parted', '-s', dev, 'rm',
                             str(num), check_exit_code=[0, 1])
    reread_partitions(dev, out=out)
Esempio n. 7
0
def remove_partition(dev, num):
    LOG.debug('Trying to remove partition: dev=%s num=%s' % (dev, num))
    if not any(x['fstype'] != 'free' and x['num'] == num
               for x in info(dev)['parts']):
        raise errors.PartitionNotFoundError('Partition %s not found' % num)
    utils.udevadm_settle()
    out, err = utils.execute('parted', '-s', dev, 'rm',
                             str(num), check_exit_code=[0, 1])
    reread_partitions(dev, out=out)
Esempio n. 8
0
def set_gpt_type(dev, num, type_guid):
    """Sets guid on a partition.

    :param dev: A device file, e.g. /dev/sda.
    :param num: Partition number
    :param type_guid: Partition type guid. Must be one of those listed
    on this page http://en.wikipedia.org/wiki/GUID_Partition_Table.
    This method does not check whether type_guid is valid or not.

    :returns: None
    """
    # TODO(kozhukalov): check whether type_guid is valid
    LOG.debug('Setting partition GUID: dev=%s num=%s guid=%s' %
              (dev, num, type_guid))
    utils.udevadm_settle()
    utils.execute('sgdisk', '--typecode=%s:%s' % (num, type_guid),
                  dev, check_exit_code=[0])
Esempio n. 9
0
def set_gpt_type(dev, num, type_guid):
    """Sets guid on a partition.

    :param dev: A device file, e.g. /dev/sda.
    :param num: Partition number
    :param type_guid: Partition type guid. Must be one of those listed
    on this page http://en.wikipedia.org/wiki/GUID_Partition_Table.
    This method does not check whether type_guid is valid or not.

    :returns: None
    """
    # TODO(kozhukalov): check whether type_guid is valid
    LOG.debug('Setting partition GUID: dev=%s num=%s guid=%s' %
              (dev, num, type_guid))
    utils.udevadm_settle()
    utils.execute('sgdisk', '--typecode=%s:%s' % (num, type_guid),
                  dev, check_exit_code=[0])
Esempio n. 10
0
def make_label(dev, label='gpt'):
    """Creates partition label on a device.

    :param dev: A device file, e.g. /dev/sda.
    :param label: Partition label type 'gpt' or 'msdos'. Optional.

    :returns: None
    """
    LOG.debug('Trying to create %s partition table on device %s' %
              (label, dev))
    if label not in ('gpt', 'msdos'):
        raise errors.WrongPartitionLabelError(
            'Wrong partition label type: %s' % label)
    utils.udevadm_settle()
    out, err = utils.execute('parted', '-s', dev, 'mklabel', label,
                             check_exit_code=[0, 1])
    LOG.debug('Parted output: \n%s' % out)
    reread_partitions(dev, out=out)
Esempio n. 11
0
def make_label(dev, label='gpt'):
    """Creates partition label on a device.

    :param dev: A device file, e.g. /dev/sda.
    :param label: Partition label type 'gpt' or 'msdos'. Optional.

    :returns: None
    """
    LOG.debug('Trying to create %s partition table on device %s' %
              (label, dev))
    if label not in ('gpt', 'msdos'):
        raise errors.WrongPartitionLabelError(
            'Wrong partition label type: %s' % label)
    utils.udevadm_settle()
    out, err = utils.execute('parted', '-s', dev, 'mklabel', label,
                             check_exit_code=[0, 1])
    LOG.debug('Parted output: \n%s' % out)
    reread_partitions(dev, out=out)
Esempio n. 12
0
def reread_partitions(dev, out='Device or resource busy', timeout=60):
    # The reason for this method to exist is that old versions of parted
    # use ioctl(fd, BLKRRPART, NULL) to tell Linux to re-read partitions.
    # This system call does not work sometimes. So we try to re-read partition
    # table several times. Besides partprobe uses BLKPG instead, which
    # is better than BLKRRPART for this case. BLKRRPART tells Linux to re-read
    # partitions while BLKPG tells Linux which partitions are available
    # BLKPG is usually used as a fallback system call.
    begin = time.time()
    while 'Device or resource busy' in out:
        if time.time() > begin + timeout:
            raise errors.BaseError('Unable to re-read partition table on'
                                   'device %s' % dev)
        LOG.debug('Last time output contained "Device or resource busy". '
                  'Trying to re-read partition table on device %s' % dev)
        time.sleep(2)
        out, err = utils.execute('partprobe', dev, check_exit_code=[0, 1])
        LOG.debug('Partprobe output: \n%s' % out)
        utils.udevadm_settle()
Esempio n. 13
0
def reread_partitions(dev, out='Device or resource busy', timeout=60):
    # The reason for this method to exist is that old versions of parted
    # use ioctl(fd, BLKRRPART, NULL) to tell Linux to re-read partitions.
    # This system call does not work sometimes. So we try to re-read partition
    # table several times. Besides partprobe uses BLKPG instead, which
    # is better than BLKRRPART for this case. BLKRRPART tells Linux to re-read
    # partitions while BLKPG tells Linux which partitions are available
    # BLKPG is usually used as a fallback system call.
    begin = time.time()
    while 'Device or resource busy' in out:
        if time.time() > begin + timeout:
            raise errors.BaseError('Unable to re-read partition table on'
                                   'device %s' % dev)
        LOG.debug('Last time output contained "Device or resource busy". '
                  'Trying to re-read partition table on device %s' % dev)
        time.sleep(2)
        out, err = utils.execute('partprobe', dev, check_exit_code=[0, 1])
        LOG.debug('Partprobe output: \n%s' % out)
        utils.udevadm_settle()
Esempio n. 14
0
def make_partition(dev, begin, end, ptype, alignment='optimal'):
    """Creates a partition on the device.

    :param dev: A device file, e.g. /dev/sda.
    :param begin: Beginning of the partition.
    :param end: Ending of the partition.
    :param ptype: Partition type: primary or logical.
    :param alignment: Set alignment mode for newly created partitions,
    valid alignment types are: none, cylinder, minimal, optimal. For more
    information about this you can find in GNU parted manual.

    :returns: None
    """
    LOG.debug('Trying to create a partition: dev=%s begin=%s end=%s' %
              (dev, begin, end))
    if ptype not in ('primary', 'logical'):
        raise errors.WrongPartitionSchemeError(
            'Wrong partition type: %s' % ptype)
    if alignment not in PARTITION_ALIGMENT:
        raise errors.WrongPartitionSchemeError(
            'Wrong partition alignment requested: %s' % alignment)

    # check begin >= end
    if begin >= end:
        raise errors.WrongPartitionSchemeError(
            'Wrong boundaries: begin >= end')

    # check if begin and end are inside one of free spaces available
    if not any(x['fstype'] == 'free' and begin >= x['begin'] and
               end <= x['end'] for x in info(dev)['parts']):
        raise errors.WrongPartitionSchemeError(
            'Invalid boundaries: begin and end '
            'are not inside available free space')

    utils.udevadm_settle()
    out, err = utils.execute(
        'parted', '-a', alignment, '-s', dev, 'unit', 'MiB',
        'mkpart', ptype, str(begin), str(end), check_exit_code=[0, 1])
    LOG.debug('Parted output: \n%s' % out)
    reread_partitions(dev, out=out)
Esempio n. 15
0
def info(dev):
    utils.udevadm_settle()
    parted_output = utils.execute('parted', '-s', dev, '-m',
                                  'unit', 'MiB',
                                  'print free',
                                  check_exit_code=[0])[0]
    LOG.debug('Parted info output: \n%s' % parted_output)

    result = parse_partition_info(parted_output)

    file_output = utils.execute('file', '-sk', dev,
                                check_exit_code=[0])[0]
    LOG.debug('File info output: \n%s' % file_output)

    result['generic']['has_bootloader'] = 'boot sector' in file_output
    for part in result['parts']:
        blkid_output = utils.execute('blkid -s UUID -o value',
                                     part['name'],
                                     check_exit_code=False)[0].strip()
        LOG.debug('Blkid output: \n%s' % blkid_output)
        part['uuid'] = blkid_output

    LOG.debug('Info result: %s' % result)
    return result
Esempio n. 16
0
def info(dev):
    utils.udevadm_settle()
    parted_output = utils.execute('parted', '-s', dev, '-m',
                                  'unit', 'MiB',
                                  'print free',
                                  check_exit_code=[0])[0]
    LOG.debug('Parted info output: \n%s' % parted_output)

    result = parse_partition_info(parted_output)

    file_output = utils.execute('file', '-sk', dev,
                                check_exit_code=[0])[0]
    LOG.debug('File info output: \n%s' % file_output)

    result['generic']['has_bootloader'] = 'boot sector' in file_output
    for part in result['parts']:
        blkid_output = utils.execute('blkid -s UUID -o value',
                                     part['name'],
                                     check_exit_code=False)[0].strip()
        LOG.debug('Blkid output: \n%s' % blkid_output)
        part['uuid'] = blkid_output

    LOG.debug('Info result: %s' % result)
    return result
Esempio n. 17
0
 def test_udevadm_settle(self, mock_exec):
     utils.udevadm_settle()
     mock_exec.assert_called_once_with("udevadm", "settle", check_exit_code=[0])
Esempio n. 18
0
 def test_udevadm_settle(self, mock_exec):
     utils.udevadm_settle()
     mock_exec.assert_called_once_with('udevadm',
                                       'settle',
                                       check_exit_code=[0])