#!/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 Linux only Arguments: nodename Returns: Globals: none Error: none Example: my $ip = xCAT::NetworkUtils->my_ip_facing($peerip) 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 $peernumber = inet_aton($peer); #TODO: IPv6 support unless ($peernumber) { return undef; } my $noden = unpack("N", inet_aton($peer)); 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) { if (wantarray) { return @ips; } else { return $ips[0]; } } else { return undef; } } #------------------------------------------------------------------------------- =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; 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)) { return $curnet; } } return undef; } #------------------------------------------------------------------------------- =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 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; 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) { $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"; } elsif ($mac) { $net_params->{ksdevice} = "ksdevice=$mac"; $net_params->{BOOTIF} = "BOOTIF=$mac"; $net_params->{bootdev} = "bootdev=$mac"; $net_params->{ip} = "ip=dhcp"; if ($nodebootif) { $net_params->{ifname} = "ifname=$nodebootif:$mac"; } $net_params->{netdevice} = "netdevice=$mac"; } return $net_params; } 1;