Support custom partitions

In addition to the volume volume management settings also
allow to setup low level table entries like in the following
example:

<partitions>
    <partition name="var" size="100" mountpoint="/var" filesystem="ext3"/>
</partitions>
This commit is contained in:
Marcus Schäfer 2021-10-21 23:33:09 +02:00 committed by Marcus Schäfer
parent bf5c9d0d55
commit 94de1336d8
No known key found for this signature in database
GPG Key ID: A16C1128698C8CAC
14 changed files with 841 additions and 334 deletions

View File

@ -707,8 +707,24 @@ that is being used as a vagrant box. For details see: :ref:`setup_vagrant`
<preferences><type><systemdisk>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Used to describe the geometry, partitions and volumes, in a
disk image. For details see: :ref:`custom_volumes`
Used to describe the volumes of the disk area which
contains the root filesystem. Volumes are either a feature
of the used filesystem or LVM is used for this purpose.
For details see: :ref:`custom_volumes`
.. note::
When both `<partitions>` and `<systemdisk>` are used, `<partitions>`
are evaluated first and mount points defined in `<partitions>` cannot
be redefined as `<systemdisk>` volumes. The two types define a
complete disk setup, so there cannot be any overlapping volumes
or mount points. As a result, whatever is written in `<partitions>`
cannot be expressed in the same way in `<volumes>`.
<preferences><type><partitions>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Used to describe the geometry of the disk on the level of the
partition table. For details see: :ref:`custom_partitions`
<preferences><type><oemconfig>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -5,132 +5,107 @@ Custom Disk Partitions
.. sidebar:: Abstract
This page provides some details about what {kiwi} supports and does
not support regarding customization over the partition scheme. It also
provides some guidance in case the user requires some custom layout
beyond {kiwi} supported features.
By design, {kiwi} does not support a customized partition table. Alternatively,
{kiwi} supports the definition of user-defined volumes which covers most of
common use cases. See :ref:`Custom Disk Volumes <custom_volumes>` for
further details about that.
This page provides details about the opportunities and limitations
to customize the partition table in addition to the volume management
settings from :ref:`custom_volumes`.
{kiwi} has its own partitioning schema which is defined according to several
different user configurations: boot firmware, boot partition,
expandable layouts, etc. Those supported features have an impact on the
partitioning schema. MBR or GUID partition tables are not flexible,
carry limitations and are tied to some specific disk geometry. Because
of that the preferred alternative to disk layouts based on traditional
partition tables is using flexible approaches like logic volumes.
partitioning schema.
As an example, expandable OEM images is a relevant {kiwi} feature that
is incompatible with the idea of adding user defined partitions on the
system area.
MBR or GUID partition tables are not flexible, carry limitations and are
tied to some specific disk geometry. Because of that the preferred alternative
to disk layouts based on traditional partition tables is using flexible
approaches like logic volumes.
Despite no full customization is supported, some aspects of the partition
schema can be customized. {kiwi} supports:
However, on certain conditions additional entries to the low level
partition table are needed. For this purpose the `<partitions>` section
exists and allows to add custom entries like shown in the following
example:
1. Adding a spare partition *before* the root (`/`) partition.
.. code:: xml
It can be achieved by using the `spare_part` type attribute.
<partitions>
<partition name="var" size="100" mountpoint="/var" filesystem="ext3"/>
</partitions>
2. Leaving some unpartitioned area at the *end* of the disk.
Each `<partition>` entry puts a partition of the configured size in the
low level partition table, creates a filesystem on it and includes
it to the system's fstab file. If parts of the root filesystem are
moved into its own partition like it's the case in the above example,
this partition will also contain the data that gets installed during
the image creation process to that area.
The following attributes must/can be set to configured a partition entry:
name="identifier"
Mandatory name of the partition as handled by {kiwi}.
.. note::
There are the following reserved names which cannot be used
because they are already represented by existing attributes:
`root`, `readonly`, `boot`, `prep`, `spare`, `swap`, `efi_csm`
and `efi`.
partition_name="name"
Optional name of the partition as it appears when listing the
table contents with tools like `gdisk`. If no name is set
{kiwi} constructs a name of the form `p.lx(identifier_from_name_attr)`
partition_type="type_identifier"
Optional partition type identifier as handled by {kiwi}.
Allowed values are `t.linux` and `t.raid`. If not specified
`t.linux` is the default.
size="size_string"
Mandatory size of the partition. A size string can end with `M` or
`G` to indicate a mega-Byte or giga-Byte value. Without a unit
specification mega-Byte is used.
mountpoint="path"
Mandatory mountpoint to mount the partition in the system.
filesystem="btrfs|ext2|ext3|ext4|squashfs|xfs
Mandatory filesystem configuration to create one of the supported
filesystems on the partition.
Despite the customization options of the partition table shown above
there are the following limitations:
1. The root partition is always the last one
Disk imags build with {kiwi} are designed to be expandable.
For this feature to work the partition containing the system
rootfs must always be the last one. If this is unwanted for
some reason {kiwi} offers an opportunity for one extra/spare
partition with the option to be also placed at the end of the
table. For details lookup `spare_part` in :ref:`image-description-elements`
2. There can be no gaps in the partition table
The way partitions are configured does not allow for gaps in the
table. As of today there was no use case were it made sense to
leave a gap between table entries. However, leaving some space
free at the **end** of the partition geometry is possible in the
following ways:
* **Deploy with unpartitioned free space.**
To leave space unpartitioned on first boot of a disk image
it is possible to configured an `<oem-systemsize>` which is
smaller than the disk the image gets deployed to. Details
about this setting can be found in :ref:`image-description-elements`
* **Build with unpartitioned free space.**
Setting some unpartitioned free space on the disk can be done using
the `unpartitioned` attribute of `size` element in type's section. [LINK]
the `unpartitioned` attribute of `size` element in type's section.
For details see :ref:`disk-the-size-element`
3. Expand built disks to a new size adding unpartitioned free space at
the *end* of the disk.
* **Resize built image adding unpartitioned free space.**
A built image can be resized by using the `kiwi-ng image resize` command
and set a new extended size for the disk. See {kiwi} commands docs
:ref:`here <db_kiwi_image_resize>`.
Custom Partitioning at Boot Time
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Adding additional partitions at boot time of {kiwi} images is also possible,
however, setting the tools and scripts for doing so needs to be handled by
the user. A possible strategy to add partitions on system area are described
below.
The main idea consists on running a first boot service that creates the
partitions that are needed. Adding custom services is simple, use the
following steps:
1. Create a unit file for a systemd service:
.. code:: shell
[Unit]
Description=Add a data partition
After=basic.target
Wants=basic.target
[Service]
Type=oneshot
ExecStart=/bin/bash /usr/local/bin/create_part
This systemd unit file will run at boot time once systemd reaches the basic
target. At this stage all basic services are up an running (devices mounted,
network interfaces up, etc.). In case the service is required to run on
earlier stages for some reason, default dependencies need to be disabled,
see `systemd man pages <https://www.freedesktop.org/software/systemd/man/systemd.service.html>`_.
2. Create partitioner shell script matching your specific needs
Consider the following steps for a partitioner shell script that
creates a new partition. Following the above unit file example
the `/usr/local/bin/create_part` script should cover the following
steps:
a. Verify partition exists
Verify the required partition is not mounted neither exists. Exit
zero (0) if is already there.
Use tools such `findmnt` to find the root device and `blkid`
or `lsblk` to find a partition with certain label or similar
criteria.
b. Create a new partition
Create a new partition. On error, exit with non zero.
Use partitioner tools such as `sgdisk` that can be easily used
in non interactive scripts. Using `partprobe` to reload partition
table to make OS aware of the changes is handy.
c. Make filesystem
Add the desired filesystem to the new partitions. On error, exit
with non zero.
Regular filesystem formatting tools (`mkfs.ext4` just to mention one)
can be used to apply the desired filesystem to the just created
new partition. At this stage it is handy to add a label to the
filesystem for easy recognition on later stages or script reruns.
d. Update fstab file
Just echo and append the desired entry in /etc/fstab.
e. Mount partition
`mount --all` will try to mount all fstab volumes, it just omits
any already mounted device.
3. Add additional files into the root overlay tree.
The above described unit files and partition creation shell script
need to be included into the overlay tree of the image, thus they should
be placed into the expected paths in root folder (or in
:file:`root.tar.gz` tarball).
4. Activate the service in :file:`config.sh`
The service needs to be enabled during image built time to be
run during the very first boot. In can be done by adding the following
snipped inside the :file:`config.sh`.

View File

@ -25,6 +25,7 @@ from typing import (
import kiwi.defaults as defaults
from kiwi.utils.temporary import Temporary
from kiwi.storage.disk import ptable_entry_type
from kiwi.defaults import Defaults
from kiwi.filesystem.base import FileSystemBase
from kiwi.bootloader.config import BootLoaderConfig
@ -98,6 +99,7 @@ class DiskBuilder:
self.blocksize = xml_state.build_type.get_target_blocksize()
self.volume_manager_name = xml_state.get_volume_management()
self.volumes = xml_state.get_volumes()
self.custom_partitions = xml_state.get_partitions()
self.volume_group_name = xml_state.get_volume_group_name()
self.mdraid = xml_state.build_type.get_mdraid()
self.hybrid_mbr = xml_state.build_type.get_gpt_hybrid_mbr()
@ -208,6 +210,10 @@ class DiskBuilder:
# representing the spare_part_mountpoint area of the disk
system_spare: Optional[FileSystemBase] = None
# a list of instances with the sync_data capability
# representing the custom partitions area of the disk
system_custom_parts: List[FileSystemBase] = []
if self.install_media and self.build_type_name != 'oem':
raise KiwiInstallMediaError(
'Install media requires oem type setup, got {0}'.format(
@ -316,6 +322,10 @@ class DiskBuilder:
# create spare filesystem on spare partition if present
system_spare = self._build_spare_filesystem(device_map)
system_custom_parts = self._build_custom_parts_filesystem(
device_map, self.custom_partitions
)
# create filesystems on boot partition(s) if any
system_boot, system_efi = self._build_boot_filesystems(device_map)
@ -439,7 +449,8 @@ class DiskBuilder:
# syncing system data to disk image
self._sync_system_to_image(
device_map, system, system_boot, system_efi, system_spare
device_map, system, system_boot, system_efi, system_spare,
system_custom_parts
)
# run post sync script hook
@ -630,12 +641,40 @@ class DiskBuilder:
if 'efi' in device_map:
exclude_list.append('boot/efi/*')
exclude_list.append('boot/efi/.*')
if self.custom_partitions:
for map_name in sorted(self.custom_partitions.keys()):
if map_name in device_map:
mountpoint = os.path.normpath(
self.custom_partitions[map_name].mountpoint
).lstrip(os.sep)
exclude_list.append(f'{mountpoint}/*')
exclude_list.append(f'{mountpoint}/.*')
return exclude_list
@staticmethod
def _get_exclude_list_for_boot_data_sync() -> list:
return ['efi/*']
def _build_custom_parts_filesystem(
self, device_map: Dict,
custom_partitions: Dict['str', ptable_entry_type]
) -> List[FileSystemBase]:
filesystem_list = []
if custom_partitions:
for map_name in sorted(custom_partitions.keys()):
if map_name in device_map:
ptable_entry = custom_partitions[map_name]
filesystem = FileSystem.new(
ptable_entry.filesystem,
device_map[map_name],
f'{self.root_dir}{ptable_entry.mountpoint}/'
)
filesystem.create_on_device(
label=map_name.upper()
)
filesystem_list.append(filesystem)
return filesystem_list
def _build_spare_filesystem(self, device_map: Dict) -> Optional[FileSystemBase]:
if 'spare' in device_map and self.spare_part_fs:
spare_part_data_path = None
@ -697,7 +736,9 @@ class DiskBuilder:
system_boot = filesystem
return system_boot, system_efi
def _build_and_map_disk_partitions(self, disk: Disk, disksize_mbytes: float) -> Dict:
def _build_and_map_disk_partitions(
self, disk: Disk, disksize_mbytes: float
) -> Dict:
disk.wipe()
disksize_used_mbytes = 0
if self.firmware.legacy_bios_mode():
@ -740,6 +781,14 @@ class DiskBuilder:
)
disksize_used_mbytes += self.swap_mbytes
if self.custom_partitions:
log.info(
'--> creating custom partition(s): {0}'.format(
sorted(self.custom_partitions.keys())
)
)
disk.create_custom_partitions(self.custom_partitions)
if self.spare_part_mbsize and not self.spare_part_is_last:
log.info('--> creating spare partition')
disk.create_spare_partition(
@ -914,6 +963,13 @@ class DiskBuilder:
self._add_fstab_entry(
device_map['swap'].get_device(), 'swap'
)
if self.custom_partitions:
for map_name in sorted(self.custom_partitions.keys()):
if device_map.get(map_name):
self._add_fstab_entry(
device_map[map_name].get_device(),
self.custom_partitions[map_name].mountpoint
)
setup.create_fstab(
self.fstab
)
@ -1040,12 +1096,18 @@ class DiskBuilder:
self, device_map: Dict, system: Any,
system_boot: Optional[FileSystemBase],
system_efi: Optional[FileSystemBase],
system_spare: Optional[FileSystemBase]
system_spare: Optional[FileSystemBase],
system_custom_parts: List[FileSystemBase]
) -> None:
log.info('Syncing system to image')
if system_spare:
log.info('--> Syncing spare partition data')
system_spare.sync_data()
for system_custom_part in system_custom_parts:
log.info('--> Syncing custom partition(s) data')
system_custom_part.sync_data()
if system_efi:
log.info('--> Syncing EFI boot data to EFI partition')
system_efi.sync_data()

View File

@ -814,3 +814,10 @@ class KiwiUmountBusyError(KiwiError):
"""
Exception raised if the attempt to umount a resource has failed
"""
class KiwiCustomPartitionConflictError(KiwiError):
"""
Exception raised if the entry in a custom partition setup
conflicts with an existing partition table layout setting
"""

View File

@ -847,38 +847,6 @@ div {
}
}
#==========================================
# common element <partition>
#
div {
k.partition.type.attribute =
## Partition Type identifier, see parted for details
attribute type { text }
k.partition.number.attribute =
## Partition ID
attribute number { text }
k.partition.size.attribute = k.size.attribute
k.partition.mountpoint.attribute =
## Mount path for this partition
attribute mountpoint { text }
k.partition.target.attribute =
## Is a real target or not which means is part of
## the /etc/fstab file or not
attribute target { xsd:boolean }
k.partition.attlist =
k.partition.type.attribute &
k.partition.number.attribute &
k.partition.size.attribute? &
k.partition.mountpoint.attribute? &
k.partition.target.attribute?
k.partition =
## A Partition
element partition {
k.partition.attlist,
empty
}
}
#==========================================
# common element <profile>
#
@ -1914,6 +1882,7 @@ div {
k.oemconfig? &
k.size? &
k.systemdisk? &
k.partitions? &
k.vagrantconfig* &
k.installmedia?
}
@ -2087,6 +2056,69 @@ div {
}
}
#==========================================
# common element <partition>
#
div {
sch:pattern [
abstract = "true"
id = "partition_name_type"
sch:rule [
context = "partition[@name]"
sch:assert [
test = "not(contains('$reserved', @name))"
"partition(name) is reserved "
"Reserved names are '$reserved'"
]
]
]
k.partition.name.attribute =
## Partition map name. The name of the partition as handled
## by KIWI. Note, that there are the following reserved
## names which cannot be used because they are already
## represented by existing KIWI attributes: root, readonly,
## boot, prep, spare, swap, efi_csm and efi. The filesystem
## created on the partition will also use this name in
## uppercase as its label
attribute name { text }
>> sch:pattern [ id = "partition_name" is-a = "partition_name_type"
sch:param [ name = "reserved" value = "root readonly boot prep spare swap efi_csm efi" ]
]
k.partition.size.attribute =
## Absolute size of the partition.
## The value is used as MB by default but you can
## add "M" and/or "G" as postfix
attribute size { partition-size-type }
k.partition.partition_type.attribute =
## Partition type name in the context of kiwi
## Allowed values are: t.linux
attribute partition_type { "t.linux" | "t.raid" }
k.partition.partition_name.attribute =
## Partition name as it appears in the table
attribute partition_name { safe-posix-short-name }
k.partition.mountpoint.attribute =
## Mountpoint below which this partition should be mounted to
attribute mountpoint { text }
k.partition.filesystem.attribute =
## Filesystem which should be created on the partition
attribute filesystem {
"btrfs" | "ext2" | "ext3" | "ext4" | "squashfs" | "xfs"
}
k.partition.attlist =
k.partition.name.attribute &
k.partition.size.attribute &
k.partition.partition_name.attribute? &
k.partition.partition_type.attribute? &
k.partition.mountpoint.attribute &
k.partition.filesystem.attribute
k.partition =
## Specify custom partition in the partition table
element partition {
k.partition.attlist,
empty
}
}
#==========================================
# common element <volume>
#
@ -2535,6 +2567,20 @@ div {
}
}
#==========================================
# main block: <partitions>
#
div {
k.partitions.attlist = empty
k.partitions =
## Partition table entries within the custom area
## of the storage device
element partitions {
k.partitions.attlist &
k.partition+
}
}
#==========================================
# main block: <environment>
#

View File

@ -1300,60 +1300,6 @@ the device is looked up in /dev/disk/by-* and /dev/mapper/*</a:documentation>
</element>
</define>
</div>
<!--
==========================================
common element <partition>
-->
<div>
<define name="k.partition.type.attribute">
<attribute name="type">
<a:documentation>Partition Type identifier, see parted for details</a:documentation>
</attribute>
</define>
<define name="k.partition.number.attribute">
<attribute name="number">
<a:documentation>Partition ID</a:documentation>
</attribute>
</define>
<define name="k.partition.size.attribute">
<ref name="k.size.attribute"/>
</define>
<define name="k.partition.mountpoint.attribute">
<attribute name="mountpoint">
<a:documentation>Mount path for this partition</a:documentation>
</attribute>
</define>
<define name="k.partition.target.attribute">
<attribute name="target">
<a:documentation>Is a real target or not which means is part of
the /etc/fstab file or not</a:documentation>
<data type="boolean"/>
</attribute>
</define>
<define name="k.partition.attlist">
<interleave>
<ref name="k.partition.type.attribute"/>
<ref name="k.partition.number.attribute"/>
<optional>
<ref name="k.partition.size.attribute"/>
</optional>
<optional>
<ref name="k.partition.mountpoint.attribute"/>
</optional>
<optional>
<ref name="k.partition.target.attribute"/>
</optional>
</interleave>
</define>
<define name="k.partition">
<element name="partition">
<a:documentation>A Partition</a:documentation>
<ref name="k.partition.attlist"/>
<empty/>
</element>
</define>
</div>
<!--
==========================================
common element <profile>
@ -2887,6 +2833,9 @@ kiwi-ng result bundle ...</a:documentation>
<optional>
<ref name="k.systemdisk"/>
</optional>
<optional>
<ref name="k.partitions"/>
</optional>
<zeroOrMore>
<ref name="k.vagrantconfig"/>
</zeroOrMore>
@ -3186,6 +3135,95 @@ scsi CD or an ide CD drive</a:documentation>
</element>
</define>
</div>
<!--
==========================================
common element <partition>
-->
<div>
<sch:pattern abstract="true" id="partition_name_type">
<sch:rule context="partition[@name]">
<sch:assert test="not(contains('$reserved', @name))">partition(name) is reserved Reserved names are '$reserved'</sch:assert>
</sch:rule>
</sch:pattern>
<define name="k.partition.name.attribute">
<attribute name="name">
<a:documentation>Partition map name. The name of the partition as handled
by KIWI. Note, that there are the following reserved
names which cannot be used because they are already
represented by existing KIWI attributes: root, readonly,
boot, prep, spare, swap, efi_csm and efi. The filesystem
created on the partition will also use this name in
uppercase as its label</a:documentation>
</attribute>
<sch:pattern id="partition_name" is-a="partition_name_type">
<sch:param name="reserved" value="root readonly boot prep spare swap efi_csm efi"/>
</sch:pattern>
</define>
<define name="k.partition.size.attribute">
<attribute name="size">
<a:documentation>Absolute size of the partition.
The value is used as MB by default but you can
add "M" and/or "G" as postfix</a:documentation>
<ref name="partition-size-type"/>
</attribute>
</define>
<define name="k.partition.partition_type.attribute">
<attribute name="partition_type">
<a:documentation>Partition type name in the context of kiwi
Allowed values are: t.linux</a:documentation>
<choice>
<value>t.linux</value>
<value>t.raid</value>
</choice>
</attribute>
</define>
<define name="k.partition.partition_name.attribute">
<attribute name="partition_name">
<a:documentation>Partition name as it appears in the table</a:documentation>
<ref name="safe-posix-short-name"/>
</attribute>
</define>
<define name="k.partition.mountpoint.attribute">
<attribute name="mountpoint">
<a:documentation>Mountpoint below which this partition should be mounted to</a:documentation>
</attribute>
</define>
<define name="k.partition.filesystem.attribute">
<attribute name="filesystem">
<a:documentation>Filesystem which should be created on the partition</a:documentation>
<choice>
<value>btrfs</value>
<value>ext2</value>
<value>ext3</value>
<value>ext4</value>
<value>squashfs</value>
<value>xfs</value>
</choice>
</attribute>
</define>
<define name="k.partition.attlist">
<interleave>
<ref name="k.partition.name.attribute"/>
<ref name="k.partition.size.attribute"/>
<optional>
<ref name="k.partition.partition_name.attribute"/>
</optional>
<optional>
<ref name="k.partition.partition_type.attribute"/>
</optional>
<ref name="k.partition.mountpoint.attribute"/>
<ref name="k.partition.filesystem.attribute"/>
</interleave>
</define>
<define name="k.partition">
<element name="partition">
<a:documentation>Specify custom partition in the partition table</a:documentation>
<ref name="k.partition.attlist"/>
<empty/>
</element>
</define>
</div>
<!--
==========================================
common element <volume>
@ -3828,6 +3866,28 @@ At least one volume must be configured</a:documentation>
</element>
</define>
</div>
<!--
==========================================
main block: <partitions>
-->
<div>
<define name="k.partitions.attlist">
<empty/>
</define>
<define name="k.partitions">
<element name="partitions">
<a:documentation>Partition table entries within the custom area
of the storage device</a:documentation>
<interleave>
<ref name="k.partitions.attlist"/>
<oneOrMore>
<ref name="k.partition"/>
</oneOrMore>
</interleave>
</element>
</define>
</div>
<!--
==========================================
main block: <environment>

View File

@ -18,7 +18,9 @@
import os
import logging
from collections import OrderedDict
from typing import Dict
from typing import (
Dict, NamedTuple
)
# project
from kiwi.utils.temporary import Temporary
@ -26,6 +28,17 @@ from kiwi.command import Command
from kiwi.storage.device_provider import DeviceProvider
from kiwi.storage.mapped_device import MappedDevice
from kiwi.partitioner import Partitioner
from kiwi.exceptions import KiwiCustomPartitionConflictError
ptable_entry_type = NamedTuple(
'ptable_entry_type', [
('mbsize', int),
('partition_name', str),
('partition_type', str),
('mountpoint', str),
('filesystem', str)
]
)
log = logging.getLogger('kiwi')
@ -47,6 +60,21 @@ class Disk(DeviceProvider):
# the correct destructor order when the device should be released.
self.storage_provider = storage_provider
# list of protected map ids. If used in a custom partitions
# setup this will lead to a raise conditition in order to
# avoid conflicts with the existing partition layout and its
# customizaton capabilities
self.protected_map_ids = [
'root',
'readonly',
'boot',
'prep',
'spare',
'swap',
'efi_csm',
'efi'
]
self.partition_map: Dict[str, str] = {}
self.public_partition_id_map: Dict[str, str] = {}
self.partition_id_map: Dict[str, str] = {}
@ -88,6 +116,33 @@ class Disk(DeviceProvider):
"""
return self.storage_provider.is_loop()
def create_custom_partitions(
self, table_entries: Dict[str, ptable_entry_type]
) -> None:
"""
Create partitions from custom data set
.. code:: python
table_entries = {
map_name: ptable_entry_type
}
:param dict table: partition table spec
"""
for map_name in table_entries:
if map_name in self.protected_map_ids:
raise KiwiCustomPartitionConflictError(
f'Cannot use reserved table entry name: {map_name!r}'
)
id_name = f'kiwi_{map_name.title()}Part'
entry = table_entries[map_name]
self.partitioner.create(
entry.partition_name, entry.mbsize, entry.partition_type
)
self._add_to_map(map_name)
self._add_to_public_id_map(id_name)
def create_root_partition(self, mbsize: str):
"""
Create root partition

View File

@ -3,7 +3,7 @@
#
# Generated by generateDS.py version 2.29.24.
# Python 3.6.12 (default, Dec 02 2020, 09:44:23) [GCC]
# Python 3.6.13 (default, Mar 10 2021, 18:30:35) [GCC]
#
# Command line options:
# ('-f', '')
@ -1757,130 +1757,6 @@ class package(GeneratedsSuper):
# end class package
class partition(GeneratedsSuper):
"""A Partition"""
subclass = None
superclass = None
def __init__(self, type_=None, number=None, size=None, mountpoint=None, target=None):
self.original_tagname_ = None
self.type_ = _cast(None, type_)
self.number = _cast(None, number)
self.size = _cast(None, size)
self.mountpoint = _cast(None, mountpoint)
self.target = _cast(bool, target)
def factory(*args_, **kwargs_):
if CurrentSubclassModule_ is not None:
subclass = getSubclassFromModule_(
CurrentSubclassModule_, partition)
if subclass is not None:
return subclass(*args_, **kwargs_)
if partition.subclass:
return partition.subclass(*args_, **kwargs_)
else:
return partition(*args_, **kwargs_)
factory = staticmethod(factory)
def get_type(self): return self.type_
def set_type(self, type_): self.type_ = type_
def get_number(self): return self.number
def set_number(self, number): self.number = number
def get_size(self): return self.size
def set_size(self, size): self.size = size
def get_mountpoint(self): return self.mountpoint
def set_mountpoint(self, mountpoint): self.mountpoint = mountpoint
def get_target(self): return self.target
def set_target(self, target): self.target = target
def validate_size_type(self, value):
# Validate type size-type, a restriction on xs:token.
if value is not None and Validate_simpletypes_:
if not self.gds_validate_simple_patterns(
self.validate_size_type_patterns_, value):
warnings_.warn('Value "%s" does not match xsd pattern restrictions: %s' % (value.encode('utf-8'), self.validate_size_type_patterns_, ))
validate_size_type_patterns_ = [['^\\d*|image$']]
def hasContent_(self):
if (
):
return True
else:
return False
def export(self, outfile, level, namespaceprefix_='', name_='partition', namespacedef_='', pretty_print=True):
imported_ns_def_ = GenerateDSNamespaceDefs_.get('partition')
if imported_ns_def_ is not None:
namespacedef_ = imported_ns_def_
if pretty_print:
eol_ = '\n'
else:
eol_ = ''
if self.original_tagname_ is not None:
name_ = self.original_tagname_
showIndent(outfile, level, pretty_print)
outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', ))
already_processed = set()
self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='partition')
if self.hasContent_():
outfile.write('>%s' % (eol_, ))
self.exportChildren(outfile, level + 1, namespaceprefix_='', name_='partition', pretty_print=pretty_print)
outfile.write('</%s%s>%s' % (namespaceprefix_, name_, eol_))
else:
outfile.write('/>%s' % (eol_, ))
def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='partition'):
if self.type_ is not None and 'type_' not in already_processed:
already_processed.add('type_')
outfile.write(' type=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.type_), input_name='type')), ))
if self.number is not None and 'number' not in already_processed:
already_processed.add('number')
outfile.write(' number=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.number), input_name='number')), ))
if self.size is not None and 'size' not in already_processed:
already_processed.add('size')
outfile.write(' size=%s' % (quote_attrib(self.size), ))
if self.mountpoint is not None and 'mountpoint' not in already_processed:
already_processed.add('mountpoint')
outfile.write(' mountpoint=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.mountpoint), input_name='mountpoint')), ))
if self.target is not None and 'target' not in already_processed:
already_processed.add('target')
outfile.write(' target="%s"' % self.gds_format_boolean(self.target, input_name='target'))
def exportChildren(self, outfile, level, namespaceprefix_='', name_='partition', fromsubclass_=False, pretty_print=True):
pass
def build(self, node):
already_processed = set()
self.buildAttributes(node, node.attrib, already_processed)
for child in node:
nodeName_ = Tag_pattern_.match(child.tag).groups()[-1]
self.buildChildren(child, node, nodeName_)
return self
def buildAttributes(self, node, attrs, already_processed):
value = find_attr_value_('type', node)
if value is not None and 'type' not in already_processed:
already_processed.add('type')
self.type_ = value
value = find_attr_value_('number', node)
if value is not None and 'number' not in already_processed:
already_processed.add('number')
self.number = value
value = find_attr_value_('size', node)
if value is not None and 'size' not in already_processed:
already_processed.add('size')
self.size = value
self.size = ' '.join(self.size.split())
self.validate_size_type(self.size) # validate type size-type
value = find_attr_value_('mountpoint', node)
if value is not None and 'mountpoint' not in already_processed:
already_processed.add('mountpoint')
self.mountpoint = value
value = find_attr_value_('target', node)
if value is not None and 'target' not in already_processed:
already_processed.add('target')
if value in ('true', '1'):
self.target = True
elif value in ('false', '0'):
self.target = False
else:
raise_parse_error(node, 'Bad boolean attribute')
def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
pass
# end class partition
class profile(GeneratedsSuper):
"""Profiles creates a namespace on an image description and thus can be
used to have one description with different profiles for example
@ -2619,7 +2495,7 @@ class type_(GeneratedsSuper):
"""The Image Type of the Logical Extend"""
subclass = None
superclass = None
def __init__(self, boot=None, bootfilesystem=None, firmware=None, bootkernel=None, bootpartition=None, bootpartsize=None, efipartsize=None, efiparttable=None, bootprofile=None, btrfs_quota_groups=None, btrfs_root_is_snapshot=None, btrfs_root_is_readonly_snapshot=None, compressed=None, devicepersistency=None, editbootconfig=None, editbootinstall=None, filesystem=None, flags=None, format=None, formatoptions=None, fsmountoptions=None, fscreateoptions=None, squashfscompression=None, gcelicense=None, hybridpersistent=None, hybridpersistent_filesystem=None, gpt_hybrid_mbr=None, force_mbr=None, initrd_system=None, image=None, metadata_path=None, installboot=None, install_continue_on_timeout=None, installprovidefailsafe=None, installiso=None, installstick=None, installpxe=None, mediacheck=None, kernelcmdline=None, luks=None, luksOS=None, mdraid=None, overlayroot=None, primary=None, ramonly=None, rootfs_label=None, spare_part=None, spare_part_mountpoint=None, spare_part_fs=None, spare_part_fs_attributes=None, spare_part_is_last=None, target_blocksize=None, target_removable=None, vga=None, vhdfixedtag=None, volid=None, wwid_wait_timeout=None, derived_from=None, xen_server=None, publisher=None, disk_start_sector=None, bundle_format=None, bootloader=None, containerconfig=None, machine=None, oemconfig=None, size=None, systemdisk=None, vagrantconfig=None, installmedia=None):
def __init__(self, boot=None, bootfilesystem=None, firmware=None, bootkernel=None, bootpartition=None, bootpartsize=None, efipartsize=None, efiparttable=None, bootprofile=None, btrfs_quota_groups=None, btrfs_root_is_snapshot=None, btrfs_root_is_readonly_snapshot=None, compressed=None, devicepersistency=None, editbootconfig=None, editbootinstall=None, filesystem=None, flags=None, format=None, formatoptions=None, fsmountoptions=None, fscreateoptions=None, squashfscompression=None, gcelicense=None, hybridpersistent=None, hybridpersistent_filesystem=None, gpt_hybrid_mbr=None, force_mbr=None, initrd_system=None, image=None, metadata_path=None, installboot=None, install_continue_on_timeout=None, installprovidefailsafe=None, installiso=None, installstick=None, installpxe=None, mediacheck=None, kernelcmdline=None, luks=None, luksOS=None, mdraid=None, overlayroot=None, primary=None, ramonly=None, rootfs_label=None, spare_part=None, spare_part_mountpoint=None, spare_part_fs=None, spare_part_fs_attributes=None, spare_part_is_last=None, target_blocksize=None, target_removable=None, vga=None, vhdfixedtag=None, volid=None, wwid_wait_timeout=None, derived_from=None, xen_server=None, publisher=None, disk_start_sector=None, bundle_format=None, bootloader=None, containerconfig=None, machine=None, oemconfig=None, size=None, systemdisk=None, partitions=None, vagrantconfig=None, installmedia=None):
self.original_tagname_ = None
self.boot = _cast(None, boot)
self.bootfilesystem = _cast(None, bootfilesystem)
@ -2707,6 +2583,10 @@ class type_(GeneratedsSuper):
self.systemdisk = []
else:
self.systemdisk = systemdisk
if partitions is None:
self.partitions = []
else:
self.partitions = partitions
if vagrantconfig is None:
self.vagrantconfig = []
else:
@ -2756,6 +2636,11 @@ class type_(GeneratedsSuper):
def add_systemdisk(self, value): self.systemdisk.append(value)
def insert_systemdisk_at(self, index, value): self.systemdisk.insert(index, value)
def replace_systemdisk_at(self, index, value): self.systemdisk[index] = value
def get_partitions(self): return self.partitions
def set_partitions(self, partitions): self.partitions = partitions
def add_partitions(self, value): self.partitions.append(value)
def insert_partitions_at(self, index, value): self.partitions.insert(index, value)
def replace_partitions_at(self, index, value): self.partitions[index] = value
def get_vagrantconfig(self): return self.vagrantconfig
def set_vagrantconfig(self, vagrantconfig): self.vagrantconfig = vagrantconfig
def add_vagrantconfig(self, value): self.vagrantconfig.append(value)
@ -2926,6 +2811,7 @@ class type_(GeneratedsSuper):
self.oemconfig or
self.size or
self.systemdisk or
self.partitions or
self.vagrantconfig or
self.installmedia
):
@ -3157,6 +3043,8 @@ class type_(GeneratedsSuper):
size_.export(outfile, level, namespaceprefix_, name_='size', pretty_print=pretty_print)
for systemdisk_ in self.systemdisk:
systemdisk_.export(outfile, level, namespaceprefix_, name_='systemdisk', pretty_print=pretty_print)
for partitions_ in self.partitions:
partitions_.export(outfile, level, namespaceprefix_, name_='partitions', pretty_print=pretty_print)
for vagrantconfig_ in self.vagrantconfig:
vagrantconfig_.export(outfile, level, namespaceprefix_, name_='vagrantconfig', pretty_print=pretty_print)
for installmedia_ in self.installmedia:
@ -3594,6 +3482,11 @@ class type_(GeneratedsSuper):
obj_.build(child_)
self.systemdisk.append(obj_)
obj_.original_tagname_ = 'systemdisk'
elif nodeName_ == 'partitions':
obj_ = partitions.factory()
obj_.build(child_)
self.partitions.append(obj_)
obj_.original_tagname_ = 'partitions'
elif nodeName_ == 'vagrantconfig':
obj_ = vagrantconfig.factory()
obj_.build(child_)
@ -4075,6 +3968,146 @@ class vmnic(GeneratedsSuper):
# end class vmnic
class partition(GeneratedsSuper):
"""Specify custom partition in the partition table"""
subclass = None
superclass = None
def __init__(self, name=None, size=None, partition_name=None, partition_type=None, mountpoint=None, filesystem=None):
self.original_tagname_ = None
self.name = _cast(None, name)
self.size = _cast(None, size)
self.partition_name = _cast(None, partition_name)
self.partition_type = _cast(None, partition_type)
self.mountpoint = _cast(None, mountpoint)
self.filesystem = _cast(None, filesystem)
def factory(*args_, **kwargs_):
if CurrentSubclassModule_ is not None:
subclass = getSubclassFromModule_(
CurrentSubclassModule_, partition)
if subclass is not None:
return subclass(*args_, **kwargs_)
if partition.subclass:
return partition.subclass(*args_, **kwargs_)
else:
return partition(*args_, **kwargs_)
factory = staticmethod(factory)
def get_name(self): return self.name
def set_name(self, name): self.name = name
def get_size(self): return self.size
def set_size(self, size): self.size = size
def get_partition_name(self): return self.partition_name
def set_partition_name(self, partition_name): self.partition_name = partition_name
def get_partition_type(self): return self.partition_type
def set_partition_type(self, partition_type): self.partition_type = partition_type
def get_mountpoint(self): return self.mountpoint
def set_mountpoint(self, mountpoint): self.mountpoint = mountpoint
def get_filesystem(self): return self.filesystem
def set_filesystem(self, filesystem): self.filesystem = filesystem
def validate_partition_size_type(self, value):
# Validate type partition-size-type, a restriction on xs:token.
if value is not None and Validate_simpletypes_:
if not self.gds_validate_simple_patterns(
self.validate_partition_size_type_patterns_, value):
warnings_.warn('Value "%s" does not match xsd pattern restrictions: %s' % (value.encode('utf-8'), self.validate_partition_size_type_patterns_, ))
validate_partition_size_type_patterns_ = [['^(\\d+|\\d+M|\\d+G)$']]
def validate_safe_posix_short_name(self, value):
# Validate type safe-posix-short-name, a restriction on xs:token.
if value is not None and Validate_simpletypes_:
if not self.gds_validate_simple_patterns(
self.validate_safe_posix_short_name_patterns_, value):
warnings_.warn('Value "%s" does not match xsd pattern restrictions: %s' % (value.encode('utf-8'), self.validate_safe_posix_short_name_patterns_, ))
validate_safe_posix_short_name_patterns_ = [['^[a-zA-Z0-9_\\-\\.]{1,32}$']]
def hasContent_(self):
if (
):
return True
else:
return False
def export(self, outfile, level, namespaceprefix_='', name_='partition', namespacedef_='', pretty_print=True):
imported_ns_def_ = GenerateDSNamespaceDefs_.get('partition')
if imported_ns_def_ is not None:
namespacedef_ = imported_ns_def_
if pretty_print:
eol_ = '\n'
else:
eol_ = ''
if self.original_tagname_ is not None:
name_ = self.original_tagname_
showIndent(outfile, level, pretty_print)
outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', ))
already_processed = set()
self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='partition')
if self.hasContent_():
outfile.write('>%s' % (eol_, ))
self.exportChildren(outfile, level + 1, namespaceprefix_='', name_='partition', pretty_print=pretty_print)
outfile.write('</%s%s>%s' % (namespaceprefix_, name_, eol_))
else:
outfile.write('/>%s' % (eol_, ))
def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='partition'):
if self.name is not None and 'name' not in already_processed:
already_processed.add('name')
outfile.write(' name=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.name), input_name='name')), ))
if self.size is not None and 'size' not in already_processed:
already_processed.add('size')
outfile.write(' size=%s' % (quote_attrib(self.size), ))
if self.partition_name is not None and 'partition_name' not in already_processed:
already_processed.add('partition_name')
outfile.write(' partition_name=%s' % (quote_attrib(self.partition_name), ))
if self.partition_type is not None and 'partition_type' not in already_processed:
already_processed.add('partition_type')
outfile.write(' partition_type=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.partition_type), input_name='partition_type')), ))
if self.mountpoint is not None and 'mountpoint' not in already_processed:
already_processed.add('mountpoint')
outfile.write(' mountpoint=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.mountpoint), input_name='mountpoint')), ))
if self.filesystem is not None and 'filesystem' not in already_processed:
already_processed.add('filesystem')
outfile.write(' filesystem=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.filesystem), input_name='filesystem')), ))
def exportChildren(self, outfile, level, namespaceprefix_='', name_='partition', fromsubclass_=False, pretty_print=True):
pass
def build(self, node):
already_processed = set()
self.buildAttributes(node, node.attrib, already_processed)
for child in node:
nodeName_ = Tag_pattern_.match(child.tag).groups()[-1]
self.buildChildren(child, node, nodeName_)
return self
def buildAttributes(self, node, attrs, already_processed):
value = find_attr_value_('name', node)
if value is not None and 'name' not in already_processed:
already_processed.add('name')
self.name = value
value = find_attr_value_('size', node)
if value is not None and 'size' not in already_processed:
already_processed.add('size')
self.size = value
self.size = ' '.join(self.size.split())
self.validate_partition_size_type(self.size) # validate type partition-size-type
value = find_attr_value_('partition_name', node)
if value is not None and 'partition_name' not in already_processed:
already_processed.add('partition_name')
self.partition_name = value
self.partition_name = ' '.join(self.partition_name.split())
self.validate_safe_posix_short_name(self.partition_name) # validate type safe-posix-short-name
value = find_attr_value_('partition_type', node)
if value is not None and 'partition_type' not in already_processed:
already_processed.add('partition_type')
self.partition_type = value
self.partition_type = ' '.join(self.partition_type.split())
value = find_attr_value_('mountpoint', node)
if value is not None and 'mountpoint' not in already_processed:
already_processed.add('mountpoint')
self.mountpoint = value
value = find_attr_value_('filesystem', node)
if value is not None and 'filesystem' not in already_processed:
already_processed.add('filesystem')
self.filesystem = value
self.filesystem = ' '.join(self.filesystem.split())
def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
pass
# end class partition
class volume(GeneratedsSuper):
"""Specify which parts of the filesystem should be on an extra volume."""
subclass = None
@ -5536,6 +5569,87 @@ class volumes(GeneratedsSuper):
# end class volumes
class partitions(GeneratedsSuper):
"""Partition table entries within the custom area of the storage device"""
subclass = None
superclass = None
def __init__(self, partition=None):
self.original_tagname_ = None
if partition is None:
self.partition = []
else:
self.partition = partition
def factory(*args_, **kwargs_):
if CurrentSubclassModule_ is not None:
subclass = getSubclassFromModule_(
CurrentSubclassModule_, partitions)
if subclass is not None:
return subclass(*args_, **kwargs_)
if partitions.subclass:
return partitions.subclass(*args_, **kwargs_)
else:
return partitions(*args_, **kwargs_)
factory = staticmethod(factory)
def get_partition(self): return self.partition
def set_partition(self, partition): self.partition = partition
def add_partition(self, value): self.partition.append(value)
def insert_partition_at(self, index, value): self.partition.insert(index, value)
def replace_partition_at(self, index, value): self.partition[index] = value
def hasContent_(self):
if (
self.partition
):
return True
else:
return False
def export(self, outfile, level, namespaceprefix_='', name_='partitions', namespacedef_='', pretty_print=True):
imported_ns_def_ = GenerateDSNamespaceDefs_.get('partitions')
if imported_ns_def_ is not None:
namespacedef_ = imported_ns_def_
if pretty_print:
eol_ = '\n'
else:
eol_ = ''
if self.original_tagname_ is not None:
name_ = self.original_tagname_
showIndent(outfile, level, pretty_print)
outfile.write('<%s%s%s' % (namespaceprefix_, name_, namespacedef_ and ' ' + namespacedef_ or '', ))
already_processed = set()
self.exportAttributes(outfile, level, already_processed, namespaceprefix_, name_='partitions')
if self.hasContent_():
outfile.write('>%s' % (eol_, ))
self.exportChildren(outfile, level + 1, namespaceprefix_='', name_='partitions', pretty_print=pretty_print)
showIndent(outfile, level, pretty_print)
outfile.write('</%s%s>%s' % (namespaceprefix_, name_, eol_))
else:
outfile.write('/>%s' % (eol_, ))
def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='', name_='partitions'):
pass
def exportChildren(self, outfile, level, namespaceprefix_='', name_='partitions', fromsubclass_=False, pretty_print=True):
if pretty_print:
eol_ = '\n'
else:
eol_ = ''
for partition_ in self.partition:
partition_.export(outfile, level, namespaceprefix_, name_='partition', pretty_print=pretty_print)
def build(self, node):
already_processed = set()
self.buildAttributes(node, node.attrib, already_processed)
for child in node:
nodeName_ = Tag_pattern_.match(child.tag).groups()[-1]
self.buildChildren(child, node, nodeName_)
return self
def buildAttributes(self, node, attrs, already_processed):
pass
def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
if nodeName_ == 'partition':
obj_ = partition.factory()
obj_.build(child_)
self.partition.append(obj_)
obj_.original_tagname_ = 'partition'
# end class partitions
class environment(GeneratedsSuper):
"""Provides details about the container environment variables At least
one environment variable must be configured"""
@ -8144,6 +8258,7 @@ __all__ = [
"package",
"packages",
"partition",
"partitions",
"port",
"preferences",
"product",

View File

@ -27,6 +27,7 @@ from textwrap import dedent
import kiwi.defaults as defaults
from kiwi import xml_parse
from kiwi.storage.disk import ptable_entry_type
from kiwi.system.uri import Uri
from kiwi.defaults import Defaults
from kiwi.utils.size import StringToSize
@ -727,6 +728,19 @@ class XMLState:
return True
return False
def get_build_type_partitions_section(self) -> Any:
"""
First partitions section from the build type section
:return: <partitions> section reference
:rtype: xml_parse::partitions
"""
partitions_sections = self.build_type.get_partitions()
if partitions_sections:
return partitions_sections[0]
return None
def get_build_type_system_disk_section(self) -> Any:
"""
First system disk section from the build type section
@ -1332,6 +1346,47 @@ class XMLState:
container_config_section.set_labels(labels)
def get_partitions(self) -> Dict[str, ptable_entry_type]:
"""
Dictionary of configured partitions.
Each entry in the dict references a ptable_entry_type
Each key in the dict references the name of the
partition entry as handled by KIWI
:return:
Contains dict of ptable_entry_type tuples
.. code:: python
{
'NAME': ptable_entry_type(
mbsize=int,
partition_name=str,
partition_type=str,
mountpoint=str,
filesystem=str
)
}
:rtype: dict
"""
partitions: Dict[str, ptable_entry_type] = {}
partitions_section = self.get_build_type_partitions_section()
if not partitions_section:
return partitions
for partition in partitions_section.get_partition():
name = partition.get_name()
partition_name = partition.get_partition_name() or f'p.lx{name}'
partitions[name] = ptable_entry_type(
mbsize=self._to_mega_byte(partition.get_size()),
partition_name=partition_name,
partition_type=partition.get_partition_type() or 't.linux',
mountpoint=partition.get_mountpoint(),
filesystem=partition.get_filesystem()
)
return partitions
def get_volumes(self) -> List[volume_type]:
"""
List of configured systemdisk volumes.

View File

@ -17,7 +17,7 @@ exclude=xml_parse.py
# we ignore warnings about quoting of escape sequences (W605)
ignore = E501, W605
# we allow a custom complexity level
max-complexity = 18
max-complexity = 19
[doc8]
max-line-length = 90

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<image schemaversion="7.4" name="TestPartitions">
<description type="system">
<author>Marcus Schäfer</author>
<contact>ms@suse.com</contact>
<specification>
test partitions setup
</specification>
</description>
<preferences>
<version>1.1.1</version>
<packagemanager>zypper</packagemanager>
<type image="oem" filesystem="ext3" installiso="true" kernelcmdline="splash" firmware="efi">
<partitions>
<partition name="var" size="100" mountpoint="/var" filesystem="ext3"/>
</partitions>
</type>
</preferences>
<repository>
<source path="obs://13.2/repo/oss"/>
</repository>
<packages type="image">
<package name="patterns-openSUSE-base"/>
</packages>
<packages type="bootstrap">
<package name="udev"/>
<package name="filesystem"/>
<package name="glibc-locale"/>
</packages>
</image>

View File

@ -18,6 +18,7 @@ from kiwi.defaults import Defaults
from kiwi.xml_description import XMLDescription
from kiwi.xml_state import XMLState
from kiwi.builder.disk import DiskBuilder
from kiwi.storage.disk import ptable_entry_type
from kiwi.storage.mapped_device import MappedDevice
from kiwi.exceptions import (
@ -55,7 +56,8 @@ class TestDiskBuilder:
'boot': MappedDevice('/dev/boot-device', Mock()),
'prep': MappedDevice('/dev/prep-device', Mock()),
'efi': MappedDevice('/dev/efi-device', Mock()),
'spare': MappedDevice('/dev/spare-device', Mock())
'spare': MappedDevice('/dev/spare-device', Mock()),
'var': MappedDevice('/dev/spare-device', Mock())
}
self.id_map = {
'kiwi_RootPart': 1,
@ -739,6 +741,38 @@ class TestDiskBuilder:
config_file='root_dir/etc/dracut.conf.d/99-luks-boot.conf'
)
@patch('kiwi.builder.disk.FileSystem.new')
@patch('kiwi.builder.disk.Command.run')
@patch('kiwi.builder.disk.Defaults.get_grub_boot_directory_name')
@patch('os.path.exists')
def test_create_disk_uses_custom_partitions(
self, mock_exists, mock_grub_dir, mock_command, mock_fs
):
mock_exists.return_value = True
self.disk_builder.custom_partitions = {
'var': ptable_entry_type(
mbsize=100,
partition_name='p.lxvar',
partition_type='t.linux',
mountpoint='/var',
filesystem='ext3'
)
}
self.disk_builder.volume_manager_name = None
filesystem = Mock()
mock_fs.return_value = filesystem
with patch('builtins.open'):
self.disk_builder.create_disk()
self.disk.create_custom_partitions.assert_called_once_with(
self.disk_builder.custom_partitions
)
assert [
call('UUID=blkid_result /var blkid_result_fs defaults 0 0')
] in self.disk_builder.fstab.add_entry.call_args_list
@patch('kiwi.builder.disk.FileSystem.new')
@patch('kiwi.builder.disk.VolumeManager.new')
@patch('kiwi.builder.disk.Command.run')

View File

@ -2,11 +2,15 @@ import logging
from mock import (
patch, mock_open
)
from pytest import fixture
from pytest import (
fixture, raises
)
import mock
from kiwi.storage.disk import ptable_entry_type
from kiwi.storage.disk import Disk
from kiwi.exceptions import KiwiCustomPartitionConflictError
class TestDisk:
@ -132,6 +136,36 @@ class TestDisk:
)
assert self.disk.public_partition_id_map['kiwi_PrepPart'] == 1
@patch('kiwi.storage.disk.Command.run')
def test_create_custom_partitions(self, mock_command):
table_entries = {
'var': ptable_entry_type(
mbsize=100,
partition_name='p.lxvar',
partition_type='t.linux',
mountpoint='/var',
filesystem='ext3'
)
}
self.disk.create_custom_partitions(table_entries)
self.partitioner.create.assert_called_once_with(
'p.lxvar', 100, 't.linux'
)
assert self.disk.public_partition_id_map['kiwi_VarPart'] == 1
def test_create_custom_partitions_reserved_name(self):
table_entries = {
'root': ptable_entry_type(
mbsize=100,
partition_name='p.lxroot',
partition_type='t.linux',
mountpoint='/',
filesystem='ext3'
)
}
with raises(KiwiCustomPartitionConflictError):
self.disk.create_custom_partitions(table_entries)
@patch('kiwi.storage.disk.Command.run')
def test_device_map_efi_partition(self, mock_command):
self.disk.create_efi_partition(100)

View File

@ -9,6 +9,7 @@ from pytest import (
from kiwi.defaults import Defaults
from kiwi.xml_state import XMLState
from kiwi.storage.disk import ptable_entry_type
from kiwi.xml_description import XMLDescription
from kiwi.exceptions import (
@ -311,6 +312,22 @@ class TestXMLState:
'composedProfile', 'vmxSimpleFlavour', 'xenDomUFlavour'
]
def test_get_partitions(self):
description = XMLDescription(
'../data/example_partitions_config.xml'
)
xml_data = description.load()
state = XMLState(xml_data)
assert state.get_partitions() == {
'var': ptable_entry_type(
mbsize=100,
partition_name='p.lxvar',
partition_type='t.linux',
mountpoint='/var',
filesystem='ext3'
)
}
def test_get_volumes_custom_root_volume_name(self):
description = XMLDescription(
'../data/example_lvm_custom_rootvol_config.xml'