xcat-core/xCAT-server/lib/xcat/plugins/route.pm
2012-08-09 04:07:40 +00:00

607 lines
14 KiB
Perl

# IBM(c) 2010 EPL license http://www.eclipse.org/legal/epl-v10.html
#-------------------------------------------------------
=head1
xCAT plugin package to handle xdsh
Supported command:
nodenetconn
ipforward (internal command)
=cut
#-------------------------------------------------------
package xCAT_plugin::route;
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";
use strict;
use xCAT::Table;
use xCAT::Utils;
use xCAT::TableUtils;
use xCAT::ServiceNodeUtils;
use xCAT::NetworkUtils;
use xCAT::MsgUtils;
use Getopt::Long;
use xCAT::NodeRange;
use Data::Dumper;
use xCAT::NodeRange;
1;
#-------------------------------------------------------
=head3 handled_commands
Return list of commands handled by this plugin
=cut
#-------------------------------------------------------
sub handled_commands
{
return {
makeroutes => "route",
ipforward => "route"
};
}
#-------------------------------------------------------
=head3 preprocess_request
Preprocess the command
=cut
#-------------------------------------------------------
sub preprocess_request
{
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $command = $request->{command}->[0];
my $args = $request->{arg};
#if already preprocessed, go straight to request
if ((defined($request->{_xcatpreprocessed}))
&& ($request->{_xcatpreprocessed}->[0] == 1))
{
return [$request];
}
if ($command eq "ipforward") {
my $nodes=$request->{node};
my @sns=();
if ($nodes) {
@sns=@$nodes;
}
#print "sns=@sns\n";
my @requests=();
foreach (@sns) {
my $reqcopy = {%$request};
$reqcopy->{'node'}=[];
$reqcopy->{'_xcatdest'}=$_;
$reqcopy->{_xcatpreprocessed}->[0] = 1;
push @requests, $reqcopy;
}
return \@requests;
} else {
# parse the options
@ARGV=();
if ($args) {
@ARGV=@{$args};
}
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("no_pass_through");
my $routelist_in;
my $delete=0;
if(!GetOptions(
'h|help' => \$::HELP,
'v|version' => \$::VERSION,
'r|routename=s' => \$routelist_in,
'd|delete' => \$delete,
))
{
&usage($callback);
return 1;
}
# display the usage if -h or --help is specified
if ($::HELP) {
&usage($callback);
return 0;
}
# display the version statement if -v or --verison is specified
if ($::VERSION)
{
my $rsp={};
$rsp->{data}->[0]= xCAT::Utils->Version();
$callback->($rsp);
return 0;
}
#make sure the input routes are in the routes table.
if ($routelist_in) {
my %all_routes=();
my $routestab=xCAT::Table->new("routes", -create =>1);
if ($routestab) {
my @tmp1=$routestab->getAllAttribs(('routename', 'net'));
if (@tmp1 > 0) {
foreach(@tmp1) {
$all_routes{$_->{routename}} = $_;
$_->{process} = 0;
}
}
}
my @badroutes=();
foreach(split(',', $routelist_in)) {
if (!exists($all_routes{$_})) {
push(@badroutes, $_);
}
}
if (@badroutes>0) {
my $rsp={};
my $badroutes_s=join(',', @badroutes);
if (@badroutes==1) {
$rsp->{error}->[0]= "The route $badroutes_s is not defined in the routes table.";
} else {
$rsp->{error}->[0]= "The routes $badroutes_s are not defined in the routes table.";
}
$callback->($rsp);
return 1;
}
}
if (@ARGV == 0) { #no noderange is specifiled, assume it is on the mn
my $reqcopy = {%$request};
$reqcopy->{_xcatpreprocessed}->[0] = 1;
if ($routelist_in) {
$reqcopy->{routelist}->[0]=$routelist_in;
}
if ($delete) {
$reqcopy->{delete}->[0]=1;
}
return [$reqcopy];
} else { #noderange is specified,
my $ret=[];
my $nr=$ARGV[0];
my @noderange = xCAT::NodeRange::noderange($nr, 1);
my @missednodes=xCAT::NodeRange::nodesmissed();
if (@missednodes > 0) {
my $rsp={};
$rsp->{error}->[0]= "Invalide nodes in noderange: " . join(',', @missednodes);
$callback->($rsp);
return 1;
}
my @servicenodes=xCAT::ServiceNodeUtils->getSNList();
#print "noderange=@noderange, missednodes=@missednodes, servicenodes=@servicenodes\n";
#pick out the service nodes from the node list
foreach my $sn (@servicenodes) {
if (grep /^$sn$/, @noderange) {
@noderange=grep(!/^$sn$/, @noderange);
my $reqcopy = {%$request};
$reqcopy->{_xcatpreprocessed}->[0] = 1;
$reqcopy->{'_xcatdest'} = $sn;
$reqcopy->{node} = [$sn];
if ($routelist_in) {
$reqcopy->{routelist}->[0]=$routelist_in;
}
if ($delete) {
$reqcopy->{delete}->[0]=1;
}
push(@$ret, $reqcopy);
}
}
#now find out the service nodes for each node and
#send the request to the service node
my $sn_hash = xCAT::ServiceNodeUtils->get_ServiceNode(\@noderange, "xcat", "MN");
# build each request for each service node
foreach my $sn (keys %$sn_hash)
{
my $reqcopy = {%$request};
$reqcopy->{node} = $sn_hash->{$sn};
$reqcopy->{'_xcatdest'} = $sn;
$reqcopy->{_xcatpreprocessed}->[0] = 1;
$reqcopy->{remote}->[0] = 1;
if ($routelist_in) {
$reqcopy->{routelist}->[0]=$routelist_in;
}
if ($delete) {
$reqcopy->{delete}->[0]=1;
}
push(@$ret, $reqcopy);
}
return $ret;
}
}
}
#-------------------------------------------------------
=head3 process_request
Process the command
=cut
#-------------------------------------------------------
sub process_request
{
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $command = $request->{command}->[0];
if ($command eq "makeroutes") {
return process_makeroutes($request, $callback, $sub_req);
} elsif ($command eq "ipforward") {
return process_ipforward($request, $callback, $sub_req);
}
return;
}
sub process_makeroutes {
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $command = $request->{command}->[0];
my $nodes;
if (exists($request->{node})) {
$nodes=$request->{node};
}
my $delete=0;
if (exists($request->{delete})) {
$delete=$request->{delete}->[0];
}
my $remote=0;
if (exists($request->{remote})) {
$remote=$request->{remote}->[0];
}
my $routelist;
if (exists($request->{routelist})) {
$routelist=$request->{routelist}->[0];
}
#get all the routes from the routes table
my %all_routes=();
my $routestab=xCAT::Table->new("routes", -create =>1);
if ($routestab) {
my @tmp1=$routestab->getAllAttribs(('routename', 'net', 'mask', 'gateway', 'ifname'));
if (@tmp1 > 0) {
foreach(@tmp1) {
$all_routes{$_->{routename}} = $_;
$_->{process} = 0;
}
}
}
if ($routelist) {
foreach(split(',', $routelist)) {
$all_routes{$_}->{process}=1;
if (($nodes) && ($remote)) {
$all_routes{$_}->{nodes}=$nodes;
}
}
} else {
#get the routes for each nodes from the noderes table (for sn and cn) and site table (for mn)
if ($nodes) {
my $nrtab=xCAT::Table->new("noderes", -create =>1);
my $nrhash = $nrtab->getNodesAttribs($nodes, ['routenames']) ;
foreach(@$nodes) {
my @badroutes=();
my $node=$_;
my $rn;
my $ent=$nrhash->{$node}->[0];
if (ref($ent) and defined $ent->{routenames}) {
$rn = $ent->{routenames};
}
if ($rn) {
my @a=split(',', $rn);
my @badroutes=();
foreach my $r (@a) {
if (! exists($all_routes{$r})) {
push(@badroutes, $r);
} else {
#print "got here...., remote=$remote\n";
$all_routes{$r}->{process}=1;
if ($remote) {
my $pa=$all_routes{$r}->{nodes};
if ($pa) {
push(@$pa, $node);
} else {
$all_routes{$r}->{nodes}=[$node];
}
}
}
}
if (@badroutes > 0) {
my $badroutes_s=join(',', @badroutes);
my $rsp={};
if (@badroutes==1) {
$rsp->{error}->[0]= "The route $badroutes_s is not defined in the routes table. Please check noderes.routenames for node $node.";
} else {
$rsp->{error}->[0]= "The routes $badroutes_s are not defined in the routes table. Please check noderes.routenames for node $node.";
}
$callback->($rsp);
return 1;
}
} else {
my $rsp={};
$rsp->{data}->[0]= "No routes defined in noderes.routenames for node $node, skiping $node.";
$callback->($rsp);
}
}
}
else { #this is mn, get the routes from the site table
my @mnroutes = xCAT::TableUtils->get_site_attribute("mnroutenames");
if ($mnroutes[0]) {
my @a=split(',', $mnroutes[0]);
my @badroutes=();
foreach my $r (@a) {
if (! exists($all_routes{$r})) {
push(@badroutes, $r);
} else {
$all_routes{$r}->{process}=1;
}
}
if (@badroutes > 0) {
my $badroutes_s=join(',', @badroutes);
my $rsp={};
if (@badroutes==1) {
$rsp->{error}->[0]= "The route $badroutes_s is not defined in the routes table. Please check site.mnroutenames for the management node.";
} else {
$rsp->{error}->[0]= "The routes $badroutes_s are not defined in the routes table. Please check site.mnroutenames for the management node.";
}
$callback->($rsp);
return 1;
}
} else {
my $rsp={};
$rsp->{data}->[0]= "No routes defined in the site.mnroutenames for the management node.";
$callback->($rsp);
return 1;
}
}
}
#print Dumper(%all_routes);
#now let's handle the route creatation and deletion
my @sns=();
my $installdir = xCAT::TableUtils->getInstallDir();
while (my ($routename, $route_hash) = each(%all_routes)) {
if ($route_hash->{process}) {
my ($gw_name, $gw_ip)=xCAT::NetworkUtils->gethostnameandip($route_hash->{gateway});
push(@sns, $gw_name);
if ($remote) { #to the nodes
my $nodes_tmp=$route_hash->{nodes};
print "nodes=@$nodes_tmp, remote=$remote, delete=$delete\n";
my $op="add";
if ($delete) { $op="delete"; }
my $output = xCAT::Utils->runxcmd(
{
command => ["xdsh"],
node => $nodes_tmp,
arg => ["-e", "/$installdir/postscripts/routeop $op " . $route_hash->{net} . " " . $route_hash->{mask} . " $gw_ip"],
_xcatpreprocessed => [1],
},
$sub_req, -1, 1);
my $rsp={};
$rsp->{data}=$output;
$callback->($rsp);
} else { #local on mn or sn
if ($delete) {
delete_route($callback, $route_hash->{net}, $route_hash->{mask}, $gw_ip, $gw_name);
}
else {
set_route($callback, $route_hash->{net}, $route_hash->{mask}, $gw_ip, $gw_name);
}
}
}
}
#not all gateways are service nodes
my %sn_hash=();
my @allSN=xCAT::ServiceNodeUtils->getAllSN();
my %allSN_hash=();
foreach(@allSN) {$allSN_hash{$_}=1;}
foreach my $sn (@sns) {
if (exists($allSN_hash{$sn})) {
$sn_hash{$sn}=1;
}
}
#update servicenode.ipforward
my $sntab=xCAT::Table->new("servicenode", -create => 1,-autocommit => 1);
my %valuehash=();
my $value=1;
if ($delete) {$value=0;}
foreach my $sn (keys %sn_hash) {
$valuehash{$sn} = { ipforward=>$value };
}
$sntab->setNodesAttribs(\%valuehash);
#go to the service nodes to enable/disable ipforwarding
my @nodes=keys(%sn_hash);
$sub_req->({
command=>['ipforward'],
node=>\@nodes,
arg=>[$delete]},
$callback);
}
sub process_ipforward {
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $args = $request->{arg};
my $delete=0;
if ($args) {
$delete = $args->[0];
}
if ($delete) {
xCAT::NetworkUtils->setup_ip_forwarding(0);
} else {
xCAT::NetworkUtils->setup_ip_forwarding(1);
}
}
sub usage {
my $cb=shift;
my $rsp={};
$rsp->{data}->[0]= "Usage: makeroutes -h";
$rsp->{data}->[1]= " makeroutes -v";
$rsp->{data}->[2]= " makeroutes [-r routename[,routename...]]";
$rsp->{data}->[3]= " makeroutes [-r routename[,routename...]] -d ";
$rsp->{data}->[4]= " makeroutes noderange [-r routename[,routename...]]";
$rsp->{data}->[5]= " makeroutes noderange [-r routename[,routename...]] -d";
$cb->($rsp);
}
#check if the route exits or not from the route table
sub route_exists {
my $net = shift;
my $mask = shift;
my $gw_ip = shift;
my $gw=shift;
my $islinux=xCAT::Utils->isLinux();
my $result;
$result=`netstat -nr|grep $net`;
if ($? == 0) {
if ($result) {
my @b=split('\n', $result);
foreach my $tmp (@b) {
chomp($tmp);
my @a=split(' ', $tmp);
if ($islinux) { #Linux
if (@a >= 3) {
my $net1=$a[0];
my $mask1=$a[2];
my $gw1=$a[1];
if (($net1 eq $net) && ($mask1 eq $mask) && (($gw1 eq $gw) || ($gw1 eq $gw_ip))) {
return 1;
}
}
}
else { #AIX
if (@a >= 2) {
my $tmp1=$a[0];
my $gw1=$a[1];
#now convert $mask to bits
$net =~ /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/;
my $netnum = ($1<<24)+($2<<16)+($3<<8)+$4;
my $bits=32;
while (($netnum % 2) == 0) {
$bits--;
$netnum=$netnum>>1;
}
my $tmp2="$net/$bits";
if (($tmp1 eq $tmp2) && (($gw1 eq $gw) || ($gw1 eq $gw_ip))) {
return 1;
}
}
}
}
}
}
return 0;
}
# sets the route with given parameters
sub set_route {
my $callback=shift;
my $net = shift;
my $mask = shift;
my $gw_ip = shift;
my $gw=shift;
my $result;
if (!route_exists($net, $mask, $gw_ip, $gw)) {
#set temporay route
my $cmd;
if (xCAT::Utils->isLinux()) {
$cmd="route add -net $net netmask $mask gw $gw_ip";
} else {
$cmd="route add -net $net -netmask $mask $gw_ip";
}
#print "cmd=$cmd\n";
$result=`$cmd 2>&1`;
if ($? != 0) {
my $rsp={};
$rsp->{error}->[0]= "$cmd\nerror code=$?, result=$result\n";
$callback->($rsp);
return 1;
} else {
#TODO: set per permanent route
}
}
return 0;
}
# deletes the route with given parameters
sub delete_route {
my $callback=shift;
my $net = shift;
my $mask = shift;
my $gw_ip = shift;
my $gw=shift;
my $result;
if (route_exists($net, $mask, $gw_ip, $gw)) {
#delete route temporarily
my $cmd;
if (xCAT::Utils->isLinux()) {
$cmd="route delete -net $net netmask $mask gw $gw_ip";
} else {
$cmd="route delete -net $net -netmask $mask $gw_ip";
}
#print "cmd=$cmd\n";
$result=`$cmd 2>&1`;
if ($? != 0) {
my $rsp={};
$rsp->{error}->[0]= "$cmd\nerror code=$?, result=$result\n";
$callback->($rsp);
return 1;
} else {
#TODO: delete route permanently
}
}
return 0;
}