diff --git a/xCAT-SoftLayer/bin/getslnodes b/xCAT-SoftLayer/bin/getslnodes index ae02b6642..d5549ad1c 100755 --- a/xCAT-SoftLayer/bin/getslnodes +++ b/xCAT-SoftLayer/bin/getslnodes @@ -38,8 +38,6 @@ if (scalar(@ARGV)>1) { $usage->(1); } my $hnmatch = $ARGV[0]; # if they specified a hostname match, only show svrs that start with that readconf("$ENV{HOME}/.slconfig"); # get the userid and api key from the config file -#my $api_username = 'SL276540'; -#my $api_key = '799d5d9267a927a330ec016f00bfe17e6fc532d203cf68b3b0d997b2d27a3ce1'; my $slinstalled = eval { push @INC, $CONFIG{apidir}; require SoftLayer::API::SOAP; }; if (!$slinstalled) { die "Error: the SoftLayer::API::SOAP perl module is not installed. Download it using 'git clone https://github.com/softlayer/softlayer-api-perl-client' and put the directory in ~/.slconfig ."; } @@ -64,12 +62,23 @@ foreach my $server (@$servers) { print "\tbmc=".$server->{remoteManagementComponent}->{ipmiIpAddress}."\n"; print "\tbmcusername=".$server->{remoteManagementAccounts}->[0]->{username}."\n"; print "\tbmcpassword=".$server->{remoteManagementAccounts}->[0]->{password}."\n"; - print "\tmac=".$server->{backendNetworkComponents}->[0]->{macAddress}."\n"; - print "\tip=".$server->{privateIpAddress}."\n"; + + # find the 1st active private nic that is not the bmc + foreach my $nic (@{$server->{backendNetworkComponents}}) { + #print "nic:\n"; foreach my $key (keys(%$nic)) { print " $key = ", $nic->{$key}, "\n"; } + if ($nic->{status} eq 'ACTIVE' && $nic->{name} eq 'eth' && $nic->{macAddress} && $nic->{primaryIpAddress}) { + # found it + print "\tmac=".$nic->{macAddress}."\n"; + print "\tip=".$nic->{primaryIpAddress}."\n"; + } + } + #print "\tip=".$server->{privateIpAddress}."\n"; # getting this from the backendNetworkComponents instead + print "\tserial=".$server->{manufacturerSerialNumber}."\n"; print "\tnetboot=xnba\n"; print "\tarch=x86_64\n"; print "\tusercomment=hostname:".$server->{fullyQualifiedDomainName}.", user:".$server->{operatingSystem}->{passwords}->[0]->{username}.", pw:".$server->{operatingSystem}->{passwords}->[0]->{password}."\n"; + verbose('SoftLayer API bare metal server entry: ' . Dumper($server)); #print Dumper($server->{remoteManagementAccounts}); #print "#Softlayer_account_info_for ".$server->{fullyQualifiedDomainName} . " Username: "; #print $server->{operatingSystem}->{passwords}->[0]->{username} . " Password: "; diff --git a/xCAT-SoftLayer/bin/khrem b/xCAT-SoftLayer/bin/khrem new file mode 100755 index 000000000..ac212c484 --- /dev/null +++ b/xCAT-SoftLayer/bin/khrem @@ -0,0 +1,88 @@ +#!/usr/bin/perl + +# remove entries from the .ssh/known_hosts file for a node + +use strict; +use Getopt::Long; +use Data::Dumper; +#$Data::Dumper::Maxdepth=2; + +# Globals - these are set once and then only read. +my $HELP; +my $VERBOSE; +my $file = '~/.ssh/known_hosts'; + +my $usage = sub { + my $exitcode = shift @_; + print "Usage: khrem \n"; + print " Removes the entries in the .ssh/known_hosts file associated with this node.\n"; + exit $exitcode; +}; + +# Process the cmd line args +Getopt::Long::Configure("bundling"); +#Getopt::Long::Configure("pass_through"); +Getopt::Long::Configure("no_pass_through"); +if (!GetOptions('h|?|help' => \$HELP, 'v|verbose' => \$VERBOSE)) { $usage->(1); } + +if ($HELP) { $usage->(0); } +if (scalar(@ARGV)!=1) { $usage->(1); } +my $node = $ARGV[0]; # if they specified a hostname match, only show svrs that start with that + +my @output = runcmd("host $node"); +my $hostname; +my $line = shift @output; +#print "line=$line\n"; +if ($line =~ m/is an alias for /) { + ($hostname) = $line =~ m/is an alias for ([^\.]+)/; + #print "hostname=$hostname\n"; + $line = shift @output; + } +#print "line=$line\n"; +my ($ip) = $line =~ m/has address (.+)$/; +if (defined($hostname)) { + print "Removing entries from $file for: $node, $hostname, $ip\n"; + runcmd("sed -i '/^$node/d;/^$hostname/d;/^$ip/d' $file"); + } +else { + print "Removing entries from $file for: $node, $ip\n"; + runcmd("sed -i '/^$node/d;/^$ip/d' $file"); +} + +exit(0); + + +# Pring msg only if -v was specified +sub verbose { if ($VERBOSE) { print shift, "\n"; } } + + + + +# Run a command. If called in the context of return an array, it will capture the output +# of the cmd and return it. Otherwise, it will display the output to stdout. +# If the cmd has a non-zero rc, this function will die with a msg. +sub runcmd +{ + my ($cmd) = @_; + my $rc; + + $cmd .= ' 2>&1' ; + verbose($cmd); + + my @output; + if (wantarray) { + @output = `$cmd`; + $rc = $?; + } + else { + system($cmd); + $rc = $?; + } + + if ($rc) { + $rc = $rc >> 8; + if ($rc > 0) { die "Error: rc $rc return from cmd: $cmd\n"; } + else { die "Error: system error returned from cmd: $cmd\n"; } + } + elsif (wantarray) { return @output; } +} diff --git a/xCAT-SoftLayer/bin/modifygrub b/xCAT-SoftLayer/bin/modifygrub index a6d28407f..a1769e8e9 100755 --- a/xCAT-SoftLayer/bin/modifygrub +++ b/xCAT-SoftLayer/bin/modifygrub @@ -12,12 +12,14 @@ use Socket; # Globals - these are set once and then only read. my $HELP; my $VERBOSE; +my $DRYRUN; my $WAITTIME; +my $PROVMETHOD; my $XCATNETBOOTTITLE = 'xCAT network boot kernel and initrd'; my $usage = sub { my $exitcode = shift @_; - print "Usage: modifygrub [-?|-h|--help] [-v|--verbose] [-w ] \n\n"; + print "Usage: modifygrub [-?|-h|--help] [-v|--verbose] [--dryrun] [-w ] [-p \n\n"; if (!$exitcode) { print "Modify the grub config file on the node to boot the specified kernel and initrd.\n"; } @@ -30,7 +32,7 @@ if (-f '/etc/os-release') { die "This script doesn't support ubuntu yet.\n"; } Getopt::Long::Configure("bundling"); #Getopt::Long::Configure("pass_through"); Getopt::Long::Configure("no_pass_through"); -if (!GetOptions('h|?|help' => \$HELP, 'v|verbose' => \$VERBOSE, 'w|waittime=s' => \$WAITTIME)) { $usage->(1); } +if (!GetOptions('h|?|help' => \$HELP, 'v|verbose' => \$VERBOSE, 'dryrun' => \$DRYRUN, 'w|waittime=s' => \$WAITTIME, 'p|provmethod=s' => \$PROVMETHOD)) { $usage->(1); } if ($HELP) { $usage->(0); } if (scalar(@ARGV) != 4) { $usage->(1); } @@ -60,35 +62,89 @@ sub addKernelParms { $args->{kernelparms} =~ s//$nodename/g; # get node ip and add it to the kernel parms - my ($nic, $ip, $netmask, $gateway) = getNodeIpInfo($args); + my ($nic, $ip, $netmask, $network, $broadcast, $gateway, $mac) = getNodeIpInfo($args); if (!$ip) { die "Error: could not find the NIC that would connect to the xCAT mgmt node's IP (".$args->{mnip}.").\n"; } - $args->{kernelparms} .= " hostip=$ip netmask=$netmask gateway=$gateway dns=$mnip hostname=$nodename netdevice=$nic netwait=$WAITTIME textmode=1"; + # if we are booting genesis, need to add the BOOTIF parm + my $bootif; + if ($args->{kernelpath} =~ m/genesis\.kernel\.x86_64/) { + $bootif = $mac; + $bootif =~ s/:/-/g; + $bootif = "BOOTIF=01-$bootif"; + } + + #todo: if you are running genesis shell (nodeset shell), this if-else will depend on the nodeset done before that. + # really should check for currstate=shell, or something like that + if (defined($PROVMETHOD) && $PROVMETHOD eq 'sysclone') { + # add additional parms for sysclone + # DEVICE=eth0 IPADDR=10.0.0.99 NETMASK=255.255.255.0 NETWORK=10.0.0.0 BROADCAST=10.0.0.255 GATEWAY=10.0.0.1 GATEWAYDEV=eth0 + #todo: should we also add ETHER_SLEEP=$WAITTIME textmode=1 dns=$mnip ? + $args->{kernelparms} .= " $bootif IPADDR=$ip NETMASK=$netmask NETWORK=$network BROADCAST=$broadcast GATEWAY=$gateway HOSTNAME=$nodename DEVICE=$nic GATEWAYDEV=$nic"; + } + else { # scripted install + #todo: the parameters for kickstart are likely different + $args->{kernelparms} .= " $bootif hostip=$ip netmask=$netmask gateway=$gateway dns=$mnip hostname=$nodename netdevice=$nic netwait=$WAITTIME textmode=1"; + } } -# get this nodes nic, ip, netmask, and gateway. Returns them in a 4 element array. +# get this nodes nic, ip, netmask, gateway, and mac. Returns them in a 5 element array. sub getNodeIpInfo { my $args = shift @_; - my ($ipprefix) = $args->{mnip}=~m/^(\d+\.\d+)\./; #todo: this is a hack, just using the 1st 2 octets of the mn ip addr + my ($ipprefix) = $args->{mnip}=~m/^(\d+)\./; #todo: this is a hack, just using the 1st octet of the mn ip addr verbose("using IP prefix $ipprefix"); - # parse ip addr show output, looking for ipprefix, to determine nic and ip + # parse ip addr show output, looking for ipprefix, to determine nic, ip, mac my @output = runcmd("ip addr show"); - my ($nic, $ipandmask); + my ($nic, $mac, $ipandmask); foreach my $line (@output) { - my ($nictmp, $iptmp); + my ($nictmp, $mactmp, $iptmp); if (($nictmp) = $line=~m/^\d+:\s+(\S+): /) { $nic = $nictmp; } # new stanza, remember it + if (($mactmp) = $line=~m|^\s+link/ether\s+(\S+) |) { $mac = $mactmp; } # got mac, remember it if (($iptmp) = $line=~m/^\s+inet\s+($ipprefix\S+) /) { $ipandmask = $iptmp; last; } # got ip, we are done } - my ($ip, $netmask) = convertIpAndMask($ipandmask); + if (!defined($ipandmask)) { die "Error: can't find a NIC with a prefix $ipprefix that communicates with".$args->{mnip}.".\n"; } + my ($ip, $netmask, $network, $broadcast) = convertIpAndMask($ipandmask); - # if the nic is a bonded nic (common on sl), then find the 1st real nic that is part of it - my $realnic = $nic; + # if the nic is a bonded nic (common on sl), then find the 1st real nic that is up that is part of it. + # also find that real nics real mac + my $realnic; if ($nic =~ /^bond/) { my @nics = grep(m/\s+master\s+$nic\s+/, @output); if (!scalar(@nics)) { die "Error: can't find the NICs that are part of $nic.\n"; } - ($realnic) = $nics[0]=~m/^\d+:\s+(\S+): /; + foreach my $line (@nics) { + my ($nictmp, $state) = $line=~m/^\d+:\s+(\S+): .* state\s+(\S+)/; + if (defined($nictmp) && defined($state) && $state eq 'UP') { $realnic = $nictmp; last; } # got ip, we are done + } + if (!defined($realnic)) { die "Error: can't find a physical NIC that is up and part of $nic.\n"; } + + # now get the real mac of this real nic (when 2 nics are bonded, ip addr show displays one of the nics + # macs for both nics and the bond). So we have to depend on /proc/net/bonding/$bond instead. + my @bondout = runcmd("cat /proc/net/bonding/$nic"); + my $foundnic; + foreach my $line (@bondout) { + my $mactmp; + if ($line=~m/^Slave Interface:\s+$realnic/) { $foundnic = 1; } # found the stanza for this nic, remember it + if ($foundnic && (($mactmp) = $line=~m/^Permanent HW addr:\s+(\S+)/)) { $mac = $mactmp; last; } + } } + else { $realnic = $nic; } + + # centos/redhat seems to name the nic in a different order than sles on some svrs. + # sles seems to name them in the same order as 'ip addr show' displays them, centos does not. + # so if we are on centos right now, we need to count down to determine the number that sles + # will give the nic that we have selected, because it is the sles naming that we care about, + # because that is the initrd that will be running in the scripted install case. + # For the sysclone case, genesis doxcat should be changed to use the mac to find the nic. + if (isRedhat()) { + my @nics = grep(m/^\d+:\s+eth/, @output); + my $i = 0; + foreach my $line (@nics) { + my ($nictmp) = $line=~m/^\d+:\s+(\S+):/; + if (defined($nictmp) && $nictmp eq $realnic) { $realnic = "eth$i"; last; } # got ip, we are done + $i++; + } + } + print "Determined that SLES will call the install NIC $realnic (it has mac $mac)\n"; # finally, find the gateway my $gateway; @@ -102,19 +158,37 @@ sub getNodeIpInfo { verbose("using xCAT mgmt node IP as the fall back gateway."); } - verbose("IP info: realnic=$realnic, ip=$ip, netmask=$netmask, gateway=$gateway"); - return ($realnic, $ip, $netmask, $gateway); + verbose("IP info: realnic=$realnic, ip=$ip, netmask=$netmask, gateway=$gateway, mac=$mac"); + return ($realnic, $ip, $netmask, $network, $broadcast, $gateway, $mac); } -# Convert an ip/mask in slash notation (like 10.0.0.1/26) to separate ip and netmask like 10.0.0.1 and 255.255.255.192 +# Convert an ip/mask in slash notation (like 10.1.1.1/26) to separate ip, netmask, network, and broadcast values, +# like: 10.1.1.1, 255.255.255.192, 10.1.1.0, 10.1.1.63 sub convertIpAndMask { my $ipandmask = shift @_; my ($ip, $masknum) = split('/', $ipandmask); - my $netbin = oct("0b" . '1' x $masknum . '0' x (32-$masknum)); # create a str like '1111100', then convert to binary - my @netarr=unpack('C4',pack('N',$netbin)); # separate into the 4 octets - my $netmask=join('.',@netarr); # put them together into the normal looking netmask - return ($ip, $netmask); + + # build the netmask + my $nmbin = oct("0b" . '1' x $masknum . '0' x (32-$masknum)); # create a str like '1111100', then convert to binary + my @nmarr=unpack('C4',pack('N',$nmbin)); # separate into the 4 octets + my $netmask=join('.',@nmarr); # put them together into the normal looking netmask + + # create binary form of ip + my @iparr=split(/\./,$ip); + my ( $ipbin ) = unpack('N', pack('C4',@iparr ) ); + + # Calculate network address by logical AND operation of ip & netmask and convert network address to IP address format + my $netbin = ( $ipbin & $nmbin ); + my @netarr=unpack('C4', pack('N',$netbin ) ); + my $network=join(".",@netarr); + + # Calculate broadcast address by inverting the netmask and adding it to the network address + my $bcbin = ( $ipbin & $nmbin ) + ( ~ $nmbin ); + my @bcarr=unpack('C4', pack('N',$bcbin ) ) ; + my $broadcast=join(".",@bcarr); + + return ($ip, $netmask, $network, $broadcast); } @@ -155,6 +229,11 @@ sub updateGrub { "\tkernel " . $fileprefix . $args->{kernelpath} . ' ' . $args->{kernelparms} . "\n", "\tinitrd " . $fileprefix . $args->{initrdpath} . "\n", ); + if ($DRYRUN) { + print "Dry run: would add this stanza to $grubfile:\n"; + foreach my $l (@entry) { print $l; } + return; + } my $needtowritefile = 1; if (grep(/^title\s+$XCATNETBOOTTITLE/, @lines)) { $needtowritefile = updateGrubEntry(\@lines, \@entry); } # there is already an entry in there diff --git a/xCAT-SoftLayer/bin/pushinitrd b/xCAT-SoftLayer/bin/pushinitrd index 7e3cd5a7e..62c19d8ba 100755 --- a/xCAT-SoftLayer/bin/pushinitrd +++ b/xCAT-SoftLayer/bin/pushinitrd @@ -13,12 +13,13 @@ use Data::Dumper; # Globals - these are set once and then only read. my $HELP; my $VERBOSE; +my $DRYRUN; my $WAITTIME; my $NOAUTOINST; my $usage = sub { my $exitcode = shift @_; - print "Usage: pushinitrd [-?|-h|--help] [-v|--verbose] [-w ] \n\n"; + print "Usage: pushinitrd [-?|-h|--help] [-v|--verbose] [--dryrun] [-w ] \n\n"; if (!$exitcode) { print "Copy the initrd, kernel, params, and static IP info to nodes, so they can net install\n"; print "even across vlans (w/o setting up pxe/dhcp broadcast relay). This assumes a working\n"; @@ -32,7 +33,7 @@ my $usage = sub { Getopt::Long::Configure("bundling"); #Getopt::Long::Configure("pass_through"); Getopt::Long::Configure("no_pass_through"); -if (!GetOptions('h|?|help' => \$HELP, 'v|verbose' => \$VERBOSE, 'w|waittime=s' => \$WAITTIME, 'a|noautoinst' => \$NOAUTOINST)) { $usage->(1); } +if (!GetOptions('h|?|help' => \$HELP, 'v|verbose' => \$VERBOSE, 'dryrun' => \$DRYRUN, 'w|waittime=s' => \$WAITTIME, 'a|noautoinst' => \$NOAUTOINST)) { $usage->(1); } if ($HELP) { $usage->(0); } if (scalar(@ARGV) != 1) { $usage->(1); } @@ -45,7 +46,11 @@ copyFilesToNodes($noderange, \%bootparms); updateGrubOnNodes($noderange, \%bootparms); -if (!$NOAUTOINST) { modifyAutoinstFiles($noderange, \%bootparms); } +if ($DRYRUN) { exit(0); } + +if ($bootparms{osimageprovmethod} eq 'install' && !$NOAUTOINST) { modifyAutoinstFiles($noderange, \%bootparms); } + +if ($bootparms{osimageprovmethod} eq 'sysclone') { copySyscloneFiles(); } exit(0); @@ -54,23 +59,31 @@ exit(0); sub getBootParms { my $nr = shift @_; my %bootparms; - my @output = runcmd("nodels $nr bootparams.kernel bootparams.initrd bootparams.kcmdline"); + my @output = runcmd("nodels $nr bootparams.kernel bootparams.initrd bootparams.kcmdline nodetype.provmethod"); # the attributes can be displayed in a different order than requested, so need to grep for them - my @gresults; - foreach my $a (qw(kernel initrd kcmdline)) { - my $attr = "bootparams.$a"; - @gresults = grep(/^\S+:\s+$attr:/, @output); + foreach my $attr (qw(bootparams.kernel bootparams.initrd bootparams.kcmdline nodetype.provmethod)) { + my ($a) = $attr =~ m/\.(.*)$/; + my @gresults = grep(/^\S+:\s+$attr:/, @output); if (!scalar(@gresults)) { die "Error: attribute $attr not defined for the noderange. Did you run 'nodeset osimage=' ?\n"; } # for now just pick the 1st one. They should all be the same, except for the node name in kcmdline chomp($gresults[0]); $gresults[0] =~ s/^\S+:\s+$attr:\s*//; + #print "gresults='$gresults[0]'\n"; + if ($gresults[0] !~ m/\S/) { die "Error: attribute $attr not defined for the noderange. Did you run 'nodeset osimage=' ?\n"; } $bootparms{$a} = $gresults[0]; - if ($a eq 'kcmdline') { $bootparms{$a} =~ s|/install/autoinst/\S+|/install/autoinst/|; } } + $bootparms{kcmdline} =~ s|/install/autoinst/\S+|/install/autoinst/|; + + # from the nodes provmethod, get the osimage provmethod, so we know the type of install + @output = runcmd("lsdef -t osimage $bootparms{provmethod} -ci provmethod"); + chomp($output[0]); + if ($output[0] =~ m/^Could not find/) { die "Error: provmethod $bootparms{provmethod} is set for the node, but there is no osimage definition by that name."; } + my ($junk, $provmethod) = split(/=/, $output[0]); + $bootparms{osimageprovmethod} = $provmethod; # get the mgmt node cluster-facing ip addr - @output = runcmd('lsdef -t site -i master -c'); + @output = runcmd('lsdef -t site -ci master'); chomp($output[0]); my ($junk, $ip) = split(/=/, $output[0]); $bootparms{mnip} = $ip; @@ -90,8 +103,13 @@ sub copyFilesToNodes { my $localfile = "/tftpboot/$file"; # for the my $remotefile = '/boot/' . remoteFilename($file); - print "Copying $localfile to $nr:$remotefile\n"; - runcmd("xdcp $nr -p $localfile $remotefile"); + if ($DRYRUN) { + print "Dry run: would copy $localfile to $nr:$remotefile\n"; + } + else { + print "Copying $localfile to $nr:$remotefile\n"; + runcmd("xdcp $nr -p $localfile $remotefile"); + } } } @@ -99,7 +117,7 @@ sub copyFilesToNodes { # Form the remote file name, using the last 2 parts of the path, separated by "-" sub remoteFilename { my $f = shift @_; - $f =~ s|^.*/([^/]+)/([^/]+)$|$1-$2|; + $f =~ s|^.*?([^/]+)/([^/]+)$|$1-$2|; return $f; } @@ -110,10 +128,11 @@ sub updateGrubOnNodes { my $nr = shift @_; my $bootparms = shift @_; my $vtxt = ($VERBOSE ? '-v' : ''); + my $dtxt = ($DRYRUN ? '--dryrun' : ''); my @output = runcmd('which modifygrub'); my $modifygrub = $output[0]; chomp($modifygrub); - my $cmd = "xdsh $nr -e $modifygrub $vtxt -w $WAITTIME " . remoteFilename($bootparms->{kernel}) . ' ' . remoteFilename($bootparms->{initrd}) . ' '; + my $cmd = "xdsh $nr -e $modifygrub $vtxt $dtxt -w $WAITTIME -p " . $bootparms->{osimageprovmethod} . ' ' . remoteFilename($bootparms->{kernel}) . ' ' . remoteFilename($bootparms->{initrd}) . ' '; # we need to quote the kernel parms, both here when passing it to xdsh, and on the node # when xdsh is passing it to modifygrub. The way to get single quotes inside single quotes # is to quote each of the outer single quotes with double quotes. @@ -124,8 +143,7 @@ sub updateGrubOnNodes { } -# Hack the autoinst files to wait in a key spot to make them work even tho it takes -# the NICs almost a min before they can transmit after a state change. +# Hack the autoinst files to overcome the nic coming up delay. #todo: this has only been tested with SLES nodes sub modifyAutoinstFiles { my $nr = shift @_; @@ -135,30 +153,145 @@ sub modifyAutoinstFiles { my @nodes = runcmd("nodels $nr"); chomp(@nodes); + # Modify chroot.sles to insert a wait in the /etc/init.d/network of each node. This is + # necessary because even tho compute.sles11.softlayer.tmpl configures bonding, when autoyast + # reboots the node after installing the rpms, it does not bring up the network in the normal way + # at first and seems to skip any bonding and the if-up.d scripts. So we are left doing this. + # (After autoyast is done with all of its post-configuration, it brings up the network in the + # normal way, so bonding gets done then, which is good at least.) + # Edit each file to have chroot.sles insert a wait at the end of /etc/init.d/network # this finds the end of boot.sh script (which is chroot.sles) my $search = '\n\]\]>\s*\s*\s*'; + # hack the /etc/init.d/network script to put a wait in it my $file = '/mnt/etc/init.d/network'; # at this point in the installation, the permanent file system is just mounted - #my $waitstring = 'echo Sleeping for 55s;sleep 55'; - # this is the string to insert in the nodes /etc/init.d/network script. It is a while loop pinging the mn, but some of the chars need to be escape for sed - my $waitstring = 'echo Waiting to reach xCAT mgmt node...;while \[ \$\(\(xcati+=1\)\) -le 60 \] \&\& ! ping -c2 -w3 ' . $bootparms->{mnip} .'; do echo i=\$xcati ; done; sleep 10'; + # this is the string to insert in the nodes /etc/init.d/network script. It is a while loop pinging the mn, but some of the chars need to be escaped for sed + my $waitstring = 'echo -n Waiting to reach xCAT mgmt node ' . $bootparms->{mnip} . '.;xcatretries=60;while \[ \$\(\(xcati+=1\)\) -le \$xcatretries \] \&\& ! ping -c2 -w3 ' . $bootparms->{mnip} .' \>\/dev\/null 2\>\&1; do echo -n .; done; if \[ \$xcati -le \$xcatretries \]; then echo success; else echo failed; fi'; # this crazy sed string is from google. It gathers up the whole file into the hold buffer, and then the substitution is done on the whole file my $sedstring = q|sed -n '1h;1!H;${;g;s/\(\t\treload_firewall\n\)\n/\1\t\t| . $waitstring . q(\n\n/g;p;}') . " $file > $file.new"; # finally create the perl replace string that will be used to modify the autoinst file my $replace = "$sedstring\nchmod 755 $file.new; mv -f $file.new $file"; - # now actually update the file + # Add a script that gets invoked by the OS after the nic is brought up + # Note: this does not work, because midway thru the autoyast process, the if-up.d scripts do not seem to get invoked + # so autoyast fails to get the media + # these are specific to SLES + #my $netdir = '/etc/sysconfig/network'; + #my $filename = '/etc/sysconfig/network/if-up.d/xcat-sl-wait'; + #my $mnip = $bootparms->{mnip}; + #todo: to support rhel, use these values instead + #my $netdir='/etc/sysconfig/network-scripts'; + #my $filename='/sbin/ifup-local'; + #my $replace = qq( +#FILENAME=$filename +#NETDIR=$netdir +#MNIP=$mnip +#); +# $replace .= q( +#cat >$FILENAME << EOF1 +#MNIP=$MNIP +#NETDIR=$NETDIR +#EOF1 +# +# this part of the file we do NOT want to expand the variables in the content +#cat >>$FILENAME << 'EOF2' +#NIC="$1" +# look in this ifcfg script to get the nics ip to see if this is the one we should be waiting on +#NICIP=`awk -F= '/^IPADDR/ {print $2}' $NETDIR/ifcfg-$NIC | tr -d \' ` +#if [ "${NICIP%.*.*}" != "${MNIP%.*.*}" ]; then exit; fi # hack: compare the 1st 2 octets +#echo -n Waiting to reach xCAT mgmt node $MNIP. +#xcatretries=60 +#while [ $((xcati+=1)) -le $xcatretries ] && ! ping -c2 -w3 $MNIP >/dev/null 2>&1; do echo -n .; done +#if [ $xcati -le $xcatretries ]; then echo " success"; else echo " failed"; fi +#sleep 3 +#EOF2 +# +#chmod +x $FILENAME +#); + + # The compute.sles11.softlayer.tmpl file contains 2 variables (node ip and netmask) that are + # not replaced by Template.pm. Substitute those in the autoinst files now. + # Also use our own multiline sed to put the network script hack in. print "Updating /install/autoinst files.\n"; foreach my $n (@nodes) { my $f = "/install/autoinst/$n"; + my ($ip, $netmask, $gateway) = getNodeIpInfo($n); + runcmd("sed -i 's/#NODEIPADDR#/$ip/;s/#NODENETMASK#/$netmask/;s/#NODEGATEWAY#/$gateway/' $f"); + my $matches = sed($f, $search, $replace, mode=>'insertbefore'); if (!$matches) { die "Error: could not find the right place in $f to insert the sed of the network wait.\n"; } } } +# Copy softlayer specific systemimager post-install scripts to the systemimager location. +# These cause si to use static ip and insert a wait into the bring up of the network. +sub copySyscloneFiles { + my $cmd = "cp -f /opt/xcat/share/xcat/sysclone/post-install/* /install/sysclone/scripts/post-install"; + print "Copying SoftLayer-specific post scripts to the SystemImager post-install directory.\n"; + runcmd($cmd); +} + + +# Get IP and network of a node +sub getNodeIpInfo { + my $node = shift; + + # get ip for the node + my @output = runcmd("nodels $node hosts.ip"); + chomp($output[0]); + my ($junk, $ip) = split(/\s+/, $output[0]); + #todo: also support getting the ip from name resolution + if (!$ip) { die "Error: the ip attribute must be set for $node.\n"; } + + # find relevant network in the networks table + # first get the networks in a hash + my %networks; + @output = runcmd("lsdef -t network -ci net,mask,gateway"); + foreach my $line (@output) { + chomp($line); + my ($netname, $attr, $val) = $line =~ m/^(.+):\s+(.+?)=(.+)$/; + $networks{$netname}->{$attr} = $val; + } + # now go thru the networks looking for the correct one + my ($netmask, $gateway); + foreach my $key (keys %networks) { + if (isIPinNet($ip, $networks{$key}->{net}, $networks{$key}->{mask})) { # found it + $netmask = $networks{$key}->{mask}; + $gateway = $networks{$key}->{gateway}; + last; + } + } + if (!$netmask) { die "Error: could not find a network in the networks table that $node $ip is part of.\n"; } + if (!$gateway) { die "Error: gateway not specified in the networks table for the network that $node $ip is part of.\n"; } + + verbose("IP info for $node: ip=$ip, netmask=$netmask, gateway=$gateway"); + return ($ip, $netmask, $gateway); +} + + +# Is the IP in the network/netmask combo +sub isIPinNet { + my ($ip, $net, $mask) = @_; + my $ipbin = convert2bin($ip); + my $netbin = convert2bin($net); + my $maskbin = convert2bin($mask); + $ipbin &= $maskbin; + if ($ipbin && $netbin && ($ipbin == $netbin)) { return 1; } + else { return 0; } +} + + +# Convert dotted decimal format (1.2.3.4) to a binary number +sub convert2bin { + my @arr=split(/\./, shift); + my ($bin) = unpack('N', pack('C4',@arr ) ); + return $bin; +} + + # this is like multi-line sed replace function -# Args: filename, search-string, replace-string +# Args: filename, search-string, replace-string, options (mode=>{insertbefore,insertafter,replace}) sub sed { my ($file, $search, $replace, %options) = @_; #my $opts = 's'; diff --git a/xCAT-SoftLayer/postscripts/configbond b/xCAT-SoftLayer/postscripts/configbond new file mode 100755 index 000000000..537ed5df4 --- /dev/null +++ b/xCAT-SoftLayer/postscripts/configbond @@ -0,0 +1,248 @@ +#!/usr/bin/perl + +# xCAT postscript for configuring bonding of nics. +# Usage: configbond bond1 eth1 [eth3] +# +# Note: this postscript currently has some assumptions that are specific to the softlayer environment. +# We only use this to configure bond1, because bond0 gets configured by the node provisioning process. +# (altho this script would work for bond0) + +use strict; +# Check number of args + +my $nargs = $#ARGV + 1; +if (scalar(@ARGV) < 2 || scalar(@ARGV) > 3) { + system("logger -t xcat -p local4.err 'Usage: configbond []'"); + exit 1; +} + +my $bond = shift(@ARGV); +my $nic = $ARGV[0]; +my @devs; +push(@devs,@ARGV); +my $nicips = $ENV{NICIPS}; +my $nicnetworks = $ENV{NICNETWORKS}; +my $net_cnt = $ENV{NETWORKS_LINES}; + +#todo: change this script so they dont need to specify nicnetworks +if (!$nicips || !$nicnetworks) { system("logger -t xcat -p local4.err 'configbond: must specify attributes nicips and nicnetworks in the xcat db for this node.'"); exit 1; } + +#todo: these are specific to softlayer. They should be another attribute or argument +my $bondingopts = 'mode=4 miimon=100 downdelay=0 updelay=0 lacp_rate=fast xmit_hash_policy=1'; + +my $netmask =''; +my $ipaddr = ''; +my $nic_num = ''; +my $subnet = ''; +my $nic_net = ''; +my $net_name = ''; +my @nic_nets = (); # array of networks for this nic +my @nic_ips =(); # array of ipaddresses for this nic +my @networks = (); # array of all networks from networks table. + # { network_name, subnet, netmask } + +system("logger -t xcat -p local4.err 'configbond: Master: $bond'"); +system("logger -t xcat -p local4.err 'configbond: Slaves: @devs'"); +#system("logger -t xcat -p local4.err 'configbond: NIC: $nic'"); +system("logger -t xcat -p local4.err 'configbond: NICNETWORKS: $nicnetworks'"); +system("logger -t xcat -p local4.err 'configbond: NICIPS: $nicips'"); + +# Update modprobe +my $file = "/etc/modprobe.d/$bond.conf"; +if (!open(FILE, ">$file")) { system("logger -t xcat -p local4.err 'configbond: cannot open $file.'"); exit 1; } + +print FILE "alias $bond bonding\n"; +# the bonding options are put in the ifcfg file instead +#print FILE "options $bond mode=balance-rr miimon=100\n"; +close FILE; + +# create array of network info. Needed in case where there are +# more than one ip address per nic and shouldn't be many networks. +my $net_info; +my $cnt = 1; + +while ( $cnt <= $net_cnt ) { + $net_info = $ENV{"NETWORKS_LINE$cnt"}; + $net_info =~ /^netname=([^\|]*)\|\|/; + $net_name = $1; + $net_info =~ /net=([^\|]*)\|\|/; + $subnet = $1; + $net_info =~ /mask=([^\|]*)\|\|/; + $netmask = $1; + push @{ $networks[$cnt-1] }, ($net_name, $subnet, $netmask); + $cnt +=1; +} + +# get network or networks for this nic from NICNETWORKS: +# eth0:1_0_0_0-255_255_0_0|network2,eth1:1_1_0_0 +# create array of networks for this nic +foreach my $nic_networks (split(/,/,$nicnetworks)) { + my @net = (); + if ( $nic_networks =~ /!/ ) { + @net = split(/!/,$nic_networks); + } else { + @net = split(/:/,$nic_networks); + } + if ($net[0] eq $nic) { + @nic_nets = split(/\|/,$net[1]); + last; + } +} + +# get all nic ipaddress from $nicips: i.e. eth0:1.0.0.1|2.0.0.1,eth1:1.1.1.1 +# Then get all ips for this specific nic, i.e. eth0. +foreach my $ips (split(/,/,$nicips)) { + my @ip = (); + if ( $ips =~ /!/ ) { + @ip = split(/!/,$ips); + } else { + @ip = split(/:/,$ips); + } + if ($ip[0] eq $nic ) { + @nic_ips = split(/\|/,$ip[1]); + } +} + + +my $i; +for ($i=0; $i < (scalar @nic_ips) ; $i++ ) { + + # Time to create the interfaces. + # loop through the nic networks, find the matching networks to get the + # subnet and netmask and then create the appropriate ifcfg file for linux + my $specific_nic = $nic; + if ($i > 0) { + $specific_nic = $nic . ":" . ($i); + } + + #todo: support case in which nicnetworks is not specified, find the correct network by calculation + $cnt = 0; + $subnet = ""; + $netmask = ""; + $net_name = ""; + while ( $cnt < $net_cnt ) { + if ( $networks[$cnt][0] eq $nic_nets[$i] ) { + + $subnet = $networks[$cnt][1]; + $netmask = $networks[$cnt][2]; + $cnt = $net_cnt; # found match - get out. + } + else { + $cnt++; + } + } + + # check that there is a subnet and netmask set + if ( !(length($subnet) > 0) || !(length($netmask) > 0) ) { + system("logger -t xcat -p local4.err 'configbond: network subnet or netmask not set.'"); + exit 1; + } + system("logger -t xcat -p local4.err 'configbond: network subnet and netmask: $subnet, $netmask'"); + system("logger -t xcat -p local4.err 'configbond: $specific_nic, $nic_ips[$i]'"); + + # Write the master info to the ifcfg file + if (-d "/etc/sysconfig/network-scripts") { + # rhel/centos/fedora + my $dir = "/etc/sysconfig/network-scripts"; + if (!open(FILE, ">$dir/ifcfg-$bond")) { system("logger -t xcat -p local4.err 'configbond: cannot open $dir/ifcfg-$bond.'"); exit 1; } + + print FILE "DEVICE=$bond\n"; + print FILE "BOOTPROTO=none\n"; + print FILE "IPADDR=$nic_ips[$i]\n"; + print FILE "NETMASK=$netmask\n"; + print FILE "ONBOOT=yes\n"; + print FILE "USERCTL=no\n"; + print FILE qq(BONDING_OPTS="$bondingopts"\n); + close FILE; + + # Configure slaves + my @output = `ip addr show 2>&1`; # to check for existance of the device later + foreach my $dev (@devs) { + # as a convenience, make sure the device exists before adding it to the bond + if (!grep(m/^\d+:\s+$dev:/, @output)) { + system("logger -t xcat -p local4.err 'configbond: not configuring $dev because it does not exist.'"); + unlink("$dir/ifcfg-$dev"); # in case it was left over in the image we are cloning + next; + } + + system("logger -t xcat -p local4.err 'configbond: slave dev: $dev'"); + if (!open(FILE, ">$dir/ifcfg-$dev")) { system("logger -t xcat -p local4.err 'configbond: cannot open $dir/ifcfg-$dev'"); exit 1; } + print FILE "DEVICE=$dev\n"; + print FILE "BOOTPROTO=none\n"; + print FILE "MASTER=$bond\n"; + print FILE "ONBOOT=yes\n"; + print FILE "SLAVE=yes\n"; + print FILE "USERCTL=no\n"; + close FILE; + } + } + elsif (-d "/etc/sysconfig/network") { + # sles + my $dir = "/etc/sysconfig/network"; + if (!open(FILE, ">$dir/ifcfg-$bond")) { system("logger -t xcat -p local4.err 'configbond: cannot open $dir/ifcfg-$bond.'"); exit 1; } + + print FILE "BOOTPROTO=static\n"; + print FILE "BONDING_MASTER=yes\n"; + print FILE "BONDING_MODULE_OPTS='$bondingopts'\n"; + print FILE "NAME='Bonded Interface'\n"; + print FILE "IPADDR=$nic_ips[$i]\n"; + print FILE "NETMASK=$netmask\n"; + print FILE "STARTMODE=onboot\n"; + print FILE "USERCONTROL=no\n"; + my $devnum = 0; + my @output = `ip addr show 2>&1`; # to check for existance of the device later + foreach my $dev (@devs) { + if (!grep(m/^\d+:\s+$dev:/, @output)) { next; } + print FILE "BONDING_SLAVE_$devnum=$dev\n"; + $devnum++; + } + close FILE; + + # Configure slaves + foreach my $dev (@devs) { + # as a convenience, make sure the device exists before adding it to the bond + if (!grep(m/^\d+:\s+$dev:/, @output)) { + system("logger -t xcat -p local4.err 'configbond: not configuring $dev because it does not exist.'"); + unlink("$dir/ifcfg-$dev"); # in case it was left over in the image we are cloning + next; + } + + system("logger -t xcat -p local4.err 'configbond: slave dev: $dev'"); + if (!open(FILE, ">$dir/ifcfg-$dev")) { system("logger -t xcat -p local4.err 'configbond: cannot open $dir/ifcfg-$dev'"); exit 1; } + print FILE "BOOTPROTO=none\n"; + print FILE "STARTMODE=hotplug\n"; + close FILE; + } + } + else { + # do not recognize this distro + system("logger -t xcat -p local4.err 'configbond: network directory is not either the Red Hat or SuSE format.'"); + exit 1; + } + + # Apply the changes. Since we are only doing bond1 right now, lets not restart the whole network + # so we dont disrupt the installnic connection. Instead we just need to bring down the slave nics, + # and then bring up the bond nic. + #runcmd("service network restart"); + foreach my $dev (@devs) { + runcmd("ifdown $dev"); + } + runcmd("ifdown $bond"); # in case it was already up + runcmd("ifup $bond"); # note: this wont reload the bonding kernel module, so we are depending on the provisioning process to already have set the correct bonding options + system("logger -t xcat -p local4.info 'configbond: successfully configured $specific_nic.'"); + +} +exit 0; + +sub runcmd { + my $cmd = shift @_; + $cmd .= ' 2>&1'; + my @output = `$cmd`; + my $rc = $? >> 8; + if ($rc) { + system("logger -t xcat -p local4.err 'configeth: command $cmd failed with rc $rc: " . join('',@output) . "'"); + my $errout= "configeth: command $cmd failed with rc $rc."; + `echo $errout`; + exit $rc; + } +} diff --git a/xCAT-SoftLayer/postscripts/setdefaultroute b/xCAT-SoftLayer/postscripts/setdefaultroute index 6932b0d79..7e574ca4c 100755 --- a/xCAT-SoftLayer/postscripts/setdefaultroute +++ b/xCAT-SoftLayer/postscripts/setdefaultroute @@ -1,6 +1,28 @@ #!/bin/bash # set the default route of the node to the ip address and nic passed in +# this should be added to the postbootscripts, NOT postscripts -set -x -ip route replace to default via $1 dev $2 +gateway="$1" +nic="$2" + +# set it temporarily +echo "ip route replace to default via $gateway dev $nic" +ip route replace to default via $gateway dev $nic + +# set it permanently +#todo: this is only for sles right now +file=/etc/sysconfig/network/routes +if grep -q -E '^default ' $file; then + # replace the default route that is already in there + sed -i 's/^default .*$/default '$gateway' - -/' $file +else + # no default route yet, append to file + echo "default $gateway - -" >>$file +fi + +# While we are here, clean up the network wait hack, if it is still there. +# (It was added during scripted install, because autoyast will not use the bond +# configuration for 1 part of the process.) Do not know a better place to clean +# this up. +sed -i '/Waiting to reach xCAT mgmt node/d' /etc/init.d/network \ No newline at end of file diff --git a/xCAT-SoftLayer/share/xcat/install/sles/compute.sles11.softlayer.tmpl b/xCAT-SoftLayer/share/xcat/install/sles/compute.sles11.softlayer.tmpl index 96bf8ed28..c95392007 100644 --- a/xCAT-SoftLayer/share/xcat/install/sles/compute.sles11.softlayer.tmpl +++ b/xCAT-SoftLayer/share/xcat/install/sles/compute.sles11.softlayer.tmpl @@ -67,7 +67,6 @@ - true #TABLE:site:key=domain:value# #TABLE:nodelist:$NODE:node# @@ -78,10 +77,53 @@ #TABLE:site:key=domain:value# + + + yes + mode=4 miimon=100 downdelay=0 updelay=0 lacp_rate=fast xmit_hash_policy=1 + eth0 + eth2 + bond0 + static + auto + #NODEIPADDR# + #NODENETMASK# + no + + + none + eth0 + Ethernet Card 0 + off + + + none + eth2 + Ethernet Card 2 + off + + false + + + default + - + #NODEGATEWAY# + - + + + + + + root + /etc/modprobe.d/bond0.conf + 644 + + #INCLUDE:#ENV:XCATROOT#/share/xcat/install/scripts/pre.sles# #INCLUDE:#ENV:XCATROOT#/share/xcat/install/scripts/chroot.sles# diff --git a/xCAT-SoftLayer/si-post-install/15all.configefi b/xCAT-SoftLayer/si-post-install/15all.configefi new file mode 100755 index 000000000..34b112f0c --- /dev/null +++ b/xCAT-SoftLayer/si-post-install/15all.configefi @@ -0,0 +1,82 @@ +#!/bin/bash + +# SI post-install script to configure the efi boot mgr or grub after SI has installed the OS +# SI post-install scripts run in a chroot environment of the final OS image + +if [ -d /sys/firmware/efi ]; then + echo "Setting Boot Manager for the next boot." + echo "delete all sysclone boot list" + str_bootnums=`efibootmgr | grep 'syscloneLinux' | awk '{print $1}' | sed 's/boot//i' | sed 's/*//'` + for str_num in $str_bootnums + do + efibootmgr -b $str_num -B -q + done + + if [ -f "/boot/efi/EFI/redhat/grub.efi" ];then + efibootmgr -c -l \\EFI\\redhat\\grub.efi -L syscloneLinux + elif [ -f "/boot/efi/efi/SuSE/elilo.efi" ];then + efibootmgr -c -l \\efi\\SuSE\\elilo.efi -L syscloneLinux + else + echo "Can not find the boot loader." + exit 1 + fi +else + echo "run grub-install to configure the MBR." + if [ -e /etc/mtab ];then + mv /etc/mtab /etc/mtab.bak + fi + grep -v rootfs /proc/mounts > /etc/mtab + boot_device='' + if [ -f "/etc/systemconfig/systemconfig.conf" ];then + boot_device=`cat /etc/systemconfig/systemconfig.conf | grep BOOTDEV | awk '{print $3}'` + else + boot_root=`mount | grep -E ' on\s+/ type ' | awk '{print $1}'` + boot_device=`echo $boot_root | sed -e 's/[0-9]*$//'` + + #str_temp=`mount | awk '{print $1","$3}'` + #for line in $str_temp + #do + # mp=`echo $line | awk -F, '{print $2}'` + # if [ "$mp" = "/" ];then + # boot_device=`echo $line | awk -F, '{print $1}' | sed -e 's/[0-9]*$//'` + # break + # fi + #done + fi + + if [ -n "$boot_device" ];then + echo "The boot device is $boot_device" + echo "The boot root device is $boot_root" + else + echo "Can not find the boot device, return error" + exit 1 + fi + + # set grub to use this boot device + if grep -qe '^VERSION\s*=\s*11' /etc/SuSE-release; then + #sles11, run grub-install.unsupported directly + echo "grub-install.unsupported --no-floppy --recheck $boot_device" + grub-install.unsupported --no-floppy --recheck $boot_device + # note: the error about grub-set-default not existing is harmless, because we want the default to be 0 anyway + else + #for sles10, should run grub-install with parameters + echo "grub-install --no-floppy --recheck $boot_device" + grub-install --no-floppy --recheck $boot_device + fi + + # change the entries in the grub conf file to use the correct boot root device + # (not the one leftover from the golden image) + if [ -f "/boot/grub/grub.conf" ];then + conffile="/boot/grub/grub.conf" + else + conffile="/boot/grub/menu.lst" + fi + sed -i 's| root=\S*| root='$boot_root'|' $conffile + sed -i 's| resume=\S*| noresume|' $conffile + + if [ -e /etc/mtab.bak ];then + mv -f /etc/mtab.bak /etc/mtab + else + rm -f /etc/mtab + fi +fi diff --git a/xCAT-SoftLayer/si-post-install/16all.updatenetwork b/xCAT-SoftLayer/si-post-install/16all.updatenetwork new file mode 100755 index 000000000..a5b58d999 --- /dev/null +++ b/xCAT-SoftLayer/si-post-install/16all.updatenetwork @@ -0,0 +1,209 @@ +#!/bin/bash + +# SI post-install script to configure network settings after SI has installed the OS +# SI post-install scripts run in a chroot environment of the final OS image + +. /tmp/post-install/variables.txt + +bondingopts='mode=4 miimon=100 downdelay=0 updelay=0 lacp_rate=fast xmit_hash_policy=1' + +# determine if we should be using a static ip or dhcp +staticIP () { + # Eventually we should use the SI variable IP_ASSIGNMENT_METHOD below to determine this. + # But this requires a patch in both xcat/sysclone (to set si_getimage -ip-assignment method) + # and SI (to set IP_ASSIGNMENT_METHOD as a result of that). Until both of those patches + # are in the main releases, assume that if we have set the IPADDR kernel parm for the boot + # kernel to use static ip, that the final OS should use static ip too. + # Note: the IPADDR environment variable will be set by SI, even if it got it thru dhcp, so + # that is not a reliable way to decide. + str=`cat /proc/cmdline` + #str='netmask=255.255.255.192 IPADDR=10.54.51.11 GATEWAY=10.54.51.1 dns=10.54.51.2 hostname=sap64-4 DEVICE=eth0' + for parm in $str; do + key=`echo $parm|awk -F= '{print $1}'` + value=`echo $parm|awk -F= '{print $2}'` + if [[ $key == "IPADDR" || $key == "ipaddr" ]]; then + return 0 # yes, we should use static ip + fi + done + if [[ -n $IP_ASSIGNMENT_METHOD && ${IP_ASSIGNMENT_METHOD,,} == "static" ]]; then + return 0 # this means yes/true + fi + return 1 +} + +#delete the udev rule in the image +rule_file=`ls /etc/udev/rules.d/*net_persistent_names.rules 2>/dev/null` +if [ -n "$rule_file" ];then + rm -f $rule_file +fi + +hostname $HOSTNAME +bond=bond0 +# this is a softlayer assumption that the two devices on the private net will be eth0 and eth2 +if [[ $DEVICE == "eth0" ]]; then + DEVICE2=eth2 +elif [[ $DEVICE == "eth2" ]]; then + DEVICE2=eth0 +fi +ip addr show|grep -q -E "^[0-9]+:\s+$DEVICE2:" # make sure it exists on the system +if [[ $? == 0 ]]; then + DEVICE2EXISTS="yes" +fi + +device_names=`ifconfig -a | grep -i hwaddr | grep -i 'Ethernet' | grep -v usb| awk '{print $1}'` +str_cfg_file='' +if [ -d "/etc/sysconfig/network-scripts/" ];then + #redhat + dir="/etc/sysconfig/network-scripts" + grep -i HOSTNAME /etc/sysconfig/network + if [ $? -eq 0 ];then + sed -i "s/HOSTNAME=.*/HOSTNAME=$HOSTNAME/g" /etc/sysconfig/network + else + echo "HOSTNAME=$HOSTNAME" >> /etc/sysconfig/network + fi + if staticIP; then + # delete all nic cfg files left over from the golden node + for i in $device_names;do + rm -f "$dir/ifcfg-$i" + done + + # set static ip from variables in variables.txt + # write ifcfg-bond0. For now we assume the installnic should be part of bond0, + # because in SL i think that is always the case. + i="$bond" + str_cfg_file="$dir/ifcfg-$i" + echo "DEVICE=$i" > $str_cfg_file + echo "BOOTPROTO=none" >> $str_cfg_file + echo "ONBOOT=yes" >> $str_cfg_file + echo "USERCTL=no" >> $str_cfg_file + echo 'BONDING_OPTS="'$bondingopts'"' >> $str_cfg_file + echo "IPADDR=$IPADDR" >> $str_cfg_file + echo "NETMASK=$NETMASK" >> $str_cfg_file + echo "NETWORK=$NETWORK" >> $str_cfg_file + echo "BROADCAST=$BROADCAST" >> $str_cfg_file + #todo: add gateway config? Not sure, because the boot kernels gateway might not be the final OS gateway + + # write ifcfg-eth0 + i="$DEVICE" + str_cfg_file="$dir/ifcfg-$i" + echo "DEVICE=$i" > $str_cfg_file + echo "BOOTPROTO=none" >> $str_cfg_file + echo "MASTER=$bond" >> $str_cfg_file + echo "ONBOOT=yes" >> $str_cfg_file + echo "SLAVE=yes" >> $str_cfg_file + echo "USERCTL=no" >> $str_cfg_file + + i="$DEVICE2" + str_cfg_file="$dir/ifcfg-$i" + if [[ $DEVICE2EXISTS == "yes" ]]; then + # write ifcfg-eth2 + echo "DEVICE=$i" > $str_cfg_file + echo "BOOTPROTO=none" >> $str_cfg_file + echo "MASTER=$bond" >> $str_cfg_file + echo "ONBOOT=yes" >> $str_cfg_file + echo "SLAVE=yes" >> $str_cfg_file + echo "USERCTL=no" >> $str_cfg_file + else + rm -f $str_cfg_file # in case it was left over in the image that was captured + fi + + # write modprobe alias config + str_cfg_file="/etc/modprobe.d/$bond.conf" + echo "alias $bond bonding" > $str_cfg_file + + #todo: figure out how to set the default gateway in rhel + else + # use dhcp for all nics + for i in $device_names;do + str_cfg_file="$dir/ifcfg-$i" + echo "DEVICE=$i" > $str_cfg_file + echo "BOOTPROTO=dhcp" >> $str_cfg_file + echo "NM_CONTROLLED=yes" >> $str_cfg_file + echo "ONBOOT=yes" >> $str_cfg_file + done + fi +elif [ -d "/etc/sysconfig/network/" ];then + #suse + dir="/etc/sysconfig/network" + echo "$HOSTNAME" > /etc/HOSTNAME + if staticIP; then + # delete all nic cfg files left over from the golden node + for i in $device_names;do + rm -f "$dir/ifcfg-$i" + done + + # set static ip from variables in variables.txt + # write ifcfg-bond0. For now we assume the installnic should be part of bond0, + # because in SL i think that is always the case. + i="$bond" + str_cfg_file="$dir/ifcfg-$i" + echo "BOOTPROTO=static" > $str_cfg_file + echo "STARTMODE=onboot" >> $str_cfg_file + echo "BONDING_MASTER=yes" >> $str_cfg_file + echo "BONDING_MODULE_OPTS='$bondingopts'" >> $str_cfg_file + echo "NAME='Bonded Interface'" >> $str_cfg_file + echo "IPADDR=$IPADDR" >> $str_cfg_file + echo "NETMASK=$NETMASK" >> $str_cfg_file + echo "NETWORK=$NETWORK" >> $str_cfg_file + echo "BROADCAST=$BROADCAST" >> $str_cfg_file + echo "USERCONTROL=no" >> $str_cfg_file + echo "BONDING_SLAVE_0=$DEVICE" >> $str_cfg_file + if [[ $DEVICE2EXISTS == "yes" ]]; then + echo "BONDING_SLAVE_1=$DEVICE2" >> $str_cfg_file + fi + + # write ifcfg-eth0 + i="$DEVICE" + str_cfg_file="$dir/ifcfg-$i" + echo "BOOTPROTO=none" > $str_cfg_file + echo "STARTMODE=hotplug" >> $str_cfg_file + + i="$DEVICE2" + str_cfg_file="$dir/ifcfg-$i" + if [[ $DEVICE2EXISTS == "yes" ]]; then + # write ifcfg-eth2 + echo "BOOTPROTO=none" > $str_cfg_file + echo "STARTMODE=hotplug" >> $str_cfg_file + else + rm -f $str_cfg_file # in case it was left over in the image that was captured + fi + + # write modprobe alias config + str_cfg_file="/etc/modprobe.d/$bond.conf" + echo "alias $bond bonding" > $str_cfg_file + + # set the default gateway (at this point this is the private nic gateway, to handle provision across vlans) + file=/etc/sysconfig/network/routes + if grep -q -E '^default ' $file; then + # replace the default route that is already in there + sed -i 's/^default .*$/default '$GATEWAY' - -/' $file + else + # no default route yet, append to file + echo "default $GATEWAY - -" >>$file + fi + + # this was the original config of the eth0 nic (without bonding) + #echo "DEVICE=$i" > $str_cfg_file + #echo "BOOTPROTO=static" >> $str_cfg_file + #echo "STARTMODE=onboot" >> $str_cfg_file + #echo "IPADDR=$IPADDR" >> $str_cfg_file + #echo "NETMASK=$NETMASK" >> $str_cfg_file + #echo "NETWORK=$NETWORK" >> $str_cfg_file + #echo "BROADCAST=$BROADCAST" >> $str_cfg_file + #todo: add gateway config? Not sure, because the boot kernels gateway might not be the final OS gateway + else + # use dhcp for all nics + for i in $device_names;do + str_cfg_file="$dir/ifcfg-$i" + echo "DEVICE=$i" > $str_cfg_file + echo "BOOTPROTO=dhcp" >> $str_cfg_file + echo "STARTMODE=onboot" >> $str_cfg_file + echo "DHCLIENT_PRIMARY_DEVICE=yes" >> $str_cfg_file + done + fi +else + #ubuntu + echo "Does not support ubuntu." + exit 1 +fi + diff --git a/xCAT-SoftLayer/si-post-install/20all.makeinitrd b/xCAT-SoftLayer/si-post-install/20all.makeinitrd new file mode 100755 index 000000000..761890045 --- /dev/null +++ b/xCAT-SoftLayer/si-post-install/20all.makeinitrd @@ -0,0 +1,18 @@ +#!/bin/bash + +# This SI post-install script is needed because the initrd that autoyast/kickstart/ubuntu builds when installing +# sles/rh/ubuntu on the golden node may not have the drivers when that initrd runs on the node that is +# being deployed with this image (specifically, drivers to be able to mount the disk). +# So rebuild the initrd on the to-node after putting the image on the disk, but before rebooting. + +#todo: Make this script work on red hat by checking for dracut and using that if it exists. +# And do whatever is necessary on ubuntu. + +if [[ -f /sbin/dracut ]]; then + #todo: implement rh case using dracut + echo "Note: not regenerating the initrd, because dracut is not supported by this node yet." +else + # suse/sles + echo "Running mkinitrd to regenerate the initrd with the drivers needed by this node:" + mkinitrd +fi diff --git a/xCAT-SoftLayer/si-post-install/99all.killsyslog b/xCAT-SoftLayer/si-post-install/99all.killsyslog new file mode 100755 index 000000000..348910189 --- /dev/null +++ b/xCAT-SoftLayer/si-post-install/99all.killsyslog @@ -0,0 +1,25 @@ +#!/bin/bash + +# SI post-install script, run after SI has installed the OS, to kill processes SI does not kill +# (so /a can be umounted cleanly) +# SI post-install scripts run in a chroot environment of the final OS image + +if [ -f "/etc/SuSE-release" ];then + str_out=`ps -ef | grep -v grep | grep syslog-ng` + if [ $? -eq 0 ];then + str_id=`echo $str_out | awk '{print $2}'` + kill -9 $str_id + fi +fi + +# SI starts klogd in the chroot, but does not kill it. Remove this line when SI fixes their bug. +killall klogd + +# flush all write buffers, just in case SI can not umount /a +echo "Syncing file systems" +sync + +#todo: remove +#echo "Processes still using /:" +#fuser -v / +#sleep 30 \ No newline at end of file diff --git a/xCAT-SoftLayer/xCAT-SoftLayer.spec b/xCAT-SoftLayer/xCAT-SoftLayer.spec index df514ddfb..7ee7e06d1 100644 --- a/xCAT-SoftLayer/xCAT-SoftLayer.spec +++ b/xCAT-SoftLayer/xCAT-SoftLayer.spec @@ -17,6 +17,11 @@ BuildArch: noarch Requires: xCAT-server #Requires: xCAT-server >= %{epoch}:%(cat Version|cut -d. -f 1,2) +# perl-ExtUtils-MakeMaker, perl-CPAN, perl-Test-Harness are only available in rhel. +# When this rpm supports being installed in sles, need to add these to xcat-dep. +# perl-SOAP-Lite is already in xcat-dep +Requires: perl-ExtUtils-MakeMaker perl-CPAN perl-Test-Harness perl-SOAP-Lite + Provides: xCAT-SoftLayer = %{epoch}:%{version} %description @@ -43,18 +48,22 @@ xCAT-SoftLayer provides Utilities to make xCAT work in a SoftLayer environment. rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT/%{prefix}/bin mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/xcat/install -mkdir -p $RPM_BUILD_ROOT/install/postscripts +mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/xcat/sysclone/postscripts mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/doc/packages/xCAT-SoftLayer mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/man/man1 mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/doc/man1 +mkdir -p $RPM_BUILD_ROOT/%{prefix}/share/xcat/sysclone/post-install cp -p -R share/xcat/install/* $RPM_BUILD_ROOT/%{prefix}/share/xcat/install/ cp -d bin/* $RPM_BUILD_ROOT/%{prefix}/bin chmod 755 $RPM_BUILD_ROOT/%{prefix}/bin/* -cp -d postscripts/* $RPM_BUILD_ROOT/install/postscripts -chmod 755 $RPM_BUILD_ROOT/install/postscripts/* +cp -d postscripts/* $RPM_BUILD_ROOT/%{prefix}/share/xcat/sysclone/postscripts +chmod 755 $RPM_BUILD_ROOT/%{prefix}/share/xcat/sysclone/postscripts/* + +cp -d si-post-install/* $RPM_BUILD_ROOT/%{prefix}/share/xcat/sysclone/post-install +chmod 755 $RPM_BUILD_ROOT/%{prefix}/share/xcat/sysclone/post-install/* cp LICENSE.html $RPM_BUILD_ROOT/%{prefix}/share/doc/packages/xCAT-SoftLayer chmod 644 $RPM_BUILD_ROOT/%{prefix}/share/doc/packages/xCAT-SoftLayer/* @@ -71,4 +80,8 @@ rm -rf $RPM_BUILD_ROOT %defattr(-,root,root) #%doc LICENSE.html %{prefix} -/install/postscripts + +%post +# We are shipping the postscripts in a sysclone dir and then copying them to /install/postscripts here, +# because we want to allow base xcat to eventually ship them and not conflict on the file name/path +cp -f /%{prefix}/share/xcat/sysclone/postscripts/* /install/postscripts diff --git a/xCAT-genesis-scripts/bin/doxcat b/xCAT-genesis-scripts/bin/doxcat index 4a383982f..7e84e097c 100755 --- a/xCAT-genesis-scripts/bin/doxcat +++ b/xCAT-genesis-scripts/bin/doxcat @@ -4,11 +4,13 @@ # Bug reported by Jeff Lang . Thanks, Jeff! # -modprobe acpi_cpufreq +modprobe acpi_cpufreq 2>/dev/null # on some machines this fails modprobe cpufreq_ondemand -for gov in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do - echo -n ondemand > $gov -done +if ls /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor &>/dev/null; then + for gov in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do + echo -n ondemand > $gov + done +fi if [ ! -z "$BOOTIF" ]; then BOOTIF=`echo $BOOTIF|sed -e s/01-// -e s/-/:/g` echo -n "Waiting for device with address $BOOTIF to appear.." @@ -74,21 +76,89 @@ echo '};' >> /var/lib/lldpad/lldpad.conf done echo '};' >> /var/lib/lldpad/lldpad.conf lldpad -d -dhclient -cf /etc/dhclient.conf -pf /var/run/dhclient.$bootnic.pid $bootnic & -#we'll kick of IPv6 and IPv4 on all nics, but not wait for them to come up unless doing discovery, to reduce -#chances that we'll perform a partial discovery -#in other scenarios where downed non-bootnics cause issues, will rely on retries to fix things up -dhclient -6 -pf /var/run/dhclient6.$bootnic.pid $bootnic -lf /var/lib/dhclient/dhclient6.leases & -NICSTOBRINGUP=`ip link|grep mtu|grep -v LOOPBACK|grep -v $bootnic|grep -v usb|grep -v ,UP|awk -F: '{print $2}'` -export NICSTOBRINGUP -for nic in $NICSTOBRINGUP; do - (while ! ethtool $nic | grep Link\ detected|grep yes > /dev/null; do sleep 5; done; dhclient -cf /etc/dhclient.conf -pf /var/run/dhclient.$nic.pid $nic ) & - (while ! ethtool $nic | grep Link\ detected|grep yes > /dev/null; do sleep 5; done; dhclient -cf /etc/dhclient.conf -6 -pf /var/run/dhclient6.$nic.pid -lf /var/lib/dhclient/dhclient6.leases $nic ) & -done -openssl genrsa -out /etc/xcat/certkey.pem 4096 > /dev/null 2>&1 & - - +# Caclulate the broadcast address of a given IP address and mask. +bcastcalc(){ + read oct1 oct2 oct3 oct4 << HERE + $(echo "$1" | sed -e 's/\./ /g') +HERE + read msk1 msk2 msk3 msk4 << HERE + $(echo "$2" | sed -e 's/\./ /g') +HERE + ipa=$(($oct1+(255-($oct1 | $msk1)))) + ipb=$(($oct2+(255-($oct2 | $msk2)))) + ipc=$(($oct3+(255-($oct3 | $msk3)))) + ipd=$(($oct4+(255-($oct4 | $msk4)))) + echo "$ipa.$ipb.$ipc.$ipd" +} + +# Calculates the number of bits in a netmask for converting something like 255.255.255.192 to 26 so +# you can use the notation 10.0.0.1/26 +mask2prefix() { + nbits=0 + old_ifs=$IFS + IFS=. + for dec in $1 ; do + case $dec in + 255) let nbits+=8;; + 254) let nbits+=7;; + 252) let nbits+=6;; + 248) let nbits+=5;; + 240) let nbits+=4;; + 224) let nbits+=3;; + 192) let nbits+=2;; + 128) let nbits+=1;; + 0);; + *) echo "Error: $dec is not recognised"; exit 1 + esac + done + IFS=$old_ifs + echo "$nbits" +} + +# see if they specified static ip info, otherwise use dhcp +for parm in `cat /proc/cmdline`; do + key=`echo $parm|awk -F= '{print $1}'` + value=`echo $parm|awk -F= '{print $2}'` + if [[ ${key,,} == "hostip" || ${key,,} == "ipaddr" ]]; then + hostip=$value + elif [[ ${key,,} == "netmask" ]]; then + netmask=$value + elif [[ ${key,,} == "gateway" ]]; then + gateway=$value + fi +done +if [[ -n $hostip && -n $netmask && -n $gateway && -n $bootnic ]]; then + # doing static ip + # the device was determined above from the bootif mac, and put in bootnic + numbits=$(mask2prefix $netmask) + broadcast=$(bcastcalc $hostip $netmask) + echo "Setting static IP=$hostip/$numbits broadcast=$broadcast gateway=$gateway device=$bootnic BOOTIF=$BOOTIF ..." + ip addr add $hostip/$numbits broadcast $broadcast dev $bootnic scope global label $bootnic + ip link set $bootnic up + ip route replace to default via $gateway dev $bootnic + # in softlayer it takes up to 60 seconds for the nic to actually be able to communicate + echo -n Waiting to reach xCAT mgmt node $gateway. + xcatretries=60 + while [ $((xcati+=1)) -le $xcatretries ] && ! ping -c2 -w3 $gateway >/dev/null 2>&1; do echo -n .; done + if [ $xcati -le $xcatretries ]; then echo " success"; else echo " failed"; fi + sleep 3 +else + echo "Setting IP via DHCP..." + dhclient -cf /etc/dhclient.conf -pf /var/run/dhclient.$bootnic.pid $bootnic & + #we'll kick of IPv6 and IPv4 on all nics, but not wait for them to come up unless doing discovery, to reduce + #chances that we'll perform a partial discovery + #in other scenarios where downed non-bootnics cause issues, will rely on retries to fix things up + dhclient -6 -pf /var/run/dhclient6.$bootnic.pid $bootnic -lf /var/lib/dhclient/dhclient6.leases & + NICSTOBRINGUP=`ip link|grep mtu|grep -v LOOPBACK|grep -v $bootnic|grep -v usb|grep -v ,UP|awk -F: '{print $2}'` + export NICSTOBRINGUP + for nic in $NICSTOBRINGUP; do + (while ! ethtool $nic | grep Link\ detected|grep yes > /dev/null; do sleep 5; done; dhclient -cf /etc/dhclient.conf -pf /var/run/dhclient.$nic.pid $nic ) & + (while ! ethtool $nic | grep Link\ detected|grep yes > /dev/null; do sleep 5; done; dhclient -cf /etc/dhclient.conf -6 -pf /var/run/dhclient6.$nic.pid -lf /var/lib/dhclient/dhclient6.leases $nic ) & + done +fi + +openssl genrsa -out /etc/xcat/certkey.pem 4096 > /dev/null 2>&1 & gripeiter=101 echo -n "Acquiring network addresses.."