diff --git a/xCAT-probe/lib/perl/probe_utils.pm b/xCAT-probe/lib/perl/probe_utils.pm index e0d5b7074..729dcebad 100644 --- a/xCAT-probe/lib/perl/probe_utils.pm +++ b/xCAT-probe/lib/perl/probe_utils.pm @@ -48,12 +48,12 @@ sub send_msg { } if ($output eq "stdout") { - print "$flag $msg\n"; + print "$flag$msg\n"; } else { if (!open(LOGFILE, ">> $output")) { return 1; } - print LOGFILE "$flag $msg\n"; + print LOGFILE "$flag$msg\n"; close LOGFILE; } return 0; @@ -389,12 +389,8 @@ sub get_network{ return $net if (!is_ip_addr($ip)); return $net if ($mask !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); - my $bin_mask = 0; - $bin_mask = (($1 + 0) << 24) + (($2 + 0) << 16) + (($3 + 0) << 8) + ($4 + 0) if ($mask =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); - - my $bin_ip = 0; - $bin_ip = (($1 + 0) << 24) + (($2 + 0) << 16) + (($3 + 0) << 8) + ($4 + 0) if ($ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); - + my $bin_mask = unpack("N", inet_aton($mask)); + my $bin_ip = unpack("N", inet_aton($ip));; my $net_int32 = $bin_mask & $bin_ip; $net = ($net_int32 >> 24) . "." . (($net_int32 >> 16) & 0xff) . "." . (($net_int32 >> 8) & 0xff) . "." . ($net_int32 & 0xff); return "$net/$mask"; diff --git a/xCAT-probe/lib/perl/xCAT/NetworkUtils.pm b/xCAT-probe/lib/perl/xCAT/NetworkUtils.pm new file mode 100644 index 000000000..2f699c8fa --- /dev/null +++ b/xCAT-probe/lib/perl/xCAT/NetworkUtils.pm @@ -0,0 +1,2620 @@ +#!/usr/bin/env perl +# IBM(c) 2010 EPL license http://www.eclipse.org/legal/epl-v10.html +package xCAT::NetworkUtils; + +BEGIN +{ + $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; +} + +# if AIX - make sure we include perl 5.8.2 in INC path. +# Needed to find perl dependencies shipped in deps tarball. +if ($^O =~ /^aix/i) { + unshift(@INC, qw(/usr/opt/perl5/lib/5.8.2/aix-thread-multi /usr/opt/perl5/lib/5.8.2 /usr/opt/perl5/lib/site_perl/5.8.2/aix-thread-multi /usr/opt/perl5/lib/site_perl/5.8.2)); +} + +use lib "$::XCATROOT/lib/perl"; +use POSIX qw(ceil); +use File::Path; +use Math::BigInt; +use Socket; +use xCAT::GlobalDef; +#use Data::Dumper; +use strict; +use warnings "all"; +my $socket6support = eval { require Socket6 }; + +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(getipaddr); + +my $utildata; #data to persist locally +#-------------------------------------------------------------------------------- + +=head1 xCAT::NetworkUtils + +=head2 Package Description + +This program module file, is a set of network utilities used by xCAT commands. + +=cut + +#------------------------------------------------------------- + +#------------------------------------------------------------------------------- + +=head3 getNodeDomains + + Gets the network domain for a list of nodes + + The domain value comes from the network definition + associated with the node ip address. + + If the network domain is not set then the default is to + use the site.domain value + + Arguments: + list of nodes + Returns: + error - undef + success - hash ref of domains for each node + Globals: + $::VERBOSE + Error: + Example: + my $nodedomains = xCAT::NetworkUtils->getNodeDomains(\@nodes, $callback); + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub getNodeDomains() +{ + my $class = shift; + my $nodes = shift; + + my @nodelist = @$nodes; + my %nodedomains; + + # Get the network info for each node + my %nethash = xCAT::DBobjUtils->getNetwkInfo(\@nodelist); + + # get the site domain value + my @domains = xCAT::TableUtils->get_site_attribute("domain"); + my $sitedomain = $domains[0]; + + # for each node - set hash value to network domain or default + # to site domain + foreach my $node (@nodelist) { + unless (defined($node)) {next;} + if (defined($nethash{$node}) && $nethash{$node}{domain}) { + $nodedomains{$node} = $nethash{$node}{domain}; + } else { + $nodedomains{$node} = $sitedomain; + } + } + + return \%nodedomains; +} + +#------------------------------------------------------------------------------- + +=head3 gethostnameandip + Works for both IPv4 and IPv6. + Takes either a host name or an IP address string + and performs a lookup on that name, + returns an array with two elements: the hostname, the ip address + if the host name or ip address can not be resolved, + the corresponding element in the array will be undef + Arguments: + hostname or ip address + Returns: the hostname and the ip address + Globals: + + Error: + none + Example: + my ($host, $ip) = xCAT::NetworkUtils->gethostnameandip($iporhost); + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub gethostnameandip() +{ + my ($class, $iporhost) = @_; + + if (($iporhost =~ /\d+\.\d+\.\d+\.\d+/) || ($iporhost =~ /:/)) #ip address + { + return (xCAT::NetworkUtils->gethostname($iporhost), $iporhost); + } + else #hostname + { + return ($iporhost, xCAT::NetworkUtils->getipaddr($iporhost)); + } +} + +#------------------------------------------------------------------------------- + +=head3 gethostname + Works for both IPv4 and IPv6. + Takes an IP address string and performs a lookup on that name, + returns the hostname of the ip address + if the ip address can not be resolved, returns undef + Arguments: + ip address + Returns: the hostname + Globals: + cache: %::iphosthash + Error: + none + Example: + my $host = xCAT::NetworkUtils->gethostname($ip); + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub gethostname() +{ + my ($class, $iporhost) = @_; + + if (!defined($iporhost)) + { + return undef; + } + + if (ref($iporhost) eq 'ARRAY') + { + $iporhost = @{$iporhost}[0]; + if (!$iporhost) + { + return undef; + } + } + + if (($iporhost !~ /\d+\.\d+\.\d+\.\d+/) && ($iporhost !~ /:/)) + { + #why you do so? pass in a hostname and only want a hostname?? + return $iporhost; + } + #cache, do not lookup DNS each time + if (defined($::iphosthash{$iporhost}) && $::iphosthash{$iporhost}) + { + return $::iphosthash{$iporhost}; + } + else + { + if ($socket6support) # the getaddrinfo and getnameinfo supports both IPv4 and IPv6 + { + my ($family, $socket, $protocol, $ip, $name) = Socket6::getaddrinfo($iporhost,0,AF_UNSPEC,SOCK_STREAM,6); #specifically ask for TCP capable records, maximizing chance of no more than one return per v4/v6 + my $host = (Socket6::getnameinfo($ip))[0]; + if ($host eq $iporhost) # can not resolve + { + return undef; + } + if ($host) + { + $host =~ s/\..*//; #short hostname + } + return $host; + } + else + { + #it is possible that no Socket6 available, + #but passing in IPv6 address, such as ::1 on loopback + if ($iporhost =~ /:/) + { + return undef; + } + my $hostname = gethostbyaddr(inet_aton($iporhost), AF_INET); + if ( $hostname ) { + $hostname =~ s/\..*//; #short hostname + } + return $hostname; + } + } +} + +#------------------------------------------------------------------------------- + +=head3 getipaddr + Works for both IPv4 and IPv6. + Takes a hostname string and performs a lookup on that name, + returns the the ip address of the hostname + if the hostname can not be resolved, returns undef + Arguments: + hostname + Optional: + GetNumber=>1 (return the address as a BigInt instead of readable string) + Returns: ip address + Globals: + cache: %::hostiphash + Error: + none + Example: + my $ip = xCAT::NetworkUtils->getipaddr($hostname); + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub getipaddr +{ + my $iporhost = shift; + if ($iporhost eq 'xCAT::NetworkUtils') { #was called with -> syntax + $iporhost = shift; + } + my %extraarguments = @_; + + if (!defined($iporhost)) + { + return undef; + } + + if (ref($iporhost) eq 'ARRAY') + { + $iporhost = @{$iporhost}[0]; + if (!$iporhost) + { + return undef; + } + } + + #go ahead and do the reverse lookup on ip, useful to 'frontend' aton/pton and also to + #spit out a common abbreviation if leading zeroes or using different ipv6 presentation rules + #if ($iporhost and ($iporhost =~ /\d+\.\d+\.\d+\.\d+/) || ($iporhost =~ /:/)) + #{ + # #pass in an ip and only want an ip?? + # return $iporhost; + #} + + #cache, do not lookup DNS each time + if ($::hostiphash and defined($::hostiphash{$iporhost}) && $::hostiphash{$iporhost}) + { + return $::hostiphash{$iporhost}; + } + else + { + if ($socket6support) # the getaddrinfo and getnameinfo supports both IPv4 and IPv6 + { + my @returns; + my $reqfamily=AF_UNSPEC; + if ($extraarguments{OnlyV6}) { + $reqfamily=AF_INET6; + } elsif ($extraarguments{OnlyV4}) { + $reqfamily=AF_INET; + } + my @addrinfo = Socket6::getaddrinfo($iporhost,0,$reqfamily,SOCK_STREAM,6); + my ($family, $socket, $protocol, $ip, $name) = splice(@addrinfo,0,5); + while ($ip) + { + if ($extraarguments{GetNumber}) { #return a BigInt for compare, e.g. for comparing ip addresses for determining if they are in a common network or range + my $ip = (Socket6::getnameinfo($ip, Socket6::NI_NUMERICHOST()))[0]; + my $bignumber = Math::BigInt->new(0); + foreach (unpack("N*",Socket6::inet_pton($family,$ip))) { #if ipv4, loop will iterate once, for v6, will go 4 times + $bignumber->blsft(32); + $bignumber->badd($_); + } + push(@returns,$bignumber); + } else { + push @returns,(Socket6::getnameinfo($ip, Socket6::NI_NUMERICHOST()))[0]; + } + if (scalar @addrinfo and $extraarguments{GetAllAddresses}) { + ($family, $socket, $protocol, $ip, $name) = splice(@addrinfo,0,5); + } else { + $ip=0; + } + } + unless ($extraarguments{GetAllAddresses}) { + return $returns[0]; + } + return @returns; + } + else + { + #return inet_ntoa(inet_aton($iporhost)) + #TODO, what if no scoket6 support, but passing in a IPv6 hostname? + if ($iporhost =~ /:/) { #ipv6 + return undef; + #die "Attempt to process IPv6 address, but system does not have requisite IPv6 perl support"; + } + my $packed_ip; + $iporhost and $packed_ip = inet_aton($iporhost); + if (!$packed_ip) + { + return undef; + } + if ($extraarguments{GetNumber}) { #only 32 bits, no for loop needed. + return Math::BigInt->new(unpack("N*",$packed_ip)); + } + return inet_ntoa($packed_ip); + } + } +} + +#------------------------------------------------------------------------------- + +=head3 linklocaladdr + Only for IPv6. + Takes a mac address, calculate the IPv6 link local address + Arguments: + mac address + Returns: + ipv6 link local address. returns undef if passed in a invalid mac address + Globals: + Error: + none + Example: + my $linklocaladdr = xCAT::NetworkUtils->linklocaladdr($mac); + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub linklocaladdr { + my ($class, $mac) = @_; + $mac = lc($mac); + my $localprefix = "fe80"; + + my ($m1, $m2, $m3, $m6, $m7, $m8); + # mac address can be 00215EA376B0 or 00:21:5E:A3:76:B0 + if($mac =~ /^([0-9A-Fa-f]{2}).*?([0-9A-Fa-f]{2}).*?([0-9A-Fa-f]{2}).*?([0-9A-Fa-f]{2}).*?([0-9A-Fa-f]{2}).*?([0-9A-Fa-f]{2})$/) + { + ($m1, $m2, $m3, $m6, $m7, $m8) = ($1, $2, $3, $4, $5, $6); + } + else + { + #not a valid mac address + return undef; + } + my ($m4, $m5) = ("ff","fe"); + + #my $bit = (int $m1) & 2; + #if ($bit) { + # $m1 = $m1 - 2; + #} else { + # $m1 = $m1 + 2; + #} + $m1 = hex($m1); + $m1 = $m1 ^ 2; + $m1 = sprintf("%x", $m1); + + $m1 = $m1 . $m2; + $m3 = $m3 . $m4; + $m5 = $m5 . $m6; + $m7 = $m7 . $m8; + + my $laddr = join ":", $m1, $m3, $m5, $m7; + $laddr = join "::", $localprefix, $laddr; + + return $laddr; +} + + +#------------------------------------------------------------------------------- + +=head3 ishostinsubnet + Works for both IPv4 and IPv6. + Takes an ip address, the netmask and a subnet, + chcek if the ip address is in the subnet + Arguments: + ip address, netmask, subnet + Returns: + 1 - if the ip address is in the subnet + 0 - if the ip address is NOT in the subnet + Globals: + Error: + none + Example: + if(xCAT::NetworkUtils->ishostinsubnet($ip, $netmask, $subnet); + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub ishostinsubnet { + my ($class, $ip, $mask, $subnet) = @_; + + #safe guard + if (!defined($ip) || !defined($mask) || !defined($subnet)) + { + return 0; + } + my $numbits=32; + if ($ip =~ /:/) {#ipv6 + $numbits=128; + } + if ($mask) { + if ($mask =~ /\//) { + $mask =~ s/^\///; + $mask=Math::BigInt->new("0b".("1"x$mask).("0"x($numbits-$mask))); + } else { + $mask=getipaddr($mask,GetNumber=>1); + } + } else { #CIDR notation supported + if ($subnet && ($subnet =~ /\//)) { + ($subnet,$mask) = split /\//,$subnet,2; + $mask=Math::BigInt->new("0b".("1"x$mask).("0"x($numbits-$mask))); + } else { + die "ishostinsubnet must either be called with a netmask or CIDR /bits notation"; + } + } + if ($subnet && ($subnet =~ /\//)) #remove CIDR suffix from subnet + { + $subnet =~ s/\/.*$//; + } + $ip = getipaddr($ip,GetNumber=>1); + $subnet = getipaddr($subnet,GetNumber=>1); + $ip &= $mask; + if ($ip && $subnet && ($ip == $subnet)) { + return 1; + } else { + return 0; + } +} + +#----------------------------------------------------------------------------- + +=head3 setup_ip_forwarding + + Sets up ip forwarding on localhost + +=cut + +#----------------------------------------------------------------------------- +sub setup_ip_forwarding +{ + my ($class, $enable)=@_; + if (xCAT::Utils->isLinux()) { + my $conf_file="/etc/sysctl.conf"; + `grep "net.ipv4.ip_forward" $conf_file`; + if ($? == 0) { + `sed -i "s/^net.ipv4.ip_forward = .*/net.ipv4.ip_forward = $enable/" $conf_file`; + `sed -i "s/^#net.ipv4.ip_forward *= *.*/net.ipv4.ip_forward = $enable/" $conf_file`; #debian/ubuntu have different default format + } else { + `echo "net.ipv4.ip_forward = $enable" >> $conf_file`; + } + `sysctl -e -p $conf_file`; # workaround for redhat bug 639821 + } + else + { + `no -o ipforwarding=$enable`; + } + return 0; +} + +#----------------------------------------------------------------------------- + +=head3 setup_ipv6_forwarding + + Sets up ipv6 forwarding on localhost + +=cut + +#----------------------------------------------------------------------------- +sub setup_ipv6_forwarding +{ + my ($class, $enable)=@_; + if (xCAT::Utils->isLinux()) { + my $conf_file="/etc/sysctl.conf"; + `grep "net.ipv6.conf.all.forwarding" $conf_file`; + if ($? == 0) { + `sed -i "s/^net.ipv6.conf.all.forwarding = .*/net.ipv6.conf.all.forwarding = $enable/" $conf_file`; + } else { + `echo "net.ipv6.conf.all.forwarding = $enable" >> $conf_file`; + } + `sysctl -e -p $conf_file`; + } + else + { + `no -o ip6forwarding=$enable`; + } + return 0; +} + +#------------------------------------------------------------------------------- + +=head3 prefixtomask + Convert the IPv6 prefix length(e.g. 64) to the netmask(e.g. ffff:ffff:ffff:ffff:0000:0000:0000:0000). + Till now, the netmask format ffff:ffff:ffff:: only works for AIX NIM + + Arguments: + prefix length + Returns: + netmask - netmask like ffff:ffff:ffff:ffff:0000:0000:0000:0000 + 0 - if the prefix length is not correct + Globals: + Error: + none + Example: + my #netmask = xCAT::NetworkUtils->prefixtomask($prefixlength); + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub prefixtomask { + my ($class, $prefixlength) = @_; + + if (($prefixlength < 1) || ($prefixlength > 128)) + { + return 0; + } + + my $number=Math::BigInt->new("0b".("1"x$prefixlength).("0"x(128-$prefixlength))); + my $mask = $number->as_hex(); + $mask =~ s/^0x//; + $mask =~ s/(....)/$1/g; + return $mask; +} + +#------------------------------------------------------------------------------- + +=head3 my_ip_in_subnet + Get the facing ip for some specific network + + Arguments: + net - subnet, such as 192.168.0.01 + mask - netmask, such as 255.255.255.0 + Returns: + facing_ip - The local ip address in the subnet, + returns undef if no local ip address is in the subnet + Globals: + Error: + none + Example: + my $facingip = xCAT::NetworkUtils->my_ip_in_subnet($net, $mask); + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub my_ip_in_subnet +{ + my ($class, $net, $mask) = @_; + + if (!$net || !$mask) + { + return undef; + } + + my $fmask = xCAT::NetworkUtils::formatNetmask($mask, 0, 1); + + my $localnets = xCAT::NetworkUtils->my_nets(); + + return $localnets->{"$net\/$fmask"}; +} + +#------------------------------------------------------------------------------- + +=head3 ip_forwarding_enabled + Check if the ip_forward enabled on the system + + Arguments: + Returns: + 1 - The ip_forwarding is eanbled + 0 - The ip_forwarding is not eanbled + Globals: + Error: + none + Example: + if(xCAT::NetworkUtils->ip_forwarding_enabled()) + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub ip_forwarding_enabled +{ + + my $enabled; + if (xCAT::Utils->isLinux()) + { + $enabled = `sysctl -n net.ipv4.ip_forward`; + chomp($enabled); + } + else + { + $enabled = `no -o ipforwarding`; + chomp($enabled); + $enabled =~ s/ipforwarding\s+=\s+//; + } + return $enabled; +} +#------------------------------------------------------------------------------- + +=head3 get_nic_ip + Get the ip address for the node nics + + Arguments: + Returns: + Hash of the mapping of the nic and the ip addresses + Globals: + Error: + none + Example: + xCAT::NetworkUtils->get_nic_ip() + Comments: + none +=cut + +#------------------------------------------------------------------------------- + +sub get_nic_ip +{ + my $nic; + my %iphash; + my $mode = "MULTICAST"; + my $payingattention=0; + my $interface; + my $keepcurrentiface; + + + if (xCAT::Utils->isAIX()) { + ############################################################## + # Should look like this for AIX: + # en0: flags=4e080863,80 + # inet 30.0.0.1 netmask 0xffffff00 broadcast 30.0.0.255 + # inet 192.168.2.1 netmask 0xffffff00 broadcast 192.168.2.255 + # en1: ... + # + ############################################################## + my $cmd = "ifconfig -a"; + my $result = `$cmd`; + ############################################# + # Error running command + ############################################# + if ( !$result ) { + return undef; + } + my @adapter = split /(\w+\d+):\s+flags=/, $result; + foreach ( @adapter ) { + if ($_ =~ /^(en\d)/) { + $nic = $1; + next; + } + if ( !($_ =~ /LOOPBACK/ ) and + $_ =~ /UP(,|>)/ and + $_ =~ /$mode/ ) { + my @ip = split /\n/; + for my $ent ( @ip ) { + if ( $ent =~ /^\s*inet\s+(\d+\.\d+\.\d+\.\d+)/ ) { + $iphash{$nic} = $1; + next; + } + } + } + } + } + else { # linux + my @ipoutput = `ip addr`; + ############################################# + # Error running command + ############################################# + if ( !@ipoutput ) { + return undef; + } + foreach my $line (@ipoutput) { + if ($line =~ /^\d/) { # new interface, new context.. + if ($interface and not $keepcurrentiface) { + #don't bother reporting unusable nics + delete $iphash{$interface}; + } + $keepcurrentiface=0; + if ( !($line =~ /LOOPBACK/ ) and + $line =~ /UP( |,|>)/ and + $line =~ /$mode/ ) { + + $payingattention=1; + $line =~ /^([^:]*): ([^:]*):/; + $interface=$2; + } else { + $payingattention=0; + next; + } + } + unless ($payingattention) { next; } + if ($line =~ /inet/) { + $keepcurrentiface=1; + } + if ( $line =~ /^\s*inet \s*(\d+\.\d+\.\d+\.\d+)/ ) { + $iphash{$interface} = $1; + } + } + } + return \%iphash; +} + +#------------------------------------------------------------------------------- + +=head3 classful_networks_for_net_and_mask + + Arguments: + network and mask + Returns: + a list of classful subnets that constitute the entire potentially classless arguments + Globals: + none + Error: + none + Example: + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub classful_networks_for_net_and_mask +{ + my $network = shift; + my $mask = shift; + my $given_mask = 0; + if ($mask =~ /\./) + { + $given_mask = 1; + my $masknumber = unpack("N", inet_aton($mask)); + $mask = 32; + until ($masknumber % 2) + { + $masknumber = $masknumber >> 1; + $mask--; + } + } + + my @results; + my $bitstoeven = (8 - ($mask % 8)); + if ($bitstoeven eq 8) { $bitstoeven = 0; } + my $resultmask = $mask + $bitstoeven; + if ($given_mask) + { + $resultmask = + inet_ntoa(pack("N", (2**$resultmask - 1) << (32 - $resultmask))); + } + push @results, $resultmask; + + my $padbits = (32 - ($bitstoeven + $mask)); + my $numchars = int(($mask + $bitstoeven) / 4); + my $curmask = 2**$mask - 1 << (32 - $mask); + my $nown = unpack("N", inet_aton($network)); + $nown = $nown & $curmask; + my $highn = $nown + ((2**$bitstoeven - 1) << (32 - $mask - $bitstoeven)); + + while ($nown <= $highn) + { + push @results, inet_ntoa(pack("N", $nown)); + + #$rethash->{substr($nowhex, 0, $numchars)} = $network; + $nown += 1 << (32 - $mask - $bitstoeven); + } + return @results; +} + + +#------------------------------------------------------------------------------- + +=head3 my_hexnets + + Arguments: + none + Returns: + Globals: + none + Error: + none + Example: + xCAT::NetworkUtils->my_hexnets + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub my_hexnets +{ + my $rethash; + my @nets = split /\n/, `/sbin/ip addr`; + foreach (@nets) + { + my @elems = split /\s+/; + unless (/^\s*inet\s/) + { + next; + } + (my $curnet, my $maskbits) = split /\//, $elems[2]; + my $bitstoeven = (4 - ($maskbits % 4)); + if ($bitstoeven eq 4) { $bitstoeven = 0; } + my $padbits = (32 - ($bitstoeven + $maskbits)); + my $numchars = int(($maskbits + $bitstoeven) / 4); + my $curmask = 2**$maskbits - 1 << (32 - $maskbits); + my $nown = unpack("N", inet_aton($curnet)); + $nown = $nown & $curmask; + my $highn = + $nown + ((2**$bitstoeven - 1) << (32 - $maskbits - $bitstoeven)); + + while ($nown <= $highn) + { + my $nowhex = sprintf("%08x", $nown); + $rethash->{substr($nowhex, 0, $numchars)} = $curnet; + $nown += 1 << (32 - $maskbits - $bitstoeven); + } + } + return $rethash; +} + +#------------------------------------------------------------------------------- + +=head3 get_host_from_ip + Description: + Get the hostname of an IP addresses. First from hosts table, and then try system resultion. + If there is a shortname, it will be returned. Otherwise it will return long name. If the IP cannot be resolved, return undef; + + Arguments: + $ip: the IP to get; + + Returns: + Return: the hostname. +For an example + + Globals: + none + + Error: + none + + Example: + xCAT::NetworkUtils::get_host_from_ip('192.168.200.1') + + Comments: +=cut + +#----------------------------------------------------------------------- +sub get_host_from_ip +{ + my $ip = shift; +} + +#------------------------------------------------------------------------------- + +=head3 isPingable + Description: + Check if an IP address can be pinged + + Arguments: + $ip: the IP to ping; + + Returns: + Return: 1 indicates yes; 0 indicates no. +For an example + + Globals: + none + + Error: + none + + Example: + xCAT::NetworkUtils::isPingable('192.168.200.1') + + Comments: + none +=cut + +#----------------------------------------------------------------------- +my %PING_CACHE; +sub isPingable +{ + my $ip = shift; + + my $rc; + if ( exists $PING_CACHE{ $ip}) + { + $rc = $PING_CACHE{ $ip}; + } + else + { + my $res = `LANG=C ping -c 1 -w 5 $ip 2>&1`; + if ( $res =~ /100% packet loss/g) + { + $rc = 1; + } + else + { + $rc = 0; + } + $PING_CACHE{ $ip} = $rc; + } + + return ! $rc; +} + +#------------------------------------------------------------------------------- + +=head3 my_nets + Description: + Return a hash ref that contains all subnet and netmask on the mn (or sn). This subroutine can be invoked on both Linux and AIX. + + Arguments: + none. + + Returns: + Return a hash ref. Each entry will be: =>; + For an example: + '192.168.200.0/255.255.255.0' => '192.168.200.246'; +For an example + + Globals: + none + + Error: + none + + Example: + xCAT::NetworkUtils::my_nets(). + + Comments: + none +=cut +#----------------------------------------------------------------------- +sub my_nets +{ + require xCAT::Table; + my $rethash; + my @nets; + my $v6net; + my $v6ip; + if ( $^O eq 'aix') + { + @nets = split /\n/, `/usr/sbin/ifconfig -a`; + } + else + { + @nets = split /\n/, `/sbin/ip addr`; #could use ip route, but to match hexnets... + } + foreach (@nets) + { + $v6net = ''; + my @elems = split /\s+/; + unless (/^\s*inet/) + { + next; + } + my $curnet; my $maskbits; + if ( $^O eq 'aix') + { + if ($elems[1] eq 'inet6') + { + $v6net=$elems[2]; + $v6ip=$elems[2]; + $v6ip =~ s/\/.*//; # ipv6 address 4000::99/64 + $v6ip =~ s/\%.*//; # ipv6 address ::1%1/128 + } + else + { + $curnet = $elems[2]; + $maskbits = formatNetmask( $elems[4], 2, 1); + } + } + else + { + if ($elems[1] eq 'inet6') + { + next; #Linux IPv6 TODO, do not return IPv6 networks on Linux for now + } + ($curnet, $maskbits) = split /\//, $elems[2]; + } + if (!$v6net) + { + my $curmask = 2**$maskbits - 1 << (32 - $maskbits); + my $nown = unpack("N", inet_aton($curnet)); + $nown = $nown & $curmask; + my $textnet=inet_ntoa(pack("N",$nown)); + $textnet.="/$maskbits"; + $rethash->{$textnet} = $curnet; + } + else + { + $rethash->{$v6net} = $v6ip; + } + } + + + # now get remote nets + my $nettab = xCAT::Table->new("networks"); + #my $sitetab = xCAT::Table->new("site"); + #my $master = $sitetab->getAttribs({key=>'master'},'value'); + #$master = $master->{value}; + my @masters = xCAT::TableUtils->get_site_attribute("master"); + my $master = $masters[0]; + my @vnets = $nettab->getAllAttribs('net','mgtifname','mask'); + + foreach(@vnets){ + my $n = $_->{net}; + my $if = $_->{mgtifname}; + my $nm = $_->{mask}; + if (!$n || !$if || !$nm) + { + next; #incomplete network + } + if ($if =~ /!remote!/) { #only take in networks with special interface + $nm = formatNetmask($nm, 0 , 1); + $n .="/$nm"; + #$rethash->{$n} = $if; + $rethash->{$n} = $master; + } + } + return $rethash; +} +#------------------------------------------------------------------------------- + +=head3 my_if_netmap + Arguments: + none + Returns: + hash of networks to interface names + Globals: + none + Error: + none + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub my_if_netmap +{ + my $net; + if (scalar(@_)) + { #called with the other syntax + $net = shift; + } + my @rtable = split /\n/, `netstat -rn`; + if ($?) + { + return "Unable to run netstat, $?"; + } + my %retmap; + foreach (@rtable) + { + if (/^\D/) { next; } #skip headers + if (/^\S+\s+\S+\s+\S+\s+\S*G/) + { + next; + } #Skip networks that require gateways to get to + /^(\S+)\s.*\s(\S+)$/; + $retmap{$1} = $2; + } + return \%retmap; +} + +#------------------------------------------------------------------------------- + +=head3 my_ip_facing + Returns my ip address in the same network with the specified node + Linux only + Arguments: + nodename + Returns: + result and error message or my ip address + 1. If node can not be resolved, the return info will be like this: + [1, "The $node can not be resolved"]. + 2. If no IP found that matching the giving node, the return info will be: + [2, "The IP address of node $node is in an undefined subnet"]. + 3. If IP found: + [0,ip1,ip2,...] + Globals: + none + Error: + none + Example: + my @ip = xCAT::NetworkUtils->my_ip_facing($peerip) # return multiple + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub my_ip_facing +{ + my $peer = shift; + if (@_) + { + $peer = shift; + } + + return my_ip_facing_aix( $peer) if ( $^O eq 'aix'); + my @rst; + my $peernumber = inet_aton($peer); #TODO: IPv6 support + unless ($peernumber) { + $rst[0] = 1; + $rst[1] = "The $peer can not be resolved"; + return @rst; } + my $noden = unpack("N", $peernumber); + my @nets = split /\n/, `/sbin/ip addr`; + + my @ips; + foreach (@nets) + { + my @elems = split /\s+/; + unless (/^\s*inet\s/) + { + next; + } + (my $curnet, my $maskbits) = split /\//, $elems[2]; + my $curmask = 2**$maskbits - 1 << (32 - $maskbits); + my $curn = unpack("N", inet_aton($curnet)); + if (($noden & $curmask) == ($curn & $curmask)) + { + push @ips, $curnet; + } + } + + if (@ips) { + $rst[0] = 0; + push @rst, @ips; + } else { + $rst[0] = 2; + $rst[1] = "The IP address of node $peer is in an undefined subnet"; + } + return @rst; +} + +#------------------------------------------------------------------------------- + +=head3 my_ip_facing_aix + Returns my ip address + AIX only + Arguments: + nodename + Returns: + Globals: + none + Error: + none + Example: + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub my_ip_facing_aix +{ + my $peer = shift; + my @nets = `ifconfig -a`; + chomp @nets; + my @ips; + my @rst; + foreach my $net (@nets) + { + my ($curnet,$netmask); + if ( $net =~ /^\s*inet\s+([\d\.]+)\s+netmask\s+(\w+)\s+broadcast/) + { + ($curnet,$netmask) = ($1,$2); + } + elsif ($net =~ /^\s*inet6\s+(.*)$/) + { + ($curnet,$netmask) = split('/', $1); + } + else + { + next; + } + if (isInSameSubnet($peer, $curnet, $netmask, 2)) + { + push @ips, $curnet; + } + } + if (@ips) + { + $rst[0] = 0; + push @rst, @ips; + } + else + { + $rst[0] = 2; + $rst[1] = "The IP address of node $peer is in an undefined subnet"; + } + return @rst; +} + +#------------------------------------------------------------------------------- + +=head3 formatNetmask + Description: + Transform netmask to one of 3 formats (255.255.255.0, 24, 0xffffff00). + + Arguments: + $netmask: the original netmask + $origType: the original netmask type. The valid value can be 0, 1, 2: + Type 0: 255.255.255.0 + Type 1: 24 + Type 2: 0xffffff00 + $newType: the new netmask type, valid values can be 0,1,2, as above. + + Returns: + Return undef if any error. Otherwise return the netmask in new format. + + Globals: + none + + Error: + none + + Example: + xCAT::NetworkUtils::formatNetmask( '24', 1, 0); #return '255.255.255.0'. + + Comments: + none +=cut +#----------------------------------------------------------------------- +sub formatNetmask +{ + my $mask = shift; + my $origType = shift; + my $newType = shift; + my $maskn; + if ( $origType == 0) + { + $maskn = unpack("N", inet_aton($mask)); + } + elsif ( $origType == 1) + { + $maskn = 2**$mask - 1 << (32 - $mask); + } + elsif( $origType == 2) + { + $maskn = hex $mask; + } + else + { + return undef; + } + + if ( $newType == 0) + { + return inet_ntoa( pack('N', $maskn)); + } + if ( $newType == 1) + { + my $bin = unpack ("B32", pack("N", $maskn)); + my @dup = ( $bin =~ /(1{1})0*/g); + return scalar ( @dup); + } + if ( $newType == 2) + { + return sprintf "0x%1x", $maskn; + } + return undef; +} + +#------------------------------------------------------------------------------- + +=head3 isInSameSubnet + Description: + Check if 2 given IP addresses are in same subnet + + Arguments: + $ip1: the first IP + $ip2: the second IP + $mask: the netmask, here are 3 possible netmask types, following are examples for these 3 types: + Type 0: 255.255.255.0 + Type 1: 24 + Type 2: 0xffffff00 + $masktype: the netmask type, 3 possible values: 0,1,2, as indicated above + + Returns: + 1: they are in same subnet + 2: not in same subnet + + Globals: + none + + Error: + none + + Example: + xCAT::NetworkUtils::isInSameSubnet( '192.168.10.1', '192.168.10.2', '255.255.255.0', 0); + + Comments: + none +=cut +#----------------------------------------------------------------------- +sub isInSameSubnet +{ + my $ip1 = shift; + my $ip2 = shift; + my $mask = shift; + my $maskType = shift; + + $ip1 = xCAT::NetworkUtils->getipaddr($ip1); + $ip2 = xCAT::NetworkUtils->getipaddr($ip2); + + if (!defined($ip1) || !defined($ip2)) + { + return undef; + } + + if ((($ip1 =~ /\d+\.\d+\.\d+\.\d+/) && ($ip2 !~ /\d+\.\d+\.\d+\.\d+/)) + ||(($ip1 !~ /\d+\.\d+\.\d+\.\d+/) && ($ip2 =~ /\d+\.\d+\.\d+\.\d+/))) + { + #ipv4 and ipv6 can not be in the same subnet + return undef; + } + + if (($ip1 =~ /\d+\.\d+\.\d+\.\d+/) && ($ip2 =~ /\d+\.\d+\.\d+\.\d+/)) + { + my $maskn; + if ( $maskType == 0) + { + $maskn = unpack("N", inet_aton($mask)); + } + elsif ( $maskType == 1) + { + $maskn = 2**$mask - 1 << (32 - $mask); + } + elsif( $maskType == 2) + { + $maskn = hex $mask; + } + else + { + return undef; + } + + my $ip1n = unpack("N", inet_aton($ip1)); + my $ip2n = unpack("N", inet_aton($ip2)); + + return ( ( $ip1n & $maskn) == ( $ip2n & $maskn) ); + } + else + { + #ipv6 + if (($ip1 =~ /\%/) || ($ip2 =~ /\%/)) + { + return undef; + } + my $netipmodule = eval {require Net::IP;}; + if ($netipmodule) { + my $eip1 = Net::IP::ip_expand_address ($ip1,6); + my $eip2 = Net::IP::ip_expand_address ($ip2,6); + my $bmask = Net::IP::ip_get_mask($mask,6); + my $bip1 = Net::IP::ip_iptobin($eip1,6); + my $bip2 = Net::IP::ip_iptobin($eip2,6); + if (($bip1 & $bmask) == ($bip2 & $bmask)) { + return 1; + } + } # else, can not check without Net::IP module + return undef; + } +} +#------------------------------------------------------------------------------- + +=head3 nodeonmynet - checks to see if node is on any network this server is attached to or remote network potentially managed by this system + Arguments: + Node name + Returns: 1 if node is on the network + Globals: + none + Error: + none + Example: + xCAT::NetworkUtils->nodeonmynet + Comments: + none +=cut + +#------------------------------------------------------------------------------- + +sub nodeonmynet +{ + require xCAT::Table; + my $nodetocheck = shift; + if (scalar(@_)) + { + $nodetocheck = shift; + } + + my $nodeip = getNodeIPaddress( $nodetocheck ); + if (!$nodeip) + { + return 0; + } + unless ($nodeip =~ /\d+\.\d+\.\d+\.\d+/) + { + #IPv6 + if ( $^O eq 'aix') + { + my @subnets = get_subnet_aix(); + for my $net_ent (@subnets) + { + if ($net_ent !~ /-/) + { + #ipv4 + next; + } + my ($net, $interface, $mask, $flag) = split/-/ , $net_ent; + if (xCAT::NetworkUtils->ishostinsubnet($nodeip, $mask, $net)) + { + return 1; + } + } + + } else { + my @v6routes = split /\n/,`ip -6 route`; + foreach (@v6routes) { + if (/via/ or /^unreachable/ or /^fe80::\/64/) { + #only count local ones, remote ones can be caught in next loop + #also, link-local would be a pitfall, + #since more context than address is + #needed to determine locality + next; + } + s/ .*//; #remove all after the space + if (xCAT::NetworkUtils->ishostinsubnet($nodeip,'',$_)) { #bank on CIDR support + return 1; + } + } + } + my $nettab=xCAT::Table->new("networks"); + my @vnets = $nettab->getAllAttribs('net','mgtifname','mask'); + foreach (@vnets) { + if ((defined $_->{mgtifname}) && ($_->{mgtifname} eq '!remote!')) + { + if (xCAT::NetworkUtils->ishostinsubnet($nodeip, $_->{mask}, $_->{net})) + { + return 1; + } + } + } + return 0; + } + my $noden = unpack("N", inet_aton($nodeip)); + my @nets; + if ($utildata->{nodeonmynetdata} and $utildata->{nodeonmynetdata}->{pid} == $$) { + @nets = @{$utildata->{nodeonmynetdata}->{nets}}; + } else { + if ( $^O eq 'aix') + { + my @subnets = get_subnet_aix(); + for my $net_ent (@subnets) + { + if ($net_ent =~ /-/) + { + #ipv6 + next; + } + my @ents = split /:/ , $net_ent; + push @nets, $ents[0] . '/' . $ents[2] . ' dev ' . $ents[1]; + } + + } + else + { + @nets = split /\n/, `/sbin/ip route`; + } + my $nettab=xCAT::Table->new("networks"); + my @vnets = $nettab->getAllAttribs('net','mgtifname','mask'); + foreach (@vnets) { + if ((defined $_->{mgtifname}) && ($_->{mgtifname} eq '!remote!')) + { #global scoped network + my $curm = unpack("N", inet_aton($_->{mask})); + my $bits=32; + until ($curm & 1) { + $bits--; + $curm=$curm>>1; + } + push @nets,$_->{'net'}."/".$bits." dev remote"; + } + } + $utildata->{nodeonmynetdata}->{pid}=$$; + $utildata->{nodeonmynetdata}->{nets} = \@nets; + } + foreach (@nets) + { + my @elems = split /\s+/; + unless ($elems[1] =~ /dev/) + { + next; + } + (my $curnet, my $maskbits) = split /\//, $elems[0]; + my $curmask = 2**$maskbits - 1 << (32 - $maskbits); + my $curn = unpack("N", inet_aton($curnet)); + if (($noden & $curmask) == $curn) + { + return 1; + } + } + return 0; +} + +#------------------------------------------------------------------------------- + +=head3 getNodeIPaddress + Arguments: + Node name only one at a time + Returns: ip address(s) + Globals: + none + Error: + none + Example: my $c1 = xCAT::NetworkUtils::getNodeIPaddress($nodetocheck); + +=cut + +#------------------------------------------------------------------------------- + +sub getNodeIPaddress +{ + require xCAT::Table; + my $nodetocheck = shift; + my $port = shift; + my $nodeip; + + $nodeip = xCAT::NetworkUtils->getipaddr($nodetocheck); + if (!$nodeip) + { + my $hoststab = xCAT::Table->new( 'hosts'); + my $ent = $hoststab->getNodeAttribs( $nodetocheck, ['ip'] ); + if ( $ent->{'ip'} ) { + $nodeip = $ent->{'ip'}; + } + } + + if ( $nodeip ) { + return $nodeip; + } else { + return undef; + } +} + + + +#------------------------------------------------------------------------------- + +=head3 thishostisnot + returns 0 if host is not the same + Arguments: + hostname + Returns: + Globals: + none + Error: + none + Example: + xCAT::NetworkUtils->thishostisnot + Comments: + none +=cut + +#------------------------------------------------------------------------------- + +sub thishostisnot +{ + my $comparison = shift; + if (scalar(@_)) + { + $comparison = shift; + } + + my @ips; + if ( $^O eq 'aix') + { + @ips = split /\n/, `/usr/sbin/ifconfig -a`; + } + else + { + @ips = split /\n/, `/sbin/ip addr`; + } + my $comp = xCAT::NetworkUtils->getipaddr($comparison); + if ($comp) + { + foreach (@ips) + { + if (/^\s*inet.?\s+/) + { + my @ents = split(/\s+/); + my $ip = $ents[2]; + $ip =~ s/\/.*//; + $ip =~ s/\%.*//; + if ($ip eq $comp) + { + return 0; + } + } + } + } + return 1; +} + +#----------------------------------------------------------------------------- + +=head3 gethost_ips (AIX and Linux) + Will use ifconfig to determine all possible ip addresses for the + host it is running on and then gethostbyaddr to get all possible hostnames + + input: + output: array of ipaddress(s) and hostnames + example: @ips=xCAT::NetworkUtils->gethost_ips(); + +=cut + +#----------------------------------------------------------------------------- +#sub gethost_ips1 +#{ +# my ($class) = @_; +# my $cmd; +# my @ipaddress; +# $cmd = "ifconfig" . " -a"; +# $cmd = $cmd . "| grep \"inet\""; +# my @result = xCAT::Utils->runcmd($cmd, 0); +# if ($::RUNCMD_RC != 0) +# { +# xCAT::MsgUtils->message("S", "Error from $cmd\n"); +# exit $::RUNCMD_RC; +# } +# foreach my $addr (@result) +# { +# my @ip; +# if (xCAT::Utils->isLinux()) +# { +# if ($addr =~ /inet6/) +# { +# #TODO, Linux ipv6 +# } +# else +# { +# my ($inet, $addr1, $Bcast, $Mask) = split(" ", $addr); +# #@ip = split(":", $addr1); +# #push @ipaddress, $ip[1]; +# $addr1 =~ s/.*://; +# push @ipaddress, $addr1; +# } +# } +# else +# { #AIX +# if ($addr =~ /inet6/) +# { +# $addr =~ /\s*inet6\s+([\da-fA-F:]+).*\/(\d+)/; +# my $v6ip = $1; +# my $v6mask = $2; +# if ($v6ip) +# { +# push @ipaddress, $v6ip; +# } +# } +# else +# { +# my ($inet, $addr1, $netmask, $mask1, $Bcast, $bcastaddr) = +# split(" ", $addr); +# push @ipaddress, $addr1; +# } +# +# } +# } +# my @names = @ipaddress; +# foreach my $ipaddr (@names) +# { +# my $hostname = xCAT::NetworkUtils->gethostname($ipaddr); +# if ($hostname) +# { +# my @shorthost = split(/\./, $hostname); +# push @ipaddress, $shorthost[0]; +# } +# } +# +# return @ipaddress; +#} + + +sub gethost_ips +{ + my ($class) = @_; + my $cmd; + my @ipaddress; + if (xCAT::Utils->isLinux()) + { + $cmd="ip -4 --oneline addr show |awk -F ' ' '{print \$4}'|awk -F '/' '{print \$1}'"; + my @result =xCAT::Utils->runcmd($cmd); + if ($::RUNCMD_RC != 0) + { + xCAT::MsgUtils->message("S", "Error from $cmd\n"); + exit $::RUNCMD_RC; + } + + push @ipaddress, @result; + } + else + { #AIX + + $cmd = "ifconfig" . " -a"; + $cmd = $cmd . "| grep \"inet\""; + my @result = xCAT::Utils->runcmd($cmd, 0); + if ($::RUNCMD_RC != 0) + { + xCAT::MsgUtils->message("S", "Error from $cmd\n"); + exit $::RUNCMD_RC; + } + + foreach my $addr (@result) + { + if ($addr =~ /inet6/) + { + $addr =~ /\s*inet6\s+([\da-fA-F:]+).*\/(\d+)/; + my $v6ip = $1; + my $v6mask = $2; + if ($v6ip) + { + push @ipaddress, $v6ip; + } + } + else + { + my ($inet, $addr1, $netmask, $mask1, $Bcast, $bcastaddr) = + split(" ", $addr); + push @ipaddress, $addr1; + } + + } + } + + my @names = @ipaddress; + foreach my $ipaddr (@names) + { + my $hostname = xCAT::NetworkUtils->gethostname($ipaddr); + if ($hostname) + { + my @shorthost = split(/\./, $hostname); + push @ipaddress, $shorthost[0]; + } + } + return @ipaddress; +} + +#------------------------------------------------------------------------------- + +=head3 get_subnet_aix + Description: + To get present subnet configuration by parsing the output of 'netstat'. Only designed for AIX. + Arguments: + None + Returns: + @aix_nrn : An array with entries in format "net:nic:netmask:flag". Following is an example entry: + 9.114.47.224:en0:27:U + Globals: + none + Error: + none + Example: + my @nrn =xCAT::NetworkUtils::get_subnet_aix + Comments: + none + +=cut + +#------------------------------------------------------------------------------- +sub get_subnet_aix +{ + my @netstat_res = `/usr/bin/netstat -rn`; + chomp @netstat_res; + my @aix_nrn; + for my $entry ( @netstat_res) + { +#We need to find entries like: +#Destination Gateway Flags Refs Use If Exp Groups +#9.114.47.192/27 9.114.47.205 U 1 1 en0 +#4000::/64 link#4 UCX 1 0 en2 - - + my ( $net, $netmask, $flag, $nic); + if ( $entry =~ /^\s*([\d\.]+)\/(\d+)\s+[\d\.]+\s+(\w+)\s+\d+\s+\d+\s(\w+)/) + { + ( $net, $netmask, $flag, $nic) = ($1,$2,$3,$4); + my @dotsec = split /\./, $net; + for ( my $i = 4; $i > scalar(@dotsec); $i--) + { + $net .= '.0'; + } + push @aix_nrn, "$net:$nic:$netmask:$flag" if ($net ne '127.0.0.0'); + } + elsif ($entry =~ /^\s*([\dA-Fa-f\:]+)\/(\d+)\s+.*?\s+(\w+)\s+\d+\s+\d+\s(\w+)/) + { + #print "=====$entry====\n"; + ( $net, $netmask, $flag, $nic) = ($1,$2,$3,$4); + # for ipv6, can not use : as the delimiter + push @aix_nrn, "$net-$nic-$netmask-$flag" if ($net ne '::') + } + } + return @aix_nrn; +} + +#----------------------------------------------------------------------------- + +=head3 determinehostname and ip address(s) + + Used on the service node to figure out what hostname and ip address(s) + are valid names and addresses + Input: None + Output: ipaddress(s),nodename +=cut + +#----------------------------------------------------------------------------- +sub determinehostname +{ + my $hostname; + my $hostnamecmd = "/bin/hostname"; + my @thostname = xCAT::Utils->runcmd($hostnamecmd, 0); + if ($::RUNCMD_RC != 0) + { # could not get hostname + xCAT::MsgUtils->message("S", + "Error $::RUNCMD_RC from $hostnamecmd command\n"); + exit $::RUNCMD_RC; + } + $hostname = $thostname[0]; + + #get all potentially valid abbreviations, and pick the one that is ok + #by 'noderange' + my @hostnamecandidates; + my $nodename; + while ($hostname =~ /\./) { + push @hostnamecandidates,$hostname; + $hostname =~ s/\.[^\.]*//; + } + push @hostnamecandidates,$hostname; + my $checkhostnames = join(',',@hostnamecandidates); + my @validnodenames = xCAT::NodeRange::noderange($checkhostnames); + unless (scalar @validnodenames) { #If the node in question is not in table, take output literrally. + push @validnodenames,$hostnamecandidates[0]; + } + #now, noderange doesn't guarantee the order, so we search the preference order, most to least specific. + foreach my $host (@hostnamecandidates) { + if (grep /^$host$/,@validnodenames) { + $nodename = $host; + last; + } + } + my @ips = xCAT::NetworkUtils->gethost_ips; + my @hostinfo = (@ips, $nodename); + + return @hostinfo; +} + +#----------------------------------------------------------------------------- + +=head3 toIP + + IPv4 function to convert hostname to IP address + +=cut + +#----------------------------------------------------------------------------- +sub toIP +{ + + if (($_[0] =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) || ($_[0] =~ /:/)) + { + return ([0, $_[0]]); + } + my $ip = xCAT::NetworkUtils->getipaddr($_[0]); + if (!$ip) + { + return ([1, "Cannot Resolve: $_[0]\n"]); + } + return ([0, $ip]); +} + +#------------------------------------------------------------------------------- + +=head3 validate_ip + Validate list of IPs + Arguments: + List of IPs + Returns: + 1 - Invalid IP address in the list + 0 - IP addresses are all valid + Globals: + none + Error: + none + Example: + if (xCAT::NetworkUtils->validate_ip($IP)) {} + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub validate_ip +{ + my ($class, @IPs) = @_; + foreach (@IPs) { + my $ip = $_; + #TODO need more check for IPv6 address + if ($ip =~ /:/) + { + return([0]); + } + ################################### + # Length is 4 for IPv4 addresses + ################################### + my (@octets) = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; + if ( scalar(@octets) != 4 ) { + return( [1,"Invalid IP address1: $ip"] ); + } + foreach my $octet ( @octets ) { + if (( $octet < 0 ) or ( $octet > 255 )) { + return( [1,"Invalid IP address2: $ip"] ); + } + } + } + return([0]); +} +#------------------------------------------------------------------------------- + +=head3 isIpaddr + + returns 1 if parameter is has a valid IP address form. + + Arguments: + dot qulaified IP address: e.g. 1.2.3.4 + Returns: + 1 - if legal IP address + 0 - if not legal IP address. + Globals: + none + Error: + none + Example: + if ($ipAddr) { blah; } + Comments: + Doesn't test if the IP address is on the network, + just tests its form. + +=cut + +#------------------------------------------------------------------------------- +sub isIpaddr +{ + my $addr = shift; + if (($addr) && ($addr =~ /xCAT::NetworkUtils/)) + { + $addr = shift; + } + + unless ( $addr ) + { + return 0; + } + #print "addr=$addr\n"; + if ($addr !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) + { + return 0; + } + + if ($1 > 255 || $1 == 0 || $2 > 255 || $3 > 255 || $4 > 255) + { + return 0; + } + else + { + return 1; + } +} + + + + +#------------------------------------------------------------------------------- +=head3 getNodeNameservers + Description: + Get nameservers of specified nodes. + The priority: noderes.nameservers > networks.nameservers > site.nameservers + Arguments: + node: node name list + Returns: + Return a hash ref, of the $nameservers{$node} + undef - Failed to get the nameservers + Globals: + none + Error: + none + Example: + my $nameservers = xCAT::NetworkUtils::getNodeNameservers(\@node); + Comments: + none + +=cut +#------------------------------------------------------------------------------- +sub getNodeNameservers{ + my $nodes=shift; + if( $nodes =~ /xCAT::NetworkUtils/) + { + $nodes=shift; + } + my @nodelist = @$nodes; + my %nodenameservers; + my $nrtab = xCAT::Table->new('noderes',-create=>0); + my %nrhash = %{$nrtab->getNodesAttribs(\@nodelist,['nameservers'])}; + + my $nettab = xCAT::Table->new("networks"); + my %nethash = xCAT::DBobjUtils->getNetwkInfo( \@nodelist ); + + my @nameservers = xCAT::TableUtils->get_site_attribute("nameservers"); + my $sitenameservers=$nameservers[0]; + + + foreach my $node (@nodelist){ + if ($nrhash{$node} and $nrhash{$node}->[0] and $nrhash{$node}->[0]->{nameservers}) + { + $nodenameservers{$node}=$nrhash{$node}->[0]->{nameservers}; + }elsif($nethash{$node}{nameservers}) + { + $nodenameservers{$node}=$nethash{$node}{nameservers}; + }elsif($sitenameservers) + { + $nodenameservers{$node}=$sitenameservers; + } + } + + return \%nodenameservers; +} + + +#------------------------------------------------------------------------------- + +=head3 getNodeNetworkCfg + Description: + Get node network configuration, including "IP, hostname(the nodename),and netmask" by this node's name. + + Arguments: + node: the nodename + Returns: + Return an array, which contains (IP,hostname,gateway,netmask'). + undef - Failed to get the network configuration info + Globals: + none + Error: + none + Example: + my ($ip,$host,undef,$mask) = xCAT::NetworkUtils::getNodeNetworkCfg('node1'); + Comments: + Presently gateway is always blank. Need to be improved. + +=cut + +#------------------------------------------------------------------------------- +sub getNodeNetworkCfg +{ + my $node = shift; + if( $node =~ /xCAT::NetworkUtils/) + { + $node =shift; + } + + my $ip = xCAT::NetworkUtils->getipaddr($node); + my $mask = undef; + my $gateway = undef; + + my $nettab = xCAT::Table->new("networks"); + if ($nettab) { + my @nets = $nettab->getAllAttribs('net','mask','gateway'); + foreach my $net (@nets) { + if (xCAT::NetworkUtils::isInSameSubnet( $net->{'net'}, $ip, $net->{'mask'}, 0)) { + $gateway=$net->{'gateway'}; + $mask=$net->{'mask'}; + } + } + } + + return ($ip, $node, $gateway, xCAT::NetworkUtils::formatNetmask($mask,0,0)); +} +#------------------------------------------------------------------------------- + +=head3 getNodesNetworkCfg + Description: + Get network configuration (ip,netmask,gateway) for a group of nodes + + Arguments: + nodes: the group of nodes + Returns: + If failed: (1, error_msg) + If success: (0, the hash variable store network configuration info for nodes that get matching network entry) + Error: + none + Example: + my ($ret, $hash) = xCAT::NetworkUtils::getNodesNetworkCfg($noderange); + Comments: + +=cut + +#------------------------------------------------------------------------------- + +sub getNodesNetworkCfg +{ + my $nodes = shift; + if ($nodes =~ /xCAT::NetworkUtils/) { + $nodes = shift; + } + my @nets = (); + my $nettab = xCAT::Table->new("networks"); + if($nettab) { + my @error_net = (); + my @all_nets = $nettab->getAllAttribs('net','mask','gateway'); + foreach my $net (@all_nets) { + my $gateway = $net->{gateway}; + if (defined($gateway) and ($gateway eq '')) { + my @gatewayd = xCAT::NetworkUtils->my_ip_facing($net->{'net'}); + unless ($gatewayd[0]) { + $gateway = $gatewayd[1]; + } + } + push @nets, {net=>$net->{net}, mask=>$net->{mask}, gateway=>$gateway}; + } + $nettab->close; + } + else { + return (1, "Open \"networks\" table failed"); + } + if (!scalar(@nets)) { + return (1, "No entry find in \"networks\" table"); + } + my %rethash = (); + foreach my $node (@$nodes) { + my $ip = xCAT::NetworkUtils->getipaddr($node); + foreach my $net (@nets) { + if (xCAT::NetworkUtils::isInSameSubnet( $net->{'net'}, $ip, $net->{'mask'}, 0)) { + $rethash{$node}->{ip} = $ip; + $rethash{$node}->{mask} = $net->{'mask'}; + $rethash{$node}->{gateway} = $net->{'gateway'}; + last; + } + } + } + return (0, \%rethash); +} + +#------------------------------------------------------------------------------- + +=head3 get_hdwr_ip + Description: + Get hardware(CEC, BPA) IP from the hosts table, and then /etc/hosts. + + Arguments: + node: the nodename(cec, or bpa) + Returns: + Return the node IP + -1 - Failed to get the IP. + Globals: + none + Error: + none + Example: + my $ip = xCAT::NetworkUtils::get_hdwr_ip('node1'); + Comments: + Used in FSPpower FSPflash, FSPinv. + +=cut + +#------------------------------------------------------------------------------- +sub get_hdwr_ip +{ + require xCAT::Table; + my $node = shift; + my $ip = undef; + my $Rc = undef; + + my $ip_tmp_res = xCAT::NetworkUtils::toIP($node); + ($Rc, $ip) = @$ip_tmp_res; + if ( $Rc ) { + my $hosttab = xCAT::Table->new( 'hosts' ); + if ( $hosttab) { + my $node_ip_hash = $hosttab->getNodeAttribs( $node,[qw(ip)]); + $ip = $node_ip_hash->{ip}; + } + + } + + if (!$ip) { + return undef; + } + + return $ip; +} + +#-------------------------------------------------------------------------------- +=head3 pingNodeStatus + This function takes an array of nodes and returns their status using nmap or fping. + Arguments: + nodes-- an array of nodes. + Returns: + a hash that has the node status. The format is: + {alive=>[node1, node3,...], unreachable=>[node4, node2...]} +=cut +#-------------------------------------------------------------------------------- +sub pingNodeStatus { + my ($class, @mon_nodes)=@_; + my %status=(); + my @active_nodes=(); + my @inactive_nodes=(); + #print "NetworkUtils->pingNodeStatus called, nodes=@mon_nodes\n"; + if ((@mon_nodes)&& (@mon_nodes > 0)) { + #get all the active nodes + my $nodes= join(' ', @mon_nodes); + if (-x '/usr/bin/nmap' or -x '/usr/local/bin/nmap') { #use nmap + #print "use nmap\n"; + my %deadnodes; + foreach (@mon_nodes) { + $deadnodes{$_}=1; + } + + # get additional options from site table + my @nmap_options = xCAT::TableUtils->get_site_attribute("nmapoptions"); + my $more_options = $nmap_options[0]; + + #call namp + open (NMAP, "nmap -PE --system-dns --send-ip -sP $more_options ". $nodes . " 2> /dev/null|") or die("Cannot open nmap pipe: $!"); + my $node; + while () { + if (/Host (.*) \(.*\) appears to be up/) { + $node=$1; + unless ($deadnodes{$node}) { + foreach (keys %deadnodes) { + if ($node =~ /^$_\./) { + $node = $_; + last; + } + } + } + delete $deadnodes{$node}; + push(@active_nodes, $node); + } elsif (/Nmap scan report for ([^ ]*) /) { + $node=$1; + } elsif (/Host is up./) { + unless ($deadnodes{$node}) { + foreach (keys %deadnodes) { + if ($node =~ /^$_\./) { + $node = $_; + last; + } + } + } + delete $deadnodes{$node}; + push(@active_nodes, $node); + } + } + foreach (sort keys %deadnodes) { + push(@inactive_nodes, $_); + } + } else { #use fping + #print "use fping\n"; + + my $temp=`fping -a $nodes 2> /dev/null`; + chomp($temp); + @active_nodes=split(/\n/, $temp); + + #get all the inactive nodes by substracting the active nodes from all. + my %temp2; + if ((@active_nodes) && ( @active_nodes > 0)) { + foreach(@active_nodes) { $temp2{$_}=1}; + foreach(@mon_nodes) { + if (!$temp2{$_}) { push(@inactive_nodes, $_);} + } + } + else {@inactive_nodes=@mon_nodes;} + } + } + + $status{$::STATUS_ACTIVE}=\@active_nodes; + $status{$::STATUS_INACTIVE}=\@inactive_nodes; + #use Data::Dumper; + #print Dumper(%status); + + return %status; +} + +#------------------------------------------------------------------------------- + +=head3 isValidMAC + Description : Validate whether specified string is a MAC string. + Arguments : macstr - the string to be validated. + Returns : 1 - valid MAC String. + 0 - invalid MAC String. +=cut + +#------------------------------------------------------------------------------- +sub isValidMAC +{ + my ($class, $macstr) = @_; + if ($macstr =~ /^[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}$/){ + return 1; + } + return 0; +} + +#------------------------------------------------------------------------------- + +=head3 isValidHostname + Description : Validate whether specified string is a valid hostname. + Arguments : hostname - the string to be validated. + Returns : 1 - valid hostname String. + 0 - invalid hostname String. +=cut + +#------------------------------------------------------------------------------- +sub isValidHostname +{ + my ($class, $hostname) = @_; + if ($hostname =~ /^[a-z0-9]/){ + if ($hostname =~ /[a-z0-9]$/){ + if ($hostname =~ /^[\-a-z0-9]+$/){ + return 1; + } + } + } + return 0; +} + + +#------------------------------------------------------------------------------- + +=head3 isValidFQDN + Description : Validate whether specified string is a valid FQDN. + Arguments : hostname - the string to be validated. + Returns : 1 - valid hostname FQDN. + 0 - invalid hostname FQDN. +=cut + +#------------------------------------------------------------------------------- +sub isValidFQDN +{ + my ($class, $hostname) = @_; + if ($hostname =~ /^[a-z0-9][\.\-a-z0-9]+[a-z0-9]$/){ + return 1; + } + return 0; +} + + +#------------------------------------------------------------------------------- + +=head3 ip_to_int + Description : convert an IPv4 string into int. + Arguments : ipstr - the IPv4 string. + Returns : ipint - int number +=cut + +#------------------------------------------------------------------------------- +sub ip_to_int +{ + my ($class, $ipstr) = @_; + my $ipint = 0; + my @ipnums = split('\.', $ipstr); + $ipint += $ipnums[0] << 24; + $ipint += $ipnums[1] << 16; + $ipint += $ipnums[2] << 8; + $ipint += $ipnums[3]; + return $ipint; +} + +#------------------------------------------------------------------------------- + +=head3 int_to_ip + Description : convert an int into IPv4 String. + Arguments : ipnit - the input int number. + Returns : ipstr - IPv4 String. +=cut + +#------------------------------------------------------------------------------- +sub int_to_ip +{ + my ($class, $ipint) = @_; + return inet_ntoa(inet_aton($ipint)); +} + +#------------------------------------------------------------------------------- + +=head3 getBroadcast + Description : Get the broadcast ips + Arguments : ipstr - the IPv4 string ip. + netmask - the subnet mask of network + Returns : bcipint - the IPv4 string of broadcast ip. +=cut + +#------------------------------------------------------------------------------- +sub getBroadcast +{ + my ($class, $ipstr, $netmask) = @_; + my $ipint = xCAT::NetworkUtils->ip_to_int($ipstr); + my $maskint = xCAT::NetworkUtils->ip_to_int($netmask); + my $tmp = sprintf("%d", ~$maskint); + my $bcnum = sprintf("%d", ($ipint | $tmp) & hex('0x00000000FFFFFFFF')); + return xCAT::NetworkUtils->int_to_ip($bcnum); +} + +#------------------------------------------------------------------------------- + +=head3 get_allips_in_range + Description : Get all IPs in a IP range, return in a list. + Arguments : $startip - start IP address + $endip - end IP address + $increment - increment factor + Returns : IP list in this range. + Example : + my $startip = "192.168.0.1"; + my $endip = "192.168.0.100"; + xCAT::NetworkUtils->get_allips_in_range($startip, $endip, 1); +=cut + +#------------------------------------------------------------------------------- +sub get_allips_in_range +{ + my $class = shift; + my $startip = shift; + my $endip = shift; + my $increment = shift; + my @iplist = (); + my $tmpip; + + my $startipnum = xCAT::NetworkUtils->ip_to_int($startip); + my $endipnum = xCAT::NetworkUtils->ip_to_int($endip); + + if ($increment > 0){ + while ($startipnum <= $endipnum){ + $tmpip = xCAT::NetworkUtils->int_to_ip($startipnum); + $startipnum += $increment; + push (@iplist, $tmpip); + } + }elsif($increment < 0){ + while ($endipnum >= $startipnum){ + $tmpip = xCAT::NetworkUtils->int_to_ip($endipnum); + $endipnum += $increment; + push (@iplist, $tmpip); + } + } + return \@iplist; +} + +#------------------------------------------------------------------------------- + +=head3 get_all_ips + Description : Get all IP addresses from table nics, column nicips. + Arguments : hashref - if not set, will return a reference of list, + if set, will return a reference of hash. + Returns : All IPs reference. +=cut + +#------------------------------------------------------------------------------- +sub get_all_nicips{ + my ($class, $hashref) = @_; + my %allipshash; + my @allipslist; + + my $table = xCAT::Table->new('nics'); + my @entries = $table->getAllNodeAttribs(['nicips']); + foreach (@entries){ + # $_->{nicips} looks like "eth0:ip1,eth1:ip2,bmc:ip3..." + if($_->{nicips}){ + my @nicandiplist = split(',', $_->{nicips}); + # Each record in @nicandiplist looks like "eth0:ip1" + # delimiter has been changed to use "!" in xCAT 2.8 + foreach (@nicandiplist){ + my @nicandip; + if ($_ =~ /!/) { + @nicandip = split('!', $_); + } else { + @nicandip = split(':', $_); + } + if ($hashref){ + $allipshash{$nicandip[1]} = 0; + } else{ + push (@allipslist, $nicandip[1]); + } + } + } + } + if ($hashref){ + return \%allipshash; + } else{ + return \@allipslist; + } +} + +#------------------------------------------------------------------------------- + +=head3 gen_net_boot_params + + Description: + This subroutine is used to generate all possible kernel parameters for network boot (rh/sles/ubuntu + diskfull/diskless) + The supported network boot parameters: + ksdevice - Specify network device for Anaconda. For rh6 and earlier. Format: 'ksdevice={$mac|$nicname}' + BOOTIF - Specify network device for Anaconda. The boot device which set by pxe. xCAT also set it if the bootload is not pxe. Format 'BOOTIF={$mac}' + ifname - Specify a interfacename<->mac pair, it will set the interfacename to the interface which has the . Format 'ifname=$ifname:$mac' + # This will only be generated when linuximage.nodebootif is set. + bootdev - Specify the boot device. Mostly it's used with parameter and when there are multiple params. Format 'bootdev={$mac|$ifname} + ip - Specify the network configuration for an interface. Format: 'ip=dhcp', 'ip=$ifname:dhcp' + + netdevice - Specify network device for Linuxrc (Suse bootloader). Format: 'netdevice={$mac|$nicname}' + + netdev - Specify the interfacename which is used by xCAT diskless boot script to select the network interface. Format: 'netdev=$nicname' + + Reference: + Redhat anaconda doc: https://github.com/rhinstaller/anaconda/blob/master/docs/boot-options.txt + Suse Linuxrc do: https://en.opensuse.org/SDB:Linuxrc + + Arguments: + $installnic <- node.installnic + $primarynic <- node.primarynic + $macmac <- node.mac + $nodebootif <- linuximage.nodebootif + + Returns: + $net_params - The key will be the parameter name, the value for the key will be the parameter value. + Valid Parameter Name: + ksdevice + netdev + netdevice + ip + ifname + BOOTIF + + And following two keys also will be returned for reference + mac + nicname + + Example: + my $netparams = xCAT::NetworkUtils->gen_net_boot_params($installnic, $primmarynic, $macmac, $nodebootif); + +=cut + +#------------------------------------------------------------------------------- + +sub gen_net_boot_params +{ + my $class = shift; + my $installnic = shift; + my $primarynic = shift; + my $macmac = shift; + my $nodebootif = shift; + + my $net_params; + + # arbitrary use primarynic if installnic is not set + unless ($installnic) { + $installnic = $primarynic; + } + + # just use the installnic to generate the nic related kernel parameters + my $mac; + my $nicname; + + # set the default nicname to nodebootif from image definition + if ($nodebootif) { + $nicname = $nodebootif; + } + + if ((! defined ($installnic)) || ($installnic eq "") || ($installnic =~ /^mac$/i)) { + $mac = $macmac; + $net_params->{mac} = $mac; + } elsif ($installnic =~ /^[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}$/) { + $mac = $installnic; + $net_params->{mac} = $mac; + $net_params->{setmac} = $mac; + } else { + $mac = $macmac; + $nicname = $installnic; + $net_params->{nicname} = $nicname; + $net_params->{mac} = $mac; + } + + # if nicname is set and mac.mac is NOT set to , use nicname in the boot parameters + if ($nicname && ! defined ($net_params->{setmac})) { + $net_params->{ksdevice} = "ksdevice=$nicname"; + $net_params->{ip} = "ip=$nicname:dhcp"; + $net_params->{netdev} = "netdev=$nicname"; + $net_params->{netdevice} = "netdevice=$nicname"; + $net_params->{ifname} = "ifname=$nicname:$mac"; # todo: may not use mac arbitrary + } elsif ($mac) { + $net_params->{ksdevice} = "ksdevice=$mac"; + $net_params->{BOOTIF} = "BOOTIF=$mac"; + $net_params->{bootdev} = "bootdev=$mac"; + $net_params->{ip} = "ip=dhcp"; + $net_params->{netdevice} = "netdevice=$mac"; + } + + return $net_params; +} + +1; diff --git a/xCAT-probe/subcmds/discovery b/xCAT-probe/subcmds/discovery index dfae06764..59791c9f5 100755 --- a/xCAT-probe/subcmds/discovery +++ b/xCAT-probe/subcmds/discovery @@ -5,6 +5,7 @@ BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/o use lib "$::XCATROOT/probe/lib/perl"; use probe_utils; +use xCAT::NetworkUtils; use File::Basename; use IO::Select; use Getopt::Long qw(:config no_ignore_case); @@ -13,16 +14,15 @@ use Data::Dumper; my $program_name = basename("$0"); my $help; my $test; -my $output = "stdout"; -my $verbose = 0; -my $rst = 0; -my $pre_check = 0; -my $monitor = 0; +my $output = "stdout"; +my $verbose = 0; +my $rst = 0; +my $no_pre_check = 0; my $discovery_type; my @valid_discovery_type = ("mtms", "switch"); my $valid_discovery_type_str = join(",", @valid_discovery_type); my $noderange; -my $nics; +my $nics; #reservation attribute, format : xxx|xxx|xxx #used for discovery monitor my %rawdata; @@ -32,23 +32,20 @@ my %monitor_nodes; $::USAGE = "Usage: $program_name -h - $program_name -t - $program_name -p [-V] [-T ] [-n ] - $program_name -m - $program_name [-V] [-T ] [-n ] + $program_name -T + $program_name [-V] [-m -n ] [--noprecheck] Description: - Do probe for discovery process, including pro-check for required configuration and realtime monitor of discovery process. - If without any option, $program_name will do pro-check first, if all checking point pass, then goes to monitor directly. + Do probe for discovery process, including pre-check for required configuration and realtime monitor of discovery process. + If all pre-check items pass, $program_name will go to monitor directly, otherwise $program_name exit for error. Options: -h : Get usage information of $program_name. - -t : To verify if $program_name can work, reserve option for probe framework. + -T : To verify if $program_name can work, reserve option for probe framework. -V : Output more information for debug. - -p : Do pre-check for disvoery, check whether required configuration have been done ahead. - -T : The type of discovery, the valid values are $valid_discovery_type_str. + -m : The method of discovery, the valid values are $valid_discovery_type_str. -n : The range of predefined node, must used with option -m. - -m : Do realtime monitor for discovery process. + --noprecheck : skip pre-checking discovery to validate correct configuration. "; #------------------------------------------ @@ -161,8 +158,15 @@ sub check_genesis_file { return 1; } - my @host_ip_arr = `ifconfig -a |awk -F" " '/inet / {gsub(/\w+:/,"",\$2);print \$2}'`; - my @netmask_arr = `ifconfig -a |awk -F" " '/inet / {gsub(/\w+:/,"",\$4);print \$4}'`; + my @host_ip_arr; + my @netmask_arr; + my @output = `ip addr show|awk -F" " '/inet / {print \$2}'`; + foreach (@output) { + my ($ip, $mask) = split("/", $_); + my $strmask = xCAT::NetworkUtils::formatNetmask($mask, 1, 0); + push(@host_ip_arr, $ip); + push(@netmask_arr, $strmask); + } @genesis_files = glob("$genesis_folder/*"); foreach (@genesis_files) { @@ -174,7 +178,7 @@ sub check_genesis_file { my $netmask_num = 0; foreach (@host_ip_arr) { chomp($_); - if (probe_utils->is_ip_belong_to_net($ip_range, $netmask_arr[$netmask_num], $_)) { + if (xCAT::NetworkUtils->ishostinsubnet($_, $netmask_arr[$netmask_num], $ip_range)) { $host_ip = $_; } $netmask_num++; @@ -280,7 +284,6 @@ sub check_genesis_file { sub dhcp_dynamic_range_check { my $nets = shift; my $rst = 0; - return $rst; } @@ -305,8 +308,6 @@ sub handle_tftp_msg { my $record = "Via TFTP $ip download $file"; probe_utils->send_msg("$output", "d", "$record"); if (exists($rawdata{"$ipmacmap{$ip}"})) { - - #push(@{$rawdata{$ipmacmap{$ip}}{"history"}}, $record) unless (/^$record$/ ~~ @{$rawdata{$ipmacmap{$ip}}{"history"}}); push(@{ $rawdata{ $ipmacmap{$ip} }{"history"} }, $record); } } @@ -355,33 +356,22 @@ sub handle_http_msg { #------------------------------------------ sub handle_dhcp_msg { - my $msg = shift; - my $nics = shift; - my @nicarray = split(",", $nics); - if ($msg =~ /.+DHCPDISCOVER\s+from\s+(.+)\s+via\s+(.+)/i) { - my @tmpmsg = split(" ", $msg); - my $mac = $tmpmsg[7]; - my $nic = $tmpmsg[9]; - $nic =~ s/(.+):/$1/g if ($nic =~ /:$/); - return 0 unless (@nicarray ~~ /^$nic$/); - if ($msg =~ /no free leases/) { + my $msg = shift; + + if ($msg =~ /.+DHCPDISCOVER\s+from\s+(.+)\s+via\s+([^:]+)(.*)/i) { + my $mac = $1; + my $nic = $2; + if ($3 =~ /no free leases/) { probe_utils->send_msg("$output", "d", "Receive DHCPDISCOVER from $mac via $nic, no free leases"); return 0; } my $record = "Receive DHCPDISCOVER from $mac via $nic"; probe_utils->send_msg("$output", "d", "$record"); - if (!exists($rawdata{$mac})) { - my @history; - push(@history, $record); - $rawdata{$mac}{"history"} = \@history; - } else { - push(@{ $rawdata{$mac}{"history"} }, $record); - } + push(@{ $rawdata{$mac}{"history"} }, $record); } elsif ($msg =~ /.+DHCPOFFER\s+on\s+(.+)\s+to\s+(.+)\s+via\s+(.+)/i) { - my $ip = $1; - my $mac = $2; - my $nic = $3; - return 0 unless (@nicarray ~~ /^$nic$/); + my $ip = $1; + my $mac = $2; + my $nic = $3; my $record = "Send DHCPOFFER on $ip back to $mac via $nic"; probe_utils->send_msg("$output", "d", "$record"); if (exists($rawdata{$mac})) { @@ -392,17 +382,15 @@ sub handle_dhcp_msg { my $server = $2; my $mac = $3; my $nic = $4; - return 0 unless (@nicarray ~~ /^$nic$/); my $record = "Receive DHCPREQUEST from $mac for $ip via $nic"; probe_utils->send_msg("$output", "d", "$record"); if (exists($rawdata{$mac})) { push(@{ $rawdata{$mac}{"history"} }, $record); } } elsif ($msg =~ /.+DHCPACK\s+on\s+(.+)\s+to\s+(.+)\s+via\s+(.+)/) { - my $ip = $1; - my $mac = $2; - my $nic = $3; - return 0 unless (@nicarray ~~ /^$nic$/); + my $ip = $1; + my $mac = $2; + my $nic = $3; my $record = "Send DHCPACK on $ip back to $mac via $nic"; probe_utils->send_msg("$output", "d", "$record"); if (exists($rawdata{$mac})) { @@ -410,30 +398,20 @@ sub handle_dhcp_msg { push(@{ $rawdata{$mac}{"history"} }, $record); $ipmacmap{$ip} = $mac; } - } elsif ($msg =~ /.+BOOTREQUEST\s+from\s+(.+)\s+via\s+(.+)/) { - my @tmpmsg = split(" ", $msg); - my $mac = $tmpmsg[7]; - my $nic = $tmpmsg[9]; - $nic =~ s/(.+):/$1/g if ($nic =~ /:$/); - return 0 unless (@nicarray ~~ /^$nic$/); - if ($msg =~ /no dynamic leases/) { + } elsif ($msg =~ /.+BOOTREQUEST\s+from\s+(.+)\s+via\s+([^:]+)(.*)/) { + my $mac = $1; + my $nic = $2; + if ($3 =~ /no dynamic leases/) { probe_utils->send_msg("$output", "d", "Receive DHCPDISCOVER from $mac via $nic, no dynamic leases"); return 0; } my $record = "Receive BOOTREQUEST from $mac via $nic"; probe_utils->send_msg("$output", "d", "$record"); - if (!exists($rawdata{$mac})) { - my @history; - push(@history, $record); - $rawdata{$mac}{"history"} = \@history; - } else { - push(@{ $rawdata{$mac}{"history"} }, $record); - } + push(@{ $rawdata{$mac}{"history"} }, $record); } elsif ($msg =~ /.+BOOTREPLY\s+for\s+(.+)\s+to\s+.+(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w).+via\s+(.+)/) { - my $ip = $1; - my $mac = $2; - my $nic = $3; - return 0 unless (@nicarray ~~ /^$nic$/); + my $ip = $1; + my $mac = $2; + my $nic = $3; my $record = "Send BOOTREPLY on $ip back to $mac via $nic"; probe_utils->send_msg("$output", "d", "$record"); if (exists($rawdata{$mac})) { @@ -468,20 +446,30 @@ sub handle_compute_msg { my @splitline = split(" ", $line); if (($splitline[4] =~ /^xcat/i) || ($splitline[5] =~ /^xcat/i)) { $sender = $splitline[3]; - for (my $i = 0 ; $i < 5 ; $i++) { - shift(@splitline); - } + splice(@splitline, 0, 5); $msg = join(" ", @splitline); - if (!probe_utils->is_ip_addr("$sender")) { - $ip = probe_utils->get_ip_from_hostname("$sender"); + + if (!xCAT::NetworkUtils->isIpaddr($sender)) { + $ip = xCAT::NetworkUtils->getipaddr($sender); } else { $ip = $sender; } - if ($ip ne "" && exists($ipmacmap{$ip})) { - my $record = "Recv from $ipmacmap{$ip}($ip) : $msg"; + + if ($ip ne "" && defined($ipmacmap{$ip})) { + my $record = "Recv from $ip : $msg"; probe_utils->send_msg("$output", "d", "$record"); push(@{ $rawdata{ $ipmacmap{$ip} }{"history"} }, $record); } + + # There is a node finish discovry process + if ($msg =~ /xcat.genesis.dodiscovery: Restart/) { + my $node = `lsdef -i mac -c 2>&1 | awk -F: '/$ipmacmap{$ip}/ {print \$1}'`; + chomp($node); + $monitor_nodes{$node} = 1 if (defined($monitor_nodes{$node})); + probe_utils->send_msg("$output", "o", "Node $node has finished it's discovery process"); + my $output = `lsdef $node 2>&1`; + print "-------------------\n$output-------------------\n"; + } } return 0; } @@ -508,13 +496,14 @@ sub handle_cluster_msg { if ($line =~ /.+\d+:\d+:\d+\s+(.+)\s+(xcat.+)/i) { $sender = $1; $msg = $2; - if (!probe_utils->is_ip_addr("$sender")) { - $ip = probe_utils->get_ip_from_hostname("$sender"); + + if (!xCAT::NetworkUtils->isIpaddr($sender)) { + $ip = xCAT::NetworkUtils->getipaddr($sender); } else { $ip = $sender; } - if ($ip ne "" && exists($ipmacmap{$ip})) { - my $record = "Recv from $ipmacmap{$ip}($ip) : $msg"; + if ($ip ne "" && defined($ipmacmap{$ip})) { + my $record = "Recv from $ip : $msg"; probe_utils->send_msg("$output", "d", "$record"); push(@{ $rawdata{ $ipmacmap{$ip} }{"history"} }, $record); } @@ -534,22 +523,26 @@ sub handle_cluster_msg { #------------------------------------------ sub dump_history { - probe_utils->send_msg("$output", "d", "======================================="); - probe_utils->send_msg("$output", "d", "= The summary of discovery:"); - probe_utils->send_msg("$output", "d", "======================================="); - my $masterip = `tabdump site 2>&1 | awk -F',' '/master/ { gsub(/"/, "", \$2) ; print \$2 }'`; - chomp($masterip); + my $title = " +============================================================= += The summary of discovery: +============================================================= +"; + print "$title\n"; + foreach $mac (keys %rawdata) { - my $nodehostname = probe_utils->get_hostname_from_ip($rawdata{$mac}{"ip"}, $masterip); + my $nodehostname = `lsdef -i mac -c 2>&1 | awk -F: '/$mac/ {print \$1}'`; + chomp($nodehostname); if ($nodehostname ne "") { probe_utils->send_msg("$output", "d", "[$mac ($nodehostname)]"); } else { probe_utils->send_msg("$output", "d", "[$mac]:"); } foreach my $line (@{ $rawdata{$mac}{"history"} }) { - probe_utils->send_msg("$output", "d", "| $line"); + probe_utils->send_msg("$output", "d", "\t$line"); } + print "\n"; } } @@ -584,6 +577,7 @@ sub check_pre_defined_node { $rst = 1; } elsif ($_ =~ /^\s*Object name: (\w+)/i) { $currentnode = $1; + $monitor_nodes{$1} = 0; } elsif ($_ =~ /^\s+(\w+)\s*=\s*(\w+)/) { $nodecheckrst{$currentnode}{$1} = $2; } @@ -594,29 +588,47 @@ sub check_pre_defined_node { foreach my $node (keys %nodecheckrst) { if (!exists($nodecheckrst{$node}{error})) { if ($discovery_type eq "mtms") { - if (exists($nodecheckrst{$node}{"mtm"}) && exists($nodecheckrst{$node}{"serial"})) { - $nodecheckrst{$node}{"error"} = "node definition is good for '$discovery_type' type discovery"; - } else { + if (!(exists($nodecheckrst{$node}{"mtm"}) && exists($nodecheckrst{$node}{"serial"}))) { $nodecheckrst{$node}{"error"} = "node definition is wrong for '$discovery_type' type discovery"; $rst = 1; } - } + } elsif ($discovery_type eq "switch") { + { #important to hold a block + if (!(exists($nodecheckrst{$node}{"switch"}) && exists($nodecheckrst{$node}{"switchport"}))) { + $nodecheckrst{$node}{"error"} = "Atrribute 'switch' or 'switchport' isn't defined for '$discovery_type' type discovery"; + $rst = 1; + last; + } - if ($discovery_type eq "switch") { - if (exists($nodecheckrst{$node}{"switch"}) && exists($nodecheckrst{$node}{"switchport"})) { - $nodecheckrst{$node}{"error"} = "node definition is good for '$discovery_type' type discovery"; - } else { - $nodecheckrst{$node}{"error"} = "node definition is wrong for '$discovery_type' type discovery"; - $rst = 1; + my $tmpoutput = `lsdef $nodecheckrst{$node}{"switch"} 2>&1`; + if ($?) { + $nodecheckrst{$node}{"error"} = "Miss definition for related switch $nodeswitch"; + $rst = 1; + last; + } + + if ($tmpoutput !~ /snmpversion=/) { + $nodecheckrst{$node}{"error"} = "Miss attribute 'snmpversion' definition for related switch $nodeswitch"; + $rst = 1; + last; + } + if ($tmpoutput !~ /username=/) { + $nodecheckrst{$node}{"error"} = "Miss attribute 'username' definition for related switch $nodeswitch"; + $rst = 1; + last; + } + if ($tmpoutput !~ /password=/) { + $nodecheckrst{$node}{"error"} = "Miss attribute 'password' definition for related switch $nodeswitch"; + $rst = 1; + last; + } } } } } - if ($verbose) { - foreach my $node (keys %nodecheckrst) { - probe_utils->send_msg("$output", "d", "$node : $nodecheckrst{$node}{error}"); - } + foreach my $node (keys %nodecheckrst) { + probe_utils->send_msg("$output", "d", "$node : $nodecheckrst{$node}{error}") if (exists($nodecheckrst{$node}{error})); } return $rst; @@ -624,28 +636,6 @@ sub check_pre_defined_node { #------------------------------------------ -=head3 - Description: - Get monitor nodes list - Arguments: - One golble attribute %monitor_nodes; - Returns: -=cut - -#------------------------------------------ -sub get_monitor_nodes_list { - my @cmdoutput = `lsdef $noderange 2>&1`; - foreach (@cmdoutput) { - if ($_ =~ /^Error: Could not find an object named '(\w+)' .+/i) { - $monitor_nodes{$1} = 0; - } elsif ($_ =~ /^\s*Object name: (\w+)/i) { - $monitor_nodes{$1} = 0; - } - } -} - -#------------------------------------------ - =head3 Description: Test if all nodes have finished job @@ -658,7 +648,10 @@ sub get_monitor_nodes_list { sub all_monitor_node_done { my $done = 1; foreach my $node (keys %monitor_nodes) { - $done = 0 if ($monitor_nodes{$node} == 0); + if ($monitor_nodes{$node} == 0) { + $done = 0; + last; + } } return $done; } @@ -684,7 +677,7 @@ sub do_pre_check { my $msg; if (defined($noderange) && defined($discovery_type)) { - $msg = "All pre_defined nodes are valid"; + $msg = "All pre_defined nodes '$noderange' are valid"; my $rc = check_pre_defined_node($discovery_type, $noderange); if ($rc) { probe_utils->send_msg("$output", "f", $msg); @@ -694,9 +687,9 @@ sub do_pre_check { } } - + #The block of $nics is ture is a reservation part, this part don't show up in usage if ($nics) { - if ($nics =~ /[^\w,]/) { + if ($nics =~ /[^\w|]/) { probe_utils->send_msg("$output", "f", "Invalid NIC list"); probe_utils->send_msg("$output", "d", "$::USAGE"); exit 1; @@ -711,14 +704,15 @@ sub do_pre_check { probe_utils->send_msg("$output", "d", "Network interface $nic doesn't exist on current server") if ($verbose); $miss = 1; } else { - my $tmp = `ifconfig $nic|awk -F" " '/inet / {print \$2,\$4}'`; + my $tmp = `echo $tmp_nic |awk -F" " '/inet / {print \$2}'`; chomp($tmp); - if (!$tmp) { + if (!length($tmp)) { probe_utils->send_msg("$output", "d", "Network interface $nic isn't set IP address") if ($verbose); $miss = 1; } else { - my ($ip, $mask) = split(" ", $tmp); - push(@nets, probe_utils->get_network($ip, $mask)); + my ($ip, $mask) = split("/", $tmp); + my $strmask = xCAT::NetworkUtils::formatNetmask($mask, 1, 0); + push(@nets, probe_utils->get_network($ip, $strmask)); } } } @@ -737,22 +731,26 @@ sub do_pre_check { probe_utils->send_msg("$output", "f", $msg); exit 1; } - if (!probe_utils->is_ip_addr("$masteripinsite")) { + + if (!xCAT::NetworkUtils->isIpaddr("$masteripinsite")) { probe_utils->send_msg("$output", "d", "The value of 'master' in 'site' table isn't a IP address") if ($verbose); probe_utils->send_msg("$output", "f", $msg); exit 1; } - `ifconfig -a 2>&1 |grep $masteripinsite`; + my $tmpoutput = `ip addr 2>&1 |grep $masteripinsite`; if ($?) { probe_utils->send_msg("$output", "d", "The IP $masteripinsite of 'master' in 'site' table dosen't belong to any network on current server") if ($verbose); probe_utils->send_msg("$output", "f", $msg); exit 1; } probe_utils->send_msg("$output", "o", $msg); - my $tmp = `ifconfig -a|awk -F" " '/$masteripinsite/ {print \$2,\$4}'`; + + chomp($tmpoutput); + my $tmp = `echo $tmpoutput | awk -F" " '{print \$2}'`; chomp($tmp); - my ($ip, $mask) = split(" ", $tmp); - push(@nets, probe_utils->get_network($ip, $mask)); + my ($ip, $mask) = split("/", $tmp); + my $strmask = xCAT::NetworkUtils::formatNetmask($mask, 1, 0); + push(@nets, probe_utils->get_network($ip, $strmask)); } my $arch = `uname -i`; @@ -812,23 +810,22 @@ sub do_pre_check { #------------------------------------------ sub do_monitor { + $SIG{TERM} = $SIG{INT} = sub { $terminal = 1; }; - if ($monitor && !$pre_check && defined($noderange) && defined($discovery_type)) { + if ($no_pre_check && defined($noderange) && defined($discovery_type)) { $msg = "All pre_defined nodes are valid"; my $rc = check_pre_defined_node($discovery_type, $noderange); if ($rc) { probe_utils->send_msg("$output", "f", $msg); - exit 1; + return 1; } else { probe_utils->send_msg("$output", "o", $msg); } } - &get_monitor_nodes_list if (defined($noderange) && defined($discovery_type)); - if (!$nics) { my $masteripinsite = `tabdump site | awk -F',' '/master/ { gsub(/"/, "", \$2) ; print \$2 }'`; chomp($masteripinsite); @@ -836,95 +833,147 @@ sub do_monitor { chomp($nics); if (!$nics) { probe_utils->send_msg("$output", "f", "The value of master in site table is $masteripinsite, can't get corresponding network interface"); - exit 1; + return 1; } } - probe_utils->send_msg("$output", "d", "Start to capture every message during discovery process......"); + my $startline = " +------------------------------------------------------------- + ___ + ____ _ _____ _.-| | |\\__/,| (`\\ + __ __/ ___| / \\|_ _| { | | |x x |__ _) ) + \\ \\/ / | / _ \\ | | \"-.|___| _.( T ) ` / + > <| |___ / ___ \\| | .--'-`-. _((_ `^--' /_< \\ + /_/\\_\\\\____/_/ \\_\\_| .+|______|__.-||__)`-'(((/ (((/ +------------------------------------------------------------- +"; + print "$startline\nStart to capture every message during discovery process......\n"; my $varlogmsg = "/var/log/messages"; my $clusterlog = "/var/log/xcat/cluster.log"; my $computelog = "/var/log/xcat/computes.log"; + #http logs are saved in different file in different distro my $httplog; - my $os = probe_utils->get_os(); - if ($os eq "redhat") { + if (-e "/var/log/httpd/access_log") { $httplog = "/var/log/httpd/access_log"; - } elsif ($os eq "sles") { - $httplog = "/var/log/apache2access_log"; - } elsif ($os eq "ubuntu") { + } elsif (-e "/var/log/apache2/access_log") { + $httplog = "/var/log/apache2/access_log"; + } elsif (-e "/var/log/apache2/access.log") { $httplog = "/var/log/apache2/access.log"; } - open(VARLOGMSGFILE, "tail -f $varlogmsg 2>&1 |"); - open(CLUSTERLOGFILE, "tail -f $clusterlog 2>&1 |"); - open(HTTPLOGFILE, "tail -f $httplog 2>&1 |"); - open(COMPUTERFILE, "tail -f $computelog 2>&1 |"); + my $clusterpid; + my $httppid; + my $computerpid; + my $varlogpid; + my $rst = 0; + { #important to hold a block + # start ot obtain logs from every log file + if (!($varlogpid = open(VARLOGMSGFILE, "tail -f $varlogmsg 2>&1 |"))) { + probe_utils->send_msg("$output", "f", "Can't open $varlogmsg to get logs"); + $rst = 1; + last; + } + if (!($clusterpid = open(CLUSTERLOGFILE, "tail -f $clusterlog 2>&1 |"))) { + probe_utils->send_msg("$output", "f", "Can't open $clusterlog to get logs"); + $rst = 1; + last; + } + if (!($httppid = open(HTTPLOGFILE, "tail -f $httplog 2>&1 |"))) { + probe_utils->send_msg("$output", "f", "Can't open $httplog to get logs"); + $rst = 1; + last; + } + if (!($computerpid = open(COMPUTERFILE, "tail -f $computelog 2>&1 |"))) { + probe_utils->send_msg("$output", "f", "Can't open $computelog to get logs"); + $rst = 1; + last; + } - my $select = new IO::Select; - $select->add(\*VARLOGMSGFILE); - $select->add(\*CLUSTERLOGFILE); - $select->add(\*HTTPLOGFILE); - $select->add(\*COMPUTERFILE); - $| = 1; + my $select = new IO::Select; + $select->add(\*VARLOGMSGFILE); + $select->add(\*CLUSTERLOGFILE); + $select->add(\*HTTPLOGFILE); + $select->add(\*COMPUTERFILE); + $| = 1; - my $line = ""; - my @hdls; - my $hdl; - for (; ;) { - if (@hdls = $select->can_read(0)) { - foreach $hdl (@hdls) { - if ($hdl == \*VARLOGMSGFILE) { - chomp($line = ); - my @tmp = split(/ /, $line); - if ($tmp[4] =~ /dhcpd:/i) { - handle_dhcp_msg("$line", $nics); - } elsif ($tmp[4] =~ /in.tftpd/i) { - handle_tftp_msg("$line"); + my $line = ""; + my @hdls; + my $hdl; + my $oldlines = 10; + my $varlogmsgcnt = 0; + my $clusterlogcnt = 0; + my $httplogcnt = 0; + my $computercnt = 0; + for (; ;) { + + if (@hdls = $select->can_read(0)) { + foreach $hdl (@hdls) { + if ($hdl == \*VARLOGMSGFILE) { + chomp($line = ); + ++$varlogmsgcnt; + last if ($varlogmsgcnt <= $oldlines); + my @tmp = split(/ /, $line); + if ($tmp[4] =~ /dhcpd:/i && $line =~ /$nics/) { + handle_dhcp_msg("$line"); + } elsif ($tmp[4] =~ /in.tftpd/i) { + handle_tftp_msg("$line"); + } + } elsif ($hdl == \*CLUSTERLOGFILE) { + chomp($line = ); + ++$clusterlogcnt; + last if ($clusterlogcnt <= $oldlines); + handle_cluster_msg("$line"); + } elsif ($hdl == \*HTTPLOGFILE) { + chomp($line = ); + ++$httplogcnt; + last if ($httplogcnt <= $oldlines); + handle_http_msg("$line"); + } elsif ($hdl == \*COMPUTERFILE) { + chomp($line = ); + ++$computercnt; + last if ($computercnt <= $oldlines); + handle_compute_msg("$line"); } - } elsif ($hdl == \*CLUSTERLOGFILE) { - chomp($line = ); - handle_cluster_msg("$line"); - } elsif ($hdl == \*HTTPLOGFILE) { - chomp($line = ); - handle_http_msg("$line"); - } elsif ($hdl == \*COMPUTERFILE) { - chomp($line = ); - handle_compute_msg("$line"); } } - } - if ($terminal || (%monitor_nodes && all_monitor_node_done())) { - &dump_history; - close(VARLOGMSGFILE); - close(CLUSTERLOGFILE); - close(HTTPLOGFILE); - close(COMPUTERFILE); - exit 0; + if ($terminal || (%monitor_nodes && all_monitor_node_done())) { + if ($terminal) { + probe_utils->send_msg("$output", "d", "Got from STDIN"); + } else { + probe_utils->send_msg("$output", "o", "All nodes need to monitor have finished discovery process"); + } + last; + } sleep 0.01; } - sleep 0.01; + &dump_history; } - close(VARLOGMSGFILE); - close(CLUSTERLOGFILE); - close(HTTPLOGFILE); - close(COMPUTERFILE); - return 0; + kill 'INT', $clusterpid if ($clusterpid); + kill 'INT', $httppid if ($httppid); + kill 'INT', $computerpid if ($computerpid); + kill 'INT', $varlogpid if ($varlogpid); + close(VARLOGMSGFILE) if (VARLOGMSGFILE); + close(CLUSTERLOGFILE) if (CLUSTERLOGFILE); + close(COMPUTERFILE) if (COMPUTERFILE); + close(HTTPLOGFILE) if (HTTPLOGFILE); + + return $rst; } #------------------------------------- -# main process +## main process #------------------------------------- if ( !GetOptions("--help|h|?" => \$help, - "t" => \$test, - "V" => \$verbose, - "m" => \$monitor, - "p" => \$pre_check, - "T=s" => \$discovery_type, - "n=s" => \$noderange, - "N=s" => \$nics)) + "T" => \$test, + "V" => \$verbose, + "--noprecheck" => \$no_pre_check, + "m=s" => \$discovery_type, + "n=s" => \$noderange, + "N=s" => \$nics)) #option N is a reservation option, dosen't show up in usage now { probe_utils->send_msg("$output", "f", "Invalid parameter for $program_name"); probe_utils->send_msg("$output", "d", "$::USAGE"); @@ -941,7 +990,7 @@ if ($help) { } if ($test) { - probe_utils->send_msg("$output", "o", "Do probe for discovery process, including pro-check for required configuration and realtime monitor of discovery process.Before using this command, please install nslookup command ahead."); + probe_utils->send_msg("$output", "o", "Do probe for discovery process, including pre-check for required configuration and realtime monitor of discovery process.Before using this command, please install nslookup command ahead."); exit 0; } @@ -959,19 +1008,11 @@ if (defined($discovery_type)) { } } -if ($pre_check) { - exit 1 if (do_pre_check($nics)); -} - -if ($monitor) { - $rst = do_monitor(); -} - -if (!$monitor && !$pre_check) { +if (!$no_pre_check) { $rst = do_pre_check(); exit 1 if ($rst); - - $rst = do_monitor(); } +$rst = do_monitor(); + exit $rst; diff --git a/xCAT-probe/xcatprobe b/xCAT-probe/xcatprobe index c911c23a3..2ed2da37d 100755 --- a/xCAT-probe/xcatprobe +++ b/xCAT-probe/xcatprobe @@ -10,27 +10,27 @@ $Term::ANSIColor::AUTORESET = 1; $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; -my $pro_name = basename($0); +my $program_name = basename($0); -my $pro_dir="$::XCATROOT/probe/"; +my $pro_dir = "$::XCATROOT/probe/"; my $plugin_dir = "$pro_dir/subcmds"; my %cmds = (); my $verbose = 0; -my $nocolor = 0; +my $color = (-t *STDOUT); +my $fullmsg = 0; my $help = 0; my $list = 0; $::USAGE = "Usage: xcatprobe -h xcatprobe -l -xcatprobe [-n] [-V] +xcatprobe [-V] Options: - -h : get usage information of $pro_name + -h : get usage information of $program_name -l : list all valid sub commands - -V : print verbose information of $pro_name - -n : print output without colors + -V : print verbose information of $program_name "; #----------------------------------- @@ -52,17 +52,17 @@ sub loadsubcmds { foreach (@candidate) { my $cmdname = basename("$_"); - $output = `$_ -t 2>&1`; + $output = `$_ -T 2>&1`; chomp($output); print "\n-->$_\n[OUTPUT]:\n$output\n" if ($verbose); if ($output !~ /\[(\w+)\]\s*:\s*(.+)/) { - print "skip $_ for doing '$_ -t' failed, bad format\n" if ($verbose); + print "skip $_ for doing '$_ -T' failed, bad format\n" if ($verbose); next; } else { my $desc = $2; unless ($1 ~~ /^ok$/) { - print "skip $_ for doing '$_ -t' failed, invalid flag\n" if ($verbose); + print "skip $_ for doing '$_ -T' failed, invalid flag\n" if ($verbose); next; } $cmds{$cmdname} = $desc; @@ -83,57 +83,93 @@ sub loadsubcmds { #---------------------------------- sub format_cmd_output { my $line = shift; - my $nocolor = shift; - if ($line =~ /\[(\w+)\]\s*:\s*(.+)/) { - my $flag = $1; - my $msg = $2; - my @tmpmsg = split(" ", $msg); - my $maxlen = 80; - my @finalmsg = (); - my $str = ""; - foreach my $word (@tmpmsg) { - $str .= $word . " "; - if (length($str) > $maxlen) { - push @finalmsg, $str; - $str = ""; + my $color = shift; + my $fullmsg = shift; + + my $msg; + my $flag; + + if ($line =~ /\[(\w+)\]\s*:(.+)/) { + $flag = $1; + $msg = $2; + } else { + $msg = $line; + } + + $msg =~ s/\t/ /g; + my $flaglen = 6; + my $desiredwidth = 120; + my $screenwidth = (`tput cols` + 0); + my $maxlen = ($screenwidth > $desiredwidth ? $desiredwidth : $screenwidth); + + my @finalmsg = (); + my $msglen = length($msg); + if ($msglen <= $maxlen) { + if (!$fullmsg && $flag) { + my $leftspace = $maxlen - length($msg); + if ($leftspace < $flaglen) { + $msg = substr($msg, 0, $maxlen - $flaglen); + $msg =~ s/(.*).../$1\.\.\./g; } } - push @finalmsg, $str if ($str ne ""); - - for (my $i = 0 ; $i < $#finalmsg + 1 ; $i++) { - if ($i ne $#finalmsg) { - print "$finalmsg[$i]\n"; - next; + push @finalmsg, $msg; + } else { + my @tmpmsg = split("", $msg); + my $head = 0; + my $tail = $maxlen; + while ($head < $msglen) { + push @finalmsg, substr($msg, $head, ($tail - $head)); + if (!$fullmsg) { + if ($flag) { + $finalmsg[0] =~ s/(.*).........$/$1\.\.\./g; + } else { + $finalmsg[0] =~ s/(.*)...$/$1\.\.\./g; + } + last; } - my $space = " " x ($maxlen - length($finalmsg[$i]) + 5); - print "$finalmsg[$i]$space"; + $head = $tail; + $tail = $head + ($maxlen <= ($msglen - $tail) ? $maxlen : ($msglen - $tail)); + } + } + + for (my $i = 0 ; $i < $#finalmsg + 1 ; ++$i) { + if ($i ne $#finalmsg) { + print "$finalmsg[$i]\n"; + next; + } + + if ($flag) { + my $leftspace = $maxlen - length($finalmsg[$i]); + my $spacenum = (($leftspace >= $flaglen) ? ($leftspace - $flaglen) : ($screenwidth - length($finalmsg[$i]) + $maxlen - $flaglen)); + my $spacestr = " " x $spacenum; + print "$finalmsg[$i]$spacestr"; if ($flag =~ /failed/i) { - if ($nocolor) { - print "[FAIL]\n"; - } else { + if ($color) { print BOLD RED "[FAIL]\n"; + } else { + print "[FAIL]\n"; } } elsif ($flag =~ /warning/i) { - if ($nocolor) { - print "[WARN]\n"; - } else { + if ($color) { print BOLD BLUE "[WARN]\n"; + } else { + print "[WARN]\n"; } } elsif ($flag =~ /ok/i) { - if ($nocolor) { - print "[ OK ]\n"; - } else { + if ($color) { print BOLD GREEN "[ OK ]\n"; + } else { + print "[ OK ]\n"; } } elsif ($flag =~ /debug/i) { print "\n"; } elsif ($flag =~ /info/i) { print "[INFO]\n"; } + } else { + print "$finalmsg[$i]\n"; } - } else { - print "$line\n"; } return 0; } @@ -152,32 +188,39 @@ sub listvalidsubcmd { $maxlen = length($key) if (length($key) > $maxlen); } $maxlen += 4; + + my $desiredwidth = 100; + my $screenwidth = (`tput cols` + 0); + my $finallen = ($screenwidth > $desiredwidth ? $desiredwidth : $screenwidth); + print "Supported sub commands are:\n"; foreach my $key (keys %cmds) { my @desc = split(" ", $cmds{$key}); my $str = ""; my @formatdesc = (); foreach my $word (@desc) { - $str .= $word . " "; - if (length($str) > 100) { + if (length($str) + length($word) > $finallen - $maxlen) { + $str =~ s/([^\s]+)\s$/$1/g; push @formatdesc, $str; $str = ""; } + $str .= $word . " "; } + $str =~ s/([^\s]+)\s$/$1/g; push @formatdesc, $str; - if ($nocolor) { - print "$key"; - } else { + + if ($color) { print BOLD GREEN "$key"; + } else { + print "$key"; } my $space = " " x ($maxlen - length($key)); - print "$space $formatdesc[0]\n"; + print "$space$formatdesc[0]\n"; delete $formatdesc[0]; $space = " " x $maxlen; foreach my $line (@formatdesc) { - print "$space $line\n" if (length($line)); + print "$space$line\n" if (length($line)); } - } } @@ -185,7 +228,7 @@ sub listvalidsubcmd { # main ####################################### my @tmpargv = @ARGV; -my @supportopt = ("-V", "-h", "-l", "-n"); +my @supportopt = ("-V", "-h", "-l", "-w"); my $pluginname; my $optnum = 0; foreach my $attr (@tmpargv) { @@ -199,13 +242,14 @@ foreach my $attr (@tmpargv) { $help = 1 if ($attr eq "-h"); $verbose = 1 if ($attr eq "-V"); $list = 1 if ($attr eq "-l"); - $nocolor = 1 if ($attr eq "-n"); + $fullmsg = 1 if ($attr eq "-w"); } else { $pluginname = $attr; last; } } + &loadsubcmds; if (defined($pluginname)) { my $hit = 0; @@ -234,39 +278,22 @@ if (!defined($pluginname)) { exit 0; } - -for (my $i = 0 ; $i < $optnum + 1 ; $i++) { - shift @tmpargv; -} +splice(@tmpargv, 0, $optnum + 1); my $pluginattrs = join(" ", @tmpargv); my $subcmd = "$plugin_dir/$pluginname $pluginattrs"; - print "\nsubcmd = $subcmd\n" if ($verbose); -open(PIPE, "$subcmd |"); - -my $terminal = 0; my $subcmdpid = 0; -my @tmpoutput = `ps axjf |grep -v grep|grep -v ps|grep -v bash|grep $$`; -foreach (@tmpoutput) { - @psline = split(" ", $_); $subcmdpid = $psline[1] if (($psline[0] eq $$) && ($psline[1] ne $$) && ($psline[2] eq $$)); -} $SIG{TERM} = $SIG{INT} = sub { - $terminal = 1; + $subcmdpid or exit 0; + kill 'INT', $subcmdpid; }; +$subcmdpid = open(PIPE, "$subcmd |") or die("Something went wrong while fork()ing to handle subcommand $subcmd: $!"); + while () { chomp; - format_cmd_output($_, $nocolor); - if ($terminal && $subcmdpid) { - kill 'INT', $subcmdpid; - while () { - chomp; - format_cmd_output($_, $nocolor); - } - close(PIPE); - exit $?; - } + format_cmd_output($_, $color, $fullmsg); } close(PIPE); # This will set the $? properly