[master] Add support for tarfiles to liveimg kickstart command

Brian C. Lane bcl at redhat.com
Mon Mar 24 16:06:48 UTC 2014


With these changes you can now pass the url of a tarfile to liveimg and
it will install it to the target system. The same rules apply, the
correct utilities need to be included in the tar for the configuration
to work correctly.

The following extensions are recognized as tarfiles:

.tar .tbz .tgz .txz .tar.bz2 .tar.gz .tar.xz
---
 pyanaconda/constants.py             |  3 ++
 pyanaconda/packaging/livepayload.py | 99 ++++++++++++++++++++++++++++---------
 2 files changed, 79 insertions(+), 23 deletions(-)

diff --git a/pyanaconda/constants.py b/pyanaconda/constants.py
index 8de29a9..8f001f7 100644
--- a/pyanaconda/constants.py
+++ b/pyanaconda/constants.py
@@ -151,3 +151,6 @@ FIRSTBOOT_ENVIRON = "firstboot"
 
 # Tainted hardware
 UNSUPPORTED_HW = 1 << 28
+
+# Recognizing a tarfile
+TAR_SUFFIX = (".tar", ".tbz", ".tgz", ".txz", ".tar.bz2", "tar.gz", "tar.xz")
diff --git a/pyanaconda/packaging/livepayload.py b/pyanaconda/packaging/livepayload.py
index 48c154f..795a4ef 100644
--- a/pyanaconda/packaging/livepayload.py
+++ b/pyanaconda/packaging/livepayload.py
@@ -43,7 +43,7 @@ import glob
 from . import ImagePayload, PayloadSetupError, PayloadInstallError
 
 from pyanaconda.constants import INSTALL_TREE, ROOT_PATH, THREAD_LIVE_PROGRESS
-from pyanaconda.constants import IMAGE_DIR
+from pyanaconda.constants import IMAGE_DIR, TAR_SUFFIX
 
 from pyanaconda import iutil
 
@@ -75,6 +75,9 @@ class LiveImagePayload(ImagePayload):
                 raise exn
         blivet.util.mount(osimg.path, INSTALL_TREE, fstype="auto", options="ro")
 
+        source = os.statvfs(INSTALL_TREE)
+        self.source_size = source.f_frsize * (source.f_blocks - source.f_bfree)
+
     def preInstall(self, packages=None, groups=None):
         """ Perform pre-installation tasks. """
         super(LiveImagePayload, self).preInstall(packages=packages, groups=groups)
@@ -84,8 +87,6 @@ class LiveImagePayload(ImagePayload):
         """Monitor the amount of disk space used on the target and source and
            update the hub's progress bar.
         """
-        source = os.statvfs(INSTALL_TREE)
-        source_size = source.f_frsize * (source.f_blocks - source.f_bfree)
         mountpoints = self.storage.mountpoints.copy()
         last_pct = -1
         while self.pct < 100:
@@ -96,7 +97,7 @@ class LiveImagePayload(ImagePayload):
             if dest_size >= self._adj_size:
                 dest_size -= self._adj_size
 
-            pct = int(100 * dest_size / source_size)
+            pct = int(100 * dest_size / self.source_size)
             if pct != last_pct:
                 with self.pct_lock:
                     self.pct = pct
@@ -312,25 +313,77 @@ class LiveImageKSPayload(LiveImagePayload):
                 if errorHandler.cb(exn) == ERROR_RAISE:
                     raise exn
 
-        # Mount the image and check to see if it is a LiveOS/*.img
-        # style squashfs image. If so, move it to IMAGE_DIR and mount the real
-        # root image on INSTALL_TREE
-        blivet.util.mount(self.image_path, INSTALL_TREE, fstype="auto", options="ro")
-        if os.path.exists(INSTALL_TREE+"/LiveOS"):
-            # Find the first .img in the directory and mount that on INSTALL_TREE
-            img_files = glob.glob(INSTALL_TREE+"/LiveOS/*.img")
-            if img_files:
-                img_file = os.path.basename(sorted(img_files)[0])
-
-                # move the mount to IMAGE_DIR
-                os.makedirs(IMAGE_DIR, 0755)
-                # work around inability to move shared filesystems
-                iutil.execWithRedirect("mount",
-                                       ["--make-rprivate", "/"])
-                iutil.execWithRedirect("mount",
-                                       ["--move", INSTALL_TREE, IMAGE_DIR])
-                blivet.util.mount(IMAGE_DIR+"/LiveOS/"+img_file, INSTALL_TREE,
-                                  fstype="auto", options="ro")
+        # If this looks like a tarfile, skip trying to mount it
+        if not any(self.data.method.url.endswith(suffix) for suffix in TAR_SUFFIX):
+            # Mount the image and check to see if it is a LiveOS/*.img
+            # style squashfs image. If so, move it to IMAGE_DIR and mount the real
+            # root image on INSTALL_TREE
+            blivet.util.mount(self.image_path, INSTALL_TREE, fstype="auto", options="ro")
+            if os.path.exists(INSTALL_TREE+"/LiveOS"):
+                # Find the first .img in the directory and mount that on INSTALL_TREE
+                img_files = glob.glob(INSTALL_TREE+"/LiveOS/*.img")
+                if img_files:
+                    img_file = os.path.basename(sorted(img_files)[0])
+
+                    # move the mount to IMAGE_DIR
+                    os.makedirs(IMAGE_DIR, 0755)
+                    # work around inability to move shared filesystems
+                    iutil.execWithRedirect("mount",
+                                           ["--make-rprivate", "/"])
+                    iutil.execWithRedirect("mount",
+                                           ["--move", INSTALL_TREE, IMAGE_DIR])
+                    blivet.util.mount(IMAGE_DIR+"/LiveOS/"+img_file, INSTALL_TREE,
+                                      fstype="auto", options="ro")
+
+                    source = os.statvfs(INSTALL_TREE)
+                    self.source_size = source.f_frsize * (source.f_blocks - source.f_bfree)
+
+    def install(self):
+        """ Install the payload if it is a tar.
+            Otherwise fall back to rsync of INSTALL_TREE
+        """
+        # If it doesn't look like a tarfile use the super's install()
+        if not any(self.data.method.url.endswith(suffix) for suffix in TAR_SUFFIX):
+            super(LiveImageKSPayload, self).install()
+            return
+
+        # Use the archive's size for want of something better
+        self.source_size = os.stat(self.image_path)[stat.ST_SIZE] * 2
+
+        self.pct_lock = Lock()
+        self.pct = 0
+        threadMgr.add(AnacondaThread(name=THREAD_LIVE_PROGRESS,
+                                     target=self.progress))
+
+        cmd = "tar"
+        # preserve: permissions, owners, groups, ACL's, xattrs, times,
+        #           symlinks, hardlinks
+        # go recursively, include devices and special files, don't cross
+        # file system boundaries
+        args = ["--selinux", "--acls", "--xattrs",
+                "--exclude", "/dev/", "--exclude", "/proc/",
+                "--exclude", "/sys/", "--exclude", "/run/", "--exclude", "/boot/*rescue*",
+                "--exclude", "/etc/machine-id", "-xaf", self.image_path, "-C", ROOT_PATH]
+        try:
+            rc = iutil.execWithRedirect(cmd, args)
+        except (OSError, RuntimeError) as e:
+            msg = None
+            err = str(e)
+            log.error(err)
+        else:
+            err = None
+            msg = "%s exited with code %d" % (cmd, rc)
+            log.info(msg)
+
+        if err:
+            exn = PayloadInstallError(err or msg)
+            if errorHandler.cb(exn) == ERROR_RAISE:
+                raise exn
+
+        # Wait for progress thread to finish
+        with self.pct_lock:
+            self.pct = 100
+        threadMgr.wait(THREAD_LIVE_PROGRESS)
 
     def postInstall(self):
         """ Unmount image, remove image file from target
-- 
1.8.5.3



More information about the anaconda-patches mailing list