From c746e8b0c376006b80fa08aadeeaed71530f7157 Mon Sep 17 00:00:00 2001 From: "Brian C. Lane" Date: Fri, 8 Jun 2018 17:05:38 -0700 Subject: [PATCH] Retry losetup if loop_attach fails It appears that sometimes the loop device doesn't get setup properly, this may be a race with other users of loop devices on the system, or some other mechanism that isn't understood. To try and prevent total failure when this happens this patch retries the loop setup 3 times before giving up. Previously it would wait for the loop device to appear (checking 5 times), that operation is now executed 3 times with a new losetup attempt each time. Resolves: rhbz#1589084 --- src/pylorax/imgutils.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/pylorax/imgutils.py b/src/pylorax/imgutils.py index df530333..817a6586 100644 --- a/src/pylorax/imgutils.py +++ b/src/pylorax/imgutils.py @@ -150,13 +150,34 @@ def loop_waitfor(loop_dev, outfile): raise RuntimeError("Unable to setup %s on %s" % (loop_dev, outfile)) def loop_attach(outfile): - '''Attach a loop device to the given file. Return the loop device name. - Raises CalledProcessError if losetup fails.''' - dev = runcmd_output(["losetup", "--find", "--show", outfile]) + """Attach a loop device to the given file. Return the loop device name. - # Sometimes the loop device isn't ready yet, make extra sure before returning - loop_waitfor(dev.strip(), outfile) - return dev.strip() + On rare occasions it appears that the device never shows up, some experiments + seem to indicate that it may be a race with another process using /dev/loop* devices. + + So we now try 3 times before actually failing. + + Raises CalledProcessError if losetup fails. + """ + retries = 0 + while True: + try: + retries += 1 + dev = runcmd_output(["losetup", "--find", "--show", outfile]).strip() + + # Sometimes the loop device isn't ready yet, make extra sure before returning + loop_waitfor(dev, outfile) + except CalledProcessError: + # Problems running losetup are always errors, raise immediately + raise + except RuntimeError as e: + # Try to setup the loop device 3 times + if retries == 3: + logger.error("loop_attach failed, retries exhausted.") + raise + logger.debug("Try %d failed, %s did not appear.", retries, dev) + break + return dev def loop_detach(loopdev): '''Detach the given loop device. Return False on failure.'''