2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-05-22 03:32:04 +00:00
2024-05-07 16:34:59 +02:00

2834 lines
91 KiB
Perl

#!/usr/bin/perl
#-------------------------------------------------------
=head1
xCAT plugin package to handle xdsh
Supported command:
nodenetconn
ipforward (internal command)
=cut
#-------------------------------------------------------
package xCAT_plugin::vlan;
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";
use strict;
use xCAT::Table;
use xCAT::Utils;
use xCAT::NetworkUtils;
use xCAT::MsgUtils;
use Getopt::Long;
use xCAT::NodeRange;
use Data::Dumper;
use Text::Balanced qw(extract_bracketed);
use xCAT::SwitchHandler;
use Safe;
my $evalcpt = new Safe;
$evalcpt->share('&mknum');
$evalcpt->permit('require');
my %Switches = ();
1;
#-------------------------------------------------------
=head3 handled_commands
Return list of commands handled by this plugin
=cut
#-------------------------------------------------------
sub handled_commands
{
return {
mkvlan => "vlan",
chvlan => "vlan",
rmvlan => "vlan",
lsvlan => "vlan",
chvlanports => "vlan",
};
}
#-------------------------------------------------------
=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];
$::CALLBACK = $callback;
#if already preprocessed, go straight to request
if ((defined($request->{_xcatpreprocessed}))
&& ($request->{_xcatpreprocessed}->[0] == 1))
{
return [$request];
}
if ($command eq "mkvlan") {
return preprocess_mkvlan($request, $callback, $sub_req);
} elsif ($command eq "chvlanports") {
return preprocess_chvlanports($request, $callback, $sub_req);
} elsif ($command eq "chvlan") {
return preprocess_chvlan($request, $callback, $sub_req);
} elsif ($command eq "rmvlan") {
return preprocess_rmvlan($request, $callback, $sub_req);
} elsif ($command eq "lsvlan") {
return preprocess_lsvlan($request, $callback, $sub_req);
} else {
my $rsp = {};
$rsp->{error}->[0] = "$command: unsupported command.";
$callback->($rsp);
return 1;
}
}
sub preprocess_mkvlan {
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $command = $request->{command}->[0];
my $args = $request->{arg};
# parse the options
@ARGV = ();
if ($args) {
@ARGV = @{$args};
}
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("no_pass_through");
my $nr;
my $net;
my $netmask;
my $prefix;
my $nic;
if (!GetOptions(
'h|help' => \$::HELP,
'v|version' => \$::VERSION,
'n|nodes=s' => \$nr,
't|net=s' => \$net,
'm|mask=s' => \$netmask,
'p|prefix=s' => \$prefix,
'i|interface=s' => \$nic,
))
{
&mkvlan_usage($callback);
return 1;
}
# display the usage if -h or --help is specified
if ($::HELP) {
&mkvlan_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;
}
my $vlan_id = 0;
if (@ARGV > 0) {
$vlan_id = $ARGV[0];
}
my @nodes = ();
if ($nr) {
#get nodes
@nodes = noderange($nr);
if (nodesmissed) {
my $rsp = {};
$rsp->{data}->[0] = "Invalid nodes in noderange:" . join(',', nodesmissed);
$callback->($rsp);
return 1;
}
} else {
my $rsp = {};
$rsp->{data}->[0] = "Please specify a list of nodes to be added to the new vlan.";
$callback->($rsp);
return 1;
}
if ($net && (!$netmask)) {
my $rsp = {};
$rsp->{data}->[0] = "Please specify a netmask for the vlan.";
$callback->($rsp);
return 1;
}
if ($netmask && (!$net)) {
my $rsp = {};
$rsp->{data}->[0] = "Please specify a network address for the vlan.";
$callback->($rsp);
return 1;
}
#let process _request to handle it
my $reqcopy = {%$request};
$reqcopy->{_xcatpreprocessed}->[0] = 1;
$reqcopy->{vlanid}->[0] = $vlan_id;
$reqcopy->{node} = \@nodes;
if ($net) { $reqcopy->{net}->[0] = $net; }
if ($netmask) { $reqcopy->{netmask}->[0] = $netmask; }
if ($prefix) { $reqcopy->{prefix}->[0] = $prefix; }
if ($nic) { $reqcopy->{nic}->[0] = $nic; }
return [$reqcopy];
}
# TODO: finish this
sub preprocess_chvlanports {
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $command = $request->{command}->[0];
my $args = $request->{arg};
# parse the options
@ARGV = ();
if ($args) {
@ARGV = @{$args};
}
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("no_pass_through");
my $nr;
my $delete;
my $nic;
if (!GetOptions(
'h|help' => \$::HELP,
'v|version' => \$::VERSION,
'n|nodes=s' => \$nr,
'd|delete' => \$delete,
'i|interface=s' => \$nic,
))
{
&chvlanports_usage($callback);
return 1;
}
# display the usage if -h or --help is specified
if ($::HELP) {
&chvlanports_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;
}
if (@ARGV == 0) {
my $rsp = {};
$rsp->{data}->[0] = "Please specify a vlan id.";
$callback->($rsp);
return 1;
}
my $vlan_id = $ARGV[0];
my @nodes = ();
if (!$nr) {
my $rsp = {};
$rsp->{data}->[0] = "Please specify a range of nodes to be added to vlan $vlan_id.";
$callback->($rsp);
return 1;
} else {
#get nodes
@nodes = noderange($nr);
if (nodesmissed) {
my $rsp = {};
$rsp->{data}->[0] = "Invalid nodes in noderange:" . join(',', nodesmissed);
$callback->($rsp);
return 1;
}
}
if (!$nic) {
my $rsp = {};
$rsp->{data}->[0] = "Please specify a network interface for nodes";
$callback->($rsp);
return 1;
}
#let process _request to handle it
my $reqcopy = {%$request};
$reqcopy->{_xcatpreprocessed}->[0] = 1;
$reqcopy->{vlanid}->[0] = $vlan_id;
$reqcopy->{node} = \@nodes;
$reqcopy->{delete}->[0] = $delete;
$reqcopy->{nic}->[0] = $nic;
return [$reqcopy];
}
sub preprocess_chvlan {
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $command = $request->{command}->[0];
my $args = $request->{arg};
# parse the options
@ARGV = ();
if ($args) {
@ARGV = @{$args};
}
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("no_pass_through");
my $nr;
my $delete;
my $nic;
if (!GetOptions(
'h|help' => \$::HELP,
'v|version' => \$::VERSION,
'n|nodes=s' => \$nr,
'd|delete' => \$delete,
'i|interface=s' => \$nic,
))
{
&chvlan_usage($callback);
return 1;
}
# display the usage if -h or --help is specified
if ($::HELP) {
&chvlan_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;
}
if (@ARGV == 0) {
my $rsp = {};
$rsp->{data}->[0] = "Please specify a vlan id.";
$callback->($rsp);
return 1;
}
my $vlan_id = $ARGV[0];
my @nodes = ();
if (!$nr) {
my $rsp = {};
$rsp->{data}->[0] = "Please specify a range of nodes to be added to vlan $vlan_id.";
$callback->($rsp);
return 1;
} else {
#get nodes
@nodes = noderange($nr);
if (nodesmissed) {
my $rsp = {};
$rsp->{data}->[0] = "Invalid nodes in noderange:" . join(',', nodesmissed);
$callback->($rsp);
return 1;
}
}
#let process _request to handle it
my $reqcopy = {%$request};
$reqcopy->{_xcatpreprocessed}->[0] = 1;
$reqcopy->{vlanid}->[0] = $vlan_id;
$reqcopy->{node} = \@nodes;
$reqcopy->{delete}->[0] = $delete;
if ($nic) { $reqcopy->{nic}->[0] = $nic; }
return [$reqcopy];
}
sub preprocess_rmvlan {
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $command = $request->{command}->[0];
my $args = $request->{arg};
# parse the options
@ARGV = ();
if ($args) {
@ARGV = @{$args};
}
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("no_pass_through");
my $nodes;
if (!GetOptions(
'h|help' => \$::HELP,
'v|version' => \$::VERSION,
))
{
&rmvlan_usage($callback);
return 1;
}
# display the usage if -h or --help is specified
if ($::HELP) {
&rmvlan_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;
}
if (@ARGV == 0) {
my $rsp = {};
$rsp->{data}->[0] = "Please specify a vlan id.";
$callback->($rsp);
return 1;
}
my $vlan_id = $ARGV[0];
#let process _request to handle it
my $reqcopy = {%$request};
$reqcopy->{_xcatpreprocessed}->[0] = 1;
$reqcopy->{vlanid}->[0] = $vlan_id;
return [$reqcopy];
}
sub preprocess_lsvlan {
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $command = $request->{command}->[0];
my $args = $request->{arg};
# parse the options
@ARGV = ();
if ($args) {
@ARGV = @{$args};
}
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("no_pass_through");
my $nodes;
if (!GetOptions(
'h|help' => \$::HELP,
'v|version' => \$::VERSION,
))
{
&lsvlan_usage($callback);
return 1;
}
# display the usage if -h or --help is specified
if ($::HELP) {
&lsvlan_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;
}
my $vlan_id = 0;
if (@ARGV > 0) {
$vlan_id = $ARGV[0];
}
#let process _request to handle it
my $reqcopy = {%$request};
$reqcopy->{_xcatpreprocessed}->[0] = 1;
$reqcopy->{vlanid}->[0] = $vlan_id;
return [$reqcopy];
}
#-------------------------------------------------------
=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];
my $args = $request->{arg};
$::CALLBACK = $callback;
if ($command eq "mkvlan") {
return process_mkvlan($request, $callback, $sub_req);
} elsif ($command eq "chvlanports") {
return process_chvlanports($request, $callback, $sub_req);
} elsif ($command eq "chvlan") {
return process_chvlan($request, $callback, $sub_req);
} elsif ($command eq "rmvlan") {
return process_rmvlan($request, $callback, $sub_req);
} elsif ($command eq "lsvlan") {
return process_lsvlan($request, $callback, $sub_req);
} else {
my $rsp = {};
$rsp->{error}->[0] = "$command: unsupported command.";
$callback->($rsp);
return 1;
}
return 0;
}
sub process_mkvlan {
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $vlan_id = 0;
if (exists($request->{vlanid})) {
$vlan_id = $request->{vlanid}->[0];
}
my @nodes = ();
if (exists($request->{node})) {
@nodes = @{ $request->{node} };
}
my $net;
if (exists($request->{net})) {
$net = $request->{net}->[0];
}
my $netmask;
if (exists($request->{netmask})) {
$netmask = $request->{netmask}->[0];
}
my $prefix;
if (exists($request->{prefix})) {
$prefix = $request->{prefix}->[0];
}
my $nic;
if (exists($request->{nic})) {
$nic = $request->{nic}->[0];
}
#check if the vlan is already defined on the table
my $nwtab = xCAT::Table->new("networks", -create => 1);
if ($vlan_id > 0) {
if ($nwtab) {
my @tmp1 = $nwtab->getAllAttribs(('vlanid', 'net', 'mask'));
if ((@tmp1) && (@tmp1 > 0)) {
foreach (@tmp1) {
if ($vlan_id eq $_->{vlanid}) {
my $rsp = {};
$rsp->{error}->[0] = "The vlan $vlan_id is already used. Please choose another vlan id.";
$callback->($rsp);
return 1;
}
}
}
}
}
#make sure the vlan id is not currently used by another vlan
#if no vlan id is supplied, automatically generate a new vlan id
if ($vlan_id > 0) {
if (!verify_vlanid($vlan_id, $callback)) { return 1; }
} else {
$vlan_id = get_next_vlanid($callback);
if (!$vlan_id) { return 1; }
}
if (!$prefix) {
$prefix = "v" . $vlan_id . "n"; #default
}
####now get the switch ports for the nodes
my %swinfo = (); # example: %swinfo=( switch1=>{ 23=>{ nodes: [node1]
# interface: eth0
# },
# 24=>{ nodes: [node2,node3],
# vmhost: kvm1
# interface: eth1
# }
# },
# switch12=>{ 3=>{ nodes: [node4]},
# 7=>{ nodes: [node4]
# interface: eth0 },
# 9=>{ nodes: [node6]}
# }
# )
my %vminfo = (); # example: %vminfo=( kvm1=>{clients:[node1,node2...]},
# kvm2=>{clients:[node2,node3...]},
# )
#
my @vmnodes = (); # vm clients
my @snodes = (); # stand alone nodes
#get the vm hosts
my $vmtab = xCAT::Table->new("vm", -create => 1);
my $vmtmphash = $vmtab->getNodesAttribs(\@nodes, [ 'host', 'nics' ]);
foreach my $node (@nodes) {
my $host;
my $ent = $vmtmphash->{$node}->[0];
if (ref($ent) and defined $ent->{host}) {
$host = $ent->{host};
if (exists($vminfo{$host})) {
my $pa = $vminfo{$host}->{clients};
push(@$pa, $node);
} else {
$vminfo{$host}->{clients} = [$node];
}
push(@vmnodes, $node);
}
}
if (@vmnodes > 0) {
foreach my $node (@nodes) {
if (!grep /^$node$/, @vmnodes) {
push(@snodes, $node);
}
}
} else {
@snodes = @nodes;
}
#get the switch and port numbers for each node
my @vmhosts = keys(%vminfo);
my @anodes = (@snodes, @vmhosts); #nodes that connects to the switch
my %swsetup = ();
my $swtab = xCAT::Table->new("switch", -create => 1);
my $swtmphash = $swtab->getNodesAttribs(\@anodes, [ 'switch', 'port', 'vlan', 'interface' ]);
my @missed_nodes = ();
foreach my $node (@anodes) {
my $node_enties = $swtmphash->{$node};
if ($node_enties) {
my $i = -1;
my $use_this = 0;
foreach my $ent (@$node_enties) {
$i++;
if (ref($ent) and defined $ent->{switch} and defined $ent->{port}) {
my $switch;
my $port;
$switch = $ent->{switch};
$port = $ent->{port};
my $interface = "primary";
if (defined $ent->{interface}) { $interface = $ent->{interface}; }
# for primary nic, the interface can be empty, "primary" or "primary:eth0"
#print "***nic=$nic, interface=$interface\n";
if ($nic) {
if ($interface =~ /primary/) {
$interface =~ s/primary(:)?//g;
}
if ($interface && ($interface eq $nic)) { $use_this = 1; }
} else {
if ($interface =~ /primary/) { $use_this = 1; }
}
if (!$use_this) {
next;
}
else {
$swsetup{$node}->{port} = $port;
$swsetup{$node}->{switch} = $switch;
if (defined $ent->{vlan}) {
$swsetup{$node}->{vlan} = $ent->{vlan};
} else {
$swsetup{$node}->{vlan} = "";
}
}
if ($interface) {
$swinfo{$switch}->{$port}->{interface} = $interface;
}
if (exists($vminfo{$node})) {
$swinfo{$switch}->{$port}->{vmhost} = $node;
$swinfo{$switch}->{$port}->{nodes} = $vminfo{$node}->{clients};
} else {
$swinfo{$switch}->{$port}->{nodes} = [$node];
}
last;
}
}
if ($use_this != 1) {
push(@missed_nodes, $node);
}
}
}
if (@missed_nodes > 0) {
my $rsp = {};
$rsp->{error}->[0] = "Cannot proceed, please define switch and port info on the switch table for the following nodes:\n @missed_nodes\n";
$callback->($rsp);
return 1;
}
#print "vminfo=" . Dumper(%vminfo) . "\n";
#print "swinfo=" . Dumper(%swinfo) . "\n";
#print "snodes=" . Dumper(@snodes) . "\n";
#print "anodes=" . Dumper(@anodes) . "\n";
#print "vmnodes=" . Dumper(@vmnodes) . "\n";
#print "swtmphash" . Dumper($swtmphash). "\n";
### verify the ports are not used by other vlans
#if (!verify_switch_ports($vlan_id, \%swinfo, $callback)) { return 1;}
### now pick a network address for the vlan
if (!$net) {
($net, $netmask) = get_subnet($vlan_id, $callback);
}
### save the vlan on the networks table
my %key_col = (netname => "vlan$vlan_id");
my %tb_cols = (vlanid => $vlan_id, net => $net, mask => $netmask);
$nwtab->setAttribs(\%key_col, \%tb_cols);
### configure vlan on the switch
if (!create_vlan($vlan_id, \%swinfo, $callback)) { return 1; }
if (!add_ports($vlan_id, \%swinfo, $callback)) { return 1; }
my @sws = keys(%swinfo);
if (!add_crossover_ports($vlan_id, \@sws, $callback)) { return 1; }
### add the vlanid for the standalone nodes on the switch table
### append the vlan id for the vmhosts on the switch table
add_vlan_to_switch_table(\%swsetup, $vlan_id);
### get node ip and vlan hostname from the hosts table.
#If it is not defined, put the default into the host table
my @allnodes = (@anodes, @vmnodes);
if (!add_vlan_ip_host($net, $netmask, $prefix, 1, \@allnodes, $callback)) { return 1; }
### for vm nodes, add an additional nic on the vm.nics
if (@vmnodes > 0) {
my %setupnics = ();
my $new_nic = "vl$vlan_id";
foreach my $node (@vmnodes) {
my $ent = $vmtmphash->{$node}->[0];
my $nics;
if (ref($ent) and defined $ent->{nics}) {
$nics = $ent->{nics};
my @a = split(",", $nics);
if (!grep(/^$new_nic$/, @a)) { $nics = "$nics,$new_nic"; }
} else {
$nics = $new_nic;
}
$setupnics{$node} = { nics => "$nics" };
}
$vmtab->setNodesAttribs(\%setupnics);
}
### populate the /etc/hosts and make the DNS server on the mn aware this change
$::CALLBACK = $callback;
my $res = xCAT::Utils->runxcmd({
command => ['makehosts'],
}, $sub_req, 0, 1);
my $rsp = {};
$rsp->{data}->[0] = "Running makehosts...";
$callback->($rsp);
if ($res && (@$res > 0)) {
$rsp = {};
$rsp->{data} = $res;
$callback->($rsp);
}
$::CALLBACK = $callback;
my $res = xCAT::Utils->runxcmd({
command => ['makedns'],
}, $sub_req, 0, 1);
my $rsp = {};
$rsp->{data}->[0] = "Running makedns...";
$callback->($rsp);
if ($res && (@$res > 0)) {
$rsp->{data} = $res;
$callback->($rsp);
}
my $cmd = "service named restart";
my $rc = system $cmd;
### now go to the nodes to configure the vlan interface
$::CALLBACK = $callback;
my $args = [ "-P", "configvlan $vlan_id --keephostname" ];
my $res = xCAT::Utils->runxcmd({
command => ['updatenode'],
node => \@snodes,
arg => $args
}, $sub_req, 0, 1);
my $rsp = {};
$rsp->{data}->[0] = "Running updatenode...";
$callback->($rsp);
if ($res && (@$res > 0)) {
$rsp->{data} = $res;
$callback->($rsp);
}
### add configvlan postscripts to the postscripts table for the node
# so that next time the node bootup, the vlan can get configured
my @pnodes = (@snodes, @vmnodes);
add_postscript($callback, \@pnodes);
my $rsp = {};
$rsp->{data}->[0] = "The vlan is successfully configured. ";
$rsp->{data}->[1] = " vlan id: $vlan_id";
$rsp->{data}->[2] = " vlan subnet: $net";
$rsp->{data}->[3] = " vlan netmask: $netmask";
#$rsp->{data}->[4]= " vlan dhcp server:";
#$rsp->{data}->[5]= " vlan dns server:";
#$rsp->{data}->[6]= " vlan gateway:";
$callback->($rsp);
return 0;
}
#-------------------------------------------------------
=head3 add_vlan_to_switch_table
It adds the vlan id to the switch.vlan for the given nodes.
=cut
#-------------------------------------------------------
sub add_vlan_to_switch_table {
my $swsetup = shift;
my $vlan_id = shift;
my $swtab1 = xCAT::Table->new('switch', -create => 1, -autocommit => 0);
foreach my $node (keys(%$swsetup)) {
my %keyhash = ();
my %updates = ();
$keyhash{'node'} = $node;
$keyhash{'switch'} = $swsetup->{$node}->{switch};
$keyhash{'port'} = $swsetup->{$node}->{port};
$updates{'vlan'} = $vlan_id;
my $vlan;
if ($swsetup->{$node}->{vlan}) {
$vlan = $swsetup->{$node}->{vlan};
my @a = split(",", $vlan);
if (!grep(/^$vlan_id$/, @a)) { $vlan = "$vlan,$vlan_id"; }
$updates{'vlan'} = $vlan;
}
$swtab1->setAttribs(\%keyhash, \%updates);
}
$swtab1->commit;
}
sub remove_vlan_from_switch_table {
my $swsetup = shift;
my $vlan_id = shift;
#remove the vlan id from the switch table for standalone nodes
my $swtab1 = xCAT::Table->new('switch', -create => 1, -autocommit => 0);
foreach my $node (keys(%$swsetup)) {
my %keyhash = ();
my %updates = ();
$keyhash{'node'} = $node;
$keyhash{'switch'} = $swsetup->{$node}->{switch};
$keyhash{'port'} = $swsetup->{$node}->{port};
$updates{'vlan'} = "";
if ($swsetup->{$node}->{vlan}) {
my @a = split(',', $swsetup->{$node}->{vlan});
my @b = grep(!/^$vlan_id$/, @a);
if (@b > 0) {
$updates{'vlan'} = join(',', @b);
}
}
$swtab1->setAttribs(\%keyhash, \%updates);
}
$swtab1->commit;
}
#-------------------------------------------------------
=head3 add_postscript
It adds the 'configvlan' postscript to the postscript table
for the given nodes.
=cut
#-------------------------------------------------------
sub add_postscript {
my $callback = shift;
my $anodes = shift;
my $posttab = xCAT::Table->new("postscripts", -create => 1);
if ($posttab) {
(my $ref1) = $posttab->getAttribs({ node => 'xcatdefaults' }, ('postscripts', 'postbootscripts'));
#if configvlan is in xcadefaults, then do nothing
if ($ref1) {
if ($ref1->{postscripts}) {
my @a = split(/,/, $ref1->{postscripts});
if (grep(/^configvlan$/, @a)) { next; }
}
if ($ref1->{postbootscripts}) {
my @a = split(/,/, $ref1->{postbootscripts});
if (grep(/^configvlan$/, @a)) { next; }
}
}
#now check for each node
my %setup_hash;
my $postcache = $posttab->getNodesAttribs($anodes, [qw(postscripts postbootscripts)]);
foreach my $node (@$anodes) {
my $ref = $postcache->{$node}->[0];
if ($ref) {
if (exists($ref->{postscripts})) {
my @a = split(/,/, $ref->{postscripts});
if (grep(/^configvlan$/, @a)) { next; }
}
if (exists($ref->{postbootscripts})) {
my $post = $ref->{postbootscripts};
my @old_a = split(',', $post);
if (grep(/^configvlan$/, @old_a)) {
next;
} else {
$setup_hash{$node} = { postbootscripts => "$post,configvlan" };
}
} else {
$setup_hash{$node} = { postbootscripts => "configvlan" };
}
} else {
$setup_hash{$node} = { postbootscripts => "configvlan" };
}
}
if (keys(%setup_hash) > 0) {
$posttab->setNodesAttribs(\%setup_hash);
}
}
return 0;
}
#-------------------------------------------------------
=head3 add_vlan_ip_host
It goes to the hosts.otherinterfaces to see if the vlan ip and hostname
is defined. If not, it will add the default in the table.
The default is v<vlanid>n<node#>
=cut
#-------------------------------------------------------
sub add_vlan_ip_host {
my $subnet = shift;
my $netmask = shift;
my $prefix = shift;
my $node_number = shift;
my $nodes = shift;
my $callback = shift;
my $hoststab = xCAT::Table->new('hosts');
my $hostscache = $hoststab->getNodesAttribs($nodes, [qw(node otherinterfaces)]);
my %setup_hash;
foreach my $node (@$nodes) {
my $ref = $hostscache->{$node}->[0];
my $found = 0;
my $otherinterfaces;
if ($ref && exists($ref->{otherinterfaces})) {
$otherinterfaces = $ref->{otherinterfaces};
my @itf_pairs = split(/,/, $otherinterfaces);
foreach (@itf_pairs) {
my ($name, $ip) = split(/:/, $_);
if ($name =~ /^-/) {
$name = $node . $name;
}
if (xCAT::NetworkUtils->ishostinsubnet($ip, $netmask, $subnet)) {
$found = 1;
}
}
}
if (!$found) {
my $hostname = $prefix . "$node_number";
my $ip = "";
if ($subnet =~ /\d+\.\d+\.\d+\.\d+/) { # ipv4 address
$subnet =~ /([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)/;
my $netnum = ($1 << 24) + ($2 << 16) + ($3 << 8) + $4;
my $ipnum = $netnum + $node_number;
my @a = ();
for (my $i = 3 ; $i > -1 ; $i--) {
$a[$i] = $ipnum % 256;
$ipnum = int($ipnum / 256);
}
$ip = "$a[0].$a[1].$a[2].$a[3]";
#print "ip=$ip\n";
} else {
my $rsp = {};
$rsp->{error}->[0] = "Does not support IPV6 address yet.";
$callback->($rsp);
return 0;
}
$node_number++;
if ($otherinterfaces) {
$setup_hash{$node} = { otherinterfaces => "$hostname:$ip,$otherinterfaces" };
} else {
$setup_hash{$node} = { otherinterfaces => "$hostname:$ip" };
}
}
} #foreach node
if (keys(%setup_hash) > 0) {
$hoststab->setNodesAttribs(\%setup_hash);
}
return 1;
}
#-------------------------------------------------------
=head3 get_prefix_and_nodenumber
It gets the prefix and max node number from the current nodes
in the given vlan.
=cut
#-------------------------------------------------------
sub get_prefix_and_nodenumber {
my $vlan_id = shift;
my $subnet = shift;
my $netmask = shift;
#get all the nodes that are in the vlan
my $swtab = xCAT::Table->new("switch", -create => 0);
my @nodes = ();
my @tmp1 = $swtab->getAllAttribs(('node', 'vlan'));
if ((@tmp1) && (@tmp1 > 0)) {
foreach my $ent (@tmp1) {
my @nodes_tmp = noderange($ent->{node});
foreach my $node (@nodes_tmp) {
if ($ent->{vlan}) {
my @a = split(",", $ent->{vlan});
if (grep(/^$vlan_id$/, @a)) {
push(@nodes, $node);
}
}
}
}
}
#get all the vm clients if the node is a vm host
my $vmtab = xCAT::Table->new("vm", -create => 0);
my @vmnodes = ();
if ($vmtab) {
my @tmp1 = $vmtab->getAllAttribs('node', 'host');
if ((@tmp1) && (@tmp1 > 0)) {
foreach (@tmp1) {
my $host = $_->{host};
if (grep(/^$host$/, @nodes)) {
my @nodes_tmp = noderange($_->{node});
foreach my $node (@nodes_tmp) {
push(@vmnodes, $node);
}
}
}
}
}
@nodes = (@nodes, @vmnodes);
#print "nodes=@nodes\n";
#now go to hosts table to get the prefix and max node number
my $hoststab = xCAT::Table->new('hosts');
my $hostscache = $hoststab->getNodesAttribs(\@nodes, [qw(node otherinterfaces)]);
my $max = 0;
my $prefix;
foreach my $node (@nodes) {
my $ref = $hostscache->{$node}->[0];
my $otherinterfaces;
if ($ref && exists($ref->{otherinterfaces})) {
$otherinterfaces = $ref->{otherinterfaces};
my @itf_pairs = split(/,/, $otherinterfaces);
my @itf_pairs2 = ();
foreach (@itf_pairs) {
my ($name, $ip) = split(/:/, $_);
if (xCAT::NetworkUtils->ishostinsubnet($ip, $netmask, $subnet)) {
$name =~ /^(.*)([\d]+)$/;
if ($2 > $max) { $max = $2; }
if (!$prefix) { $prefix = $1; }
#print "name=$name\n, 1=$1, 2=$2\n";
}
}
}
} #foreach node
return ($prefix, $max);
}
#-------------------------------------------------------
=head3 remove_vlan_ip_host
It goes to the hosts.otherinterfaces to see if the vlan ip and hostname
is defined. If it is, it will remove it. It also remove the entried in
the /etc/hosts file
=cut
#-------------------------------------------------------
sub remove_vlan_ip_host {
my $subnet = shift;
my $netmask = shift;
my $nodes = shift;
my $callback = shift;
my $hoststab = xCAT::Table->new('hosts');
my $hostscache = $hoststab->getNodesAttribs($nodes, [qw(node otherinterfaces)]);
my %setup_hash;
foreach my $node (@$nodes) {
my $ref = $hostscache->{$node}->[0];
my $otherinterfaces;
if ($ref && exists($ref->{otherinterfaces})) {
$otherinterfaces = $ref->{otherinterfaces};
my @itf_pairs = split(/,/, $otherinterfaces);
my @itf_pairs2 = ();
my $index = 0;
foreach (@itf_pairs) {
my ($name, $ip) = split(/:/, $_);
if (!xCAT::NetworkUtils->ishostinsubnet($ip, $netmask, $subnet)) {
$itf_pairs2[$index] = $_;
$index++;
} else {
my $cmd = "sed -i /$ip/d /etc/hosts";
my $rc = system $cmd;
}
}
if (@itf_pairs2 > 0) {
my $new_intf = join(",", @itf_pairs2);
$setup_hash{$node} = { otherinterfaces => $new_intf }
} else {
$setup_hash{$node} = { otherinterfaces => "" };
}
}
} #foreach node
if (keys(%setup_hash) > 0) {
$hoststab->setNodesAttribs(\%setup_hash);
}
return 1;
}
#-------------------------------------------------------
=head3 verify_vlanid
It goes to all the switches to make sure that the vlan
id is not used by other vlans.
=cut
#-------------------------------------------------------
sub verify_vlanid {
my $vlan_id = shift;
my $callback = shift;
my $switchestab = xCAT::Table->new('switches', -create => 0);
my @tmp1 = $switchestab->getAllAttribs(('switch'));
if ((@tmp1) && (@tmp1 > 0)) {
foreach (@tmp1) {
my @switches_tmp = noderange($_->{switch});
if (@switches_tmp == 0) { push @switches_tmp, $_->{switch}; } #sometimes the switch name is not on the node list table.
foreach my $switch (@switches_tmp) {
my $swh;
if (exists($Switches{$switch})) { $swh = $Switches{$switch}; }
else {
$swh = new xCAT::SwitchHandler->new($switch);
$Switches{$switch} = $swh;
}
my @ids = $swh->get_vlan_ids();
print "ids=@ids\n";
foreach my $id (@ids) {
if ($id == $vlan_id) {
my $rsp = {};
$rsp->{error}->[0] = "The vlan id $vlan_id already exists on switch $switch. Please choose another vlan id.\n";
$callback->($rsp);
return 0;
}
}
}
}
}
return 1;
}
#-------------------------------------------------------
=head3 get_next_vlanid
It automatically generates the vlan ID. It goes to all the
switches, get the smallest common integer that is not used
by any existing vlans.
=cut
#-------------------------------------------------------
sub get_next_vlanid {
my $callback = shift;
my $switchestab = xCAT::Table->new('switches', -create => 0);
my @tmp1 = $switchestab->getAllAttribs(('switch'));
my %vlanids = ();
if ((@tmp1) && (@tmp1 > 0)) {
foreach (@tmp1) {
my @switches_tmp = noderange($_->{switch});
if (@switches_tmp == 0) { push @switches_tmp, $_->{switch}; } #sometimes the switch name is not on the node list table.
foreach my $switch (@switches_tmp) {
my $swh;
if (exists($Switches{$switch})) { $swh = $Switches{$switch}; }
else {
$swh = new xCAT::SwitchHandler->new($switch);
$Switches{$switch} = $swh;
}
my @ids = $swh->get_vlan_ids();
foreach my $id (@ids) {
$vlanids{$id} = 1;
}
}
}
}
for (my $index = 2 ; $index < 255 ; $index++) {
if (!exists($vlanids{$index})) { return $index; }
}
my $rsp = {};
$rsp->{data}->[0] = "No valid vlan ID can be used any more, Please remove unused vlans.\n";
$callback->($rsp);
return 0;
}
#-------------------------------------------------------
=head3 verify_switch_ports
It checks if the switch ports to be configured are used by other vlans.
=cut
#-------------------------------------------------------
sub verify_switch_ports {
my $vlan_id = shift;
my $swinfo = shift;
my $callback = shift;
my $ret = 1;
foreach my $switch (keys %$swinfo) {
my $porthash = $swinfo->{$switch};
my $swh;
if (exists($Switches{$switch})) { $swh = $Switches{$switch}; }
else {
$swh = new xCAT::SwitchHandler->new($switch);
$Switches{$switch} = $swh;
}
my $port_vlan_hash = $swh->get_vlanids_for_ports(keys(%$porthash));
print "port_vlan_hash=" . Dumper($port_vlan_hash) . "\n";
my @error_ports = ();
foreach my $port (keys(%$port_vlan_hash)) {
my $val = $port_vlan_hash->{$port};
foreach my $tmp_vid (@$val) {
if (($tmp_vid != $vlan_id) && ($tmp_vid != 1) && ($tmp_vid ne 'NA')) {
if (exists($porthash->{$port}->{vmhost})) { next; } #skip the vmhost, vmhost can have more than one vlans
push(@error_ports, $port);
last;
}
}
}
if (@error_ports > 0) {
$ret = 0;
my $error_str;
foreach (@error_ports) {
my @tmp = @{ $porthash->{$_}->{nodes} };
my $ids_tmp = $port_vlan_hash->{$_};
$error_str .= "$_: vlan-ids=@$ids_tmp nodes=@tmp\n";
}
my $rsp = {};
$rsp->{error}->[0] = "The following ports on switch $switch are used by other vlans.\n$error_str";
$callback->($rsp);
}
}
return $ret;
}
#-------------------------------------------------------
=head3 create_vlan
It goes to the switches and create a new vlan.
Returns: 1 -- suggessful
0 -- fail
=cut
#-------------------------------------------------------
sub create_vlan {
my $vlan_id = shift;
my $swinfo = shift;
my $callback = shift;
my $ret = 1;
foreach my $switch (keys %$swinfo) {
my $swh;
if (exists($Switches{$switch})) { $swh = $Switches{$switch}; }
else {
$swh = new xCAT::SwitchHandler->new($switch);
$Switches{$switch} = $swh;
}
#check if the vlan already exists on the switch
my @ids = $swh->get_vlan_ids();
my $vlan_exists = 0;
foreach my $id (@ids) {
if ($id == $vlan_id) {
$vlan_exists = 1;
last;
}
}
if (!$vlan_exists) {
#create the vlan
print "create vlan $vlan_id on switch $switch\n";
my @ret = $swh->create_vlan($vlan_id);
if ($ret[0] != 0) {
my $rsp = {};
$rsp->{error}->[0] = "create_vlan: $ret[1]";
$callback->($rsp);
}
}
}
return 1;
}
#-------------------------------------------------------
=head3 add_ports
It adds the ports to the vlan.
Returns: 1 -- suggessful
0 -- fail
=cut
#-------------------------------------------------------
sub add_ports {
my $vlan_id = shift;
my $swinfo = shift;
my $callback = shift;
my $portmode = shift;
my $ret = 1;
foreach my $switch (keys %$swinfo) {
my $porthash = $swinfo->{$switch};
my $swh;
if (exists($Switches{$switch})) { $swh = $Switches{$switch}; }
else {
$swh = new xCAT::SwitchHandler->new($switch);
$Switches{$switch} = $swh;
}
my @ret = $swh->add_ports_to_vlan($vlan_id, $portmode, keys(%$porthash));
if ($ret[0] != 0) {
my $rsp = {};
$rsp->{error}->[0] = "add_ports_to_vlan: $ret[1]";
$callback->($rsp);
}
}
return 1;
}
#-------------------------------------------------------
=head3 add_crossover_ports
It enables the vlan on the cross-over links.
Returns: 1 -- suggessful
0 -- fail
=cut
#-------------------------------------------------------
sub add_crossover_ports {
my $vlan_id = shift;
my $psws = shift;
my $callback = shift;
#now make sure the links between the switches allows this vlan to go through
my @sws = @$psws;
print "sws=@sws\n";
if (@sws > 1) {
foreach my $switch (@sws) {
my $swh;
if (exists($Switches{$switch})) { $swh = $Switches{$switch}; }
else {
$swh = new xCAT::SwitchHandler->new($switch);
$Switches{$switch} = $swh;
}
my @sws_b = grep(!/^$switch$/, @sws);
my @ret = $swh->add_crossover_ports_to_vlan($vlan_id, @sws_b);
if ($ret[0] != 0) {
my $rsp = {};
$rsp->{error}->[0] = "add_crossover_ports: $ret[1]";
$callback->($rsp);
} else {
if ($ret[1]) {
my $rsp = {};
$rsp->{data}->[0] = "add_crossover_ports: $ret[1]";
$callback->($rsp);
}
}
}
}
return 1;
}
#-------------------------------------------------------
=head3 remove_ports
It goes to the switches and create a new vlan.
Returns: 1 -- suggessful
0 -- fail
=cut
#-------------------------------------------------------
sub remove_ports {
my $vlan_id = shift;
my $swinfo = shift;
my $callback = shift;
my $novmhost = shift;
my $ret = 1;
foreach my $switch (keys %$swinfo) {
my $porthash = $swinfo->{$switch};
my $swh;
if (exists($Switches{$switch})) { $swh = $Switches{$switch}; }
else {
$swh = new xCAT::SwitchHandler->new($switch);
$Switches{$switch} = $swh;
}
my @ports = ();
if ($novmhost) { #skip the vm hosts for chvlan
foreach my $port (keys(%$porthash)) {
if (!exists($porthash->{$port}->{vmhost})) {
push(@ports, $port);
}
}
} else {
@ports = keys(%$porthash);
}
my @ret = $swh->remove_ports_from_vlan($vlan_id, @ports);
if ($ret[0] != 0) {
my $rsp = {};
$rsp->{error}->[0] = "remove_ports_from_vlan: $ret[1]";
$callback->($rsp);
}
}
return 1;
}
#-------------------------------------------------------
=head3 get_subnet
It gets the subnet address and netmask for the given
vlan ID. The pattern is defined by "vlannets" and "vlanmask"
on the site table. The default is "10.<$vlanid>.0.0"/"255.255.0.0".
=cut
#-------------------------------------------------------
sub get_subnet {
my $vlan_id = shift;
my $callback = shift;
my $net;
my $mask;
#get vlannets and vlanidmask from the site table
my $vlannets = "|(\\d+)|10.(\$1+0).0.0|";
my $vlanmask = "255.255.0.0";
my $sitetab = xCAT::Table->new('site');
my $sent = $sitetab->getAttribs({ key => 'vlannets' }, 'value');
if ($sent and ($sent->{value})) {
$vlannets = $sent->{value};
}
$sent = $sitetab->getAttribs({ key => 'vlanmask' }, 'value');
if ($sent and ($sent->{value})) {
$vlanmask = $sent->{value};
}
$mask = $vlanmask;
if ($vlannets =~ /^\/[^\/]*\/[^\/]*\/$/)
{
my $exp = substr($vlannets, 1);
chop $exp;
my @parts = split('/', $exp, 2);
$net = $vlan_id;
$net =~ s/$parts[0]/$parts[1]/;
}
elsif ($vlannets =~ /^\|.*\|.*\|$/)
{
my $exp = substr($vlannets, 1);
chop $exp;
my @parts = split('\|', $exp, 2);
my $curr;
my $next;
my $prev;
my $retval = $parts[1];
($curr, $next, $prev) =
extract_bracketed($retval, '()', qr/[^()]*/);
unless ($curr) { #If there were no paramaters to save, treat this one like a plain regex
undef $@; #extract_bracketed would have set $@ if it didn't return, undef $@
$retval = $vlan_id;
$retval =~ s/$parts[0]/$parts[1]/;
}
while ($curr)
{
my $value = $vlan_id;
$value =~ s/$parts[0]/$curr/;
$value = $evalcpt->reval('use integer;' . $value);
$retval = $prev . $value . $next;
($curr, $next, $prev) =
extract_bracketed($retval, '()', qr/[^()]*/);
}
undef $@;
$net = $vlan_id;
$net =~ s/$parts[0]/$retval/;
}
return ($net, $mask);
}
# process_chvlanports only support physical nodes
# bond not supported and multi nics are not supported
sub process_chvlanports {
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $vlan_id = 0;
my $nic = "";
my @nodes = ();
my $delete = 0;
# validate vlan id value.
$vlan_id = $request->{vlanid}->[0];
#debug message.
xCAT::MsgUtils->message('S', "vlanid: $vlan_id");
if ($vlan_id <= 0) {
my $rsp = {};
$rsp->{error}->[0] = "Invalid vlan id: $vlan_id";
$callback->($rsp);
return;
}
#Check if the vlan is defined in networks table.
my $net = "";
my $netmask = "";
my $nwtab = xCAT::Table->new("networks", -create => 1);
if ($nwtab) {
my @nwentires = $nwtab->getAllAttribs(('vlanid', 'net', 'mask'));
foreach (@nwentires) {
if ($vlan_id eq $_->{vlanid}) {
$net = $_->{net};
$netmask = $_->{mask};
}
}
}
if ((!$net) || (!$netmask)) {
my $rsp = {};
$rsp->{data}->[0] = "Can not find valid network/netmask definition from table networks for vlan $vlan_id.";
$callback->($rsp);
return 1;
}
$nic = $request->{nic}->[0];
#debug message.
xCAT::MsgUtils->message('S', "nic is: $nic");
@nodes = @{ $request->{node} };
#debug message.
my $nodesstr = Dumper(@nodes);
xCAT::MsgUtils->message('S', "nodes are: $nodesstr");
$delete = $request->{delete}->[0];
#debug message.
xCAT::MsgUtils->message('S', "delete flag: $delete");
####now get the switch ports for the nodes
my %swinfo = (); # example: %swinfo=( switch1=>{ 23=>{ nodes: [node1]},
# 24=>{ nodes: [node2]},
# },
# switch12=>{ 3=>{ nodes: [node4],
# }
# )
my %nodeswinfo = (); # example: %nodeswinfo=(
# node1=>{eth0=>{switch => swith1,
# port => 1,
# vlanids => [2,3]},
# eth1=>{switch => switch1,
# port => 2,
# vlanids => [5]}}
# )
my %swsetup = ();
#get the switch and port numbers for each node
my $swtab = xCAT::Table->new("switch", -create => 1);
my $swtmphash = $swtab->getNodesAttribs(\@nodes, [ 'switch', 'port', 'vlan', 'interface' ]);
foreach my $node (keys(%$swtmphash)) {
my $node_enties = $swtmphash->{$node};
foreach my $ent (@$node_enties) {
if (ref($ent) and defined $ent->{switch} and defined $ent->{port}) {
my $switch = $ent->{switch};
my $port = $ent->{port};
my $interface = "";
if (defined $ent->{interface}) { $interface = $ent->{interface} }
my $vlan = "";
if (defined $ent->{vlan}) { $vlan = $ent->{vlan} }
if ($interface) {
$nodeswinfo{$node}->{$interface}->{switch} = $switch;
$nodeswinfo{$node}->{$interface}->{port} = $port;
$nodeswinfo{$node}->{$interface}->{vlan} = $vlan;
}
}
}
}
#debug message.
my $nodesinfostr = Dumper(%nodeswinfo);
xCAT::MsgUtils->message('S', "nodeswinfo: $nodesinfostr");
# Validate node's switch info %nodeswinfo and build up %swinfo, %swsetup.
my @missed_switch_nodes = ();
my @missed_vlanid_nodes = ();
foreach my $node (@nodes) {
# Check whether the switch, port and interface info defined for all nodes.
if (defined $nodeswinfo{$node} && defined $nodeswinfo{$node}->{$nic}) {
} else {
push(@missed_switch_nodes, $node);
next;
}
if ($delete) {
# For delete mode, must make sure all node's has such a vlan ID defined for the interface.
my @vlanids = split(",", $nodeswinfo{$node}->{$nic}->{vlan});
if (@vlanids && (grep /^$vlan_id$/, @vlanids)) {
my $switch = $nodeswinfo{$node}->{$nic}->{switch};
my $port = $nodeswinfo{$node}->{$nic}->{port};
#setup swinfo and swsetup .
$swinfo{$switch}->{$port}->{hosts} = [$node];
$swsetup{$node}->{switch} = $switch;
$swsetup{$node}->{port} = $port;
$swsetup{$node}->{vlan} = $nodeswinfo{$node}->{$nic}->{vlan};
} else {
push(@missed_vlanid_nodes, $node);
next;
}
} else {
# non-delete mode, just setup swinfo directly.
my $switch = $nodeswinfo{$node}->{$nic}->{switch};
my $port = $nodeswinfo{$node}->{$nic}->{port};
#setup swinfo and swsetup for add_ports, add_crossover_ports and add_vlan_to_switch_table call later.
$swinfo{$switch}->{$port}->{hosts} = [$node];
$swsetup{$node}->{switch} = $switch;
$swsetup{$node}->{port} = $port;
$swsetup{$node}->{vlan} = $nodeswinfo{$node}->{$nic}->{vlan};
}
}
if (@missed_switch_nodes > 0) {
my $rsp = {};
$rsp->{error}->[0] = "Cannot proceed, please define switch, port and interface info on the switch table for the following nodes:\n @missed_switch_nodes\n";
$callback->($rsp);
return 1;
}
if (@missed_vlanid_nodes > 0) {
my $rsp = {};
$rsp->{error}->[0] = "Cannot proceed, no such vlan ID $vlan_id defined for following nodes:\n @missed_vlanid_nodes\n";
$callback->($rsp);
return 1;
}
#debug message.
my $swinfostr = Dumper(%swinfo);
xCAT::MsgUtils->message('S', "swinfo: $swinfostr");
my $swsetupstr = Dumper(%swsetup);
xCAT::MsgUtils->message('S', "swsetup: $swsetupstr");
# Do actual configurations on switches
if (!$delete) {
### add ports to the vlan
if (!add_ports($vlan_id, \%swinfo, $callback, 1)) { return 1; }
xCAT::MsgUtils->message('S', "Adding ports to switch success!");
### add the cross-over ports to the vlan
my @sws = keys(%swinfo);
#get all the switches that are in the vlan
my $swtab = xCAT::Table->new("switch", -create => 0);
if ($swtab) {
my @tmp1 = $swtab->getAllAttribs('switch', 'vlan');
if ((@tmp1) && (@tmp1 > 0)) {
foreach my $item (@tmp1) {
my $vlan = $item->{vlan};
my $sw = $item->{switch};
if ($vlan) {
my @a = split(",", $vlan);
if (grep(/^$vlan_id$/, @a)) {
if (!grep(/^$sw$/, @sws)) {
push(@sws, $sw);
}
}
}
}
}
}
if (!add_crossover_ports($vlan_id, \@sws, $callback)) { return 1; }
xCAT::MsgUtils->message('S', "Configuring cross over ports success!");
#add the vlanid for the standalone nodes on the switch table
#append the vlan id for the vmhosts on the switch table
add_vlan_to_switch_table(\%swsetup, $vlan_id);
xCAT::MsgUtils->message('S', "Adding vlan to switch table success!");
# done
my $rsp = {};
$rsp->{data}->[0] = "The interface $nic of following nodes are added to the vlan $vlan_id:\n@nodes";
$callback->($rsp);
} else {
### remove ports from the vlan
my $novmhost = 1;
if (!remove_ports($vlan_id, \%swinfo, $callback, $novmhost)) { return 1; }
xCAT::MsgUtils->message('S', "Removing ports from vlan success!");
#remove the vlan id from the switch table.
remove_vlan_from_switch_table(\%swsetup, $vlan_id);
xCAT::MsgUtils->message('S', "Removing ports from switch table success!");
# done
my $rsp = {};
$rsp->{data}->[0] = "The interface $nic of following nodes are removed from the vlan $vlan_id:\n@nodes";
$callback->($rsp);
}
return 0;
}
sub process_chvlan {
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $vlan_id = 0;
if (exists($request->{vlanid})) {
$vlan_id = $request->{vlanid}->[0];
}
if ($vlan_id == 0) {
my $rsp = {};
$rsp->{error}->[0] = "Invalid vlan id: $vlan_id";
$callback->($rsp);
return;
}
my $nic;
if (exists($request->{nic})) {
$nic = $request->{nic}->[0];
}
my @nodes = ();
if (exists($request->{node})) {
@nodes = @{ $request->{node} };
}
my $delete = 0;
if (exists($request->{delete})) {
$delete = $request->{delete}->[0];
}
my $net;
my $netmask;
#check if the vlan is already defined on the table
my $found = 0;
my $nwtab = xCAT::Table->new("networks", -create => 1);
if ($vlan_id > 0) {
if ($nwtab) {
my @tmp1 = $nwtab->getAllAttribs(('vlanid', 'net', 'mask'));
if ((@tmp1) && (@tmp1 > 0)) {
foreach (@tmp1) {
if ($vlan_id eq $_->{vlanid}) {
$found = 1;
$net = $_->{net};
$netmask = $_->{mask};
}
}
}
}
}
if (!$found) {
my $rsp = {};
$rsp->{data}->[0] = "The vlan $vlan_id does not exist.";
$callback->($rsp);
return 1;
}
if ((!$net) || (!$netmask)) {
my $rsp = {};
$rsp->{data}->[0] = "Please make sure subnet and netmask are specified on the networks table for vlan $vlan_id.";
$callback->($rsp);
return 1;
}
####now get the switch ports for the nodes
my %swinfo = (); # example: %swinfo=( switch1=>{ 23=>{ nodes: [node1]},
# 24=>{ nodes: [node2,node3],
# vmhost: kvm1
# }
# },
# switch12=>{ 3=>{ nodes: [node4]},
# 7=>{ nodes: [node5]},
# 9=>{ nodes: [node6]}
# }
# )
my %vminfo = (); # example: %vminfo=( kvm1=>{clients:[node1,node2...]},
# kvm2=>{clients:[node2,node3...]},
# )
#
my @vmnodes = (); # vm clients
my @snodes = (); # stand alone nodes
#get the vm hosts
my $vmtab = xCAT::Table->new("vm", -create => 1);
my $vmtmphash = $vmtab->getNodesAttribs(\@nodes, [ 'host', 'nics' ]);
foreach (@nodes) {
my $node = $_;
my $host;
my $ent = $vmtmphash->{$node}->[0];
if (ref($ent) and defined $ent->{host}) {
$host = $ent->{host};
if (exists($vminfo{$host})) {
my $pa = $vminfo{$host}->{clients};
push(@$pa, $node);
} else {
$vminfo{$host}->{clients} = [$node];
}
push(@vmnodes, $node);
}
}
if (@vmnodes > 0) {
foreach my $node (@nodes) {
if (!grep /^$node$/, @vmnodes) {
push(@snodes, $node);
}
}
} else {
@snodes = @nodes;
}
#get the switch and port numbers for each node
my @vmhosts = keys(%vminfo);
my @anodes = (@snodes, @vmhosts); #nodes that connects to the switch
my %swsetup = ();
my $swtab = xCAT::Table->new("switch", -create => 1);
my $swtmphash = $swtab->getNodesAttribs(\@anodes, [ 'switch', 'port', 'vlan', 'interface' ]);
my @missed_nodes = ();
foreach my $node (@anodes) {
my $switch;
my $port;
my $node_enties = $swtmphash->{$node};
if ($node_enties) {
my $i = -1;
my $use_this = 0;
foreach my $ent (@$node_enties) {
$i++;
if (ref($ent) and defined $ent->{switch} and defined $ent->{port}) {
$switch = $ent->{switch};
$port = $ent->{port};
my $interface = "primary";
if (defined $ent->{interface}) { $interface = $ent->{interface}; }
# for primary nic, the interface can be empty, "primary" or "primary:eth0"
if ($delete) {
if (defined($ent->{vlan})) {
my @a = split(',', $ent->{vlan});
if (grep(/^$vlan_id$/, @a)) { $use_this = 1; }
}
} else {
if ($nic) {
if ($interface =~ /primary/) {
$interface =~ s/primary(:)?//g;
}
if ($interface && ($interface eq $nic)) { $use_this = 1; }
} else {
if ($interface =~ /primary/) { $use_this = 1; }
}
}
if (!$use_this) {
next;
} else {
$swsetup{$node}->{port} = $port;
$swsetup{$node}->{switch} = $switch;
if (defined $ent->{vlan}) {
$swsetup{$node}->{vlan} = $ent->{vlan};
} else {
$swsetup{$node}->{vlan} = "";
}
}
if ($interface) {
$swinfo{$switch}->{$port}->{interface} = $interface;
}
if (exists($vminfo{$node})) {
$swinfo{$switch}->{$port}->{vmhost} = $node;
$swinfo{$switch}->{$port}->{nodes} = $vminfo{$node}->{clients};
} else {
$swinfo{$switch}->{$port}->{nodes} = [$node];
}
last;
}
}
if ($use_this != 1) {
push(@missed_nodes, $node);
}
}
}
if (@missed_nodes > 0) {
my $rsp = {};
$rsp->{error}->[0] = "Cannot proceed, please define switch and port info on the switch table for the following nodes:\n @missed_nodes\n";
$callback->($rsp);
return 1;
}
#print "vminfo=" . Dumper(%vminfo) . "\n";
#print "swinfo=" . Dumper(%swinfo) . "\n";
#print "anodes=" . Dumper(@anodes) . "\n";
#print "vmnodes=" . Dumper(@vmnodes) . "\n";
if (!$delete) {
### verify the ports are not used by other vlans
#if (!verify_switch_ports($vlan_id, \%swinfo, $callback)) { return 1;}
###create the vlan if it does not exist
if (!create_vlan($vlan_id, \%swinfo, $callback)) { return 1; }
### add ports to the vlan
if (!add_ports($vlan_id, \%swinfo, $callback)) { return 1; }
### add the cross-over ports to the vlan
my @sws = keys(%swinfo);
#get all the switches that are in the vlan
my $swtab = xCAT::Table->new("switch", -create => 0);
if ($swtab) {
my @tmp1 = $swtab->getAllAttribs('switch', 'vlan');
if ((@tmp1) && (@tmp1 > 0)) {
foreach my $item (@tmp1) {
my $vlan = $item->{vlan};
my $sw = $item->{switch};
if ($vlan) {
my @a = split(",", $vlan);
if (grep(/^$vlan_id$/, @a)) {
if (!grep(/^$sw$/, @sws)) {
push(@sws, $sw);
}
}
}
}
}
}
if (!add_crossover_ports($vlan_id, \@sws, $callback)) { return 1; }
#add the vlanid for the standalone nodes on the switch table
#append the vlan id for the vmhosts on the switch table
add_vlan_to_switch_table(\%swsetup, $vlan_id);
#we'll derive the prefix and the node numbers from the existing
#nodes on the vlan
my ($prefix, $start_number) = get_prefix_and_nodenumber($vlan_id, $net, $netmask);
### get node ip and vlan hostname from the hosts table.
#If it is not defined, put the default into the host table
my @allnodes = (@anodes, @vmnodes);
if (!add_vlan_ip_host($net, $netmask, $prefix, $start_number + 1, \@allnodes, $callback)) { return 1; }
### for vm nodes, add an additional nic on the vm.nics
if (@vmnodes > 0) {
my %setupnics = ();
my $new_nic = "vl$vlan_id";
foreach my $node (@vmnodes) {
my $ent = $vmtmphash->{$node}->[0];
my $nics;
if (ref($ent) and defined $ent->{nics}) {
$nics = $ent->{nics};
my @a = split(",", $nics);
if (!grep(/^$new_nic$/, @a)) { $nics = "$nics,$new_nic"; }
} else {
$nics = $new_nic;
}
$setupnics{$node} = { nics => "$nics" };
}
$vmtab->setNodesAttribs(\%setupnics);
}
### populate the /etc/hosts and make the DNS server on the mn aware this change
$::CALLBACK = $callback;
my $res = xCAT::Utils->runxcmd({
command => ['makehosts'],
}, $sub_req, 0, 1);
my $rsp = {};
$rsp->{data}->[0] = "Running makehosts...";
$callback->($rsp);
if ($res && (@$res > 0)) {
$rsp = {};
$rsp->{data} = $res;
$callback->($rsp);
}
$callback->($rsp);
$::CALLBACK = $callback;
my $res = xCAT::Utils->runxcmd({
command => ['makedns'],
}, $sub_req, 0, 1);
my $rsp = {};
$rsp->{data}->[0] = "Running makedns...";
if ($res && (@$res > 0)) {
$callback->($rsp);
$rsp->{data} = $res;
$callback->($rsp);
}
my $cmd = "service named restart";
my $rc = system $cmd;
### now go to the nodes to configure the vlan interface
$::CALLBACK = $callback;
my $args = [ "-P", "configvlan $vlan_id --keephostname" ];
my $res = xCAT::Utils->runxcmd({
command => ['updatenode'],
node => \@snodes,
arg => $args
}, $sub_req, 0, 1);
my $rsp = {};
$rsp->{data}->[0] = "Running updatenode...";
if ($res && (@$res > 0)) {
$callback->($rsp);
$rsp->{data} = $res;
$callback->($rsp);
}
### add configvlan postscripts to the postscripts table for the node
my @pnodes = (@snodes, @vmnodes);
add_postscript($callback, \@pnodes);
# done
my $rsp = {};
$rsp->{data}->[0] = "The following nodes are added to the vlan $vlan_id:\n@nodes";
$callback->($rsp);
} else {
### go to the nodes to de-configure the vlan interface
if (@snodes > 0) {
my $args = [ "-P", "deconfigvlan $vlan_id" ];
my $res = xCAT::Utils->runxcmd({
command => ['updatenode'],
node => \@snodes,
arg => $args
}, $sub_req, 0, 1);
my $rsp = {};
$rsp->{data}->[0] = "Running updatenode...";
if ($res && (@$res > 0)) {
$callback->($rsp);
$rsp->{data} = $res;
$callback->($rsp);
}
}
### remove ports from the vlan
my $novmhost = 1;
if (!remove_ports($vlan_id, \%swinfo, $callback, $novmhost)) { return 1; }
#remove the vlan id from the switch table for standalone nodes
#cannot call this function because %swsetup contains vmhosts
#remove_vlan_from_switch_table(\%swsetup,$vlan_id);
#print "swsetup=". Dumper(%swsetup);
my $swtab1 = xCAT::Table->new('switch', -create => 1, -autocommit => 0);
foreach my $node (@snodes) {
if (exists($swsetup{$node})) {
my %keyhash = ();
my %updates = ();
$keyhash{'node'} = $node;
$keyhash{'switch'} = $swsetup{$node}->{switch};
$keyhash{'port'} = $swsetup{$node}->{port};
$updates{'vlan'} = "";
if ($swsetup{$node}->{vlan}) {
my @a = split(',', $swsetup{$node}->{vlan});
my @b = grep(!/^$vlan_id$/, @a);
if (@b > 0) {
$updates{'vlan'} = join(',', @b);
}
}
$swtab1->setAttribs(\%keyhash, \%updates);
}
}
$swtab1->commit;
#remove the vlan from the vm.nic for vm clients
if (@vmnodes > 0) {
my %setupnics = ();
my $new_nic = "vl$vlan_id";
foreach my $node (@vmnodes) {
my $ent = $vmtmphash->{$node}->[0];
my $nics = '';
if (ref($ent) and defined $ent->{nics}) {
$nics = $ent->{nics};
my @a = split(",", $nics);
my @b = grep(!/^$new_nic$/, @a);
if (@b > 0) { $nics = join(',', @b); }
}
$setupnics{$node} = { nics => "$nics" };
}
$vmtab->setNodesAttribs(\%setupnics);
}
#remove the node's vlan hostname and the ip from the host table and /etc/hosts
my @pnodes = (@snodes, @vmnodes);
if (!remove_vlan_ip_host($net, $netmask, \@pnodes, $callback)) { return 1; }
#refresh the DNS server
my $res = xCAT::Utils->runxcmd({
command => ['makedns'],
}, $sub_req, 0, 1);
my $rsp = {};
$rsp->{data}->[0] = "Running makedns...";
if ($res && (@$res > 0)) {
$callback->($rsp);
$rsp->{data} = $res;
$callback->($rsp);
}
my $cmd = "service named restart";
my $rc = system $cmd;
# remove configvlan postscripts from the postscripts table for the node
remove_postscript($callback, \@pnodes);
# done
my $rsp = {};
$rsp->{data}->[0] = "The following nodes are removed from the vlan $vlan_id:\n@nodes";
$callback->($rsp);
}
return 0;
}
sub process_rmvlan {
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $vlan_id = 0;
if (exists($request->{vlanid})) {
$vlan_id = $request->{vlanid}->[0];
}
my @anodes = ();
my %swportinfo = ();
my %swsetup = ();
my $swtab = xCAT::Table->new("switch", -create => 0);
if ($swtab) {
my @tmp1 = $swtab->getAllAttribs(('node', 'switch', 'port', 'vlan'));
if ((@tmp1) && (@tmp1 > 0)) {
foreach my $ent (@tmp1) {
my @nodes_tmp = noderange($ent->{node});
foreach my $node (@nodes_tmp) {
my $switch = $ent->{switch};
my $port = $ent->{port};
if ($ent->{vlan}) {
my @a = split(",", $ent->{vlan});
if (grep(/^$vlan_id$/, @a)) {
push(@anodes, $node);
if (exists($swportinfo{$switch})) {
my $pa = $swportinfo{$switch};
push(@$pa, $port);
} else {
$swportinfo{$switch} = [$port];
}
$swsetup{$node}->{port} = $port;
$swsetup{$node}->{switch} = $switch;
$swsetup{$node}->{vlan} = $ent->{vlan};
}
}
}
}
}
}
my $switchestab = xCAT::Table->new('switches', -create => 0);
if ($switchestab) {
my @tmp1 = $switchestab->getAllAttribs(('switch'));
if ((@tmp1) && (@tmp1 > 0)) {
foreach (@tmp1) {
my @switches_tmp = noderange($_->{switch});
if (@switches_tmp == 0) { push @switches_tmp, $_->{switch}; } #sometimes the switch name is not on the node list table.
foreach my $switch (@switches_tmp) {
my $ports = [];
if (exists($swportinfo{$switch})) {
$ports = $swportinfo{$switch};
}
my $swh;
if (exists($Switches{$switch})) { $swh = $Switches{$switch}; }
else {
$swh = new xCAT::SwitchHandler->new($switch);
$Switches{$switch} = $swh;
}
print "switch=$switch, ports=@$ports\n";
if (@$ports > 0) {
my @ret = $swh->remove_ports_from_vlan($vlan_id, @$ports);
if ($ret[0] != 0) {
my $rsp = {};
$rsp->{error}->[0] = "remove_ports_from_vlan: $ret[1]";
$callback->($rsp);
}
my @ret = $swh->remove_vlan($vlan_id);
if ($ret[0] != 0) {
my $rsp = {};
$rsp->{error}->[0] = "remove_vlan: $ret[1]";
$callback->($rsp);
}
} else {
#check if the vlan exists on the switch
my @ids = $swh->get_vlan_ids();
foreach my $id (@ids) {
if ($id == $vlan_id) {
#remove it if exists
my @ret = $swh->remove_vlan($vlan_id);
if ($ret[0] != 0) {
my $rsp = {};
$rsp->{error}->[0] = "remove_vlan: $ret[1]";
$callback->($rsp);
}
last;
}
}
}
}
}
}
}
### now go to the nodes to de-configure the vlan interface
my $args = [ "-P", "deconfigvlan $vlan_id" ];
my $res = xCAT::Utils->runxcmd({
command => ['updatenode'],
node => \@anodes,
arg => $args
}, $sub_req, 0, 1);
my $rsp = {};
$rsp->{data}->[0] = "Running updatenode...";
if ($res && (@$res > 0)) {
$callback->($rsp);
$rsp->{data} = $res;
$callback->($rsp);
}
#remove the vlan from the networks table
my $nwtab = xCAT::Table->new("networks", -create => 1);
my $sent = $nwtab->getAttribs({ vlanid => "$vlan_id" }, 'net', 'mask');
my $net;
my $netmask;
if ($sent and ($sent->{net})) {
$net = $sent->{net};
$netmask = $sent->{mask};
}
my %key_col = (vlanid => $vlan_id);
$nwtab->delEntries(\%key_col);
#remove the vlan from the switch table for standalone nodes and vm hosts
remove_vlan_from_switch_table(\%swsetup, $vlan_id);
#remove the vlan nic from vm.nics for the vm clients
my @vmnodes = ();
my %vmsetup = ();
my $vmtab = xCAT::Table->new("vm", -create => 0);
if ($vmtab) {
my @tmp1 = $vmtab->getAllAttribs(('node', 'host', 'nics'));
if ((@tmp1) && (@tmp1 > 0)) {
foreach (@tmp1) {
my @nodes_tmp = noderange($_->{node});
my $nics = $_->{nics};
my $new_nic = "vl$vlan_id";
if ($nics) {
foreach my $node (@nodes_tmp) {
my @a = split(",", $nics);
if (grep(/^$new_nic$/, @a)) {
push(@vmnodes, $node);
my @b = grep(!/^$new_nic$/, @a);
if (@b > 0) {
$vmsetup{$node} = { nics => join(',', @b) };
} else {
$vmsetup{$node} = { nics => '' };
}
}
}
}
}
}
if (keys(%vmsetup) > 0) {
$vmtab->setNodesAttribs(\%vmsetup);
}
}
#remove the node's vlan hostname and the ip from the host table and /etc/hosts
my @allnodes = (@anodes, @vmnodes);
if (!remove_vlan_ip_host($net, $netmask, \@allnodes, $callback)) { return 1; }
#refresh the DNS server
my $res = xCAT::Utils->runxcmd({
command => ['makedns'],
}, $sub_req, 0, 1);
my $rsp = {};
$rsp->{data}->[0] = "Running makedns...";
if ($res && (@$res > 0)) {
$callback->($rsp);
$rsp->{data} = $res;
$callback->($rsp);
}
my $cmd = "service named restart";
my $rc = system $cmd;
### remove configvlan postscripts from the postscripts table for the node
# note: if configvlan is in xcatdefaults, it will not get removed becase
# it may affect other vlans
#remove_postscript($callback, \@allnodes); --- will not remove for multi-vlan support
}
#-------------------------------------------------------
=head3 remove_postscript
It removes configvlan postscripts from the postscripts table for the node
Note: if configvlan is in xcatdefaults, it will not get removed becase
it may affect other vlans
=cut
#-------------------------------------------------------
sub remove_postscript {
my $callback = shift;
my $anodes = shift;
my $posttab = xCAT::Table->new("postscripts", -create => 0);
if ($posttab) {
my %setup_hash;
my $postcache = $posttab->getNodesAttribs($anodes, [qw(postscripts postbootscripts)]);
foreach my $node (@$anodes) {
my $ref = $postcache->{$node}->[0];
if ($ref) {
if (exists($ref->{postbootscripts})) {
my $post = $ref->{postbootscripts};
my @old_a = split(',', $post);
my @new_a = grep (!/^configvlan$/, @old_a);
if (scalar(@new_a) != scalar(@old_a)) {
#print "newa =@new_a\n";
$setup_hash{$node} = { postbootscripts => join(',', @new_a) };
}
}
if (exists($ref->{postscripts})) {
my $post = $ref->{postscripts};
my @old_a = split(',', $post);
my @new_a = grep (!/^configvlan$/, @old_a);
if (scalar(@new_a) != scalar(@old_a)) {
$setup_hash{$node} = { postscripts => join(',', @new_a) };
}
}
}
}
if (keys(%setup_hash) > 0) {
$posttab->setNodesAttribs(\%setup_hash);
}
}
}
sub process_lsvlan {
my $request = shift;
my $callback = shift;
my $sub_req = shift;
my $vlan_id = 0;
if (exists($request->{vlanid})) {
$vlan_id = $request->{vlanid}->[0];
}
my %vlans = ();
#get all the vm clients if the node is a vm host
my $nwtab = xCAT::Table->new("networks", -create => 0);
if ($nwtab) {
my @tmp1 = $nwtab->getAllAttribs('net', 'mask', 'vlanid');
if ((@tmp1) && (@tmp1 > 0)) {
foreach (@tmp1) {
if (exists($_->{vlanid})) {
$vlans{ $_->{vlanid} }->{net} = $_->{net};
$vlans{ $_->{vlanid} }->{mask} = $_->{mask};
}
}
}
}
if ($vlan_id != 0 && !exists($vlans{$vlan_id})) {
my $rsp = {};
$rsp->{data}->[0] = "the vlan $vlan_id is not defined for the cluster nodes.";
$rsp->{errorcode} = -1;
$callback->($rsp);
return;
}
if ($vlan_id == 0) { #just show the existing vlan ids
my $rsp = {};
my $index = 0;
if (keys(%vlans) > 0) {
foreach my $id (sort keys(%vlans)) {
$rsp->{data}->[$index] = "vlan $id:\n subnet " . $vlans{$id}->{net} . "\n netmask " . $vlans{$id}->{mask} . "\n";
$index++;
}
} else {
$rsp->{data}->[0] = "No vlans defined for the cluster nodes."
}
$callback->($rsp);
} else { #shows the details
#get all the nodes that are in the vlan
my $swtab = xCAT::Table->new("switch", -create => 0);
my @nodes = ();
if ($swtab) {
my @tmp1 = $swtab->getAllAttribs('node', 'vlan', 'interface');
if ((@tmp1) && (@tmp1 > 0)) {
foreach my $grp (@tmp1) {
my $vlan = $grp->{vlan};
my $nic = "primary";
if ($grp->{interface}) { $nic = $grp->{interface}; }
my @nodes_tmp = noderange($grp->{node});
if ($vlan) {
my @a = split(",", $vlan);
if (grep(/^$vlan_id$/, @a)) {
foreach my $node (@nodes_tmp) {
push(@nodes, $node);
$vlans{$vlan_id}->{node}->{$node}->{name} = $node;
$vlans{$vlan_id}->{node}->{$node}->{interface} = $nic;
}
}
}
}
}
}
#get all the vm clients if the node is a vm host
my $vmtab = xCAT::Table->new("vm", -create => 0);
my @vmnodes = ();
if ($vmtab) {
my @tmp1 = $vmtab->getAllAttribs('node', 'host', 'nics');
if ((@tmp1) && (@tmp1 > 0)) {
my $new_nic = "vl$vlan_id";
foreach (@tmp1) {
my $host = $_->{host};
my $nics = $_->{nics};
if ($nics) {
my @a = split(",", $nics);
if (grep(/^$new_nic$/, @a)) {
my @nodes_tmp = noderange($_->{node});
foreach my $node (@nodes_tmp) {
push(@vmnodes, $node);
$vlans{$vlan_id}->{node}->{$node}->{name} = $node;
$vlans{$vlan_id}->{node}->{$node}->{vmhost} = $host;
}
}
}
}
}
}
@nodes = (@nodes, @vmnodes);
#now go to hosts table to get the host name and ip on the vlan
my $hoststab = xCAT::Table->new('hosts');
my $hostscache = $hoststab->getNodesAttribs(\@nodes, [qw(node otherinterfaces)]);
my $max = 0;
my $prefix;
foreach my $node (@nodes) {
my $ref = $hostscache->{$node}->[0];
my $otherinterfaces;
if ($ref && exists($ref->{otherinterfaces})) {
$otherinterfaces = $ref->{otherinterfaces};
my @itf_pairs = split(/,/, $otherinterfaces);
my @itf_pairs2 = ();
foreach (@itf_pairs) {
my ($name, $ip) = split(/:/, $_);
if (xCAT::NetworkUtils->ishostinsubnet($ip, $vlans{$vlan_id}->{mask}, $vlans{$vlan_id}->{net})) {
$vlans{$vlan_id}->{node}->{$node}->{ip} = $ip;
$vlans{$vlan_id}->{node}->{$node}->{vname} = $name;
}
}
}
} #foreach node
my $rsp = {};
$rsp->{data}->[0] = "vlan $vlan_id";
$rsp->{data}->[1] = " subnet " . $vlans{$vlan_id}->{net};
$rsp->{data}->[2] = " netmask " . $vlans{$vlan_id}->{mask} . "\n";
my $node_hash = $vlans{$vlan_id}->{node};
#print Dumper($node_hash);
if ($node_hash && keys(%$node_hash) > 0) {
$rsp->{data}->[3] = " hostname\tip address\tnode \tvm host \tinterface";
my $index = 4;
foreach (sort keys(%$node_hash)) {
my $vname = $node_hash->{$_}->{vname};
if (!$vname) { $vname = " "; }
my $ip = $node_hash->{$_}->{ip};
if (!$ip) { $ip = " "; }
my $name = $node_hash->{$_}->{name};
if (!$name) { $name = " "; }
my $host = $node_hash->{$_}->{vmhost};
if (!$host) { $host = " "; }
my $nic = $node_hash->{$_}->{interface};
$rsp->{data}->[$index] = " $vname\t$ip\t$name\t$host\t$nic";
$index++;
}
}
$callback->($rsp);
}
}
sub mkvlan_usage {
my $cb = shift;
my $rsp = {};
$rsp->{data}->[0] = "Usage: mkvlan -h";
$rsp->{data}->[1] = " mkvlan -v";
$rsp->{data}->[2] = " mkvlan [vlanid] -n noderange [-t net -m mask] [-p node_prefix] [-i nic]";
$cb->($rsp);
}
sub rmvlan_usage {
my $cb = shift;
my $rsp = {};
$rsp->{data}->[0] = "Usage: rmvlan -h";
$rsp->{data}->[1] = " rmvlan -v";
$rsp->{data}->[2] = " rmvlan vlanid";
$cb->($rsp);
}
sub chvlanports_usage {
my $cb = shift;
my $rsp = {};
$rsp->{data}->[0] = "Usage: chvlanports -h";
$rsp->{data}->[1] = " chvlanports -v";
$rsp->{data}->[2] = " chvlanports vlanid -n noderange -i nic";
$rsp->{data}->[3] = " chvlanports vlanid -n noderange -i nic -d";
$cb->($rsp);
}
sub chvlan_usage {
my $cb = shift;
my $rsp = {};
$rsp->{data}->[0] = "Usage: chvlan -h";
$rsp->{data}->[1] = " chvlan -v";
$rsp->{data}->[2] = " chvlan vlanid -n noderange [-i nic]";
$rsp->{data}->[3] = " chvlan vlanid -n noderange -d";
$cb->($rsp);
}
sub lsvlan_usage {
my $cb = shift;
my $rsp = {};
$rsp->{data}->[0] = "Usage: lsvlan -h";
$rsp->{data}->[1] = " lsvlan -v";
$rsp->{data}->[2] = " lsvlan";
$rsp->{data}->[3] = " lsvlan vlanid";
$cb->($rsp);
}
#-------------------------------------------------------
=head3 getNodeVlanConfData
This function is called by Postage.pm to collect all the
environmental variables for setting up a vlan for a given
node.
=cut
#-------------------------------------------------------
sub getNodeVlanConfData {
my $node = shift;
if ($node =~ /xCAT_plugin::vlan/) {
$node = shift;
}
my @scriptd = ();
my $swtab = xCAT::Table->new("switch", -create => 0);
if ($swtab) {
my $tmp_switch = $swtab->getNodesAttribs([$node], [ 'vlan', 'interface' ], prefetchcache => 1);
#print Dumper($tmp_switch);
if (defined($tmp_switch) && (exists($tmp_switch->{$node})) && (defined($tmp_switch->{$node}->[0]))) {
my $tmp_node_array = $tmp_switch->{$node};
my $index = 0;
foreach my $tmp (@$tmp_node_array) {
if (exists($tmp->{vlan})) {
my $nic = "primary";
if (exists($tmp->{interface})) { $nic = $tmp->{interface}; }
my @vlanid_array = split(',', $tmp->{vlan});
foreach my $vlan (@vlanid_array) {
$index++;
push @scriptd, "VLANID_$index='" . $vlan . "'\n";
push @scriptd, "export VLANID_$index\n";
push @scriptd, "VLANNIC_$index='" . $nic . "'\n";
push @scriptd, "export VLANNIC_$index\n";
my @temp_data = getNodeVlanOtherConfData($node, $vlan, $index);
@scriptd = (@scriptd, @temp_data);
}
}
}
if ($index > 0) {
push @scriptd, "VLANMAXINDEX='" . $index . "'\n";
push @scriptd, "export VLANMAXINDEX\n";
}
} else {
my $vmtab = xCAT::Table->new("vm", -create => 0);
if ($vmtab) {
my $tmp1 = $vmtab->getNodeAttribs($node, [ 'nics', 'host' ], prefetchcache => 1);
my $vlan;
my $index = 0;
if (defined($tmp1) && ($tmp1) && $tmp1->{nics})
{
push @scriptd, "VMNODE='YES'\n";
push @scriptd, "export VMNODE\n";
push @scriptd, "VMNICS='" . $tmp1->{nics} . "'\n";
push @scriptd, "export VMNICS\n";
my @nics = split(',', $tmp1->{nics});
#get the vlan id and interface from the host
my $host = $tmp1->{host};
#my $host_vlan_info=get_vm_host_vlan_info($host);
my $nic_position = 0;
foreach my $nic (@nics) {
$nic_position++;
if ($nic =~ /^vl([\d]+)$/) {
$vlan = $1;
$index++;
push @scriptd, "VLANID_$index='" . $vlan . "'\n";
push @scriptd, "export VLANID_$index\n";
push @scriptd, "VLAN_VMNICPOS_$index='" . $nic_position . "'\n";
push @scriptd, "export VLAN_VMNICPOS_$index\n";
#if ($host_vlan_info && (exists($host_vlan_info->{$vlan}))) {
# push @scriptd, "HOST_VLANNIC_$index='" . $host_vlan_info->{$vlan} . "'\n";
# push @scriptd, "export HOST_VLANNIC_$index\n";
#}
my @temp_data = getNodeVlanOtherConfData($node, $vlan, $index);
@scriptd = (@scriptd, @temp_data);
}
} #end foreach
}
if ($index > 0) {
push @scriptd, "VLANMAXINDEX='" . $index . "'\n";
push @scriptd, "export VLANMAXINDEX\n";
}
}
}
}
return @scriptd;
}
sub getNodeVlanOtherConfData {
my $node = shift;
if ($node =~ /xCAT_plugin::vlan/) {
$node = shift;
}
my $vlan = shift;
my $index = shift;
my @scriptd = ();
my $nwtab = xCAT::Table->new("networks", -create => 0);
if ($nwtab) {
my $sent = $nwtab->getAttribs({ vlanid => "$vlan" }, 'net', 'mask');
my $subnet;
my $netmask;
if ($sent and ($sent->{net})) {
$subnet = $sent->{net};
$netmask = $sent->{mask};
}
if (($subnet) && ($netmask)) {
my $hoststab = xCAT::Table->new("hosts", -create => 0);
if ($hoststab) {
my $tmp = $hoststab->getNodeAttribs($node, ['otherinterfaces'], prefetchcache => 1);
if (defined($tmp) && ($tmp) && $tmp->{otherinterfaces})
{
my $otherinterfaces = $tmp->{otherinterfaces};
my @itf_pairs = split(/,/, $otherinterfaces);
foreach (@itf_pairs) {
my ($name, $ip) = split(/:/, $_);
if (xCAT::NetworkUtils->ishostinsubnet($ip, $netmask, $subnet)) {
if ($name =~ /^-/) {
$name = $node . $name;
}
push @scriptd, "VLANHOSTNAME_$index='" . $name . "'\n";
push @scriptd, "export VLANHOSTNAME_$index\n";
push @scriptd, "VLANIP_$index='" . $ip . "'\n";
push @scriptd, "export VLANIP_$index\n";
push @scriptd, "VLANSUBNET_$index='" . $subnet . "'\n";
push @scriptd, "export VLANSUBNET_$index\n";
push @scriptd, "VLANNETMASK_$index='" . $netmask . "'\n";
push @scriptd, "export VLANNETMASK_$index\n";
last;
}
}
}
}
}
}
return @scriptd;
}
#-------------------------------------------------------
=head3 get_vm_host_vlan_info
This function returns a hash pointer that has the vlan id
and the interface info for the given KVM host. A host can
support more than one vlans for a interface. For example:
{1=>"eth0", 2=>"eth0", 3=>"eth1" ...}
=cut
#-------------------------------------------------------
sub get_vm_host_vlan_info {
my $host = shift;
my $host_vlan_info = {};
my $swtab = xCAT::Table->new("switch", -create => 0);
if ($swtab) {
my $tmp_switch = $swtab->getNodesAttribs([$host], [ 'vlan', 'interface' ], prefetchcache => 1);
if (defined($tmp_switch) && (exists($tmp_switch->{$host}))) {
my $tmp_node_array = $tmp_switch->{$host};
foreach my $tmp (@$tmp_node_array) {
if (exists($tmp->{vlan})) {
my $vlans = $tmp->{vlan};
my $nic = "primary";
if (exists($tmp->{interface})) {
$nic = $tmp->{interface};
}
foreach my $vlan (split(',', $vlans)) {
$host_vlan_info->{$vlan} = $nic;
}
}
}
}
return $host_vlan_info;
}
}