def _check_mount_fstab_partitions(self, os_root_dir, skip_mounts=["/", "/boot"], skip_filesystems=["swap"], mountable_lvm_devs=None): """ Reads the contents of /etc/fstab from the VM's root directory and tries to mount all clearly identified (by UUID or path) filesystems. Returns the list of the new directories which were mounted. param: skip_mounts: list(str()): list of directories (inside the chroot) to not try to mount. param: skip_filesystems: list(str()): list of filesystem types to skip mounting entirely param: mountable_lvm_devs: list(str()): list of LVM device paths which exist and are mountable should they appear in /etc/fstab """ new_mountpoints = [] etc_fstab_path = os.path.join(os_root_dir, "etc/fstab") if not utils.test_ssh_path(self._ssh, etc_fstab_path): LOG.warn( "etc/fstab file not found in '%s'. Cannot mount non-root dirs", os_root_dir) return [] etc_fstab_raw = utils.read_ssh_file(self._ssh, etc_fstab_path) etc_fstab = etc_fstab_raw.decode('utf-8') LOG.debug("Mounting non-root partitions from fstab file: %s" % (base64.b64encode(etc_fstab_raw))) # dictionary of the form {"mountpoint": # {"device": "<dev>", "filesystem": "<fs>", "options": "<opts>"}} mounts = {} # fstab entry format: # <device> <mountpoint> <filesystem> <options> <dump> <fsck> fstab_entry_regex = ( "^(\s*([^#\s]+)\s+(\S+)\s+(\S+)\s+(\S+)(\s+(\d)(\s+(\d))?)?\s*)$") for line in etc_fstab.splitlines(): match = re.match(fstab_entry_regex, line) if not match: LOG.warn("Skipping unparseable /etc/fstab line: '%s'", line) continue device = match.group(2) mountpoint = match.group(3) if mountpoint in mounts: raise exception.CoriolisException( "Mountpoint '%s' appears to be mounted twice in " "'/etc/fstab'" % mountpoint) mounts[mountpoint] = { "device": device, "filesystem": match.group(4), "options": match.group(5) } # NOTE: we sort the mountpoints based on length to ensure # they get mounted in the correct order: mounts = collections.OrderedDict( (mountpoint, mounts[mountpoint]) for mountpoint in sorted(mounts, key=len)) # regexes for supported fstab device references: uuid_char_regex = "[0-9a-fA-F]" fs_uuid_regex = ("%(char)s{8}-%(char)s{4}-%(char)s{4}-" "%(char)s{4}-%(char)s{12}") % { "char": uuid_char_regex } fs_uuid_entry_regex = "^(UUID=%s)$" % fs_uuid_regex by_uuid_entry_regex = "^(/dev/disk/by-uuid/%s)$" % fs_uuid_regex if not mountable_lvm_devs: mountable_lvm_devs = [] device_paths = self._get_device_file_paths(mountable_lvm_devs) for (mountpoint, details) in mounts.items(): device = details['device'] if (re.match(fs_uuid_entry_regex, device) is None and re.match(by_uuid_entry_regex, device) is None): device_file_path = self._get_symlink_target(device) if device not in mountable_lvm_devs and (device_file_path not in device_paths): LOG.warn( "Found fstab entry for dir %s which references device " "%s. Only LVM volumes or devices referenced by UUID=* " "or /dev/disk/by-uuid/* notation are supported. " "Devicemapper paths for LVM volumes are also " "supported. Skipping mounting directory." % (mountpoint, device)) continue if mountpoint in skip_mounts: LOG.debug("Skipping undesired mount: %s: %s", mountpoint, details) continue if details["filesystem"] in skip_filesystems: LOG.debug("Skipping mounting undesired FS for device %s: %s", device, details) continue LOG.debug("Attempting to mount fstab device: %s: %s", device, details) # NOTE: `mountpoint` should always be an absolute path: chroot_mountpoint = "%s%s" % (os_root_dir, mountpoint) mountcmd = "sudo mount -t %s -o %s %s '%s'" % ( details["filesystem"], details["options"], device, chroot_mountpoint) try: self._exec_cmd(mountcmd) new_mountpoints.append(chroot_mountpoint) except Exception: LOG.warn( "Failed to run fstab filesystem mount command: '%s'. " "Skipping mount. Error details: %s", mountcmd, utils.get_exception_details()) if new_mountpoints: LOG.info( "The following new /etc/fstab entries were successfully " "mounted: %s", new_mountpoints) return new_mountpoints
def _read_file(self, chroot_path): path = os.path.join(self._os_root_dir, chroot_path) return utils.read_ssh_file(self._ssh, path)
def _read_file(self, chroot_path): path = os.path.join(self._os_root_dir, chroot_path) return utils.read_ssh_file(self._ssh, path)