2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-05-22 11:42:05 +00:00
2021-05-05 13:53:32 -04:00

644 lines
22 KiB
Perl

# 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 <xcatmaster>,
# to indicate to use the cluster-facing ip address
# on this management node or service node
if (!$gateway)
{
$gateway = "<xcatmaster>";
}
# 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/, `/sbin/ip -4 route`;
my @mtable = split /\n/, `/sbin/ip link|grep BROADCAST`;
my %netgw = ();
foreach my $rtent (@rtable)
{
my @entarr = split /\s+/, $rtent;
if ($entarr[1] eq 'via' )
{
$netgw{$entarr[0]} = $entarr[2];
}
}
#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/;
# Do not process network entries that
# match 169.254.0.0 or 127.0.0.0
# OR
# in range 224.x.x.x thru 239.x.x.x
# OR
# do not contain '/'
if ($ent[0] eq "169.254.0.0" or ($firstoctet >= 224 and $firstoctet <= 239) or $ent[0] eq "127.0.0.0" or $ent[0] !~ "/")
{
next;
}
if ($ent[1] eq 'dev')
{
$netandmask = $ent[0];
$mgtifname = $ent[2];
($net, $mask) = split('/', $netandmask);
$mask = xCAT::NetworkUtils::formatNetmask($mask, 1, 0);
if (defined($netgw{'default'}))
{
if (xCAT::NetworkUtils->ishostinsubnet($netgw{'default'}, $mask, $net))
{
$gw = $netgw{'default'}; #default gatetway
}
}
# set gateway to keyword <xcatmaster>,
# to indicate to use the cluster-facing ip address
# on this management node or service node
if (!$gw)
{
$gw = "<xcatmaster>";
}
# 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[4];
}
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[2];
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 => "<xcatmaster>" });
}
}
last;
}
}
}
#Nothing much sane to do for the other fields at the moment?
}
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;