def setup(self): super().setup() # Mount the live device and copy from it instead of the overlay at / osimg = payload_utils.resolve_device(self.data.method.partition) if not osimg: raise PayloadInstallError("Unable to find osimg for %s" % self.data.method.partition) osimg_path = payload_utils.get_device_path(osimg) if not stat.S_ISBLK(os.stat(osimg_path)[stat.ST_MODE]): exn = PayloadSetupError("%s is not a valid block device" % (self.data.method.partition, )) if errorHandler.cb(exn) == ERROR_RAISE: raise exn rc = payload_utils.mount(osimg_path, INSTALL_TREE, fstype="auto", options="ro") if rc != 0: raise PayloadInstallError("Failed to mount the install tree") # Grab the kernel version list now so it's available after umount self._update_kernel_version_list() source = os.statvfs(INSTALL_TREE) self.source_size = source.f_frsize * (source.f_blocks - source.f_bfree)
def _flatpak_install(self): # Install flatpak from the local source on SilverBlue progressQ.send_message(_("Starting Flatpak installation")) # Cleanup temporal repo created in the __init__ self._flatpak_payload.cleanup() # Initialize new repo on the installed system self._flatpak_payload.initialize_with_system_path() try: self._flatpak_payload.install_all() except FlatpakInstallError as e: exn = PayloadInstallError("Failed to install flatpaks: %s" % e) log.error(str(exn)) if errors.errorHandler.cb(exn) == errors.ERROR_RAISE: progressQ.send_quit(1) util.ipmi_abort(scripts=self.data.scripts) sys.exit(1) progressQ.send_message(_("Post-installation flatpak tasks")) self._flatpak_payload.add_remote("fedora", "oci+https://registry.fedoraproject.org") self._flatpak_payload.replace_installed_refs_remote("fedora") self._flatpak_payload.remove_remote(FlatpakPayload.LOCAL_REMOTE_NAME) progressQ.send_message(_("Flatpak installation has finished"))
def _safe_exec_with_redirect(self, cmd, argv, **kwargs): """Like util.execWithRedirect, but treat errors as fatal""" rc = util.execWithRedirect(cmd, argv, **kwargs) if rc != 0: exn = PayloadInstallError("%s %s exited with code %d" % (cmd, argv, rc)) if errors.errorHandler.cb(exn) == errors.ERROR_RAISE: raise exn
def run(self): """Pull a remote and delete it. All pulls in our code follow the pattern pull + delete. :raise: PayloadInstallError if the pull fails """ # pull requires this for some reason mainctx = create_new_context() mainctx.push_thread_default() cancellable = None # Variable substitute the ref: https://pagure.io/atomic-wg/issue/299 ref = RpmOstree.varsubst_basearch(self._data.ref) self.report_progress( _("Starting pull of {branch_name} from {source}").format( branch_name=ref, source=self._data.remote)) progress = OSTree.AsyncProgress.new() progress.connect('changed', self._pull_progress_cb) pull_opts = {'refs': Variant('as', [ref])} # If we're doing a kickstart, we can at least use the content as a reference: # See <https://github.com/rhinstaller/anaconda/issues/1117> # The first path here is used by <https://pagure.io/fedora-lorax-templates> # and the second by <https://github.com/projectatomic/rpm-ostree-toolbox/> # FIXME extend tests to cover this part of code if OSTree.check_version(2017, 8): for path in ['/ostree/repo', '/install/ostree/repo']: if os.path.isdir(path + '/objects'): pull_opts['localcache-repos'] = Variant('as', [path]) break sysroot_file = Gio.File.new_for_path(conf.target.physical_root) sysroot = OSTree.Sysroot.new(sysroot_file) sysroot.load(cancellable) repo = sysroot.get_repo(None)[1] # We don't support resuming from interrupted installs repo.set_disable_fsync(True) try: repo.pull_with_options(self._data.remote, Variant('a{sv}', pull_opts), progress, cancellable) except GError as e: raise PayloadInstallError("Failed to pull from repository: %s" % e) from e log.info("ostree pull: %s", progress.get_status() or "") self.report_progress(_("Preparing deployment of {}").format(ref)) # Now that we have the data pulled, delete the remote for now. This will allow a remote # configuration defined in the tree (if any) to override what's in the kickstart. # Otherwise, we'll re-add it in post. Ideally, ostree would support a pull without adding # a remote, but that would get quite complex. repo.remote_delete(self._data.remote, None) mainctx.pop_thread_default()
def install(self): """ Install the payload. """ if self.source_size <= 0: raise PayloadInstallError("Nothing to install") self.pct_lock = Lock() self.pct = 0 threadMgr.add( AnacondaThread(name=THREAD_LIVE_PROGRESS, target=self.progress)) cmd = "rsync" # preserve: permissions, owners, groups, ACL's, xattrs, times, # symlinks, hardlinks # go recursively, include devices and special files, don't cross # file system boundaries args = [ "-pogAXtlHrDx", "--exclude", "/dev/", "--exclude", "/proc/", "--exclude", "/tmp/*", "--exclude", "/sys/", "--exclude", "/run/", "--exclude", "/boot/*rescue*", "--exclude", "/boot/loader/", "--exclude", "/boot/efi/loader/", "--exclude", "/etc/machine-id", INSTALL_TREE + "/", util.getSysroot() ] try: rc = util.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 or rc == 11: 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) # Live needs to create the rescue image before bootloader is written self._create_rescue_image()
def safe_exec_with_redirect(cmd, argv, **kwargs): """Like util.execWithRedirect, but treat errors as fatal. :raise: PayloadInstallError if the call fails for any reason """ rc = execWithRedirect(cmd, argv, **kwargs) if rc != 0: raise PayloadInstallError("{} {} exited with code {}".format( cmd, argv, rc))
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 self.is_tarfile: super().install() return # Use 2x the archive's size to estimate the size of the install # This is used to drive the progress display 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: ACL's, xattrs, and SELinux context args = [ "--selinux", "--acls", "--xattrs", "--xattrs-include", "*", "--exclude", "dev/*", "--exclude", "proc/*", "--exclude", "tmp/*", "--exclude", "sys/*", "--exclude", "run/*", "--exclude", "boot/*rescue*", "--exclude", "boot/loader", "--exclude", "boot/efi/loader", "--exclude", "etc/machine-id", "-xaf", self.image_path, "-C", util.getSysroot() ] try: rc = util.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) # Live needs to create the rescue image before bootloader is written self._create_rescue_image()
def kernel_version_list(self): # If it doesn't look like a tarfile use the super's kernel_version_list if not self.is_tarfile: return super().kernel_version_list if self._kernel_version_list: return self._kernel_version_list # Cache a list of the kernels (the tar payload may be cleaned up on subsequent calls) if not os.path.exists(self.image_path): raise PayloadInstallError("kernel_version_list: missing tar payload") self._kernel_version_list = get_kernel_version_list_from_tar(self.image_path) return self._kernel_version_list
def setup(self): """ Check the availability and size of the image. """ super().setup() if self.data.liveimg.url.startswith("file://"): error = self._setup_file_image() else: error = self._setup_url_image() if error: exn = PayloadInstallError(str(error)) if errorHandler.cb(exn) == ERROR_RAISE: raise exn log.debug("liveimg size is %s", self._min_size)
def setup(self, storage): """ Check the availability and size of the image. """ # This is on purpose, we don't want to call LiveImagePayload's setup method. # FIXME: this should be solved on a inheritance level not like this Payload.setup(self, storage) if self.data.method.url.startswith("file://"): error = self._setup_file_image() else: error = self._setup_url_image() if error: exn = PayloadInstallError(str(error)) if errorHandler.cb(exn) == ERROR_RAISE: raise exn log.debug("liveimg size is %s", self._min_size)
def _install(self, data): log.info("executing ostreesetup=%r", data) from pyanaconda.modules.payloads.payload.rpm_ostree.installation import \ InitOSTreeFsAndRepoTask task = InitOSTreeFsAndRepoTask(conf.target.physical_root) task.run() # Here, we use the physical root as sysroot, because we haven't # yet made a deployment. from pyanaconda.modules.payloads.payload.rpm_ostree.installation import \ ChangeOSTreeRemoteTask task = ChangeOSTreeRemoteTask(data, use_root=False, root=conf.target.physical_root) task.run() from pyanaconda.modules.payloads.payload.rpm_ostree.installation import \ PullRemoteAndDeleteTask task = PullRemoteAndDeleteTask(data) task.progress_changed_signal.connect(self._progress_cb) task.run() from pyanaconda.modules.payloads.payload.rpm_ostree.installation import DeployOSTreeTask task = DeployOSTreeTask(data, conf.target.physical_root) task.progress_changed_signal.connect(self._progress_cb) task.run() # Reload now that we've deployed, find the path to the new deployment from pyanaconda.modules.payloads.payload.rpm_ostree.installation import SetSystemRootTask task = SetSystemRootTask(conf.target.physical_root) task.run() try: from pyanaconda.modules.payloads.payload.rpm_ostree.installation import \ CopyBootloaderDataTask task = CopyBootloaderDataTask(sysroot=conf.target.system_root, physroot=conf.target.physical_root) task.run() except (OSError, RuntimeError) as e: raise PayloadInstallError("Failed to copy bootloader data: %s" % e) from e
def kernel_version_list(self): # If it doesn't look like a tarfile use the super's kernel_version_list if not self.is_tarfile: return super().kernel_version_list if self._kernel_version_list: return self._kernel_version_list # Cache a list of the kernels (the tar payload may be cleaned up on subsequent calls) if not os.path.exists(self.image_path): raise PayloadInstallError("kernel_version_list: missing tar payload") import tarfile with tarfile.open(self.image_path) as archive: names = archive.getnames() # Strip out vmlinuz- from the names self._kernel_version_list = sorted((n.split("/")[-1][8:] for n in names if "boot/vmlinuz-" in n), key=functools.cmp_to_key(payload_utils.version_cmp)) return self._kernel_version_list
def install(self): """ Install the payload. """ if self.source_size <= 0: raise PayloadInstallError("Nothing to install") self.pct_lock = Lock() self.pct = 0 threadMgr.add(AnacondaThread(name=THREAD_LIVE_PROGRESS, target=self.progress)) # Run the installation task. task = InstallFromImageTask( sysroot=conf.target.system_root, mount_point=INSTALL_TREE + "/" ) task.run() # Wait for progress thread to finish with self.pct_lock: self.pct = 100 threadMgr.wait(THREAD_LIVE_PROGRESS)
def run(self): self.report_progress(_("Starting Flatpak installation")) flatpak_manager = FlatpakManager(self._sysroot) # Initialize new repo on the installed system flatpak_manager.initialize_with_system_path() try: flatpak_manager.install_all() except FlatpakInstallError as e: raise PayloadInstallError( "Failed to install flatpaks: {}".format(e)) from e self.report_progress(_("Post-installation flatpak tasks")) flatpak_manager.add_remote("fedora", "oci+https://registry.fedoraproject.org") flatpak_manager.replace_installed_refs_remote("fedora") flatpak_manager.remove_remote(FlatpakManager.LOCAL_REMOTE_NAME) self.report_progress(_("Flatpak installation has finished"))
def _flatpak_install(self): # Install flatpak from the local source on SilverBlue progressQ.send_message(_("Starting Flatpak installation")) # Cleanup temporal repo created in the __init__ self._flatpak_payload.cleanup() # Initialize new repo on the installed system self._flatpak_payload.initialize_with_system_path() try: self._flatpak_payload.install_all() except FlatpakInstallError as e: raise PayloadInstallError("Failed to install flatpaks: %s" % e) from e progressQ.send_message(_("Post-installation flatpak tasks")) self._flatpak_payload.add_remote( "fedora", "oci+https://registry.fedoraproject.org") self._flatpak_payload.replace_installed_refs_remote("fedora") self._flatpak_payload.remove_remote(FlatpakPayload.LOCAL_REMOTE_NAME) progressQ.send_message(_("Flatpak installation has finished"))
def install(self): mainctx = create_new_context() mainctx.push_thread_default() cancellable = None gi.require_version("OSTree", "1.0") gi.require_version("RpmOstree", "1.0") from gi.repository import OSTree, RpmOstree ostreesetup = self.data.ostreesetup log.info("executing ostreesetup=%r", ostreesetup) # Initialize the filesystem - this will create the repo as well self._safe_exec_with_redirect("ostree", [ "admin", "--sysroot=" + conf.target.physical_root, "init-fs", conf.target.physical_root ]) # Here, we use the physical root as sysroot, because we haven't # yet made a deployment. sysroot_file = Gio.File.new_for_path(conf.target.physical_root) sysroot = OSTree.Sysroot.new(sysroot_file) sysroot.load(cancellable) repo = sysroot.get_repo(None)[1] # We don't support resuming from interrupted installs repo.set_disable_fsync(True) self._remoteOptions = {} if hasattr(ostreesetup, 'nogpg') and ostreesetup.nogpg: self._remoteOptions['gpg-verify'] = Variant('b', False) if not conf.payload.verify_ssl: self._remoteOptions['tls-permissive'] = Variant('b', True) repo.remote_change(None, OSTree.RepoRemoteChange.ADD_IF_NOT_EXISTS, ostreesetup.remote, ostreesetup.url, Variant('a{sv}', self._remoteOptions), cancellable) # Variable substitute the ref: https://pagure.io/atomic-wg/issue/299 ref = RpmOstree.varsubst_basearch(ostreesetup.ref) progressQ.send_message( _("Starting pull of %(branchName)s from %(source)s") % { "branchName": ref, "source": ostreesetup.remote }) progress = OSTree.AsyncProgress.new() progress.connect('changed', self._pull_progress_cb) pull_opts = {'refs': Variant('as', [ref])} # If we're doing a kickstart, we can at least use the content as a reference: # See <https://github.com/rhinstaller/anaconda/issues/1117> # The first path here is used by <https://pagure.io/fedora-lorax-templates> # and the second by <https://github.com/projectatomic/rpm-ostree-toolbox/> if OSTree.check_version(2017, 8): for path in ['/ostree/repo', '/install/ostree/repo']: if os.path.isdir(path + '/objects'): pull_opts['localcache-repos'] = Variant('as', [path]) break try: repo.pull_with_options(ostreesetup.remote, Variant('a{sv}', pull_opts), progress, cancellable) except GError as e: exn = PayloadInstallError("Failed to pull from repository: %s" % e) log.error(str(exn)) if errors.errorHandler.cb(exn) == errors.ERROR_RAISE: progressQ.send_quit(1) util.ipmi_abort(scripts=self.data.scripts) sys.exit(1) log.info("ostree pull: %s", progress.get_status() or "") progressQ.send_message(_("Preparing deployment of %s") % (ref, )) # Now that we have the data pulled, delete the remote for now. # This will allow a remote configuration defined in the tree # (if any) to override what's in the kickstart. Otherwise, # we'll re-add it in post. Ideally, ostree would support a # pull without adding a remote, but that would get quite # complex. repo.remote_delete(self.data.ostreesetup.remote, None) self._safe_exec_with_redirect("ostree", [ "admin", "--sysroot=" + conf.target.physical_root, "os-init", ostreesetup.osname ]) admin_deploy_args = [ "admin", "--sysroot=" + conf.target.physical_root, "deploy", "--os=" + ostreesetup.osname ] admin_deploy_args.append(ostreesetup.remote + ':' + ref) log.info("ostree admin deploy starting") progressQ.send_message(_("Deployment starting: %s") % (ref, )) self._safe_exec_with_redirect("ostree", admin_deploy_args) log.info("ostree admin deploy complete") progressQ.send_message(_("Deployment complete: %s") % (ref, )) # Reload now that we've deployed, find the path to the new deployment sysroot.load(None) deployments = sysroot.get_deployments() assert len(deployments) > 0 deployment = deployments[0] deployment_path = sysroot.get_deployment_directory(deployment) util.set_system_root(deployment_path.get_path()) try: self._copy_bootloader_data() except (OSError, RuntimeError) as e: exn = PayloadInstallError("Failed to copy bootloader data: %s" % e) log.error(str(exn)) if errors.errorHandler.cb(exn) == errors.ERROR_RAISE: progressQ.send_quit(1) util.ipmi_abort(scripts=self.data.scripts) sys.exit(1) mainctx.pop_thread_default()
def _safe_exec_with_redirect(self, cmd, argv, **kwargs): """Like util.execWithRedirect, but treat errors as fatal""" rc = util.execWithRedirect(cmd, argv, **kwargs) if rc != 0: raise PayloadInstallError("%s %s exited with code %d" % (cmd, argv, rc))
def pre_install(self): """ Get image and loopback mount it. This is called after partitioning is setup, we now have space to grab the image. If it is a network source Download it to sysroot and provide feedback during the download (using urlgrabber callback). If it is a file:// source then use the file directly. """ error = None if self.data.method.url.startswith("file://"): self.image_path = self.data.method.url[7:] else: error = self._pre_install_url_image() if error: exn = PayloadInstallError(str(error)) if errorHandler.cb(exn) == ERROR_RAISE: raise exn # Used to make install progress % look correct self._adj_size = os.stat(self.image_path)[stat.ST_SIZE] if self.data.method.checksum: progressQ.send_message(_("Checking image checksum")) sha256 = hashlib.sha256() with open(self.image_path, "rb") as f: while True: data = f.read(1024 * 1024) if not data: break sha256.update(data) filesum = sha256.hexdigest() log.debug("sha256 of %s is %s", self.data.method.url, filesum) if util.lowerASCII(self.data.method.checksum) != filesum: log.error("%s does not match checksum.", self.data.method.checksum) exn = PayloadInstallError("Checksum of image does not match") if errorHandler.cb(exn) == ERROR_RAISE: raise exn # If this looks like a tarfile, skip trying to mount it if self.is_tarfile: return # 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 rc = payload_utils.mount(self.image_path, INSTALL_TREE, fstype="auto", options="ro") if rc != 0: log.error("mount error (%s) with %s", rc, self.image_path) exn = PayloadInstallError("mount error %s" % rc) if errorHandler.cb(exn) == ERROR_RAISE: raise exn # Nothing more to mount if not os.path.exists(INSTALL_TREE + "/LiveOS"): self._update_kernel_version_list() return # Mount the first .img in the directory on INSTALL_TREE img_files = glob.glob(INSTALL_TREE + "/LiveOS/*.img") if img_files: # move the mount to IMAGE_DIR os.makedirs(IMAGE_DIR, 0o755) # work around inability to move shared filesystems rc = util.execWithRedirect("mount", ["--make-rprivate", "/"]) if rc == 0: rc = util.execWithRedirect("mount", ["--move", INSTALL_TREE, IMAGE_DIR]) if rc != 0: log.error("error %s moving mount", rc) exn = PayloadInstallError("mount error %s" % rc) if errorHandler.cb(exn) == ERROR_RAISE: raise exn img_file = IMAGE_DIR+"/LiveOS/" + os.path.basename(sorted(img_files)[0]) rc = payload_utils.mount(img_file, INSTALL_TREE, fstype="auto", options="ro") if rc != 0: log.error("mount error (%s) with %s", rc, img_file) exn = PayloadInstallError("mount error %s with %s" % (rc, img_file)) if errorHandler.cb(exn) == ERROR_RAISE: raise exn self._update_kernel_version_list() source = os.statvfs(INSTALL_TREE) self.source_size = source.f_frsize * (source.f_blocks - source.f_bfree)
def pre_install(self): """ Get image and loopback mount it. This is called after partitioning is setup, we now have space to grab the image. If it is a network source Download it to sysroot and provide feedback during the download (using urlgrabber callback). If it is a file:// source then use the file directly. """ error = None if self.data.liveimg.url.startswith("file://"): self.image_path = self.data.liveimg.url[7:] else: error = self._pre_install_url_image() if error: raise PayloadInstallError(str(error)) # Verify the checksum. task = VerifyImageChecksum(image_path=self.image_path, checksum=self.data.liveimg.checksum) task.progress_changed_signal.connect(self._progress_cb) task.run() # If this looks like a tarfile, skip trying to mount it if self.is_tarfile: return # Work around inability to move shared filesystems. # Also, do not share the image mounts with /run bind-mounted to physical # target root during storage.mount_filesystems. rc = util.execWithRedirect("mount", ["--make-rprivate", "/"]) if rc != 0: log.error("mount error (%s) making mount of '/' rprivate", rc) raise PayloadInstallError("mount error %s" % rc) # 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 rc = payload_utils.mount(self.image_path, INSTALL_TREE, fstype="auto", options="ro") if rc != 0: log.error("mount error (%s) with %s", rc, self.image_path) raise PayloadInstallError("mount error %s" % rc) # Nothing more to mount if not os.path.exists(INSTALL_TREE + "/LiveOS"): self._update_kernel_version_list() return # Mount the first .img in the directory on INSTALL_TREE img_files = glob.glob(INSTALL_TREE + "/LiveOS/*.img") if img_files: # move the mount to IMAGE_DIR os.makedirs(IMAGE_DIR, 0o755) rc = util.execWithRedirect("mount", ["--move", INSTALL_TREE, IMAGE_DIR]) if rc != 0: log.error("error %s moving mount", rc) raise PayloadInstallError("mount error %s" % rc) img_file = IMAGE_DIR + "/LiveOS/" + os.path.basename( sorted(img_files)[0]) rc = payload_utils.mount(img_file, INSTALL_TREE, fstype="auto", options="ro") if rc != 0: log.error("mount error (%s) with %s", rc, img_file) raise PayloadInstallError("mount error %s with %s" % (rc, img_file)) self._update_kernel_version_list()
def install(self): """ Install the payload. """ if self.source_size <= 0: raise PayloadInstallError("Nothing to install") self.pct_lock = Lock() self.pct = 0 threadMgr.add(AnacondaThread(name=THREAD_LIVE_PROGRESS, target=self.progress)) cmd = "rsync" # preserve: permissions, owners, groups, ACL's, xattrs, times, # symlinks, hardlinks # go recursively, include devices and special files, don't cross # file system boundaries args = ["-pogAXtlHrDx", "--exclude", "/dev/", "--exclude", "/proc/", "--exclude", "/sys/", "--exclude", "/run/", "--exclude", "/boot/*rescue*", "--exclude", "/boot/loader/", "--exclude", "/boot/efi/loader/", "--exclude", "/etc/machine-id", INSTALL_TREE + "/", util.getSysroot()] try: rc = util.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 or rc == 11: 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) # Live needs to create the rescue image before bootloader is written if os.path.exists(util.getSysroot() + "/usr/sbin/new-kernel-pkg"): use_nkp = True else: log.warning("new-kernel-pkg does not exist - grubby wasn't installed?") use_nkp = False for kernel in self.kernel_version_list: log.info("Generating rescue image for %s", kernel) if use_nkp: util.execInSysroot("new-kernel-pkg", ["--rpmposttrans", kernel]) else: files = glob.glob(util.getSysroot() + "/etc/kernel/postinst.d/*") srlen = len(util.getSysroot()) files = sorted([f[srlen:] for f in files if os.access(f, os.X_OK)]) for file in files: util.execInSysroot(file, [kernel, "/boot/vmlinuz-%s" % kernel])