diff --git a/diskimage_builder/block_device/blockdevice.py b/diskimage_builder/block_device/blockdevice.py index 42d730d7..ad337c67 100644 --- a/diskimage_builder/block_device/blockdevice.py +++ b/diskimage_builder/block_device/blockdevice.py @@ -30,6 +30,47 @@ logger = logging.getLogger(__name__) 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: # one image, one partition, mounted under '/' diff --git a/diskimage_builder/block_device/level1/partitioning.py b/diskimage_builder/block_device/level1/partitioning.py index 36284af2..9fe9d82f 100644 --- a/diskimage_builder/block_device/level1/partitioning.py +++ b/diskimage_builder/block_device/level1/partitioning.py @@ -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.graph.digraph import Digraph import logging +import os +import subprocess logger = logging.getLogger(__name__) @@ -148,6 +150,51 @@ class Partitioning(object): for _, part in self.partitions.items(): 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): image_path = result[self.base]['image'] device_path = result[self.base]['device'] @@ -160,6 +207,7 @@ class Partitioning(object): assert self.label == 'mbr' + partition_devices = set() disk_size = self._size_of_block_dev(image_path) with MBR(image_path, disk_size, self.align) as part_impl: for part_name, part_cfg in self.partitions.items(): @@ -178,7 +226,10 @@ class Partitioning(object): part_size, part_type) logger.debug("Create partition [%s] [%d]" % (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._notify_os_of_partition_changes(device_path, partition_devices) return diff --git a/diskimage_builder/lib/disk-image-create b/diskimage_builder/lib/disk-image-create index 5f9fcb7a..de52b7f5 100644 --- a/diskimage_builder/lib/disk-image-create +++ b/diskimage_builder/lib/disk-image-create @@ -415,31 +415,6 @@ export EXTRA_DETACH="detach_loopback ${IMAGE_BLOCK_DEVICE_WITHOUT_PART}" export EXTRA_UNMOUNT="dib-block-device --phase=cleanup \ --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} # Tuning the rootfs uuid works only for ext filesystems. if echo "$FS_TYPE" | grep -q "^ext"; then