Refactor: block-device partitioning cleanup

Now that the main partitioning refactor patch is merged, there is
a small relict of handling partitions still in the disk-image-create
main.

This patch moves the functionality from disk-image-create to the
block-device/partitioning module: it is mostly a rewrite of the
original bash code in python.

Change-Id: Ia73baeca74180a7bc9ea487da03ff56d6a3070ce
Signed-off-by: Andreas Florath <andreas@florath.net>
This commit is contained in:
Andreas Florath 2017-01-26 18:33:02 +00:00 committed by Ian Wienand
parent f1776d72aa
commit 866a06f92d
3 changed files with 93 additions and 26 deletions

View File

@ -30,6 +30,47 @@ logger = logging.getLogger(__name__)
class BlockDevice(object): class BlockDevice(object):
"""Handles block devices.
This class handles the complete setup and deletion of all aspects
of the block device level.
A typical call sequence:
cmd_create: creates all the different aspects of the block
device. When this call is successful, the complete block level
device is set up, filesystems are created and are mounted at
the correct position.
After this call it is possible to copy / install all the needed
files into the appropriate directories.
cmd_umount: unmount and detaches all directories and used many
resources. After this call the used (e.g.) images are still
available for further handling, e.g. converting from raw in
some other format.
cmd_cleanup: removes everything that was created with the
'cmd_create' call, i.e. all images files themselves and
internal temporary configuration.
cmd_delete: unmounts and removes everything that was created
during the 'cmd_create' all. This call should be used in error
conditions when there is the need to remove all allocated
resources immediately and as good as possible.
From the functional point of view this is mostly the same as a
call to 'cmd_umount' and 'cmd_cleanup' - but is typically more
error tolerance.
In a script this should be called in the following way:
dib-block-device --phase=create ...
trap "dib-block-device --phase=delete ..." EXIT
# copy / install files
dib-block-device --phase=umount ...
# convert image(s)
dib-block-device --phase=cleanup ...
trap - EXIT
"""
# Default configuration: # Default configuration:
# one image, one partition, mounted under '/' # one image, one partition, mounted under '/'

View File

@ -19,6 +19,8 @@ from diskimage_builder.block_device.utils import parse_abs_size_spec
from diskimage_builder.block_device.utils import parse_rel_size_spec from diskimage_builder.block_device.utils import parse_rel_size_spec
from diskimage_builder.graph.digraph import Digraph from diskimage_builder.graph.digraph import Digraph
import logging import logging
import os
import subprocess
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -148,6 +150,51 @@ class Partitioning(object):
for _, part in self.partitions.items(): for _, part in self.partitions.items():
dg.add_node(part) dg.add_node(part)
def _exec_sudo(self, cmd):
sudo_cmd = ["sudo"]
sudo_cmd.extend(cmd)
logger.info("Calling [%s]" % " ".join(sudo_cmd))
subp = subprocess.Popen(sudo_cmd)
rval = subp.wait()
if rval != 0:
logger.error("Calling [%s] failed with [%s]" %
(" ".join(sudo_cmd), rval))
logger.error("Trying to continue")
def _all_part_devices_exist(self, expected_part_devices):
for part_device in expected_part_devices:
logger.debug("Checking if partition device [%s] exists" %
part_device)
if not os.path.exists(part_device):
logger.info("Partition device [%s] does not exists"
% part_device)
return False
logger.debug("Partition already exists [%s]" % part_device)
return True
def _notify_os_of_partition_changes(self, device_path, partition_devices):
"""Notify of of partition table changes
There is the need to call some programs to inform the operating
system of partition tables changes.
These calls are highly distribution and version specific. Here
a couple of different methods are used to get the best result.
"""
self._exec_sudo(["partprobe", device_path])
self._exec_sudo(["udevadm", "settle"])
if self._all_part_devices_exist(partition_devices):
return
# If running inside Docker, make our nodes manually, because udev
# will not be working.
if os.path.exists("/.dockerenv"):
# kpartx cannot run in sync mode in docker.
self._exec_sudo(["kpartx", "-av", device_path])
self._exec_sudo(["dmsetup", "--noudevsync", "mknodes"])
return
self._exec_sudo(["kpartx", "-avs", device_path])
def create(self, result, rollback): def create(self, result, rollback):
image_path = result[self.base]['image'] image_path = result[self.base]['image']
device_path = result[self.base]['device'] device_path = result[self.base]['device']
@ -160,6 +207,7 @@ class Partitioning(object):
assert self.label == 'mbr' assert self.label == 'mbr'
partition_devices = set()
disk_size = self._size_of_block_dev(image_path) disk_size = self._size_of_block_dev(image_path)
with MBR(image_path, disk_size, self.align) as part_impl: with MBR(image_path, disk_size, self.align) as part_impl:
for part_name, part_cfg in self.partitions.items(): for part_name, part_cfg in self.partitions.items():
@ -178,7 +226,10 @@ class Partitioning(object):
part_size, part_type) part_size, part_type)
logger.debug("Create partition [%s] [%d]" % logger.debug("Create partition [%s] [%d]" %
(part_name, part_no)) (part_name, part_no))
result[part_name] = {'device': device_path + "p%d" % part_no} partition_device_name = device_path + "p%d" % part_no
result[part_name] = {'device': partition_device_name}
partition_devices.add(partition_device_name)
self.already_created = True self.already_created = True
self._notify_os_of_partition_changes(device_path, partition_devices)
return return

View File

@ -415,31 +415,6 @@ export EXTRA_DETACH="detach_loopback ${IMAGE_BLOCK_DEVICE_WITHOUT_PART}"
export EXTRA_UNMOUNT="dib-block-device --phase=cleanup \ export EXTRA_UNMOUNT="dib-block-device --phase=cleanup \
--build-dir=\"${TMP_BUILD_DIR}\"" --build-dir=\"${TMP_BUILD_DIR}\""
# Create the partitions and make them visible to the system
sudo partprobe $IMAGE_BLOCK_DEVICE_WITHOUT_PART
# To ensure no race conditions exist from calling partprobe
sudo udevadm settle
# If the partition isn't under /dev/loop*p1, create it with kpartx
DM=
if [ ! -e "${IMAGE_BLOCK_DEVICE}" ]; then
DM=${IMAGE_BLOCK_DEVICE/#\/dev/\/dev\/mapper}
# If running inside Docker, make our nodes manually, because udev will not be working.
if [ -f /.dockerenv ]; then
# kpartx cannot run in sync mode in docker.
sudo kpartx -av ${IMAGE_BLOCK_DEVICE_WITHOUT_PART}
sudo dmsetup --noudevsync mknodes
else
sudo kpartx -asv ${IMAGE_BLOCK_DEVICE_WITHOUT_PART}
fi
elif [[ "$ARCH" =~ "ppc" ]]; then
sudo kpartx -asv ${IMAGE_BLOCK_DEVICE_WITHOUT_PART}
fi
# End: Creation of the partitions
sudo mkfs -t $FS_TYPE $MKFS_OPTS -L ${DIB_ROOT_LABEL} ${IMAGE_BLOCK_DEVICE} sudo mkfs -t $FS_TYPE $MKFS_OPTS -L ${DIB_ROOT_LABEL} ${IMAGE_BLOCK_DEVICE}
# Tuning the rootfs uuid works only for ext filesystems. # Tuning the rootfs uuid works only for ext filesystems.
if echo "$FS_TYPE" | grep -q "^ext"; then if echo "$FS_TYPE" | grep -q "^ext"; then