diff --git a/common/lib/Warewulf/Module/Cli/Node.pm b/common/lib/Warewulf/Module/Cli/Node.pm index 75bd883e..51fa2a59 100644 --- a/common/lib/Warewulf/Module/Cli/Node.pm +++ b/common/lib/Warewulf/Module/Cli/Node.pm @@ -96,6 +96,8 @@ help() $h .= " -N, --network Set network address of netdev\n"; $h .= " -G, --gateway Set gateway of given netdev\n"; $h .= " -H, --hwaddr Set hardware/MAC address\n"; + $h .= " -b, --bonddevs Set bonding slave devices\n"; + $h .= " -B, --bondmode Set bonding mode\n"; $h .= " -f, --fqdn Set FQDN of given netdev\n"; $h .= " -m, --mtu Set MTU of given netdev\n"; $h .= " -p, --hwprefix Specify a prefix for hardware/MAC address of a given netdev\n"; @@ -178,7 +180,9 @@ exec() my $opt_lookup = "name"; my $opt_netdev = $config_defaults->get("netdev"); my $opt_netrename; - my $opt_hwaddr; + my @opt_hwaddrs; + my @opt_bonddevs; + my $opt_bondmode; my $opt_hwprefix; my $opt_ipaddr; my $opt_netmask; @@ -219,7 +223,7 @@ exec() 'D|netdev=s' => \$opt_netdev, 'netdel' => \$opt_devremove, 'netrename=s' => \$opt_netrename, - 'H|hwaddr=s' => \$opt_hwaddr, + 'H|hwaddr=s' => \@opt_hwaddrs, 'p|hwprefix=s' => \$opt_hwprefix, 'I|ipaddr=s' => \$opt_ipaddr, 'N|network=s' => \$opt_network, @@ -230,6 +234,8 @@ exec() 'a|arch=s' => \$opt_arch, 'f|fqdn=s' => \$opt_fqdn, 'm|mtu=s' => \$opt_mtu, + 'b|bonddevs=s' => \@opt_bonddevs, + 'B|bondmode=s' => \$opt_bondmode, 'd|domain=s' => \$opt_domain, 'l|lookup=s' => \$opt_lookup, 'e|enabled=s' => \$opt_enabled, @@ -341,7 +347,7 @@ exec() printf("%15s: %-16s = %s\n", $nodename, "GROUPS", join(",", $o->groups()) || "UNDEF"); printf("%15s: %-16s = %s\n", $nodename, "ENABLED", ($o->enabled()) ? "TRUE" : "FALSE"); foreach my $devname (sort($o->netdevs_list())) { - printf("%15s: %-16s = %s\n", $nodename, "$devname.HWADDR", $o->hwaddr($devname) || "UNDEF"); + printf("%15s: %-16s = %s\n", $nodename, "$devname.HWADDR", $o->hwaddr($devname) ? join(',', $o->hwaddr($devname)) : "UNDEF"); printf("%15s: %-16s = %s\n", $nodename, "$devname.HWPREFIX", $o->hwprefix($devname) || "UNDEF"); printf("%15s: %-16s = %s\n", $nodename, "$devname.IPADDR", $o->ipaddr($devname) || "UNDEF"); printf("%15s: %-16s = %s\n", $nodename, "$devname.NETMASK", $o->netmask($devname) || "UNDEF"); @@ -349,6 +355,8 @@ exec() printf("%15s: %-16s = %s\n", $nodename, "$devname.GATEWAY", $o->gateway($devname) || "UNDEF"); printf("%15s: %-16s = %s\n", $nodename, "$devname.MTU", $o->mtu($devname) || "UNDEF"); printf("%15s: %-16s = %s\n", $nodename, "$devname.FQDN", $o->fqdn($devname) || "UNDEF"); + printf("%15s: %-16s = %s\n", $nodename, "$devname.BONDDEVS", $o->bonddevs($devname) ? join(',', $o->bonddevs($devname)) : "UNDEF"); + printf("%15s: %-16s = %s\n", $nodename, "$devname.BONDMODE", $o->bondmode($devname) || "UNDEF"); } $return_count++; } @@ -398,10 +406,15 @@ exec() push(@changes, sprintf("%8s: %-20s = %s\n", "SET", "$opt_netdev.NAME", $opt_netrename)); } else { - if ($opt_hwaddr) { + if (@opt_hwaddrs) { if ($objSet->count() == 1) { - $opt_hwaddr = lc($opt_hwaddr); - if ($opt_hwaddr =~ /^((?:[0-9a-f]{2}:){5,7}[0-9a-f]{2})$/) { + @opt_hwaddrs = split(/,/, join(',', @opt_hwaddrs)); + @opt_hwaddrs = map { lc $_ } @opt_hwaddrs; + + if (grep { $_ !~ /^((?:[0-9a-f]{2}:){5,7}[0-9a-f]{2}|undef)$/ } @opt_hwaddrs) { + &eprint("Option 'hwaddrs' has invalid characters\n"); + } + else { my $show_changes; foreach my $o ($objSet->get_list()) { my $nodename = $o->name(); @@ -414,15 +427,13 @@ exec() return undef; } } - $o->hwaddr($opt_netdev, $1); + $o->hwaddr($opt_netdev, @opt_hwaddrs); $persist_count++; $show_changes = 1; } if ($show_changes) { - push(@changes, sprintf("%8s: %-20s = %s\n", "SET", "$opt_netdev.HWADDR", $opt_hwaddr)); + push(@changes, sprintf("%8s: %-20s = %s\n", "SET", "$opt_netdev.HWADDR", join(',', @opt_hwaddrs))); } - } else { - &eprint("Option 'hwaddr' has invalid characters\n"); } } else { &eprint("Can not set HWADDR on more then 1 node!\n"); @@ -606,6 +617,60 @@ exec() &eprint("Option 'mtu' has invalid characters\n"); } } + if (@opt_bonddevs) { + @opt_bonddevs = split(/,/, join(',', @opt_bonddevs)); + + if (grep { $_ !~ /^([a-z0-9]+)$/ } @opt_bonddevs) { + &eprint("Option 'bonddevs' has invalid characters\n"); + } + else { + my $show_changes; + foreach my $o ($objSet->get_list()) { + my $nodename = $o->name(); + if (! $opt_netdev) { + my @devs = $o->netdevs_list(); + if (scalar(@devs) == 1) { + $opt_netdev = shift(@devs); + } else { + &eprint("Option --bonddevs requires the --netdev option for: $nodename\n"); + return undef; + } + } + $o->bonddevs($opt_netdev, @opt_bonddevs); + $persist_count++; + $show_changes = 1; + } + if ($show_changes) { + push(@changes, sprintf("%8s: %-20s = %s\n", "SET", "$opt_netdev.BONDDEVS", join(',', @opt_bonddevs))); + } + } + } + if ($opt_bondmode) { + if ($opt_bondmode =~ /^(.+)$/) { + my $show_changes; + foreach my $o ($objSet->get_list()) { + my $nodename = $o->name(); + if (! $opt_netdev) { + my @devs = $o->netdevs_list(); + if (scalar(@devs) == 1) { + $opt_netdev = shift(@devs); + } else { + &eprint("Option --bondmode requires the --netdev option for: $nodename\n"); + return undef; + } + } + $o->bondmode($opt_netdev, $1); + $persist_count++; + $show_changes = 1; + } + if ($show_changes) { + push(@changes, sprintf("%8s: %-20s = %s\n", "SET", "$opt_netdev.BONDMODE", $opt_bondmode)); + } + } + else { + &eprint("Option 'bondmode' has invalid characters\n"); + } + } } if ($opt_name) { diff --git a/common/lib/Warewulf/Node.pm b/common/lib/Warewulf/Node.pm index 5d12164a..69162e25 100644 --- a/common/lib/Warewulf/Node.pm +++ b/common/lib/Warewulf/Node.pm @@ -445,14 +445,14 @@ netdel() $netdev = $self->netdevs($devname); $nodename = $self->nodename() || "UNDEF"; if ($netdev) { - my $hwaddr = $netdev->get("hwaddr"); + my @hwaddrs = $netdev->get("hwaddr"); my $ipaddr = $netdev->get("ipaddr"); my $hwprefix = $netdev->get("hwprefix"); &dprint("Object $nodename del netdev $devname\n"); $self->netdevs()->del($netdev); - if ($hwaddr) { - $self->del("_hwaddr", $hwaddr); + if (@hwaddrs) { + $self->del("_hwaddr", @hwaddrs); } if ($ipaddr) { $self->del("_ipaddr", $ipaddr); @@ -532,12 +532,11 @@ Get or set the hwaddr for the network device named I<$devname>. sub hwaddr() { - my ($self, $devname, $new_hwaddr) = @_; + my ($self, $devname, @new_hwaddr) = @_; return $self->update_netdev_member($devname, "hwaddr", "_hwaddr", - (((scalar(@_) >= 3) && (!defined($new_hwaddr))) ? ("__UNDEF") - : ((defined($new_hwaddr)) ? (lc($new_hwaddr)) : (undef))), - qr/^((?:[0-9a-f]{2}:){5,7}[0-9a-f]{2})$/); + qr/^((?:[0-9a-f]{2}:){5,7}[0-9a-f]{2})$/, + ((scalar(@_) >= 3) && (!defined $new_hwaddr[0])) ? ("__UNDEF") : (@new_hwaddr)); } =item hwprefix_list() @@ -566,9 +565,9 @@ hwprefix() my ($self, $devname, $new_hwprefix) = @_; return $self->update_netdev_member($devname, "hwprefix", "_hwprefix", - (((scalar(@_) >= 3) && (!defined($new_hwprefix))) ? ("__UNDEF") - : ((defined($new_hwprefix)) ? (lc($new_hwprefix)) : (undef))), - qr/^((?:[0-9a-f]{2}:){11}[0-9a-f]{2})$/); + qr/^((?:[0-9a-f]{2}:){11}[0-9a-f]{2})$/, + ((scalar(@_) >= 3) && (!defined($new_hwprefix))) ? ("__UNDEF") + : ((defined($new_hwprefix)) ? (lc($new_hwprefix)) : (undef))); } =item ipaddr_list() @@ -598,8 +597,8 @@ ipaddr() my ($self, $devname, $new_ipaddr) = @_; return $self->update_netdev_member($devname, "ipaddr", "_ipaddr", - (((scalar(@_) >= 3) && (!defined($new_ipaddr))) ? ("__UNDEF") : ($new_ipaddr)), - qr/^(\d+\.\d+\.\d+\.\d+)$/); + qr/^(\d+\.\d+\.\d+\.\d+)$/, + ((scalar(@_) >= 3) && (!defined($new_ipaddr))) ? ("__UNDEF") : ($new_ipaddr)); } @@ -615,8 +614,8 @@ netmask() my ($self, $devname, $new_netmask) = @_; return $self->update_netdev_member($devname, "netmask", "", - (((scalar(@_) >= 3) && (!defined($new_netmask))) ? ("__UNDEF") : ($new_netmask)), - qr/^(\d+\.\d+\.\d+\.\d+)$/); + qr/^(\d+\.\d+\.\d+\.\d+)$/, + ((scalar(@_) >= 3) && (!defined($new_netmask))) ? ("__UNDEF") : ($new_netmask)); } @@ -632,8 +631,8 @@ network() my ($self, $devname, $new_network) = @_; return $self->update_netdev_member($devname, "network", "", - (((scalar(@_) >= 3) && (!defined($new_network))) ? ("__UNDEF") : ($new_network)), - qr/^(\d+\.\d+\.\d+\.\d+)$/); + qr/^(\d+\.\d+\.\d+\.\d+)$/, + ((scalar(@_) >= 3) && (!defined($new_network))) ? ("__UNDEF") : ($new_network)); } @@ -649,8 +648,8 @@ gateway() my ($self, $devname, $new_gateway) = @_; return $self->update_netdev_member($devname, "gateway", "", - (((scalar(@_) >= 3) && (!defined($new_gateway))) ? ("__UNDEF") : ($new_gateway)), - qr/^(\d+\.\d+\.\d+\.\d+|UNDEF|undef)$/); + qr/^(\d+\.\d+\.\d+\.\d+|UNDEF|undef)$/, + ((scalar(@_) >= 3) && (!defined($new_gateway))) ? ("__UNDEF") : ($new_gateway)); } @@ -666,8 +665,8 @@ fqdn() my ($self, $devname, $new_fqdn) = @_; return $self->update_netdev_member($devname, "fqdn", "", - (((scalar(@_) >= 3) && (!defined($new_fqdn))) ? ("__UNDEF") : ($new_fqdn)), - qr/^([a-zA-Z0-9\-\.\_]+)$/); + qr/^([a-zA-Z0-9\-\.\_]+)$/, + ((scalar(@_) >= 3) && (!defined($new_fqdn))) ? ("__UNDEF") : ($new_fqdn)); } @@ -683,8 +682,40 @@ mtu() my ($self, $devname, $new_mtu) = @_; return $self->update_netdev_member($devname, "mtu", "", - (((scalar(@_) >= 3) && (!defined($new_mtu))) ? ("__UNDEF") : ($new_mtu)), - qr/^([0-9]+)$/); + qr/^([0-9]+)$/, + ((scalar(@_) >= 3) && (!defined($new_mtu))) ? ("__UNDEF") : ($new_mtu)); +} + +=item bonddevs($devname, [ $value ]) + +Get or set the bond devices for the network device named I<$devname>. + +=cut + +sub +bonddevs() +{ + my ($self, $devname, @new_bonddevs) = @_; + + return $self->update_netdev_member($devname, "bonddevs", "", + qr/^([a-z0-9]+)$/, + ((scalar(@_) >= 3) && (!@new_bonddevs)) ? ("__UNDEF") : (@new_bonddevs)); +} + +=item bondmode($devname, [ $value ]) + +Get or set the bonding mode for the network device named I<$devname>. + +=cut + +sub +bondmode() +{ + my ($self, $devname, $new_bondmode) = @_; + + return $self->update_netdev_member($devname, "bondmode", "", + qr/^(.+)$/, + ((scalar(@_) >= 3) && (!defined($new_bondmode))) ? ("__UNDEF") : ($new_bondmode)); } =item enabled([$value]) @@ -753,7 +784,7 @@ validate_netdev_name() sub update_netdev_member() { - my ($self, $devname, $member, $tracker, $new_value, $validator) = @_; + my ($self, $devname, $member, $tracker, $validator, @new_values) = @_; my ($netdev, $nodename); $nodename = $self->nodename() || "UNDEF"; @@ -773,29 +804,34 @@ update_netdev_member() } $netdev = $self->netdev_get_add($devname); - if (defined($new_value)) { - my $old_value = $netdev->get($member) || ""; + if (@new_values && scalar @new_values && defined $new_values[0]) { + my @old_values = $netdev->get($member); + @old_values = () unless (@old_values); - if ($new_value eq "__UNDEF" || lc($new_value) eq "undef" ) { - $new_value = undef; - &dprint("Removing $nodename.$devname.$member (was \"$old_value\")\n"); + if (grep { $_ eq "__UNDEF" || lc($_) eq "undef" } @new_values) { + @new_values = undef; + &dprint("Removing $nodename.$devname.$member (was \"@old_values\")\n"); } else { - if ($new_value =~ $validator) { - $new_value = $1; - } else { - &eprint("Invalid value for $nodename.$devname.$member: \"$new_value\"\n"); - return undef; + if ($validator) { + foreach my $new_value (@new_values) { + if ($new_value =~ $validator) { + $new_value = $1; + } else { + &eprint("Invalid value for $nodename.$devname.$member: \"@new_values\"\n"); + return undef; + } + } } - &dprint("Updating object $nodename.$devname.$member: \"$old_value\" -> \"$new_value\"\n"); + &dprint("Updating object $nodename.$devname.$member: \"@old_values\" -> \"@new_values\"\n"); } - $netdev->set($member, $new_value); + $netdev->set($member, @new_values); if ($tracker) { - if ($old_value) { + if (@old_values) { # Delete the previous member if exists... - $self->del($tracker, $old_value); + $self->del($tracker, @old_values); } - if (defined($new_value)) { - $self->add($tracker, $new_value); + if (@new_values) { + $self->add($tracker, @new_values); } else { return undef; } diff --git a/provision/initramfs/functions b/provision/initramfs/functions index 67e1014f..526cc400 100644 --- a/provision/initramfs/functions +++ b/provision/initramfs/functions @@ -40,6 +40,12 @@ export WWDNS WWNETDEV=`sed -e '/ wwnetdev=/!d;s/.*wwnetdev=\([^ ]*\).*/\1/' /proc/cmdline` export WWNETDEV +WWBONDDEVS=`sed -e '/ wwbonddevs=/!d;s/.*wwbonddevs=\([^ ]*\).*/\1/' /proc/cmdline` +export WWBONDDEVS + +WWBONDMODE=`sed -e '/ wwbondmode=/!d;s/.*wwbondmode=\([^ ]*\).*/\1/' /proc/cmdline` +export WWBONDMODE + WWMTU=`sed -e '/ wwmtu=/!d;s/.*wwmtu=\([^ ]*\).*/\1/' /proc/cmdline` export WWMTU diff --git a/provision/initramfs/init b/provision/initramfs/init index c5fc4b90..810f4d8c 100644 --- a/provision/initramfs/init +++ b/provision/initramfs/init @@ -70,6 +70,160 @@ for module in `/sbin/detect`; do done wwsuccess +bondup() { + DEVICE=$1 + BONDDEVS=`echo "$2" | sed -e "s/,/ /g"` + HWADDR=$3 + + RETVAL=1 + + if [ -z "$WWBONDMODE" ]; then + WWBONDMODE="balance-rr" + fi + + msg_white "Configuring bonding interfaces: $DEVICE $WWBONDMODE ($BONDDEVS)\n" + + msg_gray "Loading bonding module\n" +# dont automatically created devices + modprobe bonding max_bonds=0 >/dev/null + + for slave in $BONDDEVS; do + ip addr flush dev $slave >/dev/null +# ip link set dev $slave nomaster down >/dev/null 2>&1 + done + + msg_gray "Bringing up interface $DEVICE" + + ip link add $DEVICE type bond >/dev/null +# busybox "ip" command does not support bonding modes + echo "$WWBONDMODE" > /sys/class/net/$DEVICE/bonding/mode + ip link set dev $DEVICE up + + if ifenslave $DEVICE $BONDDEVS >/dev/null ; then + wwsuccess + + if [ -n "$WWMTU" ]; then + msg_white "Setting the MTU to: $WWMTU " + if ip link set mtu $WWMTU dev $DEVICE >/dev/null; then + wwsuccess + else + wwfailure + fi + fi + +# give the device some time to come up + sleep 5 + + if [ -n "$WWIPADDR" -a -n "$WWNETMASK" -a -n "$WWMASTER" ]; then + msg_white "Configuring $DEVICE ($BONDDEVS) statically: " + msg_gray "($WWIPADDR/$WWNETMASK)" + + if ip address add $WWIPADDR/$WWNETMASK broadcast + dev $DEVICE; then + wwsuccess + if [ -n "$WWGATEWAY" ]; then + msg_white "Configuring gateway: " + msg_gray "($WWGATEWAY)" + if ip route add default via $WWGATEWAY; then + wwsuccess + else + wwfailure + fi + fi + + if [ -z "$WWNETCFGFILE" ]; then + msg_white "Creating network initialization files: " + msg_gray "($DEVICE)" + # Debian based /etc/network/interfaces + echo "# This was created by the Warewulf bootstrap" > /tmp/interfaces + echo "auto lo" >> /tmp/interfaces + echo "iface lo inet loopback" >> /tmp/interfaces + echo '' >> /tmp/interfaces + + for slave in $BONDDEVS; do + echo "auto $slave" >> /tmp/interfaces + echo "iface $slave inet manual" >> /tmp/interfaces + echo " bond-master $DEVICE" >> /tmp/interfaces + echo '' >> /tmp/interfaces + done + echo "auto $DEVICE" >> /tmp/interfaces + echo "iface $DEVICE inet static" >> /tmp/interfaces + echo " address $WWIPADDR" >> /tmp/interfaces + echo " netmask $WWNETMASK" >> /tmp/interfaces + if [ -n "$WWGATEWAY" ]; then + echo " gateway $WWGATEWAY" >> /tmp/interfaces + fi + if [ -n "$HWADDR" ]; then + echo " hwaddress ether $HWADDR" >> /tmp/interfaces + fi + if [ -n "$WWMTU" ]; then + echo " mtu $WWMTU" >> /tmp/interfaces + fi + if [ -n "$WWBONDMODE" ]; then + echo " bond-mode $WWBONDMODE" >> /tmp/interfaces + fi + echo " bond-slaves $BONDDEVS" >> /tmp/interfaces + + # RHEL based ifcfg + for slave in $BONDDEVS; do + echo "# This was created by the Warewulf bootstrap" > /tmp/ifcfg-$slave + echo "DEVICE=$slave" >> /tmp/ifcfg-$slave + echo "BOOTPROTO=none" >> /tmp/ifcfg-$slave + echo "ONBOOT=yes" >> /tmp/ifcfg-$slave + echo "MASTER=$DEVICE" >> /tmp/ifcfg-$slave + echo "SLAVE=yes" >> /tmp/ifcfg-$slave + done + + echo "# This was created by the Warewulf bootstrap" > /tmp/ifcfg-$DEVICE + echo "DEVICE=$DEVICE" >> /tmp/ifcfg-$DEVICE + echo "TYPE=Bond" >> /tmp/ifcfg-$DEVICE + echo "BONDING_MASTER=yes" >> /tmp/ifcfg-$DEVICE + echo "BOOTPROTO=static" >> /tmp/ifcfg-$DEVICE + echo "ONBOOT=yes" >> /tmp/ifcfg-$DEVICE + echo "IPADDR=$WWIPADDR" >> /tmp/ifcfg-$DEVICE + echo "NETMASK=$WWNETMASK" >> /tmp/ifcfg-$DEVICE + if [ -n "$WWGATEWAY" ]; then + echo "GATEWAY=$WWGATEWAY" >> /tmp/ifcfg-$DEVICE + fi + if [ -n "$HWADDR" ]; then + echo "HWADDR=$HWADDR" >> /tmp/ifcfg-$DEVICE + fi + if [ -n "$WWMTU" ]; then + echo "MTU=$WWMTU" >> /tmp/ifcfg-$DEVICE + fi + if [ -n "$WWBONDMODE" ]; then + echo "BONDING_OPTS=\"mode=$WWBONDMODE\"" >> /tmp/ifcfg-$DEVICE + fi + wwsuccess + fi + + COUNT=0 + msg_white "Trying to reach the master node at $WWMASTER " + while [ $COUNT -le $WWNETRETRY ]; do + if ping -c 1 $WWMASTER >/dev/null 2>&1; then + wwsuccess + WWBONDINGDEV=$DEVICE + return 0 + fi + msg_white "." + COUNT=`expr $COUNT + 1` + sleep 1 + done + wwfailure + else + wwfailure + fi + fi + else + wwfailure + fi + + # If we have gotten here, give up for this device, and bring device down + # just in case its present. + # ip link del also releases the slaves + ip link del dev $DEVICE >/dev/null + return 255 +} + ifup() { DEVICE=$1 HWADDR=$2 @@ -231,20 +385,36 @@ else wwfailure fi -# First try to find interface based on WWHWADDR -for i in `echo /sys/class/net/* | xargs -n1 /usr/bin/basename | grep -v lo`; do - HWADDR=`cat /sys/class/net/$i/address` - # Use GUID if IB - if [ ${#HWADDR} -eq 59 ]; then - HWADDR=`expr substr $HWADDR 37 23` +# First try to setup bonding if requested +if [ -n "$WWBONDDEVS" ]; then + if bondup $WWNETDEV $WWBONDDEVS $WWHWADDR; then + echo "$WWNETDEV" > /tmp/wwdev fi - if [ $HWADDR == $WWHWADDR ]; then - if ifup $i $WWHWADDR $WWNETDEV; then - echo "$i" > /tmp/wwdev - fi - break +fi + +# Else try to setup the interface as specified via kernel args +if [ ! -f /tmp/wwdev ]; then + if ifup $WWNETDEV $WWHWADDR $WWNETDEV; then + echo "$WWNETDEV" > /tmp/wwdev fi -done +fi + +# Else try to find interface based on WWHWADDR +if [ ! -f /tmp/wwdev ]; then + for i in `echo /sys/class/net/* | xargs -n1 /usr/bin/basename | grep -v lo`; do + HWADDR=`cat /sys/class/net/$i/address` + # Use GUID if IB + if [ ${#HWADDR} -eq 59 ]; then + HWADDR=`expr substr $HWADDR 37 23` + fi + if [ $HWADDR == $WWHWADDR ]; then + if ifup $i $WWHWADDR $WWNETDEV; then + echo "$i" > /tmp/wwdev + fi + break + fi + done +fi # If the above doesn't exist or fails, then try brute force if [ ! -f /tmp/wwdev ]; then @@ -365,10 +535,12 @@ if [ -x "$NEWROOT/sbin/init" -o -h "$NEWROOT/sbin/init" ]; then else wwfailure fi + if [ -n "$WWPOSTNETDOWN" ]; then NETDEV=`cat /tmp/wwdev` if [ -f "/tmp/ifcfg-$WWNETDEV" ]; then . /tmp/ifcfg-$WWNETDEV + if [ -n "$IPADDR" -a -n "$NETMASK" ]; then wwlogger "Deleting network interface address $NETDEV:$IPADDR/$NETMASK ($WWNETDEV)" msg_white "Deleting network interface address $NETDEV:$IPADDR/$NETMASK ($WWNETDEV)" @@ -400,6 +572,13 @@ if [ -x "$NEWROOT/sbin/init" -o -h "$NEWROOT/sbin/init" ]; then msg_white "Could not bring down $NETDEV ($WWNETDEV)" wwfailure fi + + # did we create a bonding device? + if [ -n "$WWBONDINGDEV" ]; then + # Delete bonding device. This will also detach the slaves. + # Deliberately not use NETDEV here; bondup() never guesses device names. + ip link del dev $WWBONDINGDEV >/dev/null + fi fi mkdir -p $NEWROOT${WWBOOTSTRAPLOGS:-/var/log} >/dev/null 2>&1 diff --git a/provision/lib/Warewulf/Provision/Dhcp/Isc.pm b/provision/lib/Warewulf/Provision/Dhcp/Isc.pm index cebf5737..671b5a2d 100644 --- a/provision/lib/Warewulf/Provision/Dhcp/Isc.pm +++ b/provision/lib/Warewulf/Provision/Dhcp/Isc.pm @@ -201,9 +201,9 @@ persist() return(1); } - $config_template =~ s/\%\{IPADDR}/$ipaddr/g; - $config_template =~ s/\%\{NETWORK}/$network/g; - $config_template =~ s/\%\{NETMASK}/$netmask/g; + $config_template =~ s/\%{IPADDR}/$ipaddr/g; + $config_template =~ s/\%{NETWORK}/$network/g; + $config_template =~ s/\%{NETMASK}/$netmask/g; &dprint("Creating DHCPD configuration file header\n"); $dhcpd_contents .= "# DHCPD Configuration written by Warewulf. Do not edit this file, rather\n"; @@ -262,7 +262,7 @@ persist() } foreach my $devname ($n->netdevs_list()) { - my $hwaddr = $n->hwaddr($devname); + my @hwaddrs = $n->hwaddr($devname); my $hwprefix = $n->hwprefix($devname); my $mtu = $n->mtu($devname); my $node_ipaddr = $n->ipaddr($devname); @@ -271,7 +271,7 @@ persist() my $node_testnetwork = $netobj->calc_network($node_ipaddr, $node_netmask); - if (! $hwaddr) { + if (! @hwaddrs) { &iprint("Skipping DHCP config for $nodename-$devname (no defined HWADDR)\n"); $dhcpd_contents .= " # Skipping $nodename-$devname: No defined HWADDR\n"; next; @@ -295,11 +295,19 @@ persist() &iprint("Skipping DHCP redundant entry for $nodename-$devname (already seen in $redundant_node)\n"); next; } - if (exists($seen{"HWADDR"}) and exists($seen{"HWADDR"}{"$hwaddr"})) { - my $redundant_node = $seen{"HWADDR"}{"$hwaddr"}; - $dhcpd_contents .= " # Skipping $nodename-$devname: duplicate HWADDR ($hwaddr)\n"; - &iprint("Skipping DHCP config for $nodename-$devname (HWADDR already seen in $redundant_node)\n"); - next; + if (exists($seen{"HWADDR"})) { + my $redundant_hwaddr; + foreach my $hwaddr (@hwaddrs) { + if (exists($seen{"HWADDR"}{"$hwaddr"})) { + $redundant_hwaddr = $hwaddr; + } + } + if ($redundant_hwaddr) { + my $redundant_node = $seen{"HWADDR"}{"$redundant_hwaddr"}; + $dhcpd_contents .= " # Skipping $nodename-$devname: duplicate HWADDR (@hwaddrs)\n"; + &iprint("Skipping DHCP config for $nodename-$devname (HWADDR already seen in $redundant_node)\n"); + next; + } } if (exists($seen{"IPADDR"}) and exists($seen{"IPADDR"}{"$node_ipaddr"})) { my $redundant_node = $seen{"IPADDR"}{"$node_ipaddr"}; @@ -308,46 +316,52 @@ persist() next; } - if ($nodename and $node_ipaddr and $hwaddr) { + if ($nodename and $node_ipaddr and @hwaddrs) { &dprint("Adding a host entry for: $nodename-$devname\n"); - $dhcpd_contents .= " # Adding host entry for $nodename-$devname\n"; - $dhcpd_contents .= " host $nodename-$devname {\n"; - $dhcpd_contents .= " option host-name $hostname;\n"; - if ($ipxeurl) { - &dprint("Overriding node's iPXE URL with $ipxeurl\n"); - $ipxeurl =~ s/\%\{IPADDR}/$ipaddr/g; - $dhcpd_contents .= " if exists user-class and option user-class = \"iPXE\" {\n"; - $dhcpd_contents .= " filename \"$ipxeurl\";\n"; - $dhcpd_contents .= " }\n"; - - } - if ($pxeloader) { - &dprint("Overriding node's PXE loader with $pxeloader\n"); - $dhcpd_contents .= " if not exists user-class or option user-class != \"iPXE\" {\n"; - $dhcpd_contents .= " filename \"/warewulf/$pxeloader\";\n"; - $dhcpd_contents .= " }\n"; - } - if ($node_gateway) { - $dhcpd_contents .= " option routers $node_gateway;\n"; - } - if ($domain) { - $dhcpd_contents .= " option domain-name \"$domain\";\n"; - } - if ($devname =~ /ib\d+/ && $hwprefix) { - $dhcpd_contents .= " option dhcp-client-identifier = $hwprefix:$hwaddr;\n"; - } else { - $dhcpd_contents .= " hardware ethernet $hwaddr;\n"; - } - if ($mtu) { - $dhcpd_contents .= " option interface-mtu $mtu;\n"; + my $idx = 0; + + foreach $hwaddr (@hwaddrs) { + $dhcpd_contents .= " # Adding host entry for $nodename-$devname\n"; + $dhcpd_contents .= " host $nodename-$devname".($idx > 0 ? "-$idx" : "")." {\n"; + $dhcpd_contents .= " option host-name $hostname;\n"; + if ($ipxeurl) { + &dprint("Overriding node's iPXE URL with $ipxeurl\n"); + $ipxeurl =~ s/\%{IPADDR}/$ipaddr/g; + $dhcpd_contents .= " if exists user-class and option user-class = \"iPXE\" {\n"; + $dhcpd_contents .= " filename \"$ipxeurl\";\n"; + $dhcpd_contents .= " }\n"; + + } + if ($pxeloader) { + &dprint("Overriding node's PXE loader with $pxeloader\n"); + $dhcpd_contents .= " if not exists user-class or option user-class != \"iPXE\" {\n"; + $dhcpd_contents .= " filename \"/warewulf/$pxeloader\";\n"; + $dhcpd_contents .= " }\n"; + } + if ($node_gateway) { + $dhcpd_contents .= " option routers $node_gateway;\n"; + } + if ($domain) { + $dhcpd_contents .= " option domain-name \"$domain\";\n"; + } + if ($devname =~ /ib\d+/ && $hwprefix) { + $dhcpd_contents .= " option dhcp-client-identifier = $hwprefix:$hwaddr;\n"; + } else { + $dhcpd_contents .= " hardware ethernet $hwaddr;\n"; + } + if ($mtu) { + $dhcpd_contents .= " option interface-mtu $mtu;\n"; + } + $dhcpd_contents .= " fixed-address $node_ipaddr;\n"; + $dhcpd_contents .= " next-server $master_ipv4_addr;\n"; + $dhcpd_contents .= " }\n"; + + $seen{"HWADDR"}{"$hwaddr"} = "$nodename-$devname"; + $idx++; } - $dhcpd_contents .= " fixed-address $node_ipaddr;\n"; - $dhcpd_contents .= " next-server $master_ipv4_addr;\n"; - $dhcpd_contents .= " }\n"; $seen{"NODESTRING"}{"$nodename-$devname"} = "$nodename-$devname"; - $seen{"HWADDR"}{"$hwaddr"} = "$nodename-$devname"; $seen{"IPADDR"}{"$node_ipaddr"} = "$nodename-$devname"; } else { diff --git a/provision/lib/Warewulf/Provision/Pxe.pm b/provision/lib/Warewulf/Provision/Pxe.pm index d8f08cfb..c9f6a7b8 100644 --- a/provision/lib/Warewulf/Provision/Pxe.pm +++ b/provision/lib/Warewulf/Provision/Pxe.pm @@ -219,7 +219,9 @@ update() foreach my $devname (sort($nodeobj->netdevs_list())) { - my $hwaddr = $nodeobj->hwaddr($devname); + my @hwaddrs = $nodeobj->hwaddr($devname); + my @bonddevs = $nodeobj->bonddevs($devname); + my $bondmode = $nodeobj->bondmode($devname); my $node_ipaddr = $nodeobj->ipaddr($devname); my $node_netmask = $nodeobj->netmask($devname) || $master_netmask; my $node_gateway = $nodeobj->gateway($devname); @@ -231,7 +233,7 @@ update() next; } - if (! $hwaddr) { + if (! @hwaddrs) { &iprint("Skipping PXE config for $nodename-$devname (No hwaddr defined)\n"); next; } @@ -241,75 +243,88 @@ update() next; } - &dprint("Creating a pxe config for node '$nodename-$devname/$hwaddr'\n"); - - if ($hwaddr =~ /^([:[:xdigit:]]+)$/) { - $hwaddr = $1; - &iprint("Building iPXE configuration for: $nodename/$hwaddr\n"); - my $config = $hwaddr; - - if (! $bootstrapid) { - &iprint("Skipping $nodename-$devname-$hwaddr: No bootstrap defined\n"); - if (-f "$statedir/warewulf/ipxe/cfg/$config") { - # If we know gotten this far, but not going to write a config, we - # can remove it. - unlink("$statedir/warewulf/ipxe/cfg/$config"); + foreach my $hwaddr (@hwaddrs) + { + &dprint("Creating a pxe config for node '$nodename-$devname/$hwaddr'\n"); + + if ($hwaddr =~ /^([:[:xdigit:]]+)$/) { + $hwaddr = $1; + &iprint("Building iPXE configuration for: $nodename/$hwaddr\n"); + my $config = $hwaddr; + + if (! $bootstrapid) { + &iprint("Skipping $nodename-$devname-$hwaddr: No bootstrap defined\n"); + if (-f "$statedir/warewulf/ipxe/cfg/$config") { + # If we know gotten this far, but not going to write a config, we + # can remove it. + unlink("$statedir/warewulf/ipxe/cfg/$config"); + } + next; } - next; - } - &dprint("Creating iPXE config at: $statedir/warewulf/ipxe/cfg/$config\n"); - if (!open(IPXE, "> $statedir/warewulf/ipxe/cfg/$config")) { - &eprint("Could not open iPXE config: $!\n"); - next; - } - print IPXE "#!ipxe\n"; - print IPXE "# Configuration for Warewulf node: $hostname\n"; - print IPXE "# Warewulf data store ID: $db_id\n"; - if (defined($bootlocal) && $bootlocal eq -1) { - print IPXE "echo Set to bootlocal (exit), exiting iPXE to continue boot order\n"; - print IPXE "exit 1\n"; - } elsif (defined($bootlocal) && $bootlocal eq 0) { - print IPXE "echo Set to bootlocal (normal), booting local disk\n"; - print IPXE "sanboot --no-describe --drive 0x80\n"; - } else { - - print IPXE "echo Now booting $hostname with Warewulf bootstrap ($bootstrapname)\n"; - print IPXE "set base http://$master_ipaddr/WW/bootstrap\n"; - print IPXE "initrd \${base}/$arch/$bootstrapid/initfs.gz\n"; - print IPXE "kernel \${base}/$arch/$bootstrapid/kernel ro initrd=initfs.gz wwhostname=$hostname "; - print IPXE join(" ", @kargs) . " "; - if ($console) { - print IPXE "console=tty0 console=$console "; - } - if (scalar(@masters) > 0) { - my $master = join(",", @masters); - print IPXE "wwmaster=$master "; - } else { - print IPXE "wwmaster=$master_ipaddr "; - } - if ($devname and $node_ipaddr and $node_netmask) { - print IPXE "wwipaddr=$node_ipaddr wwnetmask=$node_netmask wwnetdev=$devname wwhwaddr=$hwaddr "; - } else { - &dprint("$hostname: Skipping static network definition because configuration not complete\n"); + &dprint("Creating iPXE config at: $statedir/warewulf/ipxe/cfg/$config\n"); + if (!open(IPXE, "> $statedir/warewulf/ipxe/cfg/$config")) { + &eprint("Could not open iPXE config: $!\n"); + next; } - if ($node_gateway) { - print IPXE "wwgateway=$node_gateway "; + + print IPXE "#!ipxe\n"; + print IPXE "# Configuration for Warewulf node: $hostname\n"; + print IPXE "# Warewulf data store ID: $db_id\n"; + if (defined($bootlocal) && $bootlocal eq -1) { + print IPXE "echo Set to bootlocal (exit), exiting iPXE to continue boot order\n"; + print IPXE "exit 1\n"; + } elsif (defined($bootlocal) && $bootlocal eq 0) { + print IPXE "echo Set to bootlocal (normal), booting local disk\n"; + print IPXE "sanboot --no-describe --drive 0x80\n"; } else { - &dprint("$hostname: Skipping static gateway configuration as it is unconfigured\n"); + print IPXE "echo Now booting $hostname with Warewulf bootstrap ($bootstrapname)\n"; + print IPXE "set base http://$master_ipaddr/WW/bootstrap\n"; + print IPXE "initrd \${base}/$arch/$bootstrapid/initfs.gz\n"; + print IPXE "kernel \${base}/$arch/$bootstrapid/kernel ro initrd=initfs.gz wwhostname=$hostname "; + print IPXE join(" ", @kargs) . " "; + if ($console) { + print IPXE "console=tty0 console=$console "; + } + if (scalar(@masters) > 0) { + my $master = join(",", @masters); + print IPXE "wwmaster=$master "; + } else { + print IPXE "wwmaster=$master_ipaddr "; + } + if ($devname and $node_ipaddr and $node_netmask) { + print IPXE "wwipaddr=$node_ipaddr wwnetmask=$node_netmask wwnetdev=$devname wwhwaddr=$hwaddr "; + } else { + &dprint("$hostname: Skipping static network definition because configuration not complete\n"); + } + if (@bonddevs) { + print IPXE "wwbonddevs=".join(',', @bonddevs)." "; + } else { + &dprint("$hostname: Skipping network bonding devices definition because configuration not complete\n"); + } + if ($bondmode) { + print IPXE "wwbondmode=$bondmode "; + } else { + &dprint("$hostname: Skipping network bonding mode definition because configuration not complete\n"); + } + if ($node_gateway) { + print IPXE "wwgateway=$node_gateway "; + } else { + &dprint("$hostname: Skipping static gateway configuration as it is unconfigured\n"); + } + if ($mtu) { + print IPXE "wwmtu=$mtu"; + } else { + &dprint("$hostname: Skipping static MTU configuration as it is unconfigured\n"); + } + print IPXE "\nboot\n"; } - if ($mtu) { - print IPXE "wwmtu=$mtu"; - } else { - &dprint("$hostname: Skipping static MTU configuration as it is unconfigured\n"); + if (! close IPXE) { + &eprint("Could not write iPXE configuration file: $!\n"); } - print IPXE "\nboot\n"; - } - if (! close IPXE) { - &eprint("Could not write iPXE configuration file: $!\n"); + } else { + &eprint("Node: $nodename-$devname: Bad characters in hwaddr: '$hwaddr'\n"); } - } else { - &eprint("Node: $nodename-$devname: Bad characters in hwaddr: '$hwaddr'\n"); } } } @@ -341,10 +356,10 @@ delete() &dprint("Deleting PXE entries for node: $nodename\n"); foreach my $netdev ($nodeobj->get("netdevs")) { - if (defined($netdev->get("hwaddr"))) { - my $hwaddr = lc($netdev->get("hwaddr")); + my @netdev_hwaddrs = $netdev->get("hwaddr"); - if (defined($hwaddr) && !scalar(grep { lc($_) eq $hwaddr } @hwaddrs)) { + foreach my $hwaddr (@netdev_hwaddrs) { + if (defined $hwaddr && !grep { lc($_) eq lc($hwaddr) } @hwaddrs) { push @hwaddrs, $hwaddr; } }