def _find_existing_installations(devicetree): """Find existing GNU/Linux installations on devices from the device tree. :param devicetree: a device tree to find existing installations in :return: roots of all found installations """ if not os.path.exists(util.getTargetPhysicalRoot()): blivet_util.makedirs(util.getTargetPhysicalRoot()) sysroot = util.getSysroot() roots = [] direct_devices = (dev for dev in devicetree.devices if dev.direct) for device in direct_devices: if not device.format.linux_native or not device.format.mountable or \ not device.controllable: continue try: device.setup() except Exception: # pylint: disable=broad-except log_exception_info(log.warning, "setup of %s failed", [device.name]) continue options = device.format.options + ",ro" try: device.format.mount(options=options, mountpoint=sysroot) except Exception: # pylint: disable=broad-except log_exception_info(log.warning, "mount of %s as %s failed", [device.name, device.format.type]) blivet_util.umount(mountpoint=sysroot) continue if not os.access(sysroot + "/etc/fstab", os.R_OK): blivet_util.umount(mountpoint=sysroot) device.teardown() continue try: (architecture, product, version) = get_release_string() except ValueError: name = _("Linux on %s") % device.name else: # I'd like to make this finer grained, but it'd be very difficult # to translate. if not product or not version or not architecture: name = _("Unknown Linux") elif "linux" in product.lower(): name = _("%(product)s %(version)s for %(arch)s") % \ {"product": product, "version": version, "arch": architecture} else: name = _("%(product)s Linux %(version)s for %(arch)s") % \ {"product": product, "version": version, "arch": architecture} (mounts, swaps) = _parse_fstab(devicetree, chroot=sysroot) blivet_util.umount(mountpoint=sysroot) if not mounts and not swaps: # empty /etc/fstab. weird, but I've seen it happen. continue roots.append(Root(mounts=mounts, swaps=swaps, name=name)) return roots
def _find_existing_installations(devicetree): """Find existing GNU/Linux installations on devices from the device tree. :param devicetree: a device tree to find existing installations in :return: roots of all found installations """ if not os.path.exists(conf.target.physical_root): blivet_util.makedirs(conf.target.physical_root) sysroot = conf.target.physical_root roots = [] direct_devices = (dev for dev in devicetree.devices if dev.direct) for device in direct_devices: if not device.format.linux_native or not device.format.mountable or \ not device.controllable: continue try: device.setup() except Exception: # pylint: disable=broad-except log_exception_info(log.warning, "setup of %s failed", [device.name]) continue options = device.format.options + ",ro" try: device.format.mount(options=options, mountpoint=sysroot) except Exception: # pylint: disable=broad-except log_exception_info(log.warning, "mount of %s as %s failed", [device.name, device.format.type]) blivet_util.umount(mountpoint=sysroot) continue if not os.access(sysroot + "/etc/fstab", os.R_OK): blivet_util.umount(mountpoint=sysroot) device.teardown() continue try: (architecture, product, version) = get_release_string(chroot=sysroot) except ValueError: name = _("Linux on %s") % device.name else: # I'd like to make this finer grained, but it'd be very difficult # to translate. if not product or not version or not architecture: name = _("Unknown Linux") elif "linux" in product.lower(): name = _("%(product)s %(version)s for %(arch)s") % \ {"product": product, "version": version, "arch": architecture} else: name = _("%(product)s Linux %(version)s for %(arch)s") % \ {"product": product, "version": version, "arch": architecture} (mounts, swaps) = _parse_fstab(devicetree, chroot=sysroot) blivet_util.umount(mountpoint=sysroot) if not mounts and not swaps: # empty /etc/fstab. weird, but I've seen it happen. continue roots.append(Root(mounts=mounts, swaps=swaps, name=name)) return roots
def _parse_fstab(devicetree, chroot): """Parse /etc/fstab. :param devicetree: a device tree :param chroot: a path to the target OS installation :return: a tuple of a mount dict and swap list """ mounts = {} swaps = [] path = "%s/etc/fstab" % chroot if not os.access(path, os.R_OK): # XXX should we raise an exception instead? log.info("cannot open %s for read", path) return mounts, swaps blkid_tab = BlkidTab(chroot=chroot) try: blkid_tab.parse() log.debug("blkid.tab devs: %s", list(blkid_tab.devices.keys())) except Exception: # pylint: disable=broad-except log_exception_info(log.info, "error parsing blkid.tab") blkid_tab = None crypt_tab = CryptTab(devicetree, blkid_tab=blkid_tab, chroot=chroot) try: crypt_tab.parse(chroot=chroot) log.debug("crypttab maps: %s", list(crypt_tab.mappings.keys())) except Exception: # pylint: disable=broad-except log_exception_info(log.info, "error parsing crypttab") crypt_tab = None with open(path) as f: log.debug("parsing %s", path) for line in f.readlines(): (line, _pound, _comment) = line.partition("#") fields = line.split(None, 4) if len(fields) < 5: continue (devspec, mountpoint, fstype, options, _rest) = fields # find device in the tree device = devicetree.resolve_device(devspec, crypt_tab=crypt_tab, blkid_tab=blkid_tab, options=options) if device is None: continue if fstype != "swap": mounts[mountpoint] = device else: swaps.append(device) return mounts, swaps
def _find_existing_installations(devicetree): """Find existing GNU/Linux installations on devices from the device tree. :param devicetree: a device tree to find existing installations in :return: roots of all found installations """ if not os.path.exists(conf.target.physical_root): blivet_util.makedirs(conf.target.physical_root) sysroot = conf.target.physical_root roots = [] direct_devices = (dev for dev in devicetree.devices if dev.direct) for device in direct_devices: if not device.format.linux_native or not device.format.mountable or \ not device.controllable or not device.format.exists: continue try: device.setup() except Exception: # pylint: disable=broad-except log_exception_info(log.warning, "setup of %s failed", [device.name]) continue options = device.format.options + ",ro" try: device.format.mount(options=options, mountpoint=sysroot) except Exception: # pylint: disable=broad-except log_exception_info(log.warning, "mount of %s as %s failed", [device.name, device.format.type]) blivet_util.umount(mountpoint=sysroot) continue if not os.access(sysroot + "/etc/fstab", os.R_OK): blivet_util.umount(mountpoint=sysroot) device.teardown() continue architecture, product, version = get_release_string(chroot=sysroot) (mounts, swaps) = _parse_fstab(devicetree, chroot=sysroot) blivet_util.umount(mountpoint=sysroot) if not mounts and not swaps: # empty /etc/fstab. weird, but I've seen it happen. continue roots.append( Root(product=product, version=version, arch=architecture, mounts=mounts, swaps=swaps)) return roots
def find_existing_installations(devicetree): """Find existing GNU/Linux installations on devices from the device tree. :param devicetree: a device tree to find existing installations in :return: roots of all found installations """ try: roots = _find_existing_installations(devicetree) return roots except Exception: # pylint: disable=broad-except log_exception_info(log.info, "failure detecting existing installations") finally: devicetree.teardown_all() return []
def find_existing_installations(devicetree, teardown_all=True): """Find existing GNU/Linux installations on devices from the device tree. :param devicetree: a device tree to find existing installations in :param bool teardown_all: whether to tear down all devices in the end :return: roots of all found installations """ try: roots = _find_existing_installations(devicetree) return roots except Exception: # pylint: disable=broad-except log_exception_info(log.info, "failure detecting existing installations") finally: if teardown_all: devicetree.teardown_all() return []
def parse(self, chroot=""): """ Parse /etc/crypttab from an existing installation. """ if not chroot or not os.path.isdir(chroot): chroot = "" path = "%s/etc/crypttab" % chroot if not os.access(path, os.R_OK): return log.debug("parsing %s", path) with open(path) as f: if not self.blkid_tab: try: self.blkid_tab = BlkidTab(chroot=chroot) self.blkid_tab.parse() except Exception: # pylint: disable=broad-except log_exception_info(fmt_str="failed to parse blkid.tab") self.blkid_tab = None for line in f.readlines(): (line, _pound, _comment) = line.partition("#") fields = line.split() if not 2 <= len(fields) <= 4: continue elif len(fields) == 2: fields.extend(['none', '']) elif len(fields) == 3: fields.append('') (name, devspec, keyfile, options) = fields # resolve devspec to a device in the tree device = self.devicetree.resolve_device( devspec, blkid_tab=self.blkid_tab) if device: self.mappings[name] = { "device": device, "keyfile": keyfile, "options": options }
def parse(self, chroot=""): """ Parse /etc/crypttab from an existing installation. """ if not chroot or not os.path.isdir(chroot): chroot = "" path = "%s/etc/crypttab" % chroot if not os.access(path, os.R_OK): return log.debug("parsing %s", path) with open(path) as f: if not self.blkid_tab: try: self.blkid_tab = BlkidTab(chroot=chroot) self.blkid_tab.parse() except Exception: # pylint: disable=broad-except log_exception_info(fmt_str="failed to parse blkid.tab") self.blkid_tab = None for line in f.readlines(): (line, _pound, _comment) = line.partition("#") fields = line.split() if not 2 <= len(fields) <= 4: continue elif len(fields) == 2: fields.extend(['none', '']) elif len(fields) == 3: fields.append('') (name, devspec, keyfile, options) = fields # resolve devspec to a device in the tree device = self.devicetree.resolve_device(devspec, blkid_tab=self.blkid_tab) if device: self.mappings[name] = {"device": device, "keyfile": keyfile, "options": options}
def get_containing_device(path, devicetree): """ Return the device that a path resides on. """ if not os.path.exists(path): return None st = os.stat(path) major = os.major(st.st_dev) minor = os.minor(st.st_dev) link = "/sys/dev/block/%s:%s" % (major, minor) if not os.path.exists(link): return None try: device_name = os.path.basename(os.readlink(link)) except Exception: # pylint: disable=broad-except log_exception_info(fmt_str="failed to find device name for path %s", fmt_args=[path]) return None if device_name.startswith("dm-"): # have I told you lately that I love you, device-mapper? device_name = blockdev.dm.name_from_node(device_name) return devicetree.get_device_by_name(device_name)
def reset_storage(storage, scan_all=False, teardown=False, retry=True): """Reset the storage model. :param storage: an instance of the Blivet's storage object :param scan_all: should we scan all devices in the system? :param teardown: should we teardown devices in the current device tree? :param retry: should we allow to retry the reset? """ # Deactivate all devices. if teardown: try: storage.devicetree.teardown_all() except Exception: # pylint: disable=broad-except log_exception_info(log.error, "Failure tearing down device tree.") # Clear the exclusive disks to scan all devices in the system. if scan_all: disk_select_proxy = STORAGE.get_proxy(DISK_SELECTION) disk_select_proxy.SetExclusiveDisks([]) # Do the reset. while True: try: _reset_storage(storage) except StorageError as e: # Is the retry allowed? if not retry: raise # Does the user want to retry? elif error_handler.cb(e) == ERROR_RAISE: raise # Retry the storage reset. else: continue else: # No need to retry. break
def mount_filesystems(self, root_path="", read_only=None, skip_root=False): """Mount the system's filesystems. :param str root_path: the root directory for this filesystem :param read_only: read only option str for this filesystem :type read_only: str or None :param bool skip_root: whether to skip mounting the root filesystem """ devices = list(self.mountpoints.values()) + self.swap_devices devices.extend([ self.dev, self.devshm, self.devpts, self.sysfs, self.proc, self.selinux, self.usb, self.run ]) if isinstance(_platform, EFI): devices.append(self.efivars) devices.sort(key=lambda d: getattr(d.format, "mountpoint", "")) for device in devices: if not device.format.mountable or not device.format.mountpoint: continue if skip_root and device.format.mountpoint == "/": continue options = device.format.options if "noauto" in options.split(","): continue if device.format.type == "bind" and device not in [ self.dev, self.run ]: # set up the DirectoryDevice's parents now that they are # accessible # # -- bind formats' device and mountpoint are always both # under the chroot. no exceptions. none, damn it. target_dir = "%s/%s" % (root_path, device.path) parent = get_containing_device(target_dir, self.devicetree) if not parent: log.error( "cannot determine which device contains " "directory %s", device.path) device.parents = [] self.devicetree._remove_device(device) continue else: device.parents = [parent] try: device.setup() except Exception as e: # pylint: disable=broad-except log_exception_info(fmt_str="unable to set up device %s", fmt_args=[device]) if error_handler.cb(e) == ERROR_RAISE: raise else: continue if read_only: options = "%s,%s" % (options, read_only) try: device.format.setup(options=options, chroot=root_path) except Exception as e: # pylint: disable=broad-except log_exception_info(log.error, "error mounting %s on %s", [device.path, device.format.mountpoint]) if error_handler.cb(e) == ERROR_RAISE: raise self.active = True
def _parse_fstab(devicetree, chroot=None): """Parse /etc/fstab. :param devicetree: a device tree :param chroot: a path to the target OS installation :return: a tuple of a mount dict and swap list """ if not chroot or not os.path.isdir(chroot): chroot = util.getSysroot() mounts = {} swaps = [] path = "%s/etc/fstab" % chroot if not os.access(path, os.R_OK): # XXX should we raise an exception instead? log.info("cannot open %s for read", path) return mounts, swaps blkid_tab = BlkidTab(chroot=chroot) try: blkid_tab.parse() log.debug("blkid.tab devs: %s", list(blkid_tab.devices.keys())) except Exception: # pylint: disable=broad-except log_exception_info(log.info, "error parsing blkid.tab") blkid_tab = None crypt_tab = CryptTab(devicetree, blkid_tab=blkid_tab, chroot=chroot) try: crypt_tab.parse(chroot=chroot) log.debug("crypttab maps: %s", list(crypt_tab.mappings.keys())) except Exception: # pylint: disable=broad-except log_exception_info(log.info, "error parsing crypttab") crypt_tab = None with open(path) as f: log.debug("parsing %s", path) for line in f.readlines(): (line, _pound, _comment) = line.partition("#") fields = line.split(None, 4) if len(fields) < 5: continue (devspec, mountpoint, fstype, options, _rest) = fields # find device in the tree device = devicetree.resolve_device(devspec, crypt_tab=crypt_tab, blkid_tab=blkid_tab, options=options) if device is None: continue if fstype != "swap": mounts[mountpoint] = device else: swaps.append(device) return mounts, swaps
def parse_fstab(self, chroot=None): """Parse /etc/fstab. preconditions: all storage devices have been scanned, including filesystems FIXME: control which exceptions we raise XXX do we care about bind mounts? how about nodev mounts? loop mounts? """ if not chroot or not os.path.isdir(chroot): chroot = conf.target.system_root path = "%s/etc/fstab" % chroot if not os.access(path, os.R_OK): # XXX should we raise an exception instead? log.info("cannot open %s for read", path) return blkid_tab = BlkidTab(chroot=chroot) try: blkid_tab.parse() log.debug("blkid.tab devs: %s", list(blkid_tab.devices.keys())) except Exception: # pylint: disable=broad-except log_exception_info(log.info, "error parsing blkid.tab") blkid_tab = None crypt_tab = CryptTab(self.devicetree, blkid_tab=blkid_tab, chroot=chroot) try: crypt_tab.parse(chroot=chroot) log.debug("crypttab maps: %s", list(crypt_tab.mappings.keys())) except Exception: # pylint: disable=broad-except log_exception_info(log.info, "error parsing crypttab") crypt_tab = None self.blkid_tab = blkid_tab self.crypt_tab = crypt_tab with open(path) as f: log.debug("parsing %s", path) lines = f.readlines() for line in lines: (line, _pound, _comment) = line.partition("#") fields = line.split() if not 4 <= len(fields) <= 6: continue try: device = self._parse_one_line(*fields) except UnrecognizedFSTabEntryError: # just write the line back out as-is after upgrade self.preserve_lines.append(line) continue if not device: continue if device not in self.devicetree.devices: try: self.devicetree._add_device(device) except ValueError: # just write duplicates back out post-install self.preserve_lines.append(line)
def shutdown(self): """ Deactivate all devices. """ try: self.devicetree.teardown_all() except Exception: # pylint: disable=broad-except log_exception_info(log.error, "failure tearing down device tree")
def mount_filesystems(self, root_path="", read_only=None, skip_root=False): """Mount the system's filesystems. :param str root_path: the root directory for this filesystem :param read_only: read only option str for this filesystem :type read_only: str or None :param bool skip_root: whether to skip mounting the root filesystem """ devices = list(self.mountpoints.values()) + self.swap_devices devices.extend([self.dev, self.devshm, self.devpts, self.sysfs, self.proc, self.selinux, self.usb, self.run]) if isinstance(_platform, EFI): devices.append(self.efivars) devices.sort(key=lambda d: getattr(d.format, "mountpoint", "")) for device in devices: if not device.format.mountable or not device.format.mountpoint: continue if skip_root and device.format.mountpoint == "/": continue options = device.format.options if "noauto" in options.split(","): continue if device.format.type == "bind" and device not in [self.dev, self.run]: # set up the DirectoryDevice's parents now that they are # accessible # # -- bind formats' device and mountpoint are always both # under the chroot. no exceptions. none, damn it. target_dir = "%s/%s" % (root_path, device.path) parent = get_containing_device(target_dir, self.devicetree) if not parent: log.error("cannot determine which device contains " "directory %s", device.path) device.parents = [] self.devicetree._remove_device(device) continue else: device.parents = [parent] try: device.setup() except Exception as e: # pylint: disable=broad-except log_exception_info(fmt_str="unable to set up device %s", fmt_args=[device]) if error_handler.cb(e) == ERROR_RAISE: raise else: continue if read_only: options = "%s,%s" % (options, read_only) try: device.format.setup(options=options, chroot=root_path) except Exception as e: # pylint: disable=broad-except log_exception_info(log.error, "error mounting %s on %s", [device.path, device.format.mountpoint]) if error_handler.cb(e) == ERROR_RAISE: raise self.active = True
def parse_fstab(self, chroot=None): """Parse /etc/fstab. preconditions: all storage devices have been scanned, including filesystems FIXME: control which exceptions we raise XXX do we care about bind mounts? how about nodev mounts? loop mounts? """ if not chroot or not os.path.isdir(chroot): chroot = util.getSysroot() path = "%s/etc/fstab" % chroot if not os.access(path, os.R_OK): # XXX should we raise an exception instead? log.info("cannot open %s for read", path) return blkid_tab = BlkidTab(chroot=chroot) try: blkid_tab.parse() log.debug("blkid.tab devs: %s", list(blkid_tab.devices.keys())) except Exception: # pylint: disable=broad-except log_exception_info(log.info, "error parsing blkid.tab") blkid_tab = None crypt_tab = CryptTab(self.devicetree, blkid_tab=blkid_tab, chroot=chroot) try: crypt_tab.parse(chroot=chroot) log.debug("crypttab maps: %s", list(crypt_tab.mappings.keys())) except Exception: # pylint: disable=broad-except log_exception_info(log.info, "error parsing crypttab") crypt_tab = None self.blkid_tab = blkid_tab self.crypt_tab = crypt_tab with open(path) as f: log.debug("parsing %s", path) lines = f.readlines() for line in lines: (line, _pound, _comment) = line.partition("#") fields = line.split() if not 4 <= len(fields) <= 6: continue try: device = self._parse_one_line(*fields) except UnrecognizedFSTabEntryError: # just write the line back out as-is after upgrade self.preserve_lines.append(line) continue if not device: continue if device not in self.devicetree.devices: try: self.devicetree._add_device(device) except ValueError: # just write duplicates back out post-install self.preserve_lines.append(line)