mirror of
https://github.com/xcat2/xcat-core.git
synced 2025-05-21 19:22:05 +00:00
473 lines
17 KiB
Perl
473 lines
17 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;
|
|
}
|
|
my $switchestab = xCAT::Table->new('switches', -create => 0);
|
|
my $swhash = undef;
|
|
if ($switchestab) {
|
|
$swhash = $switchestab->getAllNodeAttribs(['switch'], 1);
|
|
if (!defined($swhash)) {
|
|
$callback->({ error => ["Get attributes from table 'switches' failed"], errorcode => 1 });
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
$callback->({ error => ["Open table 'switches' failed"], errorcode => 1 });
|
|
return;
|
|
}
|
|
if (defined($noderange)) {
|
|
my $nthash = 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 @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}) or !defined($swhash->{$node}->[0])) {
|
|
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;
|
|
}
|
|
else {
|
|
if (!scalar(keys %$swhash)) {
|
|
$callback->({ error => ["No switch configuration info get from 'switches' table"], errorcode => 1 });
|
|
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) + 10;
|
|
}
|
|
}
|
|
}
|
|
my $format = "%-" . $switch_name_length . "s %-" . $port_name_length . "s %-26s %s";
|
|
my %failed_switches = ();
|
|
my $header = sprintf($format, "Switch", "Port(MTU)", "MAC address(VLAN)", "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 (map{$_->[0]}sort{$a->[1] cmp $b->[1] || $a->[2] <=> $b->[2]}map{[$_, /^(.*?)(\d+)?$/]} keys %{ $macinfo->{$switch} }) {
|
|
my $node = '';
|
|
if (defined($macinfo->{$switch}->{$port}->{Node})) {
|
|
$node = $macinfo->{$switch}->{$port}->{Node};
|
|
}
|
|
|
|
my $mtu = '';
|
|
my $vlanid = '';
|
|
my @vlans = ();
|
|
if (defined($macinfo->{$switch}->{$port}->{Vlanid})) {
|
|
@vlans = @{ $macinfo->{$switch}->{$port}->{Vlanid} };
|
|
}
|
|
|
|
my @macarrary = ();
|
|
if (defined($macinfo->{$switch}->{$port}->{MACaddress})) {
|
|
@macarray = @{ $macinfo->{$switch}->{$port}->{MACaddress} };
|
|
my $ind = 0;
|
|
foreach (@macarray) {
|
|
my $mac = $_;
|
|
$vlanid = $vlans[$ind];
|
|
my $mac_vlan;
|
|
if (!$mac) {
|
|
$mac_vlan="N/A";
|
|
} elsif ($vlanid) {
|
|
$mac_vlan = "$mac($vlanid)";
|
|
} else {
|
|
$mac_vlan = $mac;
|
|
}
|
|
my $port_mtu = $port;
|
|
if (defined($macinfo->{$switch}->{$port}->{Mtu})) {
|
|
$mtu = $macinfo->{$switch}->{$port}->{Mtu}->[0];
|
|
$port_mtu = "$port($mtu)";
|
|
}
|
|
my $data = sprintf($format, $switch, $port_mtu, $mac_vlan, $node);
|
|
$cb->({ data => $data });
|
|
$ind++;
|
|
|
|
#$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') {
|
|
if (defined($req->{discoverymethod}) and defined($req->{discoverymethod}->[0]) and ($req->{discoverymethod}->[0] ne 'undef')) {
|
|
|
|
# The findme request had been processed by other module, just return
|
|
return;
|
|
}
|
|
$mac = $req->{_xcat_clientmac}->[0];
|
|
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 $discoverswitch = 0;
|
|
if (defined $req->{nodetype} and $req->{nodetype}->[0] eq 'switch') {
|
|
$discoverswitch = 1;
|
|
}
|
|
my $firstpass = 1;
|
|
if ($mac) {
|
|
$node = $macmap->find_mac($mac, $req->{cacheonly}->[0], $discoverswitch);
|
|
$firstpass = 0;
|
|
}
|
|
if (not $node) { # and $req->{checkallmacs}->[0]) {
|
|
foreach (@{ $req->{mac} }) {
|
|
/.*\|.*\|([\dABCDEFabcdef:]+)(\||$)/;
|
|
$node = $macmap->find_mac($1, $firstpass, $discoverswitch);
|
|
$firstpass = 0;
|
|
if ($node) { last; }
|
|
}
|
|
}
|
|
my $bmc_node = undef;
|
|
my @bmc_nodes = ();
|
|
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{$_}) {
|
|
push @bmc_nodes, $_;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($req->{'bmcmac'}->[0]) {
|
|
my $bmcmac = lc($req->{'bmcmac'}->[0]);
|
|
$bmcmac =~ s/\://g;
|
|
my $tmp_node = "node-$bmcmac";
|
|
push @bmc_nodes, $tmp_node if ($::XCATMPHASH{$tmp_node});
|
|
}
|
|
|
|
$bmc_node = join(",", @bmc_nodes);
|
|
if ($node) {
|
|
xCAT::MsgUtils->message("S", "xcat.discovery.switch: ($req->{_xcat_clientmac}->[0]) Found node: $node");
|
|
|
|
# No need to write mac table here, 'discovered' command will write
|
|
# my $mactab = xCAT::Table->new('mac',-create=>1);
|
|
# $mactab->setNodeAttribs($node,{mac=>$mac});
|
|
# $mactab->close();
|
|
#my %request = (
|
|
# command => ['makedhcp'],
|
|
# node => [$node]
|
|
#);
|
|
#$doreq->(\%request);
|
|
$req->{discoverymethod}->[0] = 'switch';
|
|
my $request = {%$req};
|
|
$request->{command} = ['discovered'];
|
|
$request->{noderange} = [$node];
|
|
$request->{bmc_node} = [$bmc_node] if $bmc_node;
|
|
$doreq->($request);
|
|
if (defined($request->{error})) {
|
|
$req->{error}->[0] = '1';
|
|
$req->{error_msg}->[0] = $request->{error_msg}->[0];
|
|
}
|
|
%{$request} = (); #Clear req structure, it's done..
|
|
undef $mactab;
|
|
} else {
|
|
xCAT::MsgUtils->message("S", "xcat.discovery.switch: ($req->{_xcat_clientmac}->[0]) Warning: Could not find any nodes using switch-based discovery");
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
#onie switch will processed in the onie plug in
|
|
unless ($t =~ /onie/i) {
|
|
my $rsp = {};
|
|
$rsp->{error}->[0] = "The following '$t' switches are not supported:\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;
|