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

423 lines
13 KiB
Perl

# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_plugin::switch;
BEGIN
{
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}
use lib "$::XCATROOT/lib/perl";
use IO::Socket;
use Data::Dumper;
use xCAT::MacMap;
use xCAT::NodeRange;
use Sys::Syslog;
use xCAT::Usage;
use Storable;
use xCAT::MellanoxIB;
require xCAT::TableUtils;
require xCAT::ServiceNodeUtils;
my $macmap;
sub handled_commands {
return {
findme => 'switch',
findmac => 'switch',
switchprobe => 'switch',
rspconfig => 'nodehm:mgt',
};
}
sub preprocess_request {
my $request = shift;
if (defined $request->{_xcatpreprocessed}->[0] and $request->{_xcatpreprocessed}->[0] == 1) { return [$request]; }
my $callback=shift;
my @requests;
my $noderange = $request->{node};
my $command = $request->{command}->[0];
if ($command eq "rspconfig") {
my $extrargs = $request->{arg};
my @exargs=($request->{arg});
if (ref($extrargs)) {
@exargs=@$extrargs;
}
my $usage_string=xCAT::Usage->parseCommand($command, @exargs);
if ($usage_string) {
$callback->({data=>$usage_string});
$request = {};
return;
}
if (!$noderange) {
$usage_string=xCAT::Usage->getUsage($command);
$callback->({data=>$usage_string});
$request = {};
return;
}
#make sure all the nodes are switches
my $switchestab=xCAT::Table->new('switches',-create=>0);
my @all_switches;
my @tmp=$switchestab->getAllAttribs(('switch'));
if (@tmp && (@tmp > 0)) {
foreach(@tmp) {
my @switches_tmp=noderange($_->{switch});
if (@switches_tmp==0) { push @switches_tmp, $_->{switch}; }
foreach my $switch (@switches_tmp) {
push @all_switches, $switch;
}
}
}
#print "all switches=@all_switches\n";
my @wrong_nodes;
foreach my $node (@$noderange) {
if (! grep /^$node$/, @all_switches) {
push @wrong_nodes, $node;
}
}
if (@wrong_nodes > 0) {
my $rsp = {};
$rsp->{error}->[0] = "The following nodes are not defined in the switches table:\n @wrong_nodes.";
$callback->($rsp);
return;
}
# find service nodes for requested switch
# build an individual request for each service node
my $service = "xcat";
my $sn = xCAT::ServiceNodeUtils->get_ServiceNode($noderange, $service, "MN");
# build each request for each service node
foreach my $snkey (keys %$sn)
{
#print "snkey=$snkey\n";
my $reqcopy = {%$request};
$reqcopy->{node} = $sn->{$snkey};
$reqcopy->{'_xcatdest'} = $snkey;
$reqcopy->{_xcatpreprocessed}->[0] = 1;
push @requests, $reqcopy;
}
return \@requests;
}
elsif ($command eq 'switchprobe') {
@ARGV = ();
if (ref($request->{arg})) {
@ARGV = @{$request->{arg}};
}
use Getopt::Long;
$Getopt::Long::ignorecase = 0;
Getopt::Long::Configure( "bundling" );
my $verbose = undef;
my $check = undef;
my $help = undef;
unless (GetOptions('h|help' => \$help, 'V|verbose' => \$verbose, 'c|check' => \$check)) {
$callback->({error=>["Parse args failed"], errorcode=>1});
return;
}
if (@ARGV) {
$callback->({error=>["Option @ARGV not supported.\n".xCAT::Usage->getUsage($command)], errorcode=>1});
return;
}
if (defined($help)) {
$callback->({data=>xCAT::Usage->getUsage($command)});
return;
}
if (defined($verbose)) {
$request->{opt}->{verbose} = $verbose;
}
if (defined($check)) {
$request->{opt}->{check} = $check;
}
if (defined($noderange)) {
my $nthash = undef;
my $swhash = undef;
my $nodetypetab=xCAT::Table->new('nodetype',-create=>0);
if ($nodetypetab) {
$nthash = $nodetypetab->getNodesAttribs($noderange, ['nodetype']);
if (!defined($nthash)) {
$callback->({error=>["Get attributes from table 'nodetype' failed"],errorcode=>1});
return;
}
}
else {
$callback->({error=>["Open table 'nodetype' failed"],errorcode=>1});
return;
}
my $switchestab = xCAT::Table->new('switches', -create=>0);
if ($switchestab) {
$swhash = $switchestab->getNodesAttribs($noderange, ['switch']);
if (!defined($swhash)) {
$callback->({error=>["Get attributes from table 'switches' failed"],errorcode=>1});
return;
}
}
else {
$callback->({error=>["Open table 'switches' failed"],errorcode=>1});
return;
}
my @switchnode = ();
my @errswnode = ();
my @errornode = ();
foreach my $node (@$noderange) {
if (!defined($nthash->{$node}) or $nthash->{$node}->[0]->{nodetype} ne 'switch') {
push @errornode, $node;
}
elsif (!defined($swhash->{$node})) {
push @errswnode, $node;
}
else {
push @switchnode, $node;
}
}
if (@errornode) {
$callback->({error=>["The nodetype is not 'switch' for nodes: ". join(",",@errornode)],errorcode=>1});
}
if (@errswnode) {
$callback->({error=>["No switch configuration info find for ". join(",",@errswnode)],errorcode=>1});
}
if (@switchnode) {
@{$request->{node}} = @switchnode;
return [$request];
}
return;
}
}
return [$request];
}
sub process_request {
my $req = shift;
my $cb = shift;
my $doreq = shift;
unless ($macmap) {
$macmap = xCAT::MacMap->new();
}
my $node;
my $mac = '';
if ($req->{command}->[0] eq 'findmac') {
$mac = $req->{arg}->[0];
$node = $macmap->find_mac($mac,0);
$cb->({node=>[{name=>$node,data=>$mac}]});
return;
} elsif ($req->{command}->[0] eq 'rspconfig') {
return process_switch_config($req, $cb, $doreq);
} elsif ($req->{command}->[0] eq 'switchprobe') {
my $macinfo = $macmap->dump_mac_info($req, $cb);
if ($macinfo and ref($macinfo) eq 'HASH') {
my $switch_name_length = 0;
my $port_name_length = 0;
foreach my $switch (keys %$macinfo) {
if (length($switch) > $switch_name_length) {
$switch_name_length = length($switch);
}
if (defined($macinfo->{$switch}->{ErrorStr})) {
next;
}
foreach my $portname (keys %{$macinfo->{$switch}}) {
if (length($portname) > $port_name_length) {
$port_name_length = length($portname);
}
}
}
my $format = "%-".$switch_name_length."s %-".$port_name_length."s %-18s %s";
my %failed_switches = ();
my $header = sprintf($format, "Switch", "Port", "MAC address", "Node");
if (!defined($req->{opt}->{check}) and $port_name_length) {
$cb->({data=>$header});
$cb->({data=>"------------------------------------------------------------------"})
};
foreach my $switch (keys %$macinfo) {
if (defined($macinfo->{$switch}->{ErrorStr})) {
if (defined($req->{opt}->{check})) {
$cb->({node=>[{name=>$switch, error=>[$macinfo->{$switch}->{ErrorStr}], errorcode=>1}]});
}
else {
$failed_switches{$switch} = "$macinfo->{$switch}->{ErrorStr}";
}
next;
}
elsif (defined($req->{opt}->{check})) {
$cb->({node=>[{name=>$switch, data=>["PASS"]}]});
next;
}
foreach my $port (sort keys %{$macinfo->{$switch}}) {
my $node = '';
if (defined($macinfo->{$switch}->{$port}->{Node})) {
$node = $macinfo->{$switch}->{$port}->{Node};
}
my @macarrary = ();
if (defined($macinfo->{$switch}->{$port}->{MACaddress})) {
@macarray = @{$macinfo->{$switch}->{$port}->{MACaddress}};
foreach (@macarray) {
my $data = sprintf($format, $switch, $port, ($_ ne '') ? $_ : ' N/A', $node);
$cb->({data=>$data});
#$cb->({node=>[{name=>$switch,data=>$data}]});
}
}
}
}
if (!defined($req->{opt}->{check}) and $port_name_length) {
$cb->({data=>"------------------------------------------------------------------"})
}
foreach (keys %failed_switches) {
$cb->({node=>[{name=>$_, error=>[$failed_switches{$_}], errorcode=>1}]});
}
}
return;
} elsif ($req->{command}->[0] eq 'findme') {
my $ip = $req->{'_xcat_clientip'};
if (defined $req->{nodetype} and $req->{nodetype}->[0] eq 'virtual') {
#Don't attempt switch discovery of a VM Guest
#TODO: in this case, we could/should find the host system
#and then ask it what node is associated with the mac
#Either way, it would be kinda weird since xCAT probably made up the mac addy
#anyway, however, complex network topology function may be aided by
#discovery working. Food for thought.
return;
}
my $arptable;
if ( -x "/usr/sbin/arp") {
$arptable = `/usr/sbin/arp -n`;
}
else{
$arptable = `/sbin/arp -n`;
}
my @arpents = split /\n/,$arptable;
foreach (@arpents) {
if (m/^($ip)\s+\S+\s+(\S+)\s/) {
$mac=$2;
last;
}
}
my $firstpass=1;
if ($mac) {
$node = $macmap->find_mac($mac,$req->{cacheonly}->[0]);
$firstpass=0;
}
if (not $node) { # and $req->{checkallmacs}->[0]) {
foreach (@{$req->{mac}}) {
/.*\|.*\|([\dABCDEFabcdef:]+)(\||$)/;
$node = $macmap->find_mac($1,$firstpass);
$firstpass=0;
if ($node) { last; }
}
}
my $pbmc_node = undef;
if ($req->{'mtm'}->[0] and $req->{'serial'}->[0]) {
my $mtms = $req->{'mtm'}->[0]."*".$req->{'serial'}->[0];
my $tmp_nodes = $::XCATVPDHASH{$mtms};
foreach (@$tmp_nodes) {
if ($::XCATMPHASH{$_}) {
$pbmc_node = $_;
}
}
}
if ($node) {
my $mactab = xCAT::Table->new('mac',-create=>1);
$mactab->setNodeAttribs($node,{mac=>$mac});
$mactab->close();
#my %request = (
# command => ['makedhcp'],
# node => [$node]
#);
#$doreq->(\%request);
$req->{command}=['discovered'];
$req->{noderange} = [$node];
if ($pbmc_node) {
$req->{pbmc_node} = [$pbmc_node];
}
$req->{discoverymethod} = ['switch'];
$doreq->($req);
%{$req}=();#Clear req structure, it's done..
undef $mactab;
} else {
#Shouldn't complain, might be blade, but how to log total failures?
}
}
}
sub process_switch_config {
my $request = shift;
my $callback=shift;
my $subreq=shift;
my $noderange = $request->{node};
my $command = $request->{command}->[0];
my $extrargs = $request->{arg};
my @exargs=($request->{arg});
if (ref($extrargs)) {
@exargs=@$extrargs;
}
my $subcommand=join(' ', @exargs);
my $argument;
($subcommand,$argument) = split(/=/,$subcommand);
if (!$subcommand) {
my $rsp = {};
$rsp->{error}->[0] = "No subcommand specified.";
$callback->($rsp);
return;
}
#decide what kind of swith it is
my $sw_types=getSwitchType($noderange); #hash {type=>[node1,node1...]}
foreach my $t (keys(%$sw_types)) {
my $nodes=$sw_types->{$t};
if (@$nodes>0) {
if ($t =~ /Mellanox/i) {
if (!$argument) {
xCAT::MellanoxIB::getConfig($nodes, $callback, $subreq, $subcommand);
} else {
xCAT::MellanoxIB::setConfig($nodes, $callback, $subreq, $subcommand, $argument);
}
} else {
my $rsp = {};
$rsp->{error}->[0] = "The following '$t' switches are unsuppored:\n@$nodes";
$callback->($rsp);
}
}
}
}
#--------------------------------------------------------------------------------
=head3 getSwitchType
It determins the swtich vendor and model for the given swith.
Arguments:
noderange-- an array ref to switches.
Returns:
a hash ref. the key is the switch type string and the value is an array ref to the swithces t
=cut
#--------------------------------------------------------------------------------
sub getSwitchType {
my $noderange=shift;
if ($noderange =~ /xCAT_plugin::switch/) {
$noderange=shift;
}
my $ret={};
my $switchestab = xCAT::Table->new('switches',-create=>1);
my $switches_hash = $switchestab->getNodesAttribs($noderange,['switchtype']);
foreach my $node (@$noderange) {
my $type="EtherNet";
if ($switches_hash) {
if ($switches_hash->{$node}-[0]) {
$type = $switches_hash->{$node}->[0]->{switchtype};
}
}
if (exists($ret->{$type})) {
$pa=$ret->{$type};
push @$pa, $node;
} else {
$ret->{$type}=[$node];
}
}
return $ret;
}
1;