示例#1
0
def _validate_partitioning(device):
    """Validate the final partition table.

    Check if after writing the image to disk we have a valid partition
    table by trying to read it. This will fail if the disk is junk.
    """
    try:
        # Ensure we re-read the partition table before we try to list
        # partitions
        utils.execute('partprobe', device, run_as_root=True,
                      attempts=CONF.disk_utils.partprobe_attempts)
    except (processutils.UnknownArgumentError,
            processutils.ProcessExecutionError, OSError) as e:
        LOG.warning("Unable to probe for partitions on device %(device)s "
                    "after writing the image, the partitioning table may "
                    "be broken. Error: %(error)s",
                    {'device': device, 'error': e})

    try:
        nparts = len(disk_utils.list_partitions(device))
    except (processutils.UnknownArgumentError,
            processutils.ProcessExecutionError, OSError) as e:
        msg = ("Unable to find a valid partition table on the disk after "
               "writing the image. Error {}".format(e))
        raise exception.InstanceDeployFailure(msg)

    # Check if there is at least one partition in the partition table after
    # deploy
    if not nparts:
        msg = ("No partitions found on the device {} after writing "
               "the image.".format(device))
        raise exception.InstanceDeployFailure(msg)
示例#2
0
def partition_with_path(path):
    root_dev = hardware.dispatch_to_managers('get_os_install_device')
    partitions = disk_utils.list_partitions(root_dev)
    local_path = tempfile.mkdtemp()

    for part in partitions:
        if 'esp' in part['flags'] or 'lvm' in part['flags']:
            LOG.debug('Skipping partition %s', part)
            continue

        part_path = partition_index_to_name(root_dev, part['number'])
        try:
            with utils.mounted(part_path) as local_path:
                conf_path = os.path.join(local_path, path)
                LOG.debug('Checking for path %s on %s', conf_path, part_path)
                if not os.path.isdir(conf_path):
                    continue

                LOG.info('Path found: %s on %s', conf_path, part_path)
                yield conf_path
                return
        except processutils.ProcessExecutionError as exc:
            LOG.warning('Failure when inspecting partition %s: %s', part, exc)

    raise RuntimeError("No partition found with path %s, scanned: %s"
                       % (path, partitions))
示例#3
0
def _validate_partitioning(device):
    """Validate the final partition table.

    Check if after writing the image to disk we have a valid partition
    table by trying to read it. This will fail if the disk is junk.
    """
    try:
        # Ensure we re-read the partition table before we try to list
        # partitions
        utils.execute('partprobe', device, run_as_root=True,
                      attempts=CONF.disk_utils.partprobe_attempts)
    except (processutils.UnknownArgumentError,
            processutils.ProcessExecutionError, OSError) as e:
        LOG.warning("Unable to probe for partitions on device %(device)s "
                    "after writing the image, the partitioning table may "
                    "be broken. Error: %(error)s",
                    {'device': device, 'error': e})

    try:
        nparts = len(disk_utils.list_partitions(device))
    except (processutils.UnknownArgumentError,
            processutils.ProcessExecutionError, OSError) as e:
        msg = ("Unable to find a valid partition table on the disk after "
               "writing the image. Error {}".format(e))
        raise exception.InstanceDeployFailure(msg)

    # Check if there is at least one partition in the partition table after
    # deploy
    if not nparts:
        msg = ("No partitions found on the device {} after writing "
               "the image.".format(device))
        raise exception.InstanceDeployFailure(msg)
示例#4
0
    def test_incorrect(self, log_mock, execute_mock):
        output = """
BYT;
/dev/sda:500107862016B:scsi:512:4096:msdos:ATA HGST HTS725050A7:;
1:XX1076MiB:---:524MiB:ext4::boot;
"""
        execute_mock.return_value = (output, '')
        self.assertEqual([], disk_utils.list_partitions('/dev/fake'))
        self.assertEqual(1, log_mock.call_count)
示例#5
0
    def test_incorrect(self, log_mock, execute_mock):
        output = """
BYT;
/dev/sda:500107862016B:scsi:512:4096:msdos:ATA HGST HTS725050A7:;
1:XX1076MiB:---:524MiB:ext4::boot;
"""
        execute_mock.return_value = (output, '')
        self.assertEqual([], disk_utils.list_partitions('/dev/fake'))
        self.assertEqual(1, log_mock.call_count)
示例#6
0
def get_efi_part_on_device(device):
    """Looks for the efi partition on a given device.

    A boot partition on a GPT disk is assumed to be an EFI partition as well.

    :param device: lock device upon which to check for the efi partition
    :return: the efi partition or None
    """
    is_gpt = scan_partition_table_type(device) == 'gpt'
    for part in disk_utils.list_partitions(device):
        flags = {x.strip() for x in part['flags'].split(',')}
        if 'esp' in flags or ('boot' in flags and is_gpt):
            LOG.debug("Found EFI partition %s on device %s.", part, device)
            return part['number']
    else:
        LOG.debug("No efi partition found on device %s", device)
示例#7
0
    def test_correct(self, execute_mock):
        output = """
BYT;
/dev/sda:500107862016B:scsi:512:4096:msdos:ATA HGST HTS725050A7:;
1:1.00MiB:501MiB:500MiB:ext4::boot;
2:501MiB:476940MiB:476439MiB:::;
"""
        expected = [
            {'number': 1, 'start': 1, 'end': 501, 'size': 500,
             'filesystem': 'ext4', 'flags': 'boot'},
            {'number': 2, 'start': 501, 'end': 476940, 'size': 476439,
             'filesystem': '', 'flags': ''},
        ]
        execute_mock.return_value = (output, '')
        result = disk_utils.list_partitions('/dev/fake')
        self.assertEqual(expected, result)
        execute_mock.assert_called_once_with(
            'parted', '-s', '-m', '/dev/fake', 'unit', 'MiB', 'print',
            use_standard_locale=True, run_as_root=True)
示例#8
0
    def test_correct(self, execute_mock):
        output = """
BYT;
/dev/sda:500107862016B:scsi:512:4096:msdos:ATA HGST HTS725050A7:;
1:1.00MiB:501MiB:500MiB:ext4::boot;
2:501MiB:476940MiB:476439MiB:::;
"""
        expected = [
            {'number': 1, 'start': 1, 'end': 501, 'size': 500,
             'filesystem': 'ext4', 'flags': 'boot'},
            {'number': 2, 'start': 501, 'end': 476940, 'size': 476439,
             'filesystem': '', 'flags': ''},
        ]
        execute_mock.return_value = (output, '')
        result = disk_utils.list_partitions('/dev/fake')
        self.assertEqual(expected, result)
        execute_mock.assert_called_once_with(
            'parted', '-s', '-m', '/dev/fake', 'unit', 'MiB', 'print',
            use_standard_locale=True, run_as_root=True)
示例#9
0
def find_partition_with_path(path, device=None):
    """Find a partition with the given path.

    :param path: Expected path.
    :param device: Target device. If None, the root device is used.
    :returns: A context manager that will unmount and delete the temporary
        mount point on exit.
    """
    if device is None:
        device = hardware.dispatch_to_managers('get_os_install_device')
    partitions = disk_utils.list_partitions(device)
    # Make os.path.join work as expected
    lookup_path = path.lstrip('/')

    for part in partitions:
        if 'lvm' in part['flags']:
            LOG.debug('Skipping LVM partition %s', part)
            continue

        # TODO(dtantsur): switch to ironic-lib instead:
        # https://review.opendev.org/c/openstack/ironic-lib/+/774502
        part_template = '%s%s'
        if 'nvme' in device:
            part_template = '%sp%s'
        part_path = part_template % (device, part['number'])

        LOG.debug('Inspecting partition %s for path %s', part, path)
        try:
            with ironic_utils.mounted(part_path) as local_path:
                found_path = os.path.join(local_path, lookup_path)
                if not os.path.isdir(found_path):
                    continue

                LOG.info('Path %s has been found on partition %s', path, part)
                yield found_path
                return
        except processutils.ProcessExecutionError as exc:
            LOG.warning('Failure when inspecting partition %s: %s', part, exc)

    raise errors.DeviceNotFound(
        "No partition found with path %s, scanned: %s" % (path, partitions))
def create_config_drive_partition(node_uuid, device, configdrive):
    """Create a partition for config drive

    Checks if the device is GPT or MBR partitioned and creates config drive
    partition accordingly.

    :param node_uuid: UUID of the Node.
    :param device: The device path.
    :param configdrive: Base64 encoded Gzipped configdrive content or
        configdrive HTTP URL.
    :raises: InstanceDeployFailure if config drive size exceeds maximum limit
        or if it fails to create config drive.
    """
    confdrive_file = None
    try:
        config_drive_part = get_labelled_partition(
            device, disk_utils.CONFIGDRIVE_LABEL, node_uuid)

        confdrive_mb, confdrive_file = get_configdrive(configdrive, node_uuid)
        if confdrive_mb > MAX_CONFIG_DRIVE_SIZE_MB:
            raise exception.InstanceDeployFailure(
                'Config drive size exceeds maximum limit of 64MiB. '
                'Size of the given config drive is %(size)d MiB for '
                'node %(node)s.'
                % {'size': confdrive_mb, 'node': node_uuid})

        LOG.debug("Adding config drive partition %(size)d MiB to "
                  "device: %(dev)s for node %(node)s",
                  {'dev': device, 'size': confdrive_mb, 'node': node_uuid})

        disk_utils.fix_gpt_partition(device, node_uuid)
        if config_drive_part:
            LOG.debug("Configdrive for node %(node)s exists at "
                      "%(part)s",
                      {'node': node_uuid, 'part': config_drive_part})
        else:
            cur_parts = set(part['number']
                            for part in disk_utils.list_partitions(device))

            if disk_utils.get_partition_table_type(device) == 'gpt':
                create_option = '0:-%dMB:0' % MAX_CONFIG_DRIVE_SIZE_MB
                utils.execute('sgdisk', '-n', create_option, device,
                              run_as_root=True)
            else:
                # Check if the disk has 4 partitions. The MBR based disk
                # cannot have more than 4 partitions.
                # TODO(stendulker): One can use logical partitions to create
                # a config drive if there are 3 primary partitions.
                # https://bugs.launchpad.net/ironic/+bug/1561283
                try:
                    pp_count, lp_count = disk_utils.count_mbr_partitions(
                        device)
                except ValueError as e:
                    raise exception.InstanceDeployFailure(
                        'Failed to check the number of primary partitions '
                        'present on %(dev)s for node %(node)s. Error: '
                        '%(error)s' % {'dev': device, 'node': node_uuid,
                                       'error': e})
                if pp_count > 3:
                    raise exception.InstanceDeployFailure(
                        'Config drive cannot be created for node %(node)s. '
                        'Disk (%(dev)s) uses MBR partitioning and already '
                        'has %(parts)d primary partitions.'
                        % {'node': node_uuid, 'dev': device,
                           'parts': pp_count})

                # Check if disk size exceeds 2TB msdos limit
                startlimit = '-%dMiB' % MAX_CONFIG_DRIVE_SIZE_MB
                endlimit = '-0'
                if _is_disk_larger_than_max_size(device, node_uuid):
                    # Need to create a small partition at 2TB limit
                    LOG.warning("Disk size is larger than 2TB for "
                                "node %(node)s. Creating config drive "
                                "at the end of the disk %(disk)s.",
                                {'node': node_uuid, 'disk': device})
                    startlimit = (MAX_DISK_SIZE_MB_SUPPORTED_BY_MBR
                                  - MAX_CONFIG_DRIVE_SIZE_MB - 1)
                    endlimit = MAX_DISK_SIZE_MB_SUPPORTED_BY_MBR - 1

                utils.execute('parted', '-a', 'optimal', '-s', '--', device,
                              'mkpart', 'primary', 'fat32', startlimit,
                              endlimit, run_as_root=True)
            # Trigger device rescan
            disk_utils.trigger_device_rescan(device)

            upd_parts = set(part['number']
                            for part in disk_utils.list_partitions(device))
            new_part = set(upd_parts) - set(cur_parts)
            if len(new_part) != 1:
                raise exception.InstanceDeployFailure(
                    'Disk partitioning failed on device %(device)s. '
                    'Unable to retrieve config drive partition information.'
                    % {'device': device})

            config_drive_part = disk_utils.partition_index_to_path(
                device, new_part.pop())

            disk_utils.udev_settle()

            # NOTE(vsaienko): check that devise actually exists,
            # it is not handled by udevadm when using ISCSI, for more info see:
            # https://bugs.launchpad.net/ironic/+bug/1673731
            # Do not use 'udevadm settle --exit-if-exist' here
            LOG.debug('Waiting for the config drive partition %(part)s '
                      'on node %(node)s to be ready for writing.',
                      {'part': config_drive_part, 'node': node_uuid})
            utils.execute('test', '-e', config_drive_part, attempts=15,
                          delay_on_retry=True)

        disk_utils.dd(confdrive_file, config_drive_part)
        LOG.info("Configdrive for node %(node)s successfully "
                 "copied onto partition %(part)s",
                 {'node': node_uuid, 'part': config_drive_part})

    except (processutils.UnknownArgumentError,
            processutils.ProcessExecutionError, OSError) as e:
        msg = ('Failed to create config drive on disk %(disk)s '
               'for node %(node)s. Error: %(error)s' %
               {'disk': device, 'node': node_uuid, 'error': e})
        LOG.error(msg)
        raise exception.InstanceDeployFailure(msg)
    finally:
        # If the configdrive was requested make sure we delete the file
        # after copying the content to the partition
        if confdrive_file:
            utils.unlink_without_raise(confdrive_file)