← Back to team overview

cloud-init-dev team mailing list archive

[Merge] ~smoser/cloud-init:bug/1692093-sometimes-need-settle into cloud-init:master


Scott Moser has proposed merging ~smoser/cloud-init:bug/1692093-sometimes-need-settle into cloud-init:master.

Commit message:
disk_setup: udev settle before attempting partitioning or fs creation.

This attempts to use udevadm settle to wait until devices have been
fully "realized".  If a device exists, there may still be events in
the udev queue that would create its partition table entries.
We need to wait until those have been processed also.

LP: #1692093

Requested reviews:
  Paul Meyer (paul-meyer)
  cloud-init commiters (cloud-init-dev)
Related bugs:
  Bug #1692093 in cloud-init: "Cloud init is re-executing fs and disk setup during reboot"

For more details, see:
Your team cloud-init commiters is requested to review the proposed merge of ~smoser/cloud-init:bug/1692093-sometimes-need-settle into cloud-init:master.
diff --git a/cloudinit/config/cc_disk_setup.py b/cloudinit/config/cc_disk_setup.py
index e1505b3..adde1df 100644
--- a/cloudinit/config/cc_disk_setup.py
+++ b/cloudinit/config/cc_disk_setup.py
@@ -680,14 +680,14 @@ def read_parttbl(device):
     reliable way to probe the partition table.
     blkdev_cmd = [BLKDEV_CMD, '--rereadpt', device]
-    udev_cmd = [UDEVADM_CMD, 'settle']
+    udevadm_settle()
-        util.subp(udev_cmd)
-        util.subp(udev_cmd)
     except Exception as e:
         util.logexc(LOG, "Failed reading the partition table %s" % e)
+    udevadm_settle()
 def exec_mkpart_mbr(device, layout):
@@ -737,6 +737,32 @@ def exec_mkpart(table_type, device, layout):
     return get_dyn_func("exec_mkpart_%s", table_type, device, layout)
+def udevadm_settle():
+    util.subp(['udevadm', 'settle'])
+def assert_and_settle_device(device):
+    """Assert that device exists and settle so it is fully recognized."""
+    if not os.path.exists(device):
+        udevadm_settle()
+        if not os.path.exists(device):
+            raise RuntimeError("Device %s did not exist and was not created "
+                               "with a udevamd settle." % device)
+    # The device existed, so lets make sure all its partitions
+    # are created and udev entries read by lsblk updated.
+    blkdev_cmd = [BLKDEV_CMD, '--rereadpt', device]
+    try:
+        util.subp(blkdev_cmd)
+    except util.ProcessExecutionError as e:
+        # this can fail legitimately if the device is busy.
+        # its possibly busy even as a result of udev events.
+        LOG.debug("Running blockdev --rereadpt failed (not necessarily "
+                  "significant): %s", e)
+    udevadm_settle()
 def mkpart(device, definition):
     Creates the partition table.
@@ -752,6 +778,7 @@ def mkpart(device, definition):
                 device: the device to work on.
     # ensure that we get a real device rather than a symbolic link
+    assert_and_settle_device(device)
     device = os.path.realpath(device)
     LOG.debug("Checking values for %s definition", device)
@@ -852,6 +879,7 @@ def mkfs(fs_cfg):
     overwrite = fs_cfg.get('overwrite', False)
     # ensure that we get a real device rather than a symbolic link
+    assert_and_settle_device(device)
     device = os.path.realpath(device)
     # This allows you to define the default ephemeral or swap