multinic support: Add bootdev cmdline argument
authorPhilippe Seewer <philippe.seewer@bfh.ch>
Tue, 16 Jun 2009 18:50:31 +0000 (20:50 +0200)
committerPhilippe Seewer <philippe.seewer@bfh.ch>
Wed, 17 Jun 2009 06:37:40 +0000 (08:37 +0200)
This introduces a new cmdline argument bootdev, to support the case
where multiple nics need to be up before the netroot handler is called.
Cases involved might be bonding, iscsi multipathing, bonding, ...
This argument is required to decide which interface is the primary to
use for dhcp root-path, default gw, etc.

When multiple ip= items are present on the cmdline, the ip= parser
now enforces the presence of <dev> further demands that the new argument
bootdev contains the name of the primary interface. Configurtion if of
course still delegated to netroot but in is enhance to ensure that netroot
"waits" for all required interfaces to be up.

Example: root=dhcp ip=eth0:dhcp ip=client-ip:::netmask::eth1:off bootdev=eth0

First, the ip= cmdline parser ensures that all ip items contain a <dev> then
checks the ip items and checks as well that an ip= item for the given bootdev
was found.

When the first netroot starts, probably for eth1, it checks wheter interface
configuration for all interfaces is available. If not it exits. The second
start of netroot (eth0, which was a bit delayed because of dhcp) sees that
all interfaces are present, configures them and continues.

modules.d/40network/net-genrules.sh
modules.d/40network/netroot
modules.d/40network/parse-ip-opts.sh
modules.d/40network/write-ifcfg.sh

index 0768975..3d50435 100755 (executable)
@@ -15,12 +15,20 @@ fix_bootif() {
 # Write udev rules
 {
 
-# BOOTIF says everything, use only that one
+    # BOOTIF says everything, use only that one
     BOOTIF=$(getarg 'BOOTIF=')
     if [ -n "$BOOTIF" ] ; then
        BOOTIF=$(fix_bootif "$BOOTIF")
        printf 'ACTION=="add", SUBSYSTEM=="net", ATTR{address}=="%s", RUN+="/sbin/ifup $env{INTERFACE}"\n' "$BOOTIF"
-    else 
+
+    # If we have to handle multiple interfaces, handle only them.
+    elif [ -n "$IFACES" ] ; then
+       for iface in $IFACES ; do
+           printf 'ACTION=="add", SUBSYSTEM=="net", KERNEL=="%s", RUN+="/sbin/ifup $env{INTERFACE}"\n' "$iface"
+       done
+
+    # Default: We don't know the interface to use, handle all
+    else
        printf 'ACTION=="add", SUBSYSTEM=="net", RUN+="/sbin/ifup $env{INTERFACE}"\n'
     fi
 
index d860c6a..dce18b0 100755 (executable)
@@ -30,7 +30,17 @@ fi
 [ -d $NEWROOT/proc ] && exit 0
 [ -z "$netroot" ] && exit 1
 
+# Let's see if we have to wait for other interfaces
+# Note: exit works just fine, since the last interface to be 
+#       online'd should see all files
+[ -e "/tmp/net.ifaces" ] && read IFACES < /tmp/net.ifaces
+for iface in $IFACES ; do
+    [ -e /tmp/net.$iface.up ] || exit 1
+done
+
+# Set or override primary interface 
 netif=$1
+[ -e "/tmp/net.bootdev" ] && read netif < /tmp/net.bootdev
 
 # Figure out the handler for root=dhcp by recalling all netroot cmdline 
 # handlers
@@ -41,6 +51,14 @@ if [ "$netroot" = "dhcp" ] ; then
     # Load dhcp options
     [ -e /tmp/dhclient.$netif.dhcpopts ] && . /tmp/dhclient.$netif.dhcpopts
 
+    # If we have a specific bootdev with no dhcpoptions or empty root-path, 
+    # we die. Otherwise we just warn
+    if [ -z "$new_root_path" ] ; then
+       [ -n "$BOOTDEV" ] && die "No dhcp root-path received for '$BOOTDEV'"
+       warn "No dhcp root-path received for '$BOOTDEV' trying other interfaces if available"
+       exit 1
+    fi
+
     # Set netroot to new_root_path, so cmdline parsers don't call
     netroot=$new_root_path
 
@@ -63,7 +81,11 @@ if [ -z "$netroot" ] || [ ! -e "$handler" ] ; then
 fi
 
 # We're here, so we can assume that upping interfaces is now ok
-. /tmp/net.$netif.up
+[ -z "$IFACES" ] && IFACES="$netif"
+for iface in $IFACES ; do
+    . /tmp/net.$iface.up
+done
+
 [ -e /tmp/net.$netif.gw ]          && . /tmp/net.$netif.gw
 [ -e /tmp/net.$netif.hostname ]    && . /tmp/net.$netif.hostname
 [ -e /tmp/net.$netif.resolv.conf ] && cp -f /tmp/net.$netif.resolv.conf /etc/resolv.conf
@@ -75,17 +97,21 @@ source_all netroot
 # XXX other variables to export?
 if $handler $netif $netroot $NEWROOT; then
     # Network rootfs mount successful
-    [ -f /tmp/dhclient.$netif.lease ] &&    cp /tmp/dhclient.$netif.lease    /tmp/net.$netif.lease
-    [ -f /tmp/dhclient.$netif.dhcpopts ] && cp /tmp/dhclient.$netif.dhcpopts /tmp/net.$netif.dhcpopts
+    for iface in $IFACES ; do
+       [ -f /tmp/dhclient.$iface.lease ] &&    cp /tmp/dhclient.$iface.lease    /tmp/net.$iface.lease
+       [ -f /tmp/dhclient.$iface.dhcpopts ] && cp /tmp/dhclient.$iface.dhcpopts /tmp/net.$iface.dhcpopts
+    done
 
     # Save used netif for later use
-    echo $netif > /tmp/net.bootdev
+    [ ! -f /tmp/net.ifaces ] && echo $netif > /tmp/net.ifaces
 else 
     warn "Mounting root via '$netif' failed"
     # If we're trying with multiple interfaces, put that one down.
     # ip down/flush ensures that routeing info goes away as well
-    ip link set $netif down
-    ip addr flush dev $netif
-    echo "#empty" > /etc/resolv.conf
+    if [ -z "$BOOTDEV" ] ; then
+       ip link set $netif down
+       ip addr flush dev $netif
+       echo "#empty" > /etc/resolv.conf
+    fi
 fi
 exit 0
index 4a3de34..c748fdf 100755 (executable)
@@ -7,6 +7,10 @@
 #
 #      ip=<client-IP-number>:<server-id>:<gateway-IP-number>:<netmask>:<client-hostname>:<interface>:[dhcp|on|any|none|off]
 #
+# When supplying more than only ip= line, <interface> is mandatory and
+# bootdev= must contain the name of the primary interface to use for
+# routing,dns,dhcp-options,etc.
+#
 
 # Sadly there's no easy way to split ':' separated lines into variables
 ip_to_var() {
@@ -47,6 +51,22 @@ if [ -n "$netroot" ] && [ -z "$(getarg ip=)" ] ; then
     return;
 fi
 
+# Count ip= lines to decide whether we need bootdev= or not
+if [ -z "$NEEDBOOTDEV" ] ; then
+    [ "$CMDLINE" ] || read CMDLINE < /proc/cmdline
+    local count=0
+    for p in $CMDLINE; do
+       [ "${p%%=*}" = "ip" ] && count=$(( $count + 1 ))
+    done
+    [ $count -gt 1 ] && NEEDBOOTDEV=1
+fi
+
+# If needed, check if bootdev= contains anything usable
+if [ -n "$NEEDBOOTDEV" ] ; then
+    BOOTDEV=$(getarg bootdev=) || die "Please supply bootdev argument for multiple ip= lines"
+    [ -z "$BOOTDEV" ] && die "Bootdev argument is empty"
+fi
+
 # Check ip= lines
 # XXX Would be nice if we could errorcheck ip addresses here as well
 [ "$CMDLINE" ] || read CMDLINE < /proc/cmdline
@@ -55,6 +75,9 @@ for p in $CMDLINE; do
 
     ip_to_var ${p#ip=}
 
+    # We need to have an ip= line for the specified bootdev
+    [ -n "$NEEDBOOTDEV" ] && [ "$dev" = "$BOOTDEV" ] && BOOTDEVOK=1
+
     # Empty autoconf defaults to 'dhcp'
     if [ -z "$autoconf" ] ; then
        warn "Empty autoconf values default to dhcp"
@@ -72,34 +95,52 @@ for p in $CMDLINE; do
                die "Sorry, automatic calculation of netmask is not yet supported"
            ;;
        dhcp|on|any) \
+           [ -n "$NEEDBOOTDEV" ] && [ -z "$dev" ] && \
+               die "Sorry, '$p' does not make sense for multiple interface configurations"
            [ -n "$ip" ] && \
                die "For argument '$p'\nSorry, setting client-ip does not make sense for '$autoconf'"
            ;;
        *) die "For argument '$p'\nSorry, unknown value '$autoconf'";;
     esac
 
-    # We don't like duplicate device configs
     if [ -n "$dev" ] ; then
+        # We don't like duplicate device configs
        if [ -n "$IFACES" ] ; then
            for i in $IFACES ; do
                [ "$dev" = "$i" ] && die "For argument '$p'\nDuplication configurations for '$dev'"
            done
        fi
+       # IFACES list for later use
        IFACES="$IFACES $dev"
     fi
 
-    # Do we need DHCP? (It's simpler to check for a set ip. Checks above ensure that if
-    # ip is there, we're static) 
-    [ -n "$NEEDDHCP" ] && [ -z "$ip" ] && DHCPOK="1"
-
-    # Do we need srv OR dhcp?
-    if [ -n "$DHCPORSERVER" ] ; then
-       [ -n "$DHCPOK" ] && SRVOK="1"
-       [ -n "$srv" ] && SRVOK="1"
+    # Small optimization for udev rules
+    [ -z "$NEEDBOOTDEV" ] && [ -n "$dev" ] && BOOTDEV=$dev
+
+    # Do we need to check for specific options?
+    if [ -n "$NEEDDHCP" ] || [ -n "$DHCPORSERVER" ] ; then
+       # Correct device? (Empty is ok as well)
+       [ "$dev" = "$BOOTDEV" ] || continue
+       # Server-ip is there?
+       [ -n "$DHCPORSERVER" ] && [ -n "$srv" ] && continue
+       # dhcp? (It's simpler to check for a set ip. Checks above ensure that if
+       # ip is there, we're static
+       [ -z "$ip" ] && continue
+       # Not good!
+       die "Server-ip or dhcp for netboot needed, but current arguments say otherwise"
     fi
 
 done
 
-[ -n "$NEEDDHCP" ] && [ -z "$DHCPOK" ] && die "Server-ip or dhcp for netboot needed, but current arguments say otherwise"
+# This ensures that BOOTDEV is always first in IFACES
+if [ -n "$BOOTDEV" ] && [ -n "$IFACES" ] ; then 
+    IFACES="${IFACES%$BOOTDEV*} ${IFACES#*$BOOTDEV}"
+    IFACES="$BOOTDEV $IFACES"
+fi
+
+# Store BOOTDEV and IFACES for later use
+[ -n "$BOOTDEV" ] && echo $BOOTDEV > /tmp/net.bootdev
+[ -n "$IFACES" ]  && echo $IFACES > /tmp/net.ifaces
 
-[ -n "$DHCPORSERVER" ] && [ -z "$SRVOK" ] && die "Server-ip or dhcp for netboot needed, but current arguments say otherwise"
+# We need a ip= line for the configured bootdev= 
+[ -n "$NEEDBOOTDEV" ] && [ -z "$BOOTDEVOK" ] && die "Bootdev Argument '$BOOTDEV' not found"
\ No newline at end of file
index e5f25d5..c408076 100755 (executable)
@@ -1,23 +1,25 @@
 #!/bin/sh
 
 # Don't write anything if we don't know our bootdev
-[ -f /tmp/net.bootdev ] || return 1
+[ -f /tmp/net.ifaces ] || return 1
 
-read netif < /tmp/net.bootdev
+read IFACES < /tmp/net.ifaces
 
-cat /sys/class/net/$netif/address > /tmp/net.$netif.hwaddr
-echo "# Generated by dracut initrd" > /tmp/net.$netif.ifcfg
-echo "DEVICE=$netif" >> /tmp/net.$netif.ifcfg
-echo "HWADDR=$(cat /sys/class/net/$netif/address)" >> /tmp/net.$netif.ifcfg
-echo "TYPE=Ethernet" >> /tmp/net.$netif.ifcfg
-echo "ONBOOT=yes" >> /tmp/net.$netif.ifcfg
-if [ -f /tmp/net.$netif.lease ]; then
-    echo "BOOTPROTO=dhcp" >> /tmp/net.$netif.ifcfg
-else
-    echo "BOOTPROTO=none" >> /tmp/net.$netif.ifcfg
-    # If we've booted with static ip= lines, the override file is there
-    . /tmp/net.$netif.override 
-    echo "IPADDR=$ip" >> /tmp/net.$netif.ifcfg
-    echo "NETMASK=$mask" >> /tmp/net.$netif.ifcfg
-    [ -n "$gw" ] && echo "GATEWAY=$gw" >> /tmp/net.$netif.ifcfg
-fi
+for netif in $IFACES ; do
+    cat /sys/class/net/$netif/address > /tmp/net.$netif.hwaddr
+    echo "# Generated by dracut initrd" > /tmp/net.$netif.ifcfg
+    echo "DEVICE=$netif" >> /tmp/net.$netif.ifcfg
+    echo "HWADDR=$(cat /sys/class/net/$netif/address)" >> /tmp/net.$netif.ifcfg
+    echo "TYPE=Ethernet" >> /tmp/net.$netif.ifcfg
+    echo "ONBOOT=yes" >> /tmp/net.$netif.ifcfg
+    if [ -f /tmp/net.$netif.lease ]; then
+       echo "BOOTPROTO=dhcp" >> /tmp/net.$netif.ifcfg
+    else
+       echo "BOOTPROTO=none" >> /tmp/net.$netif.ifcfg
+        # If we've booted with static ip= lines, the override file is there
+       . /tmp/net.$netif.override 
+       echo "IPADDR=$ip" >> /tmp/net.$netif.ifcfg
+       echo "NETMASK=$mask" >> /tmp/net.$netif.ifcfg
+       [ -n "$gw" ] && echo "GATEWAY=$gw" >> /tmp/net.$netif.ifcfg
+    fi
+done