[master 6/6] dd-refactor: dracut + build bits

wgwoods installerbot-noreply at redhat.com
Tue Jul 7 18:28:44 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             | 840 --------------------------------------
 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(+), 956 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 0b1679c..ab8590c 100755
--- a/dracut/anaconda-lib.sh
+++ b/dracut/anaconda-lib.sh
@@ -246,39 +246,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
@@ -304,16 +286,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 e16fe1d..0000000
--- a/dracut/driver-updates
+++ /dev/null
@@ -1,840 +0,0 @@
-#!/usr/bin/python3
-#
-# 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
-import readline # pylint:disable=unused-import
-
-log = logging.getLogger("DD")
-
-import functools
-def eintr_retry_call(func, *args, **kwargs):
-    """Retry an interruptible system call if interrupted."""
-    while True:
-        try:
-            return func(*args, **kwargs)
-        except InterruptedError:
-            continue
-open = functools.partial(eintr_retry_call, open) # pylint: disable=redefined-builtin
-
-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:
-        log.debug(" ".join(cmd))
-        proc = subprocess.Popen(cmd, universal_newlines=True,
-                                stdout=subprocess.PIPE,
-                                stderr=subprocess.DEVNULL)
-        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(x for x in dd_args if x.split(":")[0].lower() not in net_protocols)
-    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 range(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 range(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 = 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:
-        for driver in drivers:
-            setattr(driver, "selected", True)
-
-    # Copy the repository for Anaconda to use during install
-    copy_repo(dd_path, "/updates/run/install/DD-")
-
-    extracted = []
-
-    for driver in (d for d in drivers if d.selected):
-        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 ee92f69..a691365 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
-    PYTHONHASHSEED=42 $moddir/python-deps $moddir/parse-kickstart $moddir/driver-updates | while read dep; do
+    PYTHONHASHSEED=42 $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 de2c111..ec7e1df 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-3.4 -v --exclude=logpicker -a \!acceptance,\!slow "$@"


-- 
To view this commit on github, visit https://github.com/rhinstaller/anaconda/commit/e4037565d25000a3f63170aff462d4b305eb14bc


More information about the anaconda-patches mailing list