# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html package xCAT_plugin::networks; use xCAT::Table; use Data::Dumper; use Sys::Syslog; use Socket; use xCAT::Utils; use xCAT::TableUtils; use xCAT::NetworkUtils; use xCAT::ServiceNodeUtils; use Getopt::Long; sub handled_commands { return { makenetworks => "networks", }; } sub preprocess_request { my $req = shift; my $callback = shift; # exit if preprocessed if ($req->{_xcatpreprocessed}->[0] == 1) { return [$req]; } my @requests = ({%$req}); #first element is local instance $::args = $req->{arg}; if (defined($::args) && @{$::args}) { @ARGV = @{$::args}; } Getopt::Long::Configure("no_pass_through"); if ( !GetOptions( 'help|h|?' => \$::HELP, 'display|d' => \$::DISPLAY, 'verbose|V' => \$::VERBOSE, 'version|v' => \$::VERSION, ) ) { # return 1; } # Option -h for Help if ($::HELP) { &makenetworks_usage($callback); return undef; } # Option -v for version - do we need this??? if ($::VERSION) { my $rsp; my $version = xCAT::Utils->Version(); $rsp->{data}->[0] = "makenetworks - $version"; xCAT::MsgUtils->message("I", $rsp, $callback); return undef; } # process the network interfaces on this system if (&donets($callback) != 0) { my $rsp; push @{ $rsp->{data} }, "Could not get network information.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return undef; } my @sn = xCAT::ServiceNodeUtils->getSNList(); foreach my $s (@sn) { my $reqcopy = {%$req}; $reqcopy->{'_xcatdest'} = $s; $reqcopy->{_xcatpreprocessed}->[0] = 1; push @requests, $reqcopy; } return \@requests; } sub process_request { my $request = shift; my $callback = shift; $::args = $request->{arg}; if (defined($::args) && @{$::args}) { @ARGV = @{$::args}; } Getopt::Long::Configure("no_pass_through"); if ( !GetOptions( 'help|h|?' => \$::HELP, 'display|d' => \$::DISPLAY, 'verbose|V' => \$::VERBOSE, 'version|v' => \$::VERSION, ) ) { # return 1; } # process the network interfaces on this system # - management node was already done if (!xCAT::Utils->isMN()) { if (&donets($callback) != 0) { my $rsp; push @{ $rsp->{data} }, "Could not get network information.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } } return 0; } #---------------------------------------------------------------------------- =head3 donets Get network information and display or create xCAT network defs Returns: 0 - OK 1 - error Usage: my $rc = &donets($callback); =cut #----------------------------------------------------------------------------- sub donets { my $callback = shift; my $host = `hostname`; chomp $host; # get all the existing xCAT network defs my @netlist; @netlist = xCAT::DBobjUtils->getObjectsOfType('network'); my %nethash; if (scalar(@netlist)) { my %objtype; foreach my $netn (@netlist) { $objtype{$netn} = 'network'; } %nethash = xCAT::DBobjUtils->getobjdefs(\%objtype, $callback); if (!%nethash) { my $rsp; $rsp->{data}->[0] = "Could not get xCAT network definitions.\n"; xCAT::MsgUtils->message("E", $rsp, $::callback); return 1; } } my $nettab = xCAT::Table->new('networks', -create => 1, -autocommit => 0); if (xCAT::Utils->isAIX()) { # get list of interfaces "ifconfig -l" my $ifgcmd = "ifconfig -l"; my @interfaces = split(/\s+/, xCAT::Utils->runcmd($ifgcmd, 0)); if ($::RUNCMD_RC != 0) { my $rsp; push @{ $rsp->{data} }, "Could not run \'$ifgcmd\'.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } my $master = xCAT::TableUtils->get_site_Master(); my $masterip = xCAT::NetworkUtils->getipaddr($master); if ($masterip =~ /:/) { # do each ethernet interface for ipv6 foreach my $i (@interfaces) { if ($i =~ /^en/) { # "ifconfig en0 |grep fe80" to get net and prefix length my $cmd = "ifconfig $i |grep -i inet6"; my @netinfo = xCAT::Utils->runcmd($cmd, -1); if ($::RUNCMD_RC != 0) { # no ipv6 address configured next; } # only handle the ipv6 addr without % foreach my $line (@netinfo) { next if ($line =~ /\%/); my $gateway; my $netmask; my @fields; @fields = split(/ /, $line); ($gateway, $netmask) = split(/\//, $fields[1]); my $eip = Net::IP::ip_expand_address($gateway, 6); my $bip = Net::IP::ip_iptobin($eip, 6); my $bmask = Net::IP::ip_get_mask($netmask, 6); my $bnet = $bip & $bmask; my $ipnet = Net::IP::ip_bintoip($bnet, 6); my $net = Net::IP::ip_compress_address($ipnet, 6); my $netname = $net . "-" . $netmask; # see if this network (or equivalent) is already defined # - compare net and prefix_len values my $foundmatch = 0; foreach my $netn (@netlist) { # get net and prefix_len my $dnet = $nethash{$netn}{'net'}; my $dprefix_len = $nethash{$netn}{'mask'}; if (($net == $dnet) && ($netmask == $dprefix_len)) { $foundmatch = 1; last; } } if ($::DISPLAY) { my $rsp; push @{ $rsp->{data} }, "\n#From $host."; pus h @{ $rsp->{data} }, "$netname:"; push @{ $rsp->{data} }, " objtype=network"; push @{ $rsp->{data} }, " net=$net"; push @{ $rsp->{data} }, " mask=$netmask"; push @{ $rsp->{data} }, " mgtifname=$i"; push @{ $rsp->{data} }, " gateway=$gateway\n"; if ($foundmatch) { pus h @{ $rsp->{data} }, "# Note: An equivalent xCAT network definition already exists.\n"; } xCA T::MsgUtils->message("I", $rsp, $callback); } else { if ($foundmatch) { next; } # add new network def $nettab->setAttribs({ 'net' => $net, 'mask' => $netmask }, { 'netname' => $netname, 'gateway' => $gateway, 'mgtifname' => $i }); } } } } } else { # do each ethernet interface for ipv4 foreach my $i (@interfaces) { if ($i =~ /^en/) { # "mktcpip -S en0" to get nm & gw my $mkcmd = "mktcpip -S $i"; my @netinfo = xCAT::Utils->runcmd($mkcmd, 0); if ($::RUNCMD_RC != 0) { my $rsp; push @{ $rsp->{data} }, "Could not run \'$mkcmd\'.\n"; xCAT::MsgUtils->message("E", $rsp, $callback); return 1; } my $netmask; my $ipaddr; my @fields; my $gateway; foreach my $line (@netinfo) { next if ($line =~ /^\s*#/); @fields = split(/:/, $line); } $ipaddr = $fields[1]; $netmask = $fields[2]; if ($fields[6]) { if (xCAT::NetworkUtils::isInSameSubnet($fields[6], $ipaddr, $netmask, 0)) { $gateway = $fields[6]; } } # set gateway to keyword , # to indicate to use the cluster-facing ip address # on this management node or service node if (!$gateway) { $gateway = ""; } # split interface IP my ($ip1, $ip2, $ip3, $ip4) = split('\.', $ipaddr); # split mask my ($m1, $m2, $m3, $m4) = split('\.', $netmask); # AND nm and ip to get net attribute my $n1 = ((int $ip1) & (int $m1)); my $n2 = ((int $ip2) & (int $m2)); my $n3 = ((int $ip3) & (int $m3)); my $n4 = ((int $ip4) & (int $m4)); my $net = "$n1.$n2.$n3.$n4"; # use convention for netname attr my $netn; my $maskn; ($netn = $net) =~ s/\./\_/g; ($maskn = $netmask) =~ s/\./\_/g; # ( 1_2_3_4-255_255_255_192 - ugh!) my $netname = $netn . "-" . $maskn; # see if this network (or equivalent) is already defined # - compare net and mask values my $foundmatch = 0; foreach my $netn (@netlist) { # split definition mask my ($dm1, $dm2, $dm3, $dm4) = split('\.', $nethash{$netn}{'mask'}); # split definition net addr my ($dn1, $dn2, $dn3, $dn4) = split('\.', $nethash{$netn}{'net'}); # check for the same netmask and network address if (($n1 == $dn1) && ($n2 == $dn2) && ($n3 == $dn3) && ($n4 == $dn4)) { if (($m1 == $dm1) && ($m2 == $dm2) && ($m3 == $dm3) && ($m4 == $dm4)) { $foundmatch = 1; last; } } } if ($::DISPLAY) { my $rsp; push @{ $rsp->{data} }, "\n#From $host."; push @{ $rsp->{data} }, "$netname:"; push @{ $rsp->{data} }, " objtype=network"; push @{ $rsp->{data} }, " net=$net"; push @{ $rsp->{data} }, " mask=$netmask"; push @{ $rsp->{data} }, " mgtifname=$i"; push @{ $rsp->{data} }, " gateway=$gateway\n"; if ($foundmatch) { push @{ $rsp->{data} }, "# Note: An equivalent xCAT network definition already exists.\n"; } xCAT::MsgUtils->message("I", $rsp, $callback); } else { if ($foundmatch) { next; } # add new network def $nettab->setAttribs({ 'net' => $net, 'mask' => $netmask }, { 'netname' => $netname, 'gateway' => $gateway, 'mgtifname' => $i }); } } } # end foreach } #end if ipv4 } else { # For Linux systems my @ip6table = split /\n/, `/sbin/ip -6 route`; my @rtable = split /\n/, `/bin/netstat -rn`; my @mtable = split /\n/, `/bin/netstat -i`; splice @rtable, 0, 2; splice @mtable, 0, 2; my %netgw = (); foreach my $rtent (@rtable) { my @entarr = split /\s+/, $rtent; if ($entarr[3] eq 'UG') { $netgw{ $entarr[0] }{ $entarr[2] } = $entarr[1]; } } #routers advertise their role completely outside of DHCPv6 scope, we don't need to #get ipv6 routes and in fact *cannot* dictate router via DHCPv6 at this specific moment. foreach (@ip6table) { my @ent = split /\s+/, $_; if ($ent[0] eq 'fe80::/64' or $ent[0] eq 'unreachable' or $ent[1] eq 'via' or $ent[2] eq 'lo') { #Do not contemplate link-local, unreachable, gatewayed networks, # or networks connected to loopback interface #DHCPv6 relay will be manually entered into networks as was the case for IPv4 next; } my $net = shift @ent; my $dev = shift @ent; if ($dev eq 'dev') { $dev = shift @ent; } else { die "Unrecognized IPv6 routing entry $_"; } my @myv6addrs = split /\n/, `ip -6 addr show dev $dev scope global`; #for v6, deprecating mask since the CIDR slash syntax is ubiquitous my $consideredaddr = $net; $consideredaddr =~ s!/(.*)!!; my $consideredbits = $1; #below may be redundant, but apply resolution in case ambiguous net, e.g. 2001:0db8:0::/64 is the same thing as 2001:0db8::/64 $consideredaddr = xCAT::NetworkUtils->getipaddr($consideredaddr); my $netexists = 0; foreach my $netn (@netlist) { #search for network that doesn't exist yet my $curnet = $nethash{$netn}{'net'}; unless ($curnet =~ /:/) { #only ipv6 here next; } $curnet =~ s!/(.*)!!; #remove my $curnetbits = $1; unless ($consideredbits == $curnetbits) { #only matches if netmask matches next; } $currnet = xCAT::NetworkUtils->getipaddr($currnet); unless ($currnet eq $consideredaddr) { next; } $netexists = 1; } if ($::DISPLAY) { push @{ $rsp->{data} }, "\n#From $host."; push @{ $rsp->{data} }, "$net:"; push @{ $rsp->{data} }, " objtype=network"; push @{ $rsp->{data} }, " net=$net"; push @{ $rsp->{data} }, " mgtifname=$dev"; } else { unless ($netexiss) { my $tmpmask = $net; $tmpmask =~ s!^.*/!/!; $nettab->setAttribs({ 'net' => $net, 'mask' => $tmpmask }, { 'netname' => $net, 'mgtifname' => $dev }); } } } foreach (@rtable) { #should be the lines to think about, do something with U, and something else with UG my $foundmatch = 0; my $netnamematch = 0; my $rsp; my $net; my $mask; my $mgtifname; my $gw; my @ent = split /\s+/, $_; my $firstoctet = $ent[0]; $firstoctet =~ s/^(\d+)\..*/$1/; if ($ent[0] eq "169.254.0.0" or ($firstoctet >= 224 and $firstoctet <= 239) or $ent[0] eq "127.0.0.0") { next; } if ($ent[3] eq 'U') { $net = $ent[0]; $mask = $ent[2]; $mgtifname = $ent[7]; if (defined($netgw{'0.0.0.0'}{'0.0.0.0'})) { if (xCAT::NetworkUtils->ishostinsubnet($netgw{'0.0.0.0'}{'0.0.0.0'}, $mask, $net)) { $gw = $netgw{'0.0.0.0'}{'0.0.0.0'}; #default gatetway } } # set gateway to keyword , # to indicate to use the cluster-facing ip address # on this management node or service node if (!$gw) { $gw = ""; } # use convention for netname attr my $netn; my $maskn; ($netn = $net) =~ s/\./\_/g; ($maskn = $mask) =~ s/\./\_/g; # ( 1_2_3_4-255_255_255_192 - ugh!) my $netname = $netn . "-" . $maskn; # see if this network (or equivalent) is already defined # - compare net and mask values # split mask my ($m1, $m2, $m3, $m4) = split('\.', $mask); # split net addr my ($n1, $n2, $n3, $n4) = split('\.', $net); foreach my $netn (@netlist) { # check if this netname is already defined if ( $netname eq $netn ) { $netnamematch = 1; last; } # split definition mask my ($dm1, $dm2, $dm3, $dm4) = split('\.', $nethash{$netn}{'mask'}); # split definition net addr my ($dn1, $dn2, $dn3, $dn4) = split('\.', $nethash{$netn}{'net'}); # check for the same netmask and network address if (($n1 == $dn1) && ($n2 == $dn2) && ($n3 == $dn3) && ($n4 == $dn4)) { if (($m1 == $dm1) && ($m2 == $dm2) && ($m3 == $dm3) && ($m4 == $dm4)) { $foundmatch = 1; } } } # get mtu value my $mtu; my @rowm; foreach (grep /\s*$mgtifname\b/, @mtable) { @rowm = split(/\s+/, $_); $mtu = $rowm[1]; } if ($::DISPLAY) { push @{ $rsp->{data} }, "\n#From $host."; push @{ $rsp->{data} }, "$netname:"; push @{ $rsp->{data} }, " objtype=network"; push @{ $rsp->{data} }, " net=$net"; push @{ $rsp->{data} }, " mask=$mask"; if ($gw) { push @{ $rsp->{data} }, " gateway=$gw"; } push @{ $rsp->{data} }, " mgtifname=$mgtifname"; if ($mtu) { push @{ $rsp->{data} }, " mtu=$mtu"; } } else { # if this net entry exists, go to next line in networks table if ($netnamematch) { $callback->({ warning => "The network entry \'$netname\' already exists in xCAT networks table. Cannot create a definition for \'$netname\'" }); next; } if (!$foundmatch) { $nettab->setAttribs({ 'net' => $net, 'mask' => $mask }, { 'netname' => $netname, 'mgtifname' => $mgtifname, 'gateway' => $gw, 'mtu' => $mtu }); } } my $tent=$nethash{$netname}; unless ($tent and $tent->{tftpserver}) { my $netdev = $ent[7]; my @netlines = split /\n/, `/sbin/ip addr show dev $netdev`; foreach (grep /\s*inet\b/, @netlines) { my @row = split(/\s+/, $_); my $ipaddr = $row[2]; $ipaddr =~ s/\/.*//; my @maska = split(/\./, $mask); my @ipa = split(/\./, $ipaddr); my @neta = split(/\./, $net); my $isme = 1; foreach (0 .. 3) { my $oct = (0 + $maska[$_]) & ($ipa[$_] + 0); unless ($oct == $neta[$_]) { $isme = 0; last; } } if ($isme) { if ($::DISPLAY) { push @{ $rsp->{data} }, " tftpserver=$ipaddr"; } else { if (!$foundmatch) { $nettab->setAttribs({ 'net' => $net, 'mask' => $mask }, { tftpserver => "" }); } } last; } } } #Nothing much sane to do for the other fields at the moment? } elsif ($ent[3] eq 'UG') { #TODO: networks through gateway. and how we might care.. } else { #TODO: anything to do with such entries? } if ($::DISPLAY) { if ($foundmatch) { push @{ $rsp->{data} }, "# Note: An equivalent xCAT network definition already exists.\n"; } xCAT::MsgUtils->message("I", $rsp, $callback); } } } $nettab->commit; return 0; } #---------------------------------------------------------------------------- =head3 makenetworks_usage =cut #----------------------------------------------------------------------------- sub makenetworks_usage { my $callback = shift; my $rsp; push @{ $rsp->{data} }, "\nUsage: makenetworks - Gather cluster network information and add it to the xCAT database.\n"; push @{ $rsp->{data} }, " makenetworks [-h|--help ]\n"; push @{ $rsp->{data} }, " makenetworks [-v|--version]\n"; push @{ $rsp->{data} }, " makenetworks [-V|--verbose] [-d|--display]\n"; xCAT::MsgUtils->message("I", $rsp, $callback); return 0; } 1;