2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-10-21 23:28:19 +00:00
Files
xcat-core/xCAT-server/lib/xcat/plugins/route.pm
T
Vinícius Ferrão 2652cb152f feat(openeuler): use native NetworkManager configuration
Reuse NetworkManager for persistent routes, bond setup and connection
properties on openEuler. Preserve ifcfg handling where the connection still
uses it. Propagate route failures to local callers.
2026-09-14 23:15:14 -03:00

1417 lines
43 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;
use IO::File;
use File::Copy;
use File::Path;
use Sys::Hostname;
my $xcat_config_start = "# xCAT_CONFIG_START";
my $xcat_config_end = "# xCAT_CONFIG_END";
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 $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 ($route_hash->{net} =~ /:/) {
# Remove the subnet postfix like /64
if ($route_hash->{net} =~ /\//) {
$route_hash->{net} =~ s/\/.*$//;
}
# Remove the "/" from the ipv6 prefixlength
if ($route_hash->{mask}) {
if ($route_hash->{mask} =~ /\//) {
$route_hash->{mask} =~ s/^\///;
}
}
}
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" . " $route_hash->{ifname}" ],
_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, $route_hash->{ifname});
}
else {
set_route($callback, $route_hash->{net}, $route_hash->{mask}, $gw_ip, $gw_name, $route_hash->{ifname});
}
}
}
}
#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 $ifname = shift;
my $islinux = xCAT::Utils->isLinux();
# ipv6 net
if ($net =~ /:/) {
if ($islinux) {
my $result = `ip -6 route show $net/$mask`;
# ip -6 route show will return nothing if the route does not exist
if (!$result || ($? != 0))
{
return 0;
} else {
return 1;
}
} else { # AIX
# TODO
}
} else {
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];
my $ifname1 = $a[7];
if (($net1 eq $net) && ($mask1 eq $mask) && (($gw1 eq $gw) || ($gw1 eq $gw_ip) || ($ifname1 eq $ifname))) {
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;
}
} # end if (@a >= 2)
} #end else linux/aix
} # end foreach
} # end if ($result)
} # end if ($? == 0
} # end else ipv4/ipv6
return 0;
}
sub run_routeop {
my ($callback, $op, $net, $mask, $gw_ip, $ifname) = @_;
my $installdir = xCAT::TableUtils->getInstallDir();
my $host = hostname();
my $result = eval {
xCAT::Utils::runcmd3(command => [
"$installdir/postscripts/routeop", $op, $net, $mask,
$gw_ip // '', $ifname // ''
]);
};
if (!$result) {
$callback->({ error => ["$host: Unable to run routeop: $@"] });
return 1;
}
if ($result->{output}) {
$callback->({ data => [map { "$host: $_" } split(/\n/, $result->{output})] });
}
if ($result->{exitcode}) {
$callback->({ error => ["$host: routeop failed with exit code $result->{exitcode}: " . ($result->{errors} // '')] });
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 $ifname = shift;
if (xCAT::Utils->isLinux() && xCAT::Utils->osver() =~ /^openeuler/) {
return run_routeop($callback, "add", $net, $mask, $gw_ip, $ifname);
}
my $host = hostname();
#print "set_route get called\n";
my $result;
if (!route_exists($net, $mask, $gw_ip, $gw, $ifname)) {
#set temporay route
my $cmd;
# ipv6 network
if ($net =~ /:/) {
if (xCAT::Utils->isLinux()) {
if ($gw_ip eq "" || $gw_ip eq "::") {
$cmd = "ip -6 route add $net/$mask dev $ifname";
} else {
$cmd = "ip -6 route add $net/$mask via $gw_ip";
}
} else {
# AIX TODO
}
} else {
if (xCAT::Utils->isLinux()) {
if ($gw_ip eq "" || $gw_ip eq "0.0.0.0") {
$cmd = "route add -net $net netmask $mask dev $ifname";
} else {
$cmd = "route add -net $net netmask $mask gw $gw_ip";
}
} else {
$cmd = "route add -net $net -netmask $mask $gw_ip";
}
}
#print "cmd=$cmd\n";
my $rsp = {};
$rsp->{data}->[0] = "$host: Adding temporary route: $cmd";
$callback->($rsp);
$result = `$cmd 2>&1`;
if ($? != 0) {
my $rsp = {};
$rsp->{error}->[0] = "$host: $cmd\nerror code=$?, result=$result\n";
$callback->($rsp);
#return 1;
}
} else {
my $rsp = {};
$rsp->{data}->[0] = "$host: The temporary route already exists for $net.";
$callback->($rsp);
}
#handle persistent routes
if (xCAT::Utils->isLinux()) { #Linux
my $os = xCAT::Utils->osver();
#print "os=$os $net, $mask, $gw_ip, $gw, $ifname\n";
if ($os =~ /sles/) { #sles
addPersistentRoute_Sles($callback, $net, $mask, $gw_ip, $gw, $ifname);
} elsif ($os =~ /ubuntu|debian/) { #ubuntu or Debian?
addPersistentRoute_Debian($callback, $net, $mask, $gw_ip, $gw, $ifname);
}
elsif ($os =~ /rh|fedora|centos|alma|rocky/) { #RH, Ferdora, CentOS
addPersistentRoute_RH($callback, $net, $mask, $gw_ip, $gw, $ifname);
}
} else { #AIX
# chdev -l inet0 -a route=net,-hopcount,0,,0,192.168.1.1
# chdev -l inet0 -a route=net, -hopcount,255.255.255.128,,,,,192.168.3.155,192.168.2.1
# lsattr -El inet0 -a route
my $rsp = {};
$rsp->{data}->[0] = "$host: Adding persistent route on AIX is not supported yet.";
$callback->($rsp);
}
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 $ifname = shift;
if (xCAT::Utils->isLinux() && xCAT::Utils->osver() =~ /^openeuler/) {
return run_routeop($callback, "delete", $net, $mask, $gw_ip, $ifname);
}
my $host = hostname();
#print "delete_route get called\n";
my $result;
if (route_exists($net, $mask, $gw_ip, $gw, $ifname)) {
#delete route temporarily
my $cmd;
if ($net =~ /:/) {
if (xCAT::Utils->isLinux()) {
if ($gw_ip eq "" || $gw_ip eq "::") {
$cmd = "ip -6 route delete $net/$mask dev $ifname";
} else {
$cmd = "ip -6 route delete $net/$mask via $gw_ip";
}
} else {
# AIX TODO
}
} else {
if (xCAT::Utils->isLinux()) {
if ($gw_ip eq "" || $gw_ip eq "0.0.0.0") {
$cmd = "route delete -net $net netmask $mask dev $ifname";
} else {
$cmd = "route delete -net $net netmask $mask gw $gw_ip";
}
} else {
$cmd = "route delete -net $net -netmask $mask $gw_ip";
}
}
#print "cmd=$cmd\n";
my $rsp = {};
$rsp->{data}->[0] = "$host: Removing the temporary route: $cmd";
$callback->($rsp);
$result = `$cmd 2>&1`;
if ($? != 0) {
my $rsp = {};
$rsp->{error}->[0] = "$host: $cmd\nerror code=$?, result=$result\n";
$callback->($rsp);
}
}
else {
my $rsp = {};
if ($net =~ /:/) {
$rsp->{data}->[0] = "$host: The temporary route does not exist for $net/$mask.";
} else {
$rsp->{data}->[0] = "$host: The temporary route does not exist for $net.";
}
$callback->($rsp);
}
#handle persistent route
if (xCAT::Utils->isLinux()) { #Linux
my $os = xCAT::Utils->osver();
if ($os =~ /sles/) { #sles
deletePersistentRoute_Sles($callback, $net, $mask, $gw_ip, $gw, $ifname);
} elsif ($os =~ /ubuntu|debian/) { #ubuntu or Debian?
deletePersistentRoute_Debian($callback, $net, $mask, $gw_ip, $gw, $ifname);
}
elsif ($os =~ /rh|fedora|centos|alma|rocky/) { #RH, Ferdora
deletePersistentRoute_RH($callback, $net, $mask, $gw_ip, $gw, $ifname);
}
}
else { #AIX
# chdev -l inet0 -a delroute=net,-hopcount,0,,0,192.168.1.1
# chdev -l inet0 -a delroute=net,-hopcount,255.255.255.128,,,,,192.168.3.128,192.168.2.1
my $rsp = {};
$rsp->{data}->[0] = "$host: Removing persistent route on AIX is not supported yet.";
$callback->($rsp);
}
return 0;
}
#set the given route to the configuration file
sub setConfig {
my $filename = shift;
my $new_conf_block = shift;
#print "filename=$filename\n";
my $new_config = join("\n", @$new_conf_block);
my $last_char = substr $new_config, -1, 1;
if ($last_char ne "\n") { $new_config .= "\n"; }
my $filename_tmp = "$filename.$$";
open(OUTFILE, '>', $filename_tmp);
my $found = 0;
if (-f $filename) {
open(INFILE, '<', $filename);
my $inblock = 0;
while (<INFILE>) {
my $line = $_;
if (!$inblock) {
print OUTFILE $line;
}
if ($line =~ /$xcat_config_start/) {
$found = 1;
$inblock = 1;
print OUTFILE $new_config;
} elsif ($line =~ /$xcat_config_end/) {
$inblock = 0;
print OUTFILE "$xcat_config_end\n";
}
}
}
if (!$found) {
print OUTFILE "$xcat_config_start\n";
print OUTFILE $new_config;
print OUTFILE "$xcat_config_end\n";
}
close(INFILE);
close(OUTFILE);
copy($filename_tmp, $filename);
unlink($filename_tmp);
}
#gets the xCAT configurations from the given file
sub getConfig {
my $filename = shift;
my @output = ();
if (-f $filename) {
open(FILE, "<", $filename);
my $xcatconf = 0;
my $first = 0;
while (<FILE>) {
chomp;
if (/$xcat_config_start/) {
$xcatconf = 1;
$first = 1;
}
elsif (/$xcat_config_end/) {
$xcatconf = 0;
}
if ($first) {
$first = 0;
next;
}
if ($xcatconf) {
push @output, $_;
}
}
}
return @output;
}
#add the routes to the /etc/sysconfig/network/routes file
#The format is: destination gateway mask ifname
sub addPersistentRoute_Sles {
my $callback = shift;
my $net = shift;
my $mask = shift;
my $gw_ip = shift;
my $gw = shift;
my $ifname = shift;
my $host = hostname();
my $filename = "/etc/sysconfig/network/routes";
my @output = getConfig($filename);
#print "old output=" . join("\n", @output) . "\n";
my $hasConfiged = 0;
if (@output && (@output > 0)) {
$hasConfiged = checkConfig_Sles($net, $mask, $gw_ip, $gw, $ifname, \@output);
}
#print "hasConfiged=$hasConfiged\n";
my $new_config;
if ($net =~ /:/) {
if ($gw_ip eq "" || $gw_ip eq "::") {
$new_config = "$net/$mask :: - $ifname\n";
} else {
$new_config = "$net/$mask $gw_ip - -\n";
}
} else {
if ($gw_ip eq "" || $gw_ip eq "0.0.0.0") {
$new_config = "$net 0.0.0.0 $mask $ifname\n";
} else {
$new_config = "$net $gw_ip $mask $ifname\n";
}
}
if (!$hasConfiged) {
push(@output, $new_config);
#print "new output=" . join("\n", @output) . "\n";
#Add the route to the configuration file
#the format is: destination gateway mask ifname
setConfig($filename, \@output);
chomp($new_config);
my $rsp = {};
$rsp->{data}->[0] = "$host: Added persistent route \"$new_config\" to $filename.";
$callback->($rsp);
} else {
chomp($new_config);
my $rsp = {};
$rsp->{data}->[0] = "$host: Persistent route \"$new_config\" already exists in $filename.";
$callback->($rsp);
}
}
#remove the routes from the /etc/sysconfig/network/routes file
sub deletePersistentRoute_Sles {
my $callback = shift;
my $net = shift;
my $mask = shift;
my $gw_ip = shift;
my $gw = shift;
my $ifname = shift;
my $host = hostname();
my $filename = "/etc/sysconfig/network/routes";
my @output = getConfig($filename);
#print "old output=" . join("\n", @output) . "\n";
my @new_output = ();
my $bigfound = 0;
foreach my $tmp_conf (@output) {
my $found = checkConfig_Sles($net, $mask, $gw_ip, $gw, $ifname, [$tmp_conf]);
if (!$found) {
push(@new_output, $tmp_conf);
} else {
$bigfound = 1;
}
}
#print "new output=" . join("\n", @new_output) . "\n";
#set the new configuration to the configuration file
setConfig($filename, \@new_output);
if ($bigfound) {
my $rsp = {};
if ($net =~ /:/) {
$rsp->{data}->[0] = "$host: Removed persistent route \"$net/$mask $gw_ip\" from $filename.";
} else {
$rsp->{data}->[0] = "$host: Removed persistent route \"$net $gw_ip $mask $ifname\" from $filename.";
}
$callback->($rsp);
} else {
my $rsp = {};
if ($net =~ /:/) {
$rsp->{data}->[0] = "$host: Persistent route \"$net/$mask $gw_ip\" does not exist in $filename.";
} else {
$rsp->{data}->[0] = "$host: Persistent route \"$net $gw_ip $mask $ifname\" does not exist in $filename.";
}
$callback->($rsp);
}
}
#check if the route is in the SLES network configuration file
sub checkConfig_Sles {
my $net = shift;
my $mask = shift;
my $gw_ip = shift;
my $gw = shift;
my $ifname = shift;
my $output = shift;
# Format:
# DESTINATION GATEWAY NETMASK INTERFACE
# DESTINATION/PREFIXLEN GATEWAY - INTERFACE
# ipv4 format: 192.168.0.0 207.68.156.51 255.255.0.0 eth1
# ipv6 format: fd59::/64 fd57:faaf:e1ab:336:21a:64ff:fe01:1 - -
foreach my $line (@$output) {
my @a = split(' ', $line);
my ($net1, $mask1, $gw1, $ifname1);
if ($net =~ /:/) {
if (@a > 0) {
my $ipv6net = $a[0];
($net1, $mask1) = split("/", $ipv6net);
}
if (@a > 1) {
$gw1 = $a[1];
if ($gw1 eq '-') { $gw1 = $gw_ip; }
}
if (@a > 3) {
$ifname1 = $a[3];
if ($ifname1 eq '-') { $ifname1 = $ifname; }
}
} else {
if (@a > 0) {
$net1 = $a[0];
if ($net1 eq '-') { $net1 = $net; }
}
if (@a > 1) {
$gw1 = $a[1];
if ($gw1 eq '-') { $gw1 = $gw_ip; }
}
if (@a > 2) {
$mask1 = $a[2];
if ($mask1 eq '-') { $mask1 = $mask; }
}
if (@a > 3) {
$ifname1 = $a[3];
if ($ifname1 eq '-') { $ifname1 = $ifname; }
}
}
#print "net=$net1,$net mask=$mask1,$mask gw=$gw1,$gw_ip ifname=$ifname1\n";
if (($net1 && $net1 eq $net) && ($mask1 && $mask1 eq $mask) && (($gw1 && $gw1 eq $gw) || ($gw1 && $gw1 eq $gw_ip) || ($ifname1 && $ifname1 eq $ifname))) {
return 1;
}
}
return 0;
}
#add the routes to the /etc/sysconfig/static-routes file
#The format is: any net 172.16.0.0 netmask 255.240.0.0 gw 192.168.0.1 eth0
sub addPersistentRoute_RH {
my $callback = shift;
my $net = shift;
my $mask = shift;
my $gw_ip = shift;
my $gw = shift;
my $ifname = shift;
my $host = hostname();
my $filename;
# ipv6
if ($net =~ /:/) {
$filename = "/etc/sysconfig/static-routes-ipv6";
} else {
$filename = "/etc/sysconfig/static-routes";
}
my @output = getConfig($filename);
#print "old output=" . join("\n", @output) . "\n";
my $hasConfiged = 0;
if (@output && (@output > 0)) {
$hasConfiged = checkConfig_RH($net, $mask, $gw_ip, $gw, $ifname, \@output);
}
#print "hasConfiged=$hasConfiged\n";
my $new_config;
if ($net =~ /:/) {
# ifname is required for ipv6 routing
if (!$ifname) {
my $rsp = {};
$rsp->{data}->[0] = "$host: Could not add persistent route for ipv6 network $net/$mask, the ifname is required in the routes table.";
$callback->($rsp);
return;
}
$new_config = "$ifname $net/$mask $gw_ip";
} else {
if ($gw_ip eq "" || $gw_ip eq "0.0.0.0") {
$new_config = "any net $net netmask $mask dev $ifname\n";
} else {
$new_config = "any net $net netmask $mask gw $gw_ip\n";
}
}
if (!$hasConfiged) {
push(@output, $new_config);
#print "new output=" . join("\n", @output) . "\n";
#Add the route to the configuration file
#the format is: destination gateway mask ifname
setConfig($filename, \@output);
chomp($new_config);
my $rsp = {};
$rsp->{data}->[0] = "$host: Added persistent route \"$new_config\" to $filename.";
$callback->($rsp);
} else {
chomp($new_config);
my $rsp = {};
$rsp->{data}->[0] = "$host: Persistent route \"$new_config\" already exists in $filename.";
$callback->($rsp);
}
}
#remove the routes from the /etc/sysconfig/static-routes file
sub deletePersistentRoute_RH {
my $callback = shift;
my $net = shift;
my $mask = shift;
my $gw_ip = shift;
my $gw = shift;
my $ifname = shift;
my $host = hostname();
my $filename;
# ipv6
if ($net =~ /:/) {
$filename = "/etc/sysconfig/static-routes-ipv6";
} else {
$filename = "/etc/sysconfig/static-routes";
}
my @output = getConfig($filename);
#print "old output=" . join("\n", @output) . "\n";
my @new_output = ();
my $bigfound = 0;
foreach my $tmp_conf (@output) {
my $found = checkConfig_RH($net, $mask, $gw_ip, $gw, $ifname, [$tmp_conf]);
if (!$found) {
push(@new_output, $tmp_conf);
} else {
$bigfound = 1;
}
}
#print "new output=" . join("\n", @new_output) . "\n";
#set the new configuration to the configuration file
setConfig($filename, \@new_output);
if ($bigfound) {
my $rsp = {};
if ($net =~ /:/) {
$rsp->{data}->[0] = "$host: Removed persistent route \"$ifname $net/$mask $gw_ip\" from $filename.";
} else {
$rsp->{data}->[0] = "$host: Removed persistent route \"any net $net netmask $mask gw $gw_ip $ifname\" from $filename.";
}
$callback->($rsp);
} else {
my $rsp = {};
if ($net =~ /:/) {
$rsp->{data}->[0] = "$host: Persistent route \"$ifname $net/$mask $gw_ip\" does not exist in $filename.";
} else {
$rsp->{data}->[0] = "$host: Persistent route \"any net $net netmask $mask gw $gw_ip $ifname\" does not exist in $filename.";
}
$callback->($rsp);
}
}
sub checkConfig_RH {
my $net = shift;
my $mask = shift;
my $gw_ip = shift;
my $gw = shift;
my $ifname = shift;
my $output = shift;
foreach my $line (@$output) {
my @a = split(' ', $line);
#The format is: any net 172.16.0.0 netmask 255.240.0.0 gw 192.168.0.1 eth0
# ipv6 format: eth1 fd60::/64 fd57::214:5eff:fe15:1
my ($net1, $mask1, $gw1, $ifname1);
if ($net =~ /:/) {
$ifname1 = $a[0];
if (@a > 1) {
my $ipv6net = $a[1];
($net1, $mask1) = split("/", $ipv6net);
}
if (@a > 2) {
$gw1 = $a[2];
}
} else {
if (@a > 2) {
$net1 = $a[2];
if ($net1 eq '-') { $net1 = $net; }
}
if (@a > 4) {
$mask1 = $a[4];
if ($mask1 eq '-') { $mask1 = $mask; }
}
if (@a > 6) {
if ($a[5] eq 'dev') {
$ifname1 = $a[6];
if ($ifname1 eq '-') { $ifname1 = $ifname; }
} else {
$gw1 = $a[6];
if ($gw1 eq '-') { $gw1 = $gw_ip; }
}
}
}
#print "net=$net1,$net mask=$mask1,$mask gw=$gw1,$gw_ip ifname=$ifname1,ifname\n";
if (($net1 && $net1 eq $net) && ($mask1 && $mask1 eq $mask) && (($gw1 && $gw1 eq $gw) || ($gw1 && $gw1 eq $gw_ip) || ($ifname1 && $ifname1 eq $ifname))) {
return 1;
}
}
return 0;
}
sub addPersistentRoute_Debian {
my $callback = shift;
my $net = shift;
my $mask = shift;
my $gw_ip = shift;
my $gw_name = shift;
my $ifname = shift;
my $host = hostname();
my $conf_file = "/etc/network/interfaces.d/$ifname";
my $cmd = '';
my $route_conf = '';
preParse_Debian();
#ipv6
if ($net =~ /:/) {
if ($gw_ip eq "" || $gw_ip eq "::") {
$cmd = "grep \"$net/$mask dev $ifname\" $conf_file";
$route_conf = " up route -A inet6 add $net/$mask dev $ifname \n down route -A inet6 del $net/$mask dev $ifname \n";
} else {
$cmd = "grep \"$net/$mask gw $gw_ip\" $conf_file";
$route_conf = " up route -A inet6 add $net/$mask gw $gw_ip \n down route -A inet6 del $net/$mask gw $gw_ip \n";
}
}
else { #ipv4
$cmd = "grep \"-net $net netmask $mask gw $gw_ip\" $conf_file";
if ($gw_ip eq "" || $gw_ip eq "0.0.0.0") {
$route_conf = " up route add -net $net netmask $mask dev $ifname \n down route del -net $net netmask $mask dev $ifname \n";
} else {
$route_conf = " up route add -net $net netmask $mask gw $gw_ip \n down route del -net $net netmask $mask gw $gw_ip\n";
}
}
#fine the corresponding config in the config file
my @returninfo = `$cmd`;
if (@returninfo) {
my $rsp = {};
$rsp->{data}->[0] = "$host: Persistent route \"$returninfo[0]\" already exists in $conf_file.";
callback->($rsp);
return;
}
#add the configuration to the config file
my $readyflag = 0;
open(FH, "<", $conf_file);
my @content = <FH>;
close(FH);
#read each line of the file and find the insert place
open(FH, ">", $conf_file);
foreach my $line (@content) {
#add the route line at the end of this dev part
if (($readyflag == 1) && ($line =~ /iface|modprobe/)) {
$readyflag = 0;
print FH $route_conf;
}
if ($line =~ /iface $ifname/) {
$readyflag = 1;
}
print FH $line;
}
#the dev is the last one, add the route at the end of the file
if ($readyflag == 1) {
print FH $route_conf;
}
close(FH);
}
sub deletePersistentRoute_Debian {
my $callback = shift;
my $net = shift;
my $mask = shift;
my $gw_ip = shift;
my $gw = shift;
my $ifname = shift;
my $host = hostname();
my $conf_file = "/etc/network/interfaces.d/$ifname";
my $match = "";
my $modflag = 0;
preParse_Debian();
#ipv6
if ($net =~ /:/) {
if ($gw_ip eq "" || $gw_ip eq "::") {
$match = "$net/$mask dev $ifname";
} else {
$match = "$net/$mask gw $gw_ip";
}
}
else {
if ($gw_ip eq "" || $gw_ip eq "0.0.0.0") {
$match = "net $net netmask $mask dev $ifname";
} else {
$match = "net $net netmask $mask gw $gw_ip";
}
}
open(FH, "<", $conf_file);
my @lines = <FH>;
close(FH);
open(FH, ">", $conf_file);
foreach my $line (@lines) {
#match the route config, jump to next line
if ($line =~ /$match/) {
$modflag = 1;
}
else {
print FH $line;
}
}
close(FH);
my $rsp = {};
if ($modflag) {
$rsp->{data}->[0] = "$host: Removed persistent route \"$match\" from $conf_file.";
}
else {
$rsp->{data}->[0] = "$host: Persistent route \"$match\" does not exist in $conf_file.";
}
$callback->($rsp);
}
sub preParse_Debian {
my $configfile;
open(FH, "<", "/etc/network/interfaces");
my @lines = <FH>;
close(FH);
if ($lines[0] =~ /XCAT_CONFIG/i) {
return;
}
unless (-e "/etc/network/interfaces.bak") {
copy("/etc/network/interfaces", "/etc/network/interfaces.bak");
}
unless (-d "/etc/network/interfaces.d") {
mkpath("/etc/network/interfaces.d");
}
open(FH, ">", "/etc/network/interfaces");
print FH "#XCAT_CONFIG\n";
print FH "source /etc/network/interfaces.d/* \n";
close(FH);
foreach my $line (@lines) {
if ($line =~ /^\s*$/) {
next;
}
if ($line =~ /^#.*/) {
next;
}
my @attr = split /\s+/, $line;
if ($attr[0] =~ /auto|allow-hotplug/) {
my $i = 1;
while ($i < @attr) {
open(SFH, ">", "/etc/network/interfaces.d/$attr[$i]");
print SFH "$attr[0] $attr[$i]\n";
close(SFH);
print FH "source /etc/network/interfaces.d/$attr[$i] \n";
$i = $i + 1;
}
}
elsif ($attr[0] =~ /mapping|iface/) {
$configfile = "/etc/network/interfaces.d/$attr[1]";
open(SFH, ">>", $configfile);
unless (-e $configfile) {
print SFH "auto $attr[1] \n";
}
print SFH $line;
close(SFH);
}
else {
open(SFH, ">>", $configfile);
print SFH $line;
close(SFH);
}
}
return;
}