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])
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)
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)
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)
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])
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)
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()
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)
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
def test_udevadm_settle(self, mock_exec): utils.udevadm_settle() mock_exec.assert_called_once_with("udevadm", "settle", check_exit_code=[0])
def test_udevadm_settle(self, mock_exec): utils.udevadm_settle() mock_exec.assert_called_once_with('udevadm', 'settle', check_exit_code=[0])