def setup(self, storage, instClass): super(LiveImagePayload, self).setup(storage, instClass) # Mount the live device and copy from it instead of the overlay at / osimg = storage.devicetree.get_device_by_path( self.data.method.partition) if not osimg: raise PayloadInstallError("Unable to find osimg for %s" % self.data.method.partition) 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 = blivet.util.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._updateKernelVersionList() source = os.statvfs(INSTALL_TREE) self.source_size = source.f_frsize * (source.f_blocks - source.f_bfree)
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", "/etc/machine-id", INSTALL_TREE + "/", iutil.getSysroot() ] 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 or rc == 12: 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 not os.path.exists(iutil.getSysroot() + "/usr/sbin/new-kernel-pkg"): log.error( "new-kernel-pkg does not exist - grubby wasn't installed? skipping" ) return for kernel in self.kernelVersionList: log.info("Generating rescue image for %s", kernel) iutil.execInSysroot("new-kernel-pkg", ["--rpmposttrans", kernel])
def _safeExecWithRedirect(self, cmd, argv, **kwargs): """Like iutil.execWithRedirect, but treat errors as fatal""" rc = iutil.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 install(self): """ Install the payload. """ 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", "/etc/machine-id", INSTALL_TREE+"/", 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 or rc == 12: 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 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(LiveImageKSPayload, self).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", "/sys/", "--exclude", "/run/", "--exclude", "/boot/*rescue*", "--exclude", "/etc/machine-id", "-xaf", self.image_path, "-C", iutil.getSysroot() ] 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) # Live needs to create the rescue image before bootloader is written for kernel in self.kernelVersionList: log.info("Generating rescue image for %s", kernel) iutil.execInSysroot("new-kernel-pkg", ["--rpmposttrans", kernel])
def setup(self, storage, instClass): """ Check the availability and size of the image. """ # This is on purpose, we don't want to call LiveImagePayload's setup method. ImagePayload.setup(self, storage, instClass) 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 setup(self, storage, instClass): super(LiveImagePayload, self).setup(storage, instClass) # Mount the live device and copy from it instead of the overlay at / osimg = storage.devicetree.getDeviceByPath(self.data.method.partition) 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 = blivet.util.mount(osimg.path, INSTALL_TREE, fstype="auto", options="ro") if rc != 0: raise PayloadInstallError("Failed to mount the install tree") source = iutil.eintr_retry_call(os.statvfs, INSTALL_TREE) self.source_size = source.f_frsize * (source.f_blocks - source.f_bfree)
def setup(self, storage, instClass): """ Check the availability and size of the image. """ # This is on purpose, we don't want to call LiveImagePayload's setup method. ImagePayload.setup(self, storage, instClass) self._proxies = {} if self.data.method.proxy: try: proxy = ProxyString(self.data.method.proxy) self._proxies = {"http": proxy.url, "https": proxy.url} except ProxyStringError as e: log.info("Failed to parse proxy for liveimg --proxy=\"%s\": %s", self.data.method.proxy, e) error = None try: req = urllib.urlopen(self.data.method.url, proxies=self._proxies) except IOError as e: log.error("Error opening liveimg: %s", e) error = e else: # If it is a http request we need to check the code method = self.data.method.url.split(":", 1)[0] if method.startswith("http") and req.getcode() != 200: error = "http request returned %s" % req.getcode() if error: exn = PayloadInstallError(str(error)) if errorHandler.cb(exn) == ERROR_RAISE: raise exn # At this point we know we can get the image and what its size is # Make a guess as to minimum size needed: # Enough space for image and image * 3 if req.info().get("content-length"): self._min_size = int(req.info().get("content-length")) * 4 log.debug("liveimg size is %s", self._min_size)
def install(self): mainctx = GLib.MainContext.new() mainctx.push_thread_default() cancellable = None gi.require_version("OSTree", "1.0") from gi.repository import OSTree ostreesetup = self.data.ostreesetup log.info("executing ostreesetup=%r", ostreesetup) # Initialize the filesystem - this will create the repo as well self._safeExecWithRedirect("ostree", [ "admin", "--sysroot=" + iutil.getTargetPhysicalRoot(), "init-fs", iutil.getTargetPhysicalRoot() ]) self._sysroot_path = Gio.File.new_for_path( iutil.getTargetPhysicalRoot()) sysroot = OSTree.Sysroot.new(self._sysroot_path) 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'] = GLib.Variant('b', False) if flags.noverifyssl: self._remoteOptions['tls-permissive'] = GLib.Variant('b', True) repo.remote_change(None, OSTree.RepoRemoteChange.ADD_IF_NOT_EXISTS, ostreesetup.remote, ostreesetup.url, GLib.Variant('a{sv}', self._remoteOptions), cancellable) progressQ.send_message(_("Starting pull of %(branchName)s from %(source)s") % \ {"branchName": ostreesetup.ref, "source": ostreesetup.remote}) progress = OSTree.AsyncProgress.new() progress.connect('changed', self._pullProgressCb) try: repo.pull(ostreesetup.remote, [ostreesetup.ref], 0, progress, cancellable) except GLib.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) iutil.ipmi_report(constants.IPMI_ABORTED) sys.exit(1) progressQ.send_message( _("Preparing deployment of %s") % (ostreesetup.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._safeExecWithRedirect("ostree", [ "admin", "--sysroot=" + iutil.getTargetPhysicalRoot(), "os-init", ostreesetup.osname ]) admin_deploy_args = [ "admin", "--sysroot=" + iutil.getTargetPhysicalRoot(), "deploy", "--os=" + ostreesetup.osname ] admin_deploy_args.append(ostreesetup.remote + ':' + ostreesetup.ref) log.info("ostree admin deploy starting") progressQ.send_message( _("Deployment starting: %s") % (ostreesetup.ref, )) self._safeExecWithRedirect("ostree", admin_deploy_args) log.info("ostree admin deploy complete") progressQ.send_message( _("Deployment complete: %s") % (ostreesetup.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) iutil.setSysroot(deployment_path.get_path()) try: self._copyBootloaderData() 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) iutil.ipmi_report(constants.IPMI_ABORTED) sys.exit(1) mainctx.pop_thread_default()
def preInstall(self, *args, **kwargs): """ 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._preInstall_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 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 = blivet.util.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._updateKernelVersionList() 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 = iutil.execWithRedirect("mount", ["--make-rprivate", "/"]) if rc == 0: rc = iutil.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 = blivet.util.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._updateKernelVersionList() source = os.statvfs(INSTALL_TREE) self.source_size = source.f_frsize * (source.f_blocks - source.f_bfree)
def preInstall(self, *args, **kwargs): """ Download image and loopback mount it. This is called after partitioning is setup, we now have space to grab the image. Download it to ROOT_PATH and provide feedback during the download (using urlgrabber callback). """ # Setup urlgrabber and call back to download image to ROOT_PATH progress = URLGrabberProgress() ugopts = {"ssl_verify_peer": not self.data.method.noverifyssl, "ssl_verify_host": not self.data.method.noverifyssl, "proxies" : self._proxies, "progress_obj" : progress, "copy_local" : True} error = None try: ug = URLGrabber() ug.urlgrab(self.data.method.url, self.image_path, **ugopts) except URLGrabError as e: log.error("Error downloading liveimg: %s", e) error = e else: if not os.path.exists(self.image_path): error = "Failed to download %s, file doesn't exist" % self.data.method.url log.error(error) 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 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 # 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")