supporting Mellanox IB switch configurations in rspconfig command, more to come
git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@10761 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
This commit is contained in:
		
							
								
								
									
										624
									
								
								xCAT-server/lib/perl/xCAT/MellanoxIB.pm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										624
									
								
								xCAT-server/lib/perl/xCAT/MellanoxIB.pm
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,624 @@ | ||||
| # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| package xCAT::MellanoxIB; | ||||
| BEGIN | ||||
| { | ||||
|   $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; | ||||
| } | ||||
| use lib "$::XCATROOT/lib/perl"; | ||||
|  | ||||
|  | ||||
| use IO::Socket; | ||||
| use Data::Dumper; | ||||
| use xCAT::NodeRange; | ||||
| use xCAT::Utils; | ||||
| use Sys::Syslog; | ||||
| use Expect; | ||||
| use Storable; | ||||
| use strict; | ||||
|  | ||||
| #------------------------------------------------------------------------------- | ||||
| =head1  xCAT::MellanoxIB | ||||
| =head2    Package Description | ||||
|   It handles Mellanox IB switch related function. It used the CLI interface of  | ||||
|   Mellanox IB switch | ||||
|  | ||||
| =cut | ||||
| #-------------------------------------------------------------------------------- | ||||
|  | ||||
| #-------------------------------------------------------------------------------- | ||||
| =head3    getConfigure | ||||
|       It queries the info from the given swithes.       | ||||
|     Arguments: | ||||
|         noderange-- an array ref to switches. | ||||
|         callback -- pointer for writes response back to the client. | ||||
|         suncommand --- attribute to query about. | ||||
|     Returns: | ||||
|         0 --- sucessful | ||||
|         none 0 --- unsuccessful. | ||||
| =cut | ||||
| #-------------------------------------------------------------------------------- | ||||
| sub getConfig { | ||||
|     my $noderange=shift; | ||||
|     if ($noderange =~ /xCAT::MellanoxIB/) { | ||||
| 	$noderange=shift; | ||||
|     } | ||||
|     my $callback=shift; | ||||
|     my $subreq=shift; | ||||
|     my $subcommand=shift; | ||||
|      | ||||
|     #handle sshcfg with expect script | ||||
|     if ($subcommand eq "sshcfg") { | ||||
| 	return querySSHcfg($noderange,$callback); | ||||
|     } | ||||
|  | ||||
|     #get the username and the password  | ||||
|     my $swstab=xCAT::Table->new('switches',-create=>1); | ||||
|     my $sws_hash = $swstab->getNodesAttribs($noderange,['sshusername']); | ||||
|      | ||||
|     my $passtab = xCAT::Table->new('passwd'); | ||||
|     my $ent; | ||||
|     ($ent) = $passtab->getAttribs({key => "switch"}, qw(username)); | ||||
|  | ||||
|     foreach my $node (@$noderange) { | ||||
| 	my $cmd; | ||||
| 	my $username; | ||||
| 	if ($sws_hash->{$node}->[0]) { | ||||
| 	    $username=$sws_hash->{$node}->[0]->{sshusername}; | ||||
| 	} | ||||
| 	if (!$username) { | ||||
| 	    if ($ent) { | ||||
| 	       $username=$ent->{username}; | ||||
| 	    } | ||||
| 	} | ||||
| 	if (!$username) { | ||||
| 	    $username="xcat"; | ||||
| 	}  | ||||
| 	 | ||||
| 	if (($subcommand eq "alert") || ($subcommand eq "snmpcfg") || ($subcommand eq "community") || ($subcommand eq "snmpdest"))  { | ||||
| 	    $cmd='show snmp'; | ||||
| 	} | ||||
| 	else { | ||||
| 	    my $rsp = {}; | ||||
| 	    $rsp->{error}->[0] = "Unsupported subcommand: $subcommand"; | ||||
| 	    $callback->($rsp); | ||||
| 	    return; | ||||
| 	} | ||||
|  | ||||
| 	#now goto the switch and get the output | ||||
| 	my  $output = xCAT::Utils->runxcmd({command => ["xdsh"], node =>[$node], arg => ["--devicetype", "IBSwitch::Mellanox", "$cmd"], env=>["DSH_TO_USERID=$username"]}, $subreq, -1, 1); | ||||
| 	if ($output) { | ||||
| 	    my $result=parseOutput($node, $subcommand, $output); | ||||
| 	    my $rsp = {}; | ||||
| 	    my $i=-1;  | ||||
| 	    foreach my $o (@$result) { | ||||
| 		$i++; | ||||
| 		$rsp->{data}->[$i] = $o;  | ||||
| 	    } | ||||
| 	    $callback->($rsp); | ||||
| 	} | ||||
|  | ||||
|     } #end foreach | ||||
| } | ||||
|  | ||||
| sub parseOutput { | ||||
|     my $node=shift; | ||||
|     my $subcommand=shift; | ||||
|     my $input=shift; #an array pointer | ||||
|  | ||||
|     my $output; | ||||
|     if ($subcommand eq "alert") { | ||||
| 	foreach my $tmpstr (@$input) { | ||||
| 	    if ($tmpstr =~ /Traps enabled:/) { | ||||
| 		if ($tmpstr =~ /yes/) { | ||||
| 		    $output=["$node: Switch Alerting enabled"]; | ||||
| 		} else { | ||||
| 		    $output=["$node: Switch Alerting disabled"]; | ||||
| 		} | ||||
| 	    } | ||||
| 	} | ||||
| 	if ($output) { return $output; } | ||||
|     } elsif ($subcommand eq "snmpcfg") { | ||||
| 	foreach my $tmpstr (@$input) { | ||||
| 	    if ($tmpstr =~ /SNMP enabled:/) { | ||||
| 		if ($tmpstr =~ /yes/) { | ||||
| 		    $output=["$node: SNMP enabled"]; | ||||
| 		} else { | ||||
| 		    $output=["$node: SNMP disabled"]; | ||||
| 		} | ||||
| 	    } | ||||
| 	} | ||||
| 	if ($output) { return $output; } | ||||
|     } elsif ($subcommand eq "snmpdest") { | ||||
|         my $found=0; | ||||
| 	my $j=0; | ||||
| 	foreach my $tmpstr (@$input) { | ||||
| 	    if ((!$found) && ($tmpstr =~ /Trap sinks:/)) { | ||||
| 		$found=1; | ||||
| 		$output->[0]="$node: SNMP Destination:"; | ||||
| 	    } | ||||
|  | ||||
| 	    if ($tmpstr =~ /Events for which/) { | ||||
| 		if (!$found) { | ||||
| 		    next; | ||||
| 		} else { | ||||
| 		    last; | ||||
| 		} | ||||
| 	    } | ||||
| 	    if ($found) { | ||||
| 		$tmpstr =~ s/$node: //g; | ||||
| 		$output->[++$j]=$tmpstr; | ||||
| 	    }               | ||||
| 	} | ||||
| 	if ($output) { return $output; } | ||||
|     }  elsif ($subcommand eq "community") { | ||||
| 	foreach my $tmpstr (@$input) { | ||||
| 	    if ($tmpstr =~ /Read-only community:/) { | ||||
| 		my @a=split(':', $tmpstr); | ||||
| 		my $c_str; | ||||
| 		if (@a > 2) { | ||||
| 		    $c_str=$a[2]; | ||||
| 		} | ||||
| 		$output=["$node: SNMP Community: $c_str"]; | ||||
| 	    } | ||||
| 	} | ||||
| 	if ($output) { return $output; } | ||||
|     } | ||||
|  | ||||
|     return $input #an array pointer | ||||
| } | ||||
|  | ||||
|  | ||||
| #-------------------------------------------------------------------------------- | ||||
| =head3    setConfigure | ||||
|       It configures the the given swithes.       | ||||
|     Arguments: | ||||
|         noderange-- an array ref to switches. | ||||
|         callback -- pointer for writes response back to the client. | ||||
|         suncommand --- attribute to set. | ||||
|     Returns: | ||||
|         0 --- sucessful | ||||
|         none 0 --- unsuccessful. | ||||
| =cut | ||||
| #-------------------------------------------------------------------------------- | ||||
| sub setConfig { | ||||
|     my $noderange=shift; | ||||
|     if ($noderange =~ /xCAT::MellanoxIB/) { | ||||
| 	$noderange=shift; | ||||
|     } | ||||
|     my $callback=shift; | ||||
|     my $subreq=shift; | ||||
|     my $subcommand=shift; | ||||
|     my $argument=shift; | ||||
|  | ||||
|     #handle sshcfg with expect script | ||||
|     if ($subcommand eq "sshcfg") { | ||||
| 	if($argument eq "on" or $argument =~ /^en/ or $argument =~ /^enable/) { | ||||
| 	    return setSSHcfg($noderange, $callback, 1); | ||||
| 	} | ||||
| 	elsif ($argument eq "off" or $argument =~ /^dis/ or $argument =~ /^disable/) { | ||||
| 	    return setSSHcfg($noderange, $callback, 0); | ||||
| 	} else { | ||||
| 	    my $rsp = {}; | ||||
| 	    $rsp->{error}->[0] = "Unsupported argument for sshcfg: $argument"; | ||||
| 	    $callback->($rsp); | ||||
| 	    return; | ||||
| 	}	 | ||||
|     } | ||||
|  | ||||
|     #get the username and the password  | ||||
|     my $swstab=xCAT::Table->new('switches',-create=>1); | ||||
|     my $sws_hash = $swstab->getNodesAttribs($noderange,['sshusername']); | ||||
|      | ||||
|     my $passtab = xCAT::Table->new('passwd'); | ||||
|     my $ent; | ||||
|     ($ent) = $passtab->getAttribs({key => "switch"}, qw(username)); | ||||
|  | ||||
|     foreach my $node (@$noderange) { | ||||
| 	my @cfgcmds; | ||||
| 	my $username; | ||||
| 	if ($sws_hash->{$node}->[0]) { | ||||
| 	    $username=$sws_hash->{$node}->[0]->{sshusername}; | ||||
| 	} | ||||
| 	if (!$username) { | ||||
| 	    if ($ent) { | ||||
| 	       $username=$ent->{username}; | ||||
| 	    } | ||||
| 	} | ||||
| 	if (!$username) { | ||||
| 	    $username="xcat"; #default ssh username | ||||
| 	}  | ||||
|  | ||||
| 	if ($subcommand eq "alert") { | ||||
| 	    if($argument eq "on" or $argument =~ /^en/ or $argument =~ /^enable/) { | ||||
| 		my $ip="9.114.154.69"; | ||||
| 		$cfgcmds[0]="snmp-server enable traps"; | ||||
| 		$cfgcmds[1]="snmp-server host$ip traps version 2c public"; | ||||
| 	    } | ||||
| 	    elsif ($argument eq "off" or $argument =~ /^dis/ or $argument =~ /^disable/) { | ||||
| 		my $ip="9.114.154.69"; | ||||
| 		$cfgcmds[0]="snmp-server host $ip disable"; | ||||
| 	    } else { | ||||
| 		my $rsp = {}; | ||||
| 		$rsp->{error}->[0] = "Unsupported argument for $subcommand: $argument"; | ||||
| 		$callback->($rsp); | ||||
| 		return; | ||||
| 	    } | ||||
| 	} | ||||
| 	elsif ($subcommand eq "snmpcfg") {  | ||||
| 	    if($argument eq "on" or $argument =~ /^en/ or $argument =~ /^enable/) { | ||||
| 		$cfgcmds[0]="snmp-server enable"; | ||||
| 	    } | ||||
| 	    elsif ($argument eq "off" or $argument =~ /^dis/ or $argument =~ /^disable/) { | ||||
| 		$cfgcmds[0]="no snmp-server enable"; | ||||
| 	    } else { | ||||
| 		my $rsp = {}; | ||||
| 		$rsp->{error}->[0] = "Unsupported argument for $subcommand: $argument"; | ||||
| 		$callback->($rsp); | ||||
| 		return; | ||||
| 	    } | ||||
| 	} | ||||
| 	elsif ($subcommand eq "community") {  | ||||
| 	    $cfgcmds[0]="snmp-server community $argument"; | ||||
| 	}  | ||||
| 	elsif ($subcommand eq "snmpdest") {  | ||||
| 	    $cfgcmds[0]="snmp-server host $argument traps version 2c public"; | ||||
| 	}  | ||||
| 	else { | ||||
| 	    my $rsp = {}; | ||||
| 	    $rsp->{error}->[0] = "Unsupported subcommand: $subcommand"; | ||||
| 	    $callback->($rsp); | ||||
| 	    return; | ||||
| 	} | ||||
|  | ||||
| 	#now do the real bussiness | ||||
| 	my $cmd="enable;configure terminal"; | ||||
| 	    foreach (@cfgcmds) { | ||||
| 		$cmd .= ";$_"; | ||||
| 	} | ||||
| 	my  $output = xCAT::Utils->runxcmd({command => ["xdsh"], node =>[$node], arg => ["--devicetype", "IBSwitch::Mellanox", "$cmd"], env=>["DSH_TO_USERID=$username"]}, $subreq, -1, 1); | ||||
| 	 | ||||
|         #only print out the error | ||||
| 	if ($::RUNCMD_RC != 0) { | ||||
| 	    if ($output) { | ||||
| 		my $rsp = {}; | ||||
| 		my $i=-1;  | ||||
| 		foreach my $o (@$output) { | ||||
| 		    $i++; | ||||
| 		    $rsp->{data}->[$i] = $o;  | ||||
| 		} | ||||
| 		$callback->($rsp); | ||||
| 	    } | ||||
| 	} | ||||
|  | ||||
| 	#now qerry | ||||
| 	return getConfig($noderange, $callback, $subreq, $subcommand);  | ||||
|     }   | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| #-------------------------------------------------------------------------------- | ||||
| =head3    querySSHcfg | ||||
|       It checks if the current host can ssh to the given switches without password.       | ||||
|     Arguments: | ||||
|         noderange-- an array ref to switches. | ||||
|         callback -- pointer for writes response back to the client. | ||||
|     Returns: | ||||
|         0 --- sucessful | ||||
|         none 0 --- unsuccessful. | ||||
| =cut | ||||
| #-------------------------------------------------------------------------------- | ||||
| sub querySSHcfg { | ||||
|     my $noderange=shift; | ||||
|     if ($noderange =~ /xCAT::MellanoxIB/) { | ||||
| 	$noderange=shift; | ||||
|     } | ||||
|     my $callback=shift; | ||||
|  | ||||
|     my $mysw; | ||||
|     my $enable_cmd="enable\r"; | ||||
|     my $exit_cmd="exit\r"; | ||||
|  | ||||
|     my $pwd_prompt   = "Password: "; | ||||
|     my $sw_prompt = "^.*\] > "; | ||||
|     my $enable_prompt="^.*\] \#"; | ||||
|     my $expstr="SSH authorized keys:"; | ||||
|  | ||||
|  | ||||
|     my $debug        = 0; | ||||
|     if ($::VERBOSE) | ||||
|     { | ||||
|         $debug = 1; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     #get the username and the password  | ||||
|     my $swstab=xCAT::Table->new('switches',-create=>1); | ||||
|     my $sws_hash = $swstab->getNodesAttribs($noderange,['sshusername','sshpassword']); | ||||
|     my $passtab = xCAT::Table->new('passwd'); | ||||
|     my $ent; | ||||
|     ($ent) = $passtab->getAttribs({key => "switch"}, qw(username password)); | ||||
|  | ||||
|  | ||||
|    #get the ssh public key from this host | ||||
|    my $fname = ((xCAT::Utils::isAIX()) ? "/.ssh/":"/root/.ssh/")."id_rsa.pub"; | ||||
|     unless ( open(FH,"<$fname") ) { | ||||
| 	$callback->({error=>["Error opening file $fname."],errorcode=>[1]}); | ||||
| 	return 1; | ||||
|     } | ||||
|     my ($sshkey) = <FH>; | ||||
|     close(FH); | ||||
|     #remove the userid@host part | ||||
|     my @tmpa=split(' ', $sshkey); | ||||
|     if (@tmpa > 2) { | ||||
| 	$sshkey=$tmpa[0] . ' ' . $tmpa[1]; | ||||
|     } | ||||
|  | ||||
|      | ||||
|     foreach my $node (@$noderange) { | ||||
| 	my $username; | ||||
| 	my $passwd; | ||||
| 	if ($sws_hash->{$node}->[0]) { | ||||
| 	    #print "got to switches table\n"; | ||||
| 	    $username=$sws_hash->{$node}->[0]->{sshusername}; | ||||
| 	    $passwd=$sws_hash->{$node}->[0]->{sshpassword}; | ||||
| 	} | ||||
| 	if (!$username) { | ||||
| 	    #print "got to passwd table\n"; | ||||
| 	    if ($ent) { | ||||
| 	       $username=$ent->{username}; | ||||
| 	       $passwd=$ent->{password}; | ||||
| 	    } | ||||
| 	} | ||||
| 	 | ||||
| 	unless ($username) { | ||||
| 	    $callback->({error=>["Unable to get the username and the password for node $node. Please fill the switches table or the password table."],errorcode=>[1]}); | ||||
| 	    next; | ||||
| 	}  | ||||
|  | ||||
| 	#print "username=$username, password=$passwd\n"; | ||||
|  | ||||
| 	$mysw = new Expect; | ||||
| 	$mysw->exp_internal($debug); | ||||
| 	# | ||||
| 	# log_stdout(0) prevent the program's output from being shown. | ||||
| 	#  turn on if debugging error | ||||
| 	$mysw->log_stdout($debug); | ||||
|  | ||||
| 	my @cfgcmds=(); | ||||
| 	$cfgcmds[0]="show ssh client\r"; | ||||
| 	my $login_cmd = "ssh -l $username $node\r"; | ||||
| 	my $passwd_cmd="$passwd\r"; | ||||
| 	unless ($mysw->spawn($login_cmd)) | ||||
| 	{ | ||||
| 	    $mysw->soft_close(); | ||||
|             my $rsp; | ||||
|             $rsp->{data}->[0]="Unable to run $login_cmd."; | ||||
| 	    xCAT::MsgUtils->message("I", $rsp, $callback); | ||||
| 	    next; | ||||
| 	} | ||||
|  | ||||
| 	my @result = $mysw->expect( | ||||
| 	    5, | ||||
| 	    [ | ||||
| 	     $pwd_prompt, | ||||
| 	     sub { | ||||
| 		 $mysw->clear_accum(); | ||||
| 		 $mysw->send($passwd_cmd); | ||||
|                  print "$node: password sent\n"; | ||||
| 		 $mysw->exp_continue(); | ||||
| 	     } | ||||
| 	    ], | ||||
| 	    [ | ||||
| 	     "-re", $sw_prompt, | ||||
| 	     sub { | ||||
| 		 print "$node: sending command: $enable_cmd\n"; | ||||
| 		 $mysw->clear_accum(); | ||||
| 		 $mysw->send($enable_cmd); | ||||
| 		 $mysw->exp_continue(); | ||||
| 	     } | ||||
| 	    ], | ||||
| 	    [ | ||||
| 	     "-re", $enable_prompt, | ||||
| 	     sub { | ||||
| 		 print "$node: sending command: $cfgcmds[0]\n"; | ||||
| 		 $mysw->clear_accum(); | ||||
| 		 $mysw->send($cfgcmds[0]); | ||||
| 		 $mysw->send(" "); | ||||
|                  sleep 1; | ||||
| 		 $mysw->exp_continue(); | ||||
| 	     } | ||||
| 	    ], | ||||
| 	    [ | ||||
| 	     $expstr, | ||||
| 	     sub { | ||||
|                  print "got here\n"; | ||||
| 		 my $tmp=$mysw->after(); | ||||
| 		 my $tmp1=`echo "$tmp" \|col`; | ||||
|                  my @a=split('\n', $tmp1); | ||||
| 		 my $line=0; | ||||
| 		 foreach (@a) { | ||||
| 		     $line++; | ||||
| 		     print "$line ::: $_\n"; | ||||
| 		 } | ||||
|  | ||||
| 		 #$rsp->{data}->[0]="$node: $outstr"; | ||||
| 		 #$callback->($rsp); | ||||
| 		 $mysw->clear_accum(); | ||||
| 	    	 $mysw->send($exit_cmd); | ||||
| 	     } | ||||
| 	    ] | ||||
|  | ||||
| 	    ); | ||||
|  | ||||
| 	if (defined($result[1])) | ||||
| 	{ | ||||
| 	    my $errmsg = $result[1]; | ||||
| 	    $mysw->soft_close(); | ||||
|             my $rsp; | ||||
| 	    $rsp->{data}->[0]="$node: command error: $result[1]";	 | ||||
| 	    xCAT::MsgUtils->message("I",$rsp, $callback); | ||||
| 	    next; | ||||
| 	     | ||||
| 	} | ||||
| 	$mysw->soft_close(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #-------------------------------------------------------------------------------- | ||||
| =head3    setSSHcfg | ||||
|       It enables/diables the current host to ssh to the given switches without password.       | ||||
|     Arguments: | ||||
|         noderange-- an array ref to switches. | ||||
|         callback -- pointer for writes response back to the client. | ||||
|     Returns: | ||||
|         0 --- sucessful | ||||
|         none 0 --- unsuccessful. | ||||
| =cut | ||||
| #-------------------------------------------------------------------------------- | ||||
| sub setSSHcfg { | ||||
|     my $noderange=shift; | ||||
|     if ($noderange =~ /xCAT::MellanoxIB/) { | ||||
| 	$noderange=shift; | ||||
|     } | ||||
|     my $callback=shift; | ||||
|     my $enable=shift; | ||||
|  | ||||
|     my $mysw; | ||||
|     my $enable_cmd="enable\r"; | ||||
|     my $config_cmd="configure terminal\r"; | ||||
|     my $exit_cmd="exit\r"; | ||||
|  | ||||
|     my $pwd_prompt   = "Password: "; | ||||
|     my $sw_prompt = "^.*\] > "; | ||||
|     my $enable_prompt="^.*\] \#"; | ||||
|     my $config_prompt="^.*\\\(config\\\) \#"; | ||||
|  | ||||
|  | ||||
|     my $debug        = 0; | ||||
|     if ($::VERBOSE) | ||||
|     { | ||||
|         $debug = 1; | ||||
|     } | ||||
|  | ||||
|     #get the username and the password  | ||||
|     my $swstab=xCAT::Table->new('switches',-create=>1); | ||||
|     my $sws_hash = $swstab->getNodesAttribs($noderange,['sshusername','sshpassword']); | ||||
|      | ||||
|     my $passtab = xCAT::Table->new('passwd'); | ||||
|     my $ent; | ||||
|     ($ent) = $passtab->getAttribs({key => "switch"}, qw(username password)); | ||||
|  | ||||
|    #get the ssh public key from this host | ||||
|    my $fname = ((xCAT::Utils::isAIX()) ? "/.ssh/":"/root/.ssh/")."id_rsa.pub"; | ||||
|     unless ( open(FH,"<$fname") ) { | ||||
| 	$callback->({error=>["Error opening file $fname."],errorcode=>[1]}); | ||||
| 	return 1; | ||||
|     } | ||||
|     my ($sshkey) = <FH>; | ||||
|     close(FH); | ||||
|     #remove the userid@host part | ||||
|     #my @tmpa=split(' ', $sshkey); | ||||
|     #if (@tmpa > 2) { | ||||
|     #	$sshkey=$tmpa[0] . ' ' . $tmpa[1]; | ||||
|     #} | ||||
|  | ||||
|     foreach my $node (@$noderange) { | ||||
| 	my $username; | ||||
| 	my $passwd; | ||||
| 	if ($sws_hash->{$node}->[0]) { | ||||
| 	    #print "got to switches table\n"; | ||||
| 	    $username=$sws_hash->{$node}->[0]->{sshusername}; | ||||
| 	    $passwd=$sws_hash->{$node}->[0]->{sshpassword}; | ||||
| 	} | ||||
| 	if (!$username) { | ||||
| 	    #print "got to passwd table\n"; | ||||
| 	    if ($ent) { | ||||
| 	       $username=$ent->{username}; | ||||
| 	       $passwd=$ent->{password}; | ||||
| 	    } | ||||
| 	} | ||||
| 	 | ||||
| 	unless ($username) { | ||||
| 	    $callback->({error=>["Unable to get the username and the password for node $node. Please fill the switches table or the password table."],errorcode=>[1]}); | ||||
| 	    next; | ||||
| 	}  | ||||
|  | ||||
| 	print "username=$username, password=$passwd\n"; | ||||
| 	$mysw = new Expect; | ||||
| 	$mysw->exp_internal($debug); | ||||
| 	# | ||||
| 	# log_stdout(0) prevent the program's output from being shown. | ||||
| 	#  turn on if debugging error | ||||
| 	$mysw->log_stdout($debug); | ||||
|  | ||||
| 	my @cfgcmds=(); | ||||
| 	$cfgcmds[0]="ssh client user $username authorized-key sshv2 \"$sshkey\"\r"; | ||||
|     	my $login_cmd = "ssh -l $username $node\r"; | ||||
| 	my $passwd_cmd="$passwd\r"; | ||||
| 	unless ($mysw->spawn($login_cmd)) | ||||
| 	{ | ||||
| 	    $mysw->soft_close(); | ||||
|             my $rsp; | ||||
|             $rsp->{data}->[0]="Unable to run $login_cmd."; | ||||
| 	    xCAT::MsgUtils->message("I", $rsp, $callback); | ||||
| 	    next; | ||||
| 	} | ||||
|  | ||||
| 	my @result = $mysw->expect( | ||||
| 	    5, | ||||
| 	    [ | ||||
| 	     $pwd_prompt, | ||||
| 	     sub { | ||||
| 		 $mysw->clear_accum(); | ||||
| 		 $mysw->send($passwd_cmd); | ||||
|                  print "$node: password sent\n"; | ||||
| 		 $mysw->exp_continue(); | ||||
| 	     } | ||||
| 	    ], | ||||
| 	    [ | ||||
| 	     "-re", $sw_prompt, | ||||
| 	     sub { | ||||
| 		 print "$node: sending command: $enable_cmd\n"; | ||||
| 		 $mysw->clear_accum(); | ||||
| 		 $mysw->send($enable_cmd); | ||||
| 		 $mysw->exp_continue(); | ||||
| 	     } | ||||
| 	    ], | ||||
| 	    [ | ||||
| 	     "-re", $enable_prompt, | ||||
| 	     sub { | ||||
| 		 print "$node: sending command: $config_cmd\n"; | ||||
| 		 $mysw->clear_accum(); | ||||
| 		 $mysw->send($config_cmd); | ||||
| 		 $mysw->exp_continue(); | ||||
| 	     } | ||||
| 	    ], | ||||
| 	    [ | ||||
| 	     "-re", $config_prompt, | ||||
| 	     sub { | ||||
| 		 print "$node: sending command: $cfgcmds[0]\n"; | ||||
| 		 $mysw->clear_accum(); | ||||
| 		 $mysw->send($cfgcmds[0]); | ||||
|                  sleep 1; | ||||
| 		 $mysw->send($exit_cmd); | ||||
| 	     } | ||||
| 	    ], | ||||
| 	    ); | ||||
|  | ||||
| 	if (defined($result[1])) | ||||
| 	{ | ||||
| 	    my $errmsg = $result[1]; | ||||
| 	    $mysw->soft_close(); | ||||
|             my $rsp; | ||||
| 	    $rsp->{data}->[0]="$node: command error: $result[1]";	 | ||||
| 	    xCAT::MsgUtils->message("I",$rsp, $callback); | ||||
| 	    next; | ||||
| 	     | ||||
| 	} | ||||
| 	$mysw->soft_close(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| 1; | ||||
| @@ -1,80 +1,249 @@ | ||||
| # 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 Storable; | ||||
| use xCAT::MellanoxIB; | ||||
|  | ||||
| my $macmap; | ||||
| sub handled_commands { | ||||
|   $macmap = xCAT::MacMap->new(); | ||||
|   return { | ||||
|     findme => 'switch', | ||||
|     findmac => 'switch', | ||||
|   }; | ||||
|     $macmap = xCAT::MacMap->new(); | ||||
|     return { | ||||
| 	findme => 'switch', | ||||
| 	findmac => '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]; | ||||
|   my $extrargs = $request->{arg}; | ||||
|   my @exargs=($request->{arg}); | ||||
|   if (ref($extrargs)) { | ||||
|     @exargs=@$extrargs; | ||||
|   } | ||||
|  | ||||
|   if ($command eq "rspconfig") { | ||||
|       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 (defined(@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::Utils->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;   | ||||
|   } | ||||
|   return [$request]; | ||||
| } | ||||
|  | ||||
| sub process_request { | ||||
|  my $req = shift; | ||||
|  my $cb = shift; | ||||
|  my $doreq = shift; | ||||
|  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; | ||||
|  } | ||||
|  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 = `/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 $req = shift; | ||||
|     my $cb = shift; | ||||
|     my $doreq = shift; | ||||
|     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 '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 = `/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; } | ||||
| 	    } | ||||
| 	} | ||||
| 	  | ||||
| 	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]; | ||||
| 	    $doreq->($req);  | ||||
| 	    %{$req}=();#Clear req structure, it's done.. | ||||
| 	    undef $mactab; | ||||
| 	} else {  | ||||
| 	    #Shouldn't complain, might be blade, but how to log total failures? | ||||
| 	} | ||||
|     } | ||||
|  } | ||||
|      | ||||
|  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]; | ||||
|   $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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user