[rhel7-branch 6/9] dd-refactor: dracut + build bits
wgwoods
installerbot-noreply at redhat.com
Thu Jul 9 14:59:55 UTC 2015
From: Will Woods <wwoods at redhat.com>
Here's where we actually modify stuff to use `driver_updates.py`:
* Parse commandline options to /tmp/dd_{net,disk,interactive,todo}
* Generate udev rules etc. in `driver-updates-genrules.sh`
* `fetch-driver-net.sh`: fetch .iso file and call driver-updates
* Modify `driver-updates at .service`:
* Use `dmesg -n1` and `/proc/sys/kernel/printk` to hide console messages
* Make sure we run after vconsole is set up
* Use `plymouth --hide-splash` rather than `plymouth quit`
(this is how dracut handles emergency shells)
* Drop `driver-updates.sh` (which ran driver-updates in `pre-trigger`)
* Drop `driver-updates-net.sh` (`driver_updates.py` handles saving repos)
* Drop old `driver-updates`
* Make necessary changes to `Makefile.am` and `module-setup.sh`
* Add `dracut/test_*.py` to `nosetests.sh`
* `anaconda-lib.sh`: Add `wait_for_dd`, remove `start_driver_update`,
fix `run_kickstart` for the new stuff above
---
dracut/Makefile.am | 6 +-
dracut/anaconda-lib.sh | 50 +--
dracut/driver-updates | 830 --------------------------------------
dracut/driver-updates-genrules.sh | 23 ++
dracut/driver-updates-net.sh | 10 -
dracut/driver-updates.sh | 23 --
dracut/driver-updates at .service | 12 +-
dracut/fetch-driver-net.sh | 36 +-
dracut/module-setup.sh | 7 +-
dracut/parse-anaconda-dd.sh | 32 +-
tests/nosetests.sh | 2 +-
11 files changed, 85 insertions(+), 946 deletions(-)
delete mode 100755 dracut/driver-updates
create mode 100644 dracut/driver-updates-genrules.sh
delete mode 100755 dracut/driver-updates-net.sh
delete mode 100755 dracut/driver-updates.sh
diff --git a/dracut/Makefile.am b/dracut/Makefile.am
index ae704e9..44a5b46 100644
--- a/dracut/Makefile.am
+++ b/dracut/Makefile.am
@@ -44,9 +44,9 @@ dist_dracut_SCRIPTS = module-setup.sh \
parse-anaconda-dd.sh \
fetch-driver-net.sh \
driver-updates at .service \
- driver-updates.sh \
- driver-updates-net.sh \
+ driver-updates-genrules.sh \
anaconda-depmod.sh \
- driver-updates
+ driver_updates.py \
+ test_driver_updates.py
MAINTAINERCLEANFILES = Makefile.in
diff --git a/dracut/anaconda-lib.sh b/dracut/anaconda-lib.sh
index f084ff6..9e74e3d 100755
--- a/dracut/anaconda-lib.sh
+++ b/dracut/anaconda-lib.sh
@@ -216,39 +216,21 @@ run_kickstart() {
. $hookdir/cmdline/*parse-anaconda-repo.sh
. $hookdir/cmdline/*parse-livenet.sh
. $hookdir/cmdline/*parse-anaconda-dd.sh
- case "$repotype" in
- http*|ftp|nfs*) do_net=1 ;;
- cdrom|hd|bd) do_disk=1 ;;
- esac
- [ "$root" = "anaconda-auto-cd" ] && do_disk=1
-
- # kickstart Driver Disk Handling
- # parse-kickstart may have added network inst.dd entries to the cmdline
- # Or it may have written devices to /tmp/dd_ks
-
- # Does network need to be rerun?
- dd_args="$(getargs dd= inst.dd=)"
- for dd in $dd_args; do
- case "${dd%%:*}" in
- http|https|ftp|nfs|nfs4)
- do_net=1
- rm /tmp/dd_net.done
- break
- ;;
- esac
- done
- # Run the driver update UI for disks
- if [ -e "/tmp/dd_args_ks" ]; then
- # TODO: Seems like this should be a function, a mostly same version is used in 3 places
- start_driver_update "Kickstart Driver Update Disk"
- rm /tmp/dd_args_ks
- fi
+ # Figure out whether we need to retry disk/net stuff
+ case "$root" in
+ anaconda-net:*) do_net=1 ;;
+ anaconda-disk:*) do_disk=1 ;;
+ anaconda-auto-cd) do_disk=1 ;;
+ esac
+ [ -f /tmp/dd_net ] && do_net=1
+ [ -f /tmp/dd_disk ] && do_disk=1
# disk: replay udev events to trigger actions
if [ "$do_disk" ]; then
# set up new rules
. $hookdir/pre-trigger/*repo-genrules.sh
+ . $hookdir/pre-trigger/*driver-updates-genrules.sh
udevadm control --reload
# trigger the rules for all the block devices we see
udevadm trigger --action=change --subsystem-match=block
@@ -277,16 +259,6 @@ wait_for_updates() {
echo "[ -e /tmp/liveupdates.done ]" > $hookdir/initqueue/finished/updates.sh
}
-start_driver_update() {
- local title="$1"
-
- tty=$(find_tty)
-
- # save module state
- cat /proc/modules > /tmp/dd_modules
-
- info "Starting $title Service on $tty"
- systemctl start driver-updates@$tty.service
- status=$(systemctl -p ExecMainStatus show driver-updates@$tty.service)
- info "DD status=$status"
+wait_for_dd() {
+ echo "[ -e /tmp/dd.done ]" > $hookdir/initqueue/finished/dd.sh
}
diff --git a/dracut/driver-updates b/dracut/driver-updates
deleted file mode 100755
index 373884f..0000000
--- a/dracut/driver-updates
+++ /dev/null
@@ -1,830 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (C) 2013 by Red Hat, Inc. All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-# Author(s): Brian C. Lane <bcl at brianlane.com>
-#
-"""
-Driver Update Disk UI
-
-/tmp/dd_modules is a copy of /proc/modules at startup time
-/tmp/dd_args is a parsed list of the inst.dd= cmdline args, and may include
- 'dd' or 'inst.dd' if it was specified without arguments
-/tmp/dd_args_ks is the same format, but skips processing existing OEMDRV devices.
-
-Pass a path and it will install the driver rpms from the path before checking
-for new OEMDRV devices.
-
-Repositories for installed drivers are copied into /run/install/DD-X where X
-starts at 1 and increments for each repository.
-
-Selected driver package names are saved in /run/install/dd_packages
-
-Anaconda uses the repository and package list to install the same set of drivers
-to the target system.
-"""
-import logging
-from logging.handlers import SysLogHandler
-import sys
-import os
-import subprocess
-import time
-import glob
-
-log = logging.getLogger("DD")
-
-
-class RunCmdError(Exception):
- """ Raised when run_cmd gets a non-zero returncode
- """
- pass
-
-
-def run_cmd(cmd):
- """ Run a command, collect stdout and the returncode. stderr is ignored.
-
- :param cmd: command and arguments to run
- :type cmd: list of strings
- :returns: exit code and stdout from the command
- :rtype: (int, string)
- :raises: OSError if the cmd doesn't exist, RunCmdError if the rc != 0
- """
- try:
- with open("/dev/null", "w") as fd_null:
- log.debug(" ".join(cmd))
- proc = subprocess.Popen(cmd,
- stdout=subprocess.PIPE,
- stderr=fd_null)
- out = proc.communicate()[0]
- if out:
- for line in out.splitlines():
- log.debug(line)
- except OSError as e:
- log.error("Error running %s: %s", cmd[0], e.strerror)
- raise
- if proc.returncode:
- log.debug("%s returned %s", cmd[0], proc.returncode)
- raise RunCmdError()
- return (proc.returncode, out)
-
-
-def oemdrv_list():
- """ Get a list of devices labeled as OEMDRV
-
- :returns: list of devices
- :rtype: list
- """
- try:
- outlines = run_cmd(["blkid", "-t", "LABEL=OEMDRV", "-o", "device"])[1]
- except (OSError, RunCmdError):
- # Nothing with that label
- return []
- else:
- return outlines.splitlines()
-
-
-def get_dd_args():
- """ Get the dd arguments from /tmp/dd_args or /tmp/dd_args_ks
-
- :returns: List of arguments
- :rtype: list of strings
- """
- net_protocols = ["http", "https", "ftp", "nfs", "nfs4"]
- args = []
- for dd_args_file in ["/tmp/dd_args", "/tmp/dd_args_ks"]:
- if not os.path.exists(dd_args_file):
- continue
- try:
- dd_args = open(dd_args_file, "r").readline().split()
- except IOError:
- return []
-
- # skip dd args that need networking
- args.extend(filter(lambda x: x.split(":")[0].lower() not in net_protocols, dd_args))
- return args
-
-
-def is_interactive():
- """ Determine if the user requested interactive driver selection
-
- :returns: True if 'dd' or 'inst.dd' included in /tmp/dd_args False if not
- :rtype: bool
- """
- dd_args = get_dd_args()
- if "dd" in dd_args or "inst.dd" in dd_args:
- return True
- else:
- return False
-
-
-def umount(device):
- """ Unmount the device
-
- :param device: Device or mountpoint to unmount
- :type device: string
- :returns: None
- """
- if not device:
- return
-
- try:
- run_cmd(["umount", device])
- except (OSError, RunCmdError):
- pass
-
-
-def mount_device(device, mnt="/media/DD/"):
- """ Mount a device and check to see if it really is a driver disk
-
- :param device: path to device to mount
- :type device: string
- :param mnt: path to mount the device on
- :type mnt: string
- :returns: True if it is a DD, False if not
- :rtype: bool
-
- It is unmounted if it is not a DD and left mounted if it is.
- """
- try:
- run_cmd(["mount", device, mnt])
- except (OSError, RunCmdError):
- return False
- return True
-
-
-def copy_repo(dd_path, dest_prefix):
- """ Copy the current arch's repository to a unique destination
-
- :param dd_path: Path to the driver repo directory
- :type dd_path: string
- :param dest_prefix: Destination directory prefix, a number is added
- :type dest_prefix: string
- :returns: None
-
- The destination directory names are in the order that the drivers
- were loaded, starting from 1
- """
- suffix = 1
- while os.path.exists(dest_prefix+str(suffix)):
- suffix += 1
- dest = dest_prefix+str(suffix)
- os.makedirs(dest)
- try:
- run_cmd(["cp", "-ar", dd_path, dest])
- except (OSError, RunCmdError):
- pass
-
-
-def copy_file(src, dest):
- """ Copy a file
-
- :param src: Source file
- :type src: string
- :param dest: Destination file
- :type dest: string
- :returns: None
- """
- try:
- run_cmd(["cp", "-a", src, dest])
- except (OSError, RunCmdError):
- pass
-
-
-def move_file(src, dest):
- """ Move a file
-
- :param src: Source file
- :type src: string
- :param dest: Destination file
- :type dest: string
- :returns: None
- """
- try:
- run_cmd(["mv", "-f", src, dest])
- except (OSError, RunCmdError):
- pass
-
-
-def find_dd(mnt="/media/DD"):
- """ Find all suitable DD repositories under a path
-
- :param mnt: Top of the directory tree to search
- :type mnt: string
- :returns: list of DD repositories
- :rtype: list
- """
- dd_repos = []
- arch = os.uname()[4]
- for root, dirs, files in os.walk(mnt, followlinks=True):
- if "rhdd3" in files and "rpms" in dirs and \
- os.path.exists(root+"/rpms/"+arch):
- dd_repos.append(root+"/rpms/"+arch)
- log.debug("Found repos - %s", " ".join(dd_repos))
- return dd_repos
-
-
-def get_module_set(fname):
- """ Read a module list and return a set of the names
-
- :param fname: Full path to filename
- :type fname: string
- :returns: set of the module names
- """
- modules = set()
- if os.path.exists(fname):
- with open(fname, "r") as f:
- for line in f:
- mod_args = line.strip().split()
- if mod_args:
- modules.update([mod_args[0]])
- return modules
-
-def to_modname(modfile):
- return os.path.basename(modfile)[:-3].replace('-','_')
-
-def reload_modules(newfiles):
- """ Reload new module versions from /lib/modules/<kernel>/updates/
- """
- try:
- run_cmd(["depmod", "-a"])
- except (OSError, RunCmdError):
- pass
-
- # Make a list of modules added since startup
- startup_modules = get_module_set("/tmp/dd_modules")
- current_modules = get_module_set("/proc/modules")
- new_modules = current_modules.difference(startup_modules)
- log.debug("new_modules = %s", " ".join(new_modules))
-
- # And a list of modules contained in the disk we just extracted
- dd_modules = set(to_modname(f) for f in newfiles if f.endswith(".ko"))
- # TODO: what modules do we unload when there's new firmware?
- log.debug("dd_modules = %s", " ".join(dd_modules))
- new_modules.update(dd_modules)
-
- # I think we can just iterate once using modprobe -r to remove unused deps
- for module in new_modules:
- try:
- run_cmd(["modprobe", "-r", module])
- except (OSError, RunCmdError):
- pass
-
- time.sleep(2)
-
- # Reload the modules, using the new versions from /lib/modules/<kernel>/updates/
- try:
- run_cmd(["udevadm", "trigger"])
- except (OSError, RunCmdError):
- pass
-
-
-class Driver(object):
- def __init__(self):
- self.source = ""
- self.name = ""
- self.flags = ""
- self.description = []
- self.selected = False
-
- @property
- def args(self):
- return ["--%s" % a for a in self.flags.split()]
-
- @property
- def rpm(self):
- return self.source
-
-
-def fake_drivers(num):
- """ Generate a number of fake drivers for testing
- """
- drivers = []
- for i in xrange(0, num):
- d = Driver()
- d.source = "driver-%d" % i
- d.flags = "modules"
- drivers.append(d)
- return drivers
-
-
-def dd_list(dd_path, kernel_ver=None, anaconda_ver=None):
- """ Build a list of the drivers in the directory
-
- :param dd_path: Path to the driver repo
- :type dd_path: string
- :returns: list of drivers
- :rtype: Driver object
-
- By default none of the drivers are selected
- """
- if not kernel_ver:
- kernel_ver = os.uname()[2]
- if not anaconda_ver:
- anaconda_ver = "19.0"
-
- try:
- outlines = run_cmd(["dd_list", "-k", kernel_ver, "-a", anaconda_ver, "-d", dd_path])[1]
- except (OSError, RunCmdError):
- return []
-
- # Output format is:
- # source rpm\n
- # name\n
- # flags\n
- # description (multi-line)\n
- # ---\n
- drivers = []
- new_driver = Driver()
- line_idx = 0
- for line in outlines.splitlines():
- log.debug(line)
- if line == "---":
- drivers.append(new_driver)
- new_driver = Driver()
- line_idx = 0
- elif line_idx == 0:
- new_driver.source = line
- line_idx += 1
- elif line_idx == 1:
- new_driver.name = line
- line_idx += 1
- elif line_idx == 2:
- new_driver.flags = line
- line_idx += 1
- elif line_idx == 3:
- new_driver.description.append(line)
-
- return drivers
-
-
-def dd_extract(driver, dest_path="/updates/", kernel_ver=None):
- """ Extract a driver rpm to a destination path
-
- :param driver: Driver to extract
- :type driver: Driver object
- :param dest_path: Top directory of the destination path
- :type dest_path: string
- :returns: list of paths to extracted firmware and modules
-
- This extracts the driver's files into 'dest_path' (which defaults
- to /updates/ so that the normal live updates handling will overlay
- any binary or library updates onto the initrd automatically.
- """
- if not kernel_ver:
- kernel_ver = os.uname()[2]
-
- cmd = ["dd_extract", "-k", kernel_ver]
- cmd += driver.args
- cmd += ["--rpm", driver.rpm, "--directory", dest_path]
- log.info("Extracting files from %s", driver.rpm)
-
- # make sure the to be used directory exists
- if not os.path.isdir(dest_path):
- os.makedirs(dest_path)
-
- try:
- run_cmd(cmd)
- except (OSError, RunCmdError):
- log.error("dd_extract failed, skipped %s", driver.rpm)
- return
-
- # Create the destination directories
- initrd_updates = "/lib/modules/" + os.uname()[2] + "/updates/"
- ko_updates = dest_path + initrd_updates
- initrd_firmware = "/lib/firmware/updates/"
- firmware_updates = dest_path + initrd_firmware
- for d in (initrd_updates, ko_updates, initrd_firmware, firmware_updates):
- if not os.path.exists(d):
- os.makedirs(d)
-
- filelist = []
-
- # Copy *.ko files over to /updates/lib/modules/<kernel>/updates/
- for root, _dirs, files in os.walk(dest_path+"/lib/modules/"):
- if root.endswith("/updates") and os.path.isdir(root):
- continue
- for f in (f for f in files if f.endswith(".ko")):
- src = root+"/"+f
- filelist.append(src)
- copy_file(src, ko_updates)
- move_file(src, initrd_updates)
-
- # Copy the firmware updates
- for root, _dirs, files in os.walk(dest_path+"/lib/firmware/"):
- if root.endswith("/updates") and os.path.isdir(root):
- continue
- for f in (f for f in files):
- src = root+"/"+f
- filelist.append(src)
- copy_file(src, firmware_updates)
- move_file(src, initrd_firmware)
-
- # Tell our caller about the newly-extracted stuff
- return filelist
-
-
-# an arbitrary value to signal refreshing the menu contents
-DoRefresh = True
-
-def selection_menu(items, title, info_func, multi_choice=True, refresh=False):
- """ Display menu and let user select one or more choices.
-
- :param items: list of items
- :type items: list of objects (with the 'selected' property/attribute if
- multi_choice=True is used)
- :param title: title for the menu
- :type title: str
- :param info_func: function providing info about items
- :type info_func: item -> str
- :param multi_choice: whether it is a multiple choice menu or not
- :type multi_choice: bool
- :returns: the selected item in case of multi_choice=False and user did
- selection, None otherwise
- """
-
- page_length = 20
- page = 1
- num_pages = len(items) / page_length
- if len(items) % page_length > 0:
- num_pages += 1
-
- if multi_choice:
- choice_format = "[%s]"
- else:
- choice_format = ""
- format_str = "%3d) " + choice_format + " %s"
-
- while True:
- # show a page of items
- print("\nPage %d of %d" % (page, num_pages))
- print(title)
- if page * page_length <= len(items):
- num_items = page_length
- else:
- num_items = len(items) % page_length
- for i in xrange(0, num_items):
- item_idx = ((page-1) * page_length) + i
- if multi_choice:
- if items[item_idx].selected:
- selected = "x"
- else:
- selected = " "
- args = (i+1, selected, info_func(items[item_idx]))
- else:
- args = (i+1, info_func(items[item_idx]))
- print(format_str % args)
-
- # Select an item to toggle, continue or change pages
- opts = ["# to select",
- "'n'-next page",
- "'p'-previous page",
- "'c'-continue"]
- if multi_choice:
- opts[0] = "# to toggle selection"
- if refresh:
- opts.insert(1,"'r'-refresh")
- idx = raw_input(''.join(['\n',
- ", ".join(opts[:-1]),
- " or ", opts[-1], ": "]))
- if idx.isdigit() and not (int(idx) < 1 or int(idx) > num_items):
- item_idx = ((page-1) * page_length) + int(idx) - 1
- if multi_choice:
- items[item_idx].selected = not items[item_idx].selected
- else:
- # single choice only, we can return now
- return items[item_idx]
- elif idx.lower() == 'n':
- if page < num_pages:
- page += 1
- else:
- print("Last page")
- elif idx.lower() == 'p':
- if page > 1:
- page -= 1
- else:
- print("First page")
- elif idx.lower() == 'r' and refresh:
- return DoRefresh
- elif idx.lower() == 'c':
- return
- else:
- print("Invalid selection")
-
-def select_drivers(drivers):
- """ Display pages of drivers to be loaded.
-
- :param drivers: Drivers to be selected by the user
- :type drivers: list of Driver objects
- :returns: None
- """
- if not drivers:
- return
-
- selection_menu(drivers, "Select drivers to install",
- lambda driver: driver.source)
-
-def process_dd(dd_path):
- """ Handle installing modules, firmware, enhancements from the dd repo
-
- :param dd_path: Path to the driver repository
- :type dd_path: string
- :returns: None
- """
- drivers = dd_list(dd_path)
- log.debug("drivers = %s", " ".join([d.rpm for d in drivers]))
-
- # If interactive mode or rhdd3.rules pass flag to deselect by default?
- if os.path.exists(dd_path+"/rhdd3.rules") or is_interactive():
- select_drivers(drivers)
- if not any((d.selected for d in drivers)):
- return
- else:
- map(lambda d: setattr(d, "selected", True), drivers)
-
- # Copy the repository for Anaconda to use during install
- copy_repo(dd_path, "/updates/run/install/DD-")
-
- extracted = []
-
- for driver in filter(lambda d: d.selected, drivers):
- extracted += dd_extract(driver, "/updates/")
-
- # Write the package names for all modules and firmware for Anaconda
- if "modules" in driver.flags or "firmwares" in driver.flags:
- with open("/run/install/dd_packages", "a") as f:
- f.write("%s\n" % driver.name)
-
- reload_modules(extracted)
-
-
-def select_dd(device):
- """ Mount a device and check it for Driver Update repos
-
- :param device: Path to the device to mount and check
- :type device: string
- :returns: None
- """
- mnt = "/media/DD/"
- if not os.path.isdir(mnt):
- os.makedirs(mnt)
- if not mount_device(device, mnt):
- return
-
- dd_repos = find_dd(mnt)
- for repo in dd_repos:
- log.info("Processing DD repo %s on %s", repo, device)
- process_dd(repo)
-
- # TODO - does this need to be done before module reload?
- umount(device)
-
-
-def network_driver(dd_path):
- """ Handle network driver download, then scan for new OEMDRV devices.
-
- :param dd_path: Path to the downloaded driver rpms
- :type dd_path: string
- :returns: None
- """
- skip_dds = set(oemdrv_list())
-
- log.info("Processing Network Drivers from %s", dd_path)
- isos = glob.glob(os.path.join(dd_path, "*.iso"))
- for iso in isos:
- select_dd(iso)
-
- process_dd(dd_path)
-
- # TODO: May need to add new drivers to /tmp/dd_modules to prevent them from being unloaded
-
- # Scan for new OEMDRV devices and ignore dd_args
- dd_scan(skip_dds, scan_dd_args=False, skip_device_menu=True)
-
-class DeviceInfo(object):
- def __init__(self, **kwargs):
- self.device = kwargs.get("device", None)
- self.label = kwargs.get("label", None)
- self.uuid = kwargs.get("uuid", None)
- self.fs_type = kwargs.get("fs_type", None)
-
- def __str__(self):
- return "%-10s %-20s %-15s %s" % (self.device or "", self.fs_type or "",
- self.label or "", self.uuid or "")
-
-def parse_blkid(line):
- """ Parse a line of output from blkid
-
- :param line: line of output from blkid
- :param type: string
- :returns: {} or dict of NAME=VALUE pairs including "device"
- :rtype: dict
-
- blkid output cannot be trusted. labels may be missing or in a different
- order so we parse what we get and return a dict with their values.
- """
- import shlex
-
- device = {"device":None, "label":None, "uuid":None, "fs_type":None}
- fields = shlex.split(line)
- if len(fields) < 2 or not fields[0].startswith("/dev/"):
- return {}
-
- # device is in [0] and the remainder are NAME=VALUE with possible spaces
- # Use the sda1 part of device "/dev/sda1:"
- device['device'] = fields[0][5:-1]
- for f in fields[1:]:
- if "=" in f:
- (key, val) = f.split("=", 1)
- if key == "TYPE":
- key = "fs_type"
- device[key.lower()] = val
- return device
-
-def select_iso():
- """ Let user select device and DD ISO on it.
-
- :returns: path to the selected ISO file and mountpoint to be unmounted
- or (None, None) if no ISO file is selected
- :rtype: (str, str)
- """
- header = " %-10s %-20s %-15s %s" % ("DEVICE", "TYPE", "LABEL", "UUID")
-
- iso_dev = DoRefresh
- while iso_dev is DoRefresh:
- try:
- _ret, out = run_cmd(["blkid"])
- except (OSError, RunCmdError):
- return (None, None)
-
- devices = []
- for line in out.splitlines():
- dev = parse_blkid(line)
- if dev:
- devices.append(DeviceInfo(**dev))
-
- iso_dev = selection_menu(devices,
- "Driver disk device selection\n" + header,
- str, multi_choice=False, refresh=True)
-
- if not iso_dev:
- return (None, None)
-
- mnt = "/media/DD-search"
- if not os.path.isdir(mnt):
- os.makedirs(mnt)
- if not mount_device("/dev/" + iso_dev.device, mnt):
- print("===Cannot mount the chosen device!===\n")
- return select_iso()
-
- # is this device a Driver Update Disc?
- if find_dd(mnt):
- umount(mnt) # BLUH. unmount it first so select_dd can mount it OK
- return ("/dev/" + iso_dev.device, None)
-
- # maybe it's a device containing multiple DUDs - let the user pick one
- isos = list()
- for dir_path, _dirs, files in os.walk(mnt):
- # trim the mount point path
- rel_dir = dir_path[len(mnt):]
-
- # and the starting "/" (if any)
- if rel_dir.startswith("/"):
- rel_dir = rel_dir[1:]
-
- isos += (os.path.join(rel_dir, iso_file)
- for iso_file in files if iso_file.endswith(".iso"))
-
- if not isos:
- print("===No ISO files found on %s!===\n" % iso_dev.device)
- umount(mnt)
- return select_iso()
- else:
- # mount writes out some mounting information, add blank line
- print
-
- # let user choose the ISO file
- dd_iso = selection_menu(isos, "Choose driver disk ISO file",
- lambda iso_file: iso_file,
- multi_choice=False)
-
- if not dd_iso:
- return (None, None)
-
- return (os.path.join(mnt, dd_iso), "/media/DD-search")
-
-def dd_scan(skip_dds=None, scan_dd_args=True, skip_device_menu=False):
- """ Scan the system for OEMDRV devices and and specified by dd=/dev/<device>
-
- :param skip_dds: devices to skip when checking for OEMDRV label
- :type skip_dds: set()
- :param scan_dd_args: Scan devices passed in /tmp/dd_args or dd_args_ks
- :type scan_dd_args: bool
- :returns: None
- """
- dd_todo = set(oemdrv_list())
-
- if skip_dds is None:
- skip_dds = set()
-
- if skip_dds:
- dd_todo.difference_update(skip_dds)
- if dd_todo:
- log.info("Found new OEMDRV device(s) - %s", ", ".join(dd_todo))
-
- if scan_dd_args:
- # Add the user specified devices
- dd_devs = get_dd_args()
- dd_devs = [dev for dev in dd_devs if dev not in ("dd", "inst.dd")]
- dd_todo.update(dd_devs)
- log.info("Checking devices %s", ", ".join(dd_todo))
-
- # Process each Driver Disk, checking for new disks after each one
- dd_finished = dd_load(dd_todo, skip_dds=skip_dds)
- skip_dds.update(dd_finished)
-
- # Skip interactive selection of an iso if OEMDRV was found
- if skip_dds or skip_device_menu or not is_interactive():
- return
-
- # Handle interactive driver selection
- mount_point = None
- while True:
- iso, mount_point = select_iso()
- if iso:
- if iso in skip_dds:
- skip_dds.remove(iso)
- dd_load(set([iso]), skip_dds=skip_dds)
- # NOTE: we intentionally do not add the newly-loaded device to
- # skip_dds - the user might (e.g.) swap DVDs and use /dev/sr0 twice
- umount(mount_point)
- else:
- break
-
-def dd_load(dd_todo, skip_dds=None):
- """ Process each Driver Disk, checking for new disks after each one.
- Return the set of devices that loaded stuff from.
-
- :param dd_todo: devices to load drivers from
- :type dd_todo: set
- :param skip_dds: devices to skip when checking for OEMDRV label
- :type skip_dds: set
- :returns: set of devices that have been loaded
- """
- if skip_dds is None:
- skip_dds = set()
-
- dd_finished = set()
- while dd_todo:
- device = dd_todo.pop()
- if device in skip_dds:
- continue
- log.info("Checking device %s", device)
- select_dd(device)
- dd_finished.add(device)
- new_oemdrv = set(oemdrv_list()).difference(dd_finished, dd_todo)
- if new_oemdrv:
- log.info("Found new OEMDRV device(s) - %s", ", ".join(new_oemdrv))
- dd_todo.update(new_oemdrv)
- return dd_finished
-
-if __name__ == '__main__':
- log.setLevel(logging.DEBUG)
- handler = SysLogHandler(address="/dev/log")
- log.addHandler(handler)
- handler = logging.StreamHandler()
- handler.setLevel(logging.INFO)
- formatter = logging.Formatter("DD: %(message)s")
- handler.setFormatter(formatter)
- log.addHandler(handler)
-
- if len(sys.argv) > 1:
- # Network driver source
- network_driver(sys.argv[1])
- elif os.path.exists("/tmp/DD-net/"):
- network_driver("/tmp/DD-net/")
- elif os.path.exists("/tmp/dd_args_ks"):
- # Kickstart driverdisk command, skip existing OEMDRV devices and
- # process cmdline dd entries. This will process any OEMDRV that
- # appear after loading the other drivers.
- skip_devices = set(oemdrv_list())
- dd_scan(skip_devices, skip_device_menu=True)
- else:
- # Process /tmp/dd_args and OEMDRV devices
- # Show device selection menu when inst.dd passed and no OEMDRV devices
- dd_scan()
-
- sys.exit(0)
-
diff --git a/dracut/driver-updates-genrules.sh b/dracut/driver-updates-genrules.sh
new file mode 100644
index 0000000..b20dbe2
--- /dev/null
+++ b/dracut/driver-updates-genrules.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+command -v wait_for_dd >/dev/null || . /lib/anaconda-lib.sh
+
+# Don't leave initqueue until we've finished with the requested dd stuff
+[ -f /tmp/dd_todo ] && wait_for_dd
+
+if [ -f /tmp/dd_interactive ]; then
+ initqueue --onetime --settled --name zz_dd_interactive \
+ systemctl start driver-updates@$(find_tty).service
+fi
+
+# Run driver-updates for LABEL=OEMDRV and any other requested disk
+for dd in LABEL=OEMDRV $(cat /tmp/dd_disk); do
+ when_diskdev_appears "$(disk_to_dev_path $dd)" \
+ driver-updates --disk $dd \$devnode
+done
+
+# force us to wait at least until we've settled at least once
+echo '> /tmp/settle.done' > $hookdir/initqueue/settled/settle_done.sh
+echo '[ -f /tmp/settle.done ]' > $hookdir/initqueue/finished/wait_for_settle.sh
+
+# NOTE: dd_net is handled by fetch-driver-net.sh
diff --git a/dracut/driver-updates-net.sh b/dracut/driver-updates-net.sh
deleted file mode 100755
index f8bed52..0000000
--- a/dracut/driver-updates-net.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-[ -e /tmp/DD-net ] || return 0
-
-command -v getarg >/dev/null || . /lib/dracut-lib.sh
-. /lib/anaconda-lib.sh
-
-if [ -n "$(ls /tmp/DD-net)" ]; then
- start_driver_update "Network Driver Update Disk"
- rm -rf /tmp/DD-net
-fi
diff --git a/dracut/driver-updates.sh b/dracut/driver-updates.sh
deleted file mode 100755
index fca0b83..0000000
--- a/dracut/driver-updates.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-# Determine if a Driver Update Disk is present, or inst.dd passed on the cmdline
-# and launch the driver update systemd service
-
-# load all modules
-udevadm trigger
-udevadm settle
-
-# Look for devices with the OEMDRV label
-blkid -t LABEL=OEMDRV > /dev/null
-blkid_rc=$?
-
-command -v getarg >/dev/null || . /lib/dracut-lib.sh
-dd_args="$(getargs dd= inst.dd=)"
-if [ -n "$dd_args" -o $blkid_rc -eq 0 ]; then
- command -v getarg >/dev/null || . /lib/dracut-lib.sh
- . /lib/anaconda-lib.sh
-
- # kludge to let kernel spit out some extra info w/o stomping on our UI
- sleep 5
- echo "$dd_args" > /tmp/dd_args
- start_driver_update "Driver Update Disk"
-fi
diff --git a/dracut/driver-updates at .service b/dracut/driver-updates at .service
index 210bcd5..e8c876e 100644
--- a/dracut/driver-updates at .service
+++ b/dracut/driver-updates at .service
@@ -1,15 +1,19 @@
[Unit]
Description=Driver Update Disk UI on %I
DefaultDependencies=no
+After=systemd-vconsole-setup.service
+Wants=systemd-vconsole-setup.service
Before=shutdown.target
[Service]
Type=oneshot
RemainAfterExit=no
-WorkingDirectory=/tmp
-Environment=LANG=en_US.UTF-8
-ExecStartPre=-/bin/plymouth quit
-ExecStart=/bin/driver-updates
+WorkingDirectory=/
+ExecStartPre=-/bin/plymouth --hide-splash
+ExecStartPre=-/bin/cp -n /proc/sys/kernel/printk /tmp/printk
+ExecStartPre=-/bin/dmesg -n 1
+ExecStart=/bin/driver-updates --interactive
+ExecStopPost=-/bin/cp /tmp/printk /proc/sys/kernel/printk
StandardInput=tty-force
StandardOutput=inherit
StandardError=inherit
diff --git a/dracut/fetch-driver-net.sh b/dracut/fetch-driver-net.sh
index 5adc39b..1632d00 100755
--- a/dracut/fetch-driver-net.sh
+++ b/dracut/fetch-driver-net.sh
@@ -5,26 +5,20 @@
# initqueue/online hook passes interface name as $1
netif="$1"
-# We already processed the dd_args - exit
-[ -e /tmp/dd_net.done ] && return 0
-
-command -v getarg >/dev/null || . /lib/dracut-lib.sh
-dd_args="$(getargs dd= inst.dd=)"
-[ -n "$dd_args" ] || return 0
+# No dd_net was requested - exit
+[ -f /tmp/dd_net ] || return 0
. /lib/url-lib.sh
-dd_repo=/tmp/DD-net/
-for dd in $dd_args; do
- case "${dd%%:*}" in
- http|https|ftp|nfs|nfs4)
- [ -e "$dd_repo" ] || mkdir -p $dd_repo
- info "Fetching driver from $dd"
- if driver=$(fetch_url "$dd"); then
- mv "$driver" $dd_repo
- else
- warn "Failed to fetch driver from $dd"
- fi
- ;;
- esac
-done
-echo > /tmp/dd_net.done
+
+while read dd; do
+ # If we already fetched this URL, skip it
+ grep -Fqx "$dd" /tmp/dd_net.done && continue
+ # Otherwise try to fetch it
+ info "Fetching driverdisk from $dd"
+ if driver=$(fetch_url "$dd"); then
+ echo "$dd" >> /tmp/dd_net.done # mark it done so we don't fetch it again
+ driver-updates --net "$dd" "$driver"
+ else
+ warn "Failed to fetch driver from $dd"
+ fi
+done < /tmp/dd_net
diff --git a/dracut/module-setup.sh b/dracut/module-setup.sh
index 287a61e..7e2df9f 100755
--- a/dracut/module-setup.sh
+++ b/dracut/module-setup.sh
@@ -46,16 +46,15 @@ install() {
inst "$moddir/parse-kickstart" "/sbin/parse-kickstart"
# Driver Update Disks
inst_hook cmdline 29 "$moddir/parse-anaconda-dd.sh"
+ inst_hook pre-trigger 55 "$moddir/driver-updates-genrules.sh"
inst_hook initqueue/online 20 "$moddir/fetch-driver-net.sh"
- inst_hook pre-trigger 40 "$moddir/driver-updates.sh"
- inst_hook pre-pivot 10 "$moddir/driver-updates-net.sh"
inst_hook pre-pivot 50 "$moddir/anaconda-depmod.sh"
- inst "$moddir/driver-updates" "/bin/driver-updates"
+ inst "$moddir/driver_updates.py" "/bin/driver-updates"
inst_simple "$moddir/driver-updates at .service" "/etc/systemd/system/driver-updates at .service"
# rpm configuration file (needed by dd_extract)
inst "/usr/lib/rpm/rpmrc"
# python deps for parse-kickstart. DOUBLE WOOOO
- $moddir/python-deps $moddir/parse-kickstart $moddir/driver-updates | while read dep; do
+ $moddir/python-deps $moddir/parse-kickstart $moddir/driver_updates.py | while read dep; do
case "$dep" in
*.so) inst_library $dep ;;
*.py) inst_simple $dep ;;
diff --git a/dracut/parse-anaconda-dd.sh b/dracut/parse-anaconda-dd.sh
index ebabd07..ee65501 100755
--- a/dracut/parse-anaconda-dd.sh
+++ b/dracut/parse-anaconda-dd.sh
@@ -1,18 +1,28 @@
#!/bin/bash
# parse-anaconda-dd.sh: handle driver update disk settings
-# no need to do this twice
-[ -f /tmp/dd_net.done ] && return
+# Creates the following files:
+# /tmp/dd_net: list of URLs to fetch
+# /tmp/dd_disk: list of disk devices to load from
+# /tmp/dd_interactive: "menu" if interactive mode requested
+# /tmp/dd_todo: concatenation of the above files
-command -v getarg >/dev/null || . /lib/dracut-lib.sh
+# clear everything to ensure idempotency
+rm -f /tmp/dd_interactive /tmp/dd_net /tmp/dd_disk /tmp/dd_todo
-# inst.dd: May provide a "URI" for the driver rpm (possibly more than one)
-dd_args="$(getargs dd= inst.dd=)"
-for dd in $dd_args; do
- case "${dd%%:*}" in
- http|https|ftp|nfs|nfs4)
- set_neednet
- break
- ;;
+# parse any dd/inst.dd args found
+for dd in $(getargs dd= inst.dd=); do
+ case "$dd" in
+ # plain 'dd'/'inst.dd': Engage interactive mode!
+ dd|inst.dd) echo menu > /tmp/dd_interactive ;;
+ # network URLs: add to dd_net
+ http:*|https:*|ftp:*|nfs:*|nfs4:*) echo $dd >> /tmp/dd_net ;;
+ # disks: strip "cdrom:" or "hd:" and add to dd_disk
+ cdrom:*|hd:*) echo ${dd#*:} >> /tmp/dd_disk ;;
+ # anything else is assumed to be a disk
+ *) echo $dd >> /tmp/dd_disk
esac
done
+
+# for convenience's sake, mash 'em all into one list
+cat /tmp/dd_net /tmp/dd_disk /tmp/dd_interactive > /tmp/dd_todo
diff --git a/tests/nosetests.sh b/tests/nosetests.sh
index e0b1c1e..6db3c3d 100755
--- a/tests/nosetests.sh
+++ b/tests/nosetests.sh
@@ -8,7 +8,7 @@ fi
# If no tests were selected, select all of them
if [ $# -eq 0 ]; then
- set -- "${top_srcdir}"/tests/*_tests
+ set -- "${top_srcdir}"/tests/*_tests "${top_srcdir}"/dracut/test_*.py
fi
exec nosetests -v --exclude=logpicker -a \!acceptance,\!slow "$@"
--
To view this commit on github, visit https://github.com/rhinstaller/anaconda/commit/faf2432db9f03b94ad1b096cea33103c1ceeae63
More information about the anaconda-patches
mailing list