← Back to team overview

curtin-dev team mailing list archive

Re: [Merge] ~mwhudson/curtin:action-objects-more into curtin:master

 

Do we have the ability to test the Dasd parts of this?  I think it looks safe enough but if it's wrong then a future change is doomed.

Diff comments:

> diff --git a/curtin/block/mkfs.py b/curtin/block/mkfs.py
> index 20ee7b8..4e716e7 100644
> --- a/curtin/block/mkfs.py
> +++ b/curtin/block/mkfs.py
> @@ -230,15 +230,12 @@ def mkfs(path, fstype, strict=False, label=None, uuid=None, force=False,
>      return uuid
>  
>  
> -def mkfs_from_config(path, info, strict=False):
> +def mkfs_from_action(path, format, strict=False):

I think we want type names on these

>      """Make filesystem on block device with given path according to storage
>         config given"""
> -    fstype = info.get('fstype')
> -    if fstype is None:
> -        raise ValueError("fstype must be specified")
>      # NOTE: Since old metadata on partitions that have not been wiped can cause
>      #       some mkfs commands to refuse to work, it's best to use force=True
> -    mkfs(path, fstype, strict=strict, force=True, uuid=info.get('uuid'),
> -         label=info.get('label'), extra_options=info.get('extra_options'))
> +    mkfs(path, format.fstype, strict=strict, force=True, uuid=format.uuid,
> +         label=format.label, extra_options=format.extra_options)
>  
>  # vi: ts=4 expandtab syntax=python
> diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py
> index 11972b9..fc61e21 100644
> --- a/curtin/commands/block_meta.py
> +++ b/curtin/commands/block_meta.py
> @@ -733,92 +748,103 @@ def dasd_handler(info, storage_config, context):
>       'disk_layout': 'cdl',
>      }
>      """
> -    device_id = info.get('device_id')
> -    blocksize = info.get('blocksize')
> -    disk_layout = info.get('disk_layout')
> -    label = info.get('label')
> -    mode = info.get('mode')
> -    force_format = config.value_as_boolean(info.get('wipe'))
> -
> -    dasd_device = dasd.DasdDevice(device_id)
> -    if (force_format or dasd_device.needs_formatting(blocksize,
> -                                                     disk_layout, label)):
> -        if config.value_as_boolean(info.get('preserve')):
> +    dasd: Dasd = storage_actions.asobject(info)
> +    force_format = config.value_as_boolean(dasd.wipe)
> +
> +    dasd_device = DasdDevice(dasd.device_id)
> +    if (force_format or dasd_device.needs_formatting(dasd.blocksize,
> +                                                     dasd.disk_layout,
> +                                                     dasd.label)):
> +        if config.value_as_boolean(dasd.preserve):
>              raise ValueError(
>                  "dasd '%s' does not match configured properties and"
>                  "preserve is set to true.  The dasd needs formatting"
> -                "with the specified parameters to continue." % info.get('id'))
> +                "with the specified parameters to continue." % dasd.id)
>  
>          LOG.debug('Formatting dasd id=%s device_id=%s devname=%s',
> -                  info.get('id'), device_id, dasd_device.devname)
> -        dasd_device.format(blksize=blocksize, layout=disk_layout,
> -                           set_label=label, mode=mode)
> +                  dasd.id, dasd.device_id, dasd_device.devname)
> +        dasd_device.format(blksize=dasd.blocksize, layout=dasd.disk_layout,
> +                           set_label=dasd.label, mode=dasd.mode)
>  
>          # check post-format to ensure values match
> -        if dasd_device.needs_formatting(blocksize, disk_layout, label):
> +        if dasd_device.needs_formatting(
> +                dasd.blocksize, dasd.disk_layout, dasd.label):
>              raise RuntimeError(
>                  "Dasd %s failed to format" % dasd_device.devname)
>  
>  
> +@attr.s(auto_attribs=True)
> +class _Partitionable:
> +    ptable: Optional[str] = None
> +    preserve: bool = storage_actions.boolean(default=False)
> +    wipe: Optional[str] = None
> +    name: Optional[str] = None
> +
> +
> +@storage_actions.define("disk")
> +class Disk(_Partitionable):
> +    pass
> +
> +
>  def disk_handler(info, storage_config, context):
> +    disk: _Partitionable = storage_actions.asobject(info)
>      _dos_names = ['dos', 'msdos']
> -    ptable = info.get('ptable')
> -    if ptable and ptable not in PTABLES_VALID:
> +    if disk.ptable and disk.ptable not in PTABLES_VALID:
>          raise ValueError(
> -            'Invalid partition table type: %s in %s' % (ptable, info))
> +            'Invalid partition table type: %s in %s' % (disk.ptable, disk))
>  
> -    disk = get_path_to_storage_volume(info.get('id'), storage_config)
> -    context.id_to_device[info['id']] = disk
> +    disk_path = get_path_to_storage_volume(disk.id, storage_config)
> +    context.id_to_device[disk.id] = disk_path
>      # For disks, 'preserve' is what indicates whether the partition
>      # table should be reused or recreated but for compound devices
>      # such as raids, it indicates if the raid should be created or
>      # assumed to already exist. So to allow a pre-existing raid to get
>      # a new partition table, we use presence of 'wipe' field to
>      # indicate if the disk should be reformatted or not.
> -    if info['type'] == 'disk':
> -        preserve_ptable = config.value_as_boolean(info.get('preserve'))
> +    if disk.type == 'disk':
> +        preserve_ptable = disk.preserve
>      else:
> -        preserve_ptable = config.value_as_boolean(info.get('preserve')) \
> -                          and not config.value_as_boolean(info.get('wipe'))
> +        preserve_ptable = disk.preserve and not disk.wipe
>      if preserve_ptable:
>          # Handle preserve flag, verifying if ptable specified in config
> -        if ptable and ptable != PTABLE_UNSUPPORTED:
> -            current_ptable = block.get_part_table_type(disk)
> +        if disk.ptable and disk.ptable != PTABLE_UNSUPPORTED:
> +            current_ptable = block.get_part_table_type(disk_path)
>              LOG.debug('disk: current ptable type: %s', current_ptable)
>              if current_ptable not in PTABLES_SUPPORTED:
>                  raise ValueError(
>                      "disk '%s' does not have correct partition table or "
>                      "cannot be read, but preserve is set to true (or wipe is "
>                      "not set).  cannot continue installation." %
> -                    info.get('id'))
> +                    disk.id)
>          LOG.info("disk '%s' marked to be preserved, so keeping partition "
> -                 "table" % disk)
> +                 "table" % disk.id)
>      else:
>          # wipe the disk and create the partition table if instructed to do so
> -        if config.value_as_boolean(info.get('wipe')):
> -            block.wipe_volume(disk, mode=info.get('wipe'))
> -        if config.value_as_boolean(ptable):
> -            LOG.info("labeling device: '%s' with '%s' partition table", disk,
> -                     ptable)
> -            if ptable == "gpt":
> +        if config.value_as_boolean(disk.wipe):
> +            block.wipe_volume(disk_path, mode=disk.wipe)
> +        if disk.ptable is not None:
> +            LOG.info(
> +                "labeling device: '%s' with '%s' partition table", disk_path,
> +                disk.ptable)
> +            if disk.ptable == "gpt":
>                  # Wipe both MBR and GPT that may be present on the disk.
>                  # N.B.: wipe_volume wipes 1M at front and end of the disk.
>                  # This could destroy disk data in filesystems that lived
>                  # there.
> -                block.wipe_volume(disk, mode='superblock')
> -            elif ptable in _dos_names:
> -                util.subp(["parted", disk, "--script", "mklabel", "msdos"])
> -            elif ptable == "vtoc":
> -                util.subp(["fdasd", "-c", "/dev/null", disk])
> -        holders = clear_holders.get_holders(disk)
> +                block.wipe_volume(disk_path, mode='superblock')

totally unrelated to this PR but the above comment about front and end of the disk wiping makes me think we need to make that an opt-in behavior for wipe_volume to reduce surprises

> +            elif disk.ptable in _dos_names:
> +                util.subp(["parted", disk_path, "--script", "mklabel", "msdos"])
> +            elif disk.ptable == "vtoc":
> +                util.subp(["fdasd", "-c", "/dev/null", disk_path])
> +        holders = clear_holders.get_holders(disk_path)
>          if len(holders) > 0:
>              LOG.info('Detected block holders on disk %s: %s', disk, holders)
> -            clear_holders.clear_holders(disk)
> -            clear_holders.assert_clear(disk)
> +            clear_holders.clear_holders(disk_path)
> +            clear_holders.assert_clear(disk_path)
>  
>      # Make the name if needed
> -    if info.get('name'):
> -        make_dname(info.get('id'), storage_config)
> +    if disk.name:
> +        make_dname(disk.id, storage_config)
>  
>  
>  def getnumberoflogicaldisks(device, storage_config):
> @@ -1002,23 +1027,28 @@ def check_passed_path(info, actual_path):
>              info["type"], info, info['path'], actual_path))
>  
>  
> +@storage_actions.define("partition")
> +class Partition:

We have people with devices out there that have ptables on a partition and I don't know why that is done or how that would appear without forcing it, but I think we have to at least handle it.  So should Partition be a _Partitionable?  (could save that fun for a future PR if it causes too much trouble)

> +    device: str
> +    size: int = storage_actions.size()
> +    flag: Optional[str] = None
> +    path: Optional[str] = None
> +    preserve: bool = storage_actions.boolean(default=False)
> +    wipe: Optional[str] = None
> +
> +
>  def partition_handler(info, storage_config, context):
> -    device = info.get('device')
> -    size = info.get('size')
> -    flag = info.get('flag')
> -    disk_ptable = storage_config.get(device).get('ptable')
> +    partition: Partition = storage_actions.asobject(info)

I wonder if asobject should take an expected type value.  We already know that manual partitioning through curtin actions is a bit of a challenge, getting the type value incorrect can be as easy as a copy-paste error.

If the type name is outright flawed, _type_to_cls will fail to find it, so that's not my concern.  more along the lines that we're writing curtin storage config and attempting to construct a partition object and we copy-paste a disk and leave it as type: disk, could such an object make it to here?

> +    disk_ptable = storage_config.get(partition.device).get('ptable')
>      partition_type = None
> -    if not device:
> -        raise ValueError("device must be set for partition to be created")
> -    if not size:
> -        raise ValueError("size must be specified for partition to be created")
>  
> -    disk = get_path_to_storage_volume(device, storage_config)
> -    partnumber = determine_partition_number(info.get('id'), storage_config)
> +    disk = get_path_to_storage_volume(partition.device, storage_config)
> +    partnumber = determine_partition_number(partition.id, storage_config)
>      disk_kname = block.path_to_kname(disk)
>      part_path = block.dev_path(block.partition_kname(disk_kname, partnumber))
> -    check_passed_path(info, part_path)
> -    context.id_to_device[info['id']] = part_path
> +    if partition.path is not None:

this is a small logic change that drops a warning, but we actually use the fixed value so I think existing users are OK.

Are you intentionally dropping the warning for the `partition.path is None` case?

> +        check_passed_path({'path': partition.path}, part_path)
> +    context.id_to_device[partition.id] = part_path
>  
>      # consider the disks logical sector size when calculating sectors
>      try:
> @@ -1080,26 +1112,25 @@ def partition_handler(info, storage_config, context):
>                                        previous_size_sectors +
>                                        alignment_offset)
>  
> -    length_bytes = util.human2bytes(size)
>      # start sector is part of the sectors that define the partitions size
>      # so length has to be "size in sectors - 1"
> -    length_sectors = int(length_bytes / logical_block_size_bytes) - 1
> +    length_sectors = int(partition.size / logical_block_size_bytes) - 1
>      # logical partitions can't share their start sector with the extended
>      # partition and logical partitions can't go head-to-head, so we have to
>      # realign and for that increase size as required
> -    if info.get('flag') == "extended":
> -        logdisks = getnumberoflogicaldisks(device, storage_config)
> +    if partition.flag == "extended":
> +        logdisks = getnumberoflogicaldisks(partition.device, storage_config)
>          length_sectors = length_sectors + (logdisks * alignment_offset)
>  
>      # Handle preserve flag
>      create_partition = True
> -    if config.value_as_boolean(info.get('preserve')):
> +    if partition.preserve:

In some cases we're keeping the value_as_boolean and some not (maybe it was wipe), what would we like to standardize on?

>          if disk_ptable == 'vtoc':
> -            partition_verify_fdasd(disk, partnumber, info)
> +            partition_verify_fdasd(disk, partnumber, partition)
>          else:
>              sfdisk_info = block.sfdisk_info(disk)
>              part_info = block.get_partition_sfdisk_info(part_path, sfdisk_info)
> -            partition_verify_sfdisk(info, sfdisk_info['label'], part_info)
> +            partition_verify_sfdisk(partition, sfdisk_info['label'], part_info)
>          LOG.debug(
>              '%s partition %s already present, skipping create',
>              disk, partnumber)


-- 
https://code.launchpad.net/~mwhudson/curtin/+git/curtin/+merge/448442
Your team curtin developers is requested to review the proposed merge of ~mwhudson/curtin:action-objects-more into curtin:master.



References