mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-30 19:02:27 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1388 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			1388 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head1
 | |
|   xCAT plugin package to handle BMC discovery
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| package xCAT_plugin::bmcdiscover;
 | |
| 
 | |
| BEGIN
 | |
| {
 | |
|     $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
 | |
| }
 | |
| use lib "$::XCATROOT/lib/perl";
 | |
| 
 | |
| use IO::Socket;
 | |
| use Thread qw(yield);
 | |
| use POSIX "WNOHANG";
 | |
| use Storable qw(store_fd fd_retrieve);
 | |
| use strict;
 | |
| use warnings "all";
 | |
| use Getopt::Long;
 | |
| use xCAT::Table;
 | |
| use xCAT::Utils;
 | |
| use xCAT::MsgUtils;
 | |
| use Getopt::Long;
 | |
| use Data::Dumper;
 | |
| use File::Basename;
 | |
| use File::Path;
 | |
| use Cwd;
 | |
| use LWP;
 | |
| use HTTP::Cookies;
 | |
| use HTTP::Response;
 | |
| use JSON;
 | |
| 
 | |
| my $log_label = "bmcdiscover:";
 | |
| my $nmap_path;
 | |
| my %ipmac = ();
 | |
| 
 | |
| my $debianflag = 0;
 | |
| my $tempstring = xCAT::Utils->osver();
 | |
| if ($tempstring =~ /debian/ || $tempstring =~ /ubuntu/) {
 | |
|     $debianflag = 1;
 | |
| }
 | |
| my $parent_fd;
 | |
| my $bmc_user;
 | |
| my $bmc_pass;
 | |
| my $openbmc_user;
 | |
| my $openbmc_pass;
 | |
| my $done_num = 0;
 | |
| $::P9_WITHERSPOON_MFG_ID     = "42817";
 | |
| $::P9_WITHERSPOON_PRODUCT_ID = "16975";
 | |
| %::VPDHASH = ();
 | |
| my %node_in_list = ();
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| =head3  handled_commands
 | |
| 
 | |
| Return list of commands handled by this plugin
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| 
 | |
| sub handled_commands
 | |
| {
 | |
|     return {
 | |
|         bmcdiscover => "bmcdiscover",
 | |
|     };
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| #
 | |
| sub preprocess_request {
 | |
|     my $request = shift;
 | |
|     if (defined $request->{_xcatpreprocessed}->[0] and $request->{_xcatpreprocessed}->[0] == 1) { return [$request]; }
 | |
| 
 | |
|     my $callback = shift;
 | |
|     my $extargs = $request->{arg};
 | |
|     my @exargs = ($request->{arg});
 | |
|     if (ref($extargs)) {
 | |
|         @exargs = @$extargs;
 | |
|     }
 | |
|     @ARGV = @exargs;
 | |
|     $Getopt::Long::ignorecase=0;
 | |
|     Getopt::Long::Configure("bundling");
 | |
|     my $sns = undef;
 | |
|     if ((grep /--sn/, @ARGV) and (Getopt::Long::GetOptions('sn=s' => \$sns))) {
 | |
|         unless ($sns) {
 | |
|             $callback->({ error => ["The value for --sn is invalid"], errorcode => [1] });
 | |
|             $request = ();
 | |
|             return;
 | |
|         }
 | |
|         my $nettab = xCAT::Table->new("networks");
 | |
|         my @entries   = $nettab->getAllAttribs('dhcpserver', 'dynamicrange');
 | |
|         my @dhcpservers = ();
 | |
|         foreach (@entries) {
 | |
|             if (!defined($_->{dynamicrange})) {next;}
 | |
|             push @dhcpservers, $_->{dhcpserver};
 | |
|         }
 | |
|         my @requests = ();
 | |
|         foreach (split (/,/, $sns)) {
 | |
|             my $reqcopy = {%$request};
 | |
|             $reqcopy->{'_xcatdest'} = $_;
 | |
|             $reqcopy->{'sn'} = $_;
 | |
|             $reqcopy->{'dhcpservers'} = \@dhcpservers;
 | |
|             $reqcopy->{_xcatpreprocessed}->[0] = 1;
 | |
|             push @requests, $reqcopy;
 | |
|         }
 | |
|         return \@requests;
 | |
|     } elsif (grep /--check/, @ARGV) {
 | |
|         $callback->({ error => ["The option '--check' is not supported"], errorcode=>[1]});
 | |
|         $request = ();
 | |
|         return;
 | |
|     } elsif (grep /--ipsource/, @ARGV) {
 | |
|         $callback->({ error => ["The option '--ipsource' is not supported"], errorcode=>[1]});
 | |
|         $request = ();
 | |
|         return;
 | |
|     } else {
 | |
|         return [$request];
 | |
|     }
 | |
| }
 | |
| 
 | |
| =head3  process_request
 | |
| 
 | |
|   Process the command
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-------------------------------------------------------
 | |
| sub process_request
 | |
| {
 | |
| 
 | |
|     my $request         = shift;
 | |
|     my $callback        = shift;
 | |
|     my $request_command = shift;
 | |
|     $::CALLBACK = $callback;
 | |
| 
 | |
|     if ($request->{sn}) {
 | |
|         my $dhcpservers = $request->{dhcpservers};
 | |
|         if (!defined($dhcpservers) or ref($dhcpservers) ne 'ARRAY') {
 | |
|             $callback->({ error => ["the ". $request->{command}->[0]. " doesn't work when no dynamic range set."], errorcode => [1] });
 | |
|             return 1;
 | |
|         }
 | |
|         else {
 | |
|             my $have_dynamicrange_set = 0;
 | |
|             foreach (@$dhcpservers) {
 | |
|                 unless (xCAT::NetworkUtils->thishostisnot($_)) {
 | |
|                     $have_dynamicrange_set = 1;
 | |
|                     last;
 | |
|                 }
 | |
|             }
 | |
|             unless ($have_dynamicrange_set) {
 | |
|                 $callback->({ error => ["the ". $request->{command}->[0]. " won't work since no dynamic range set on $request->{sn}->[0]"], errorcode => [1] });
 | |
|                 return 1;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     unless (defined($request->{arg})) {
 | |
|         bmcdiscovery_usage();
 | |
|         return 2;
 | |
|     }
 | |
|     @ARGV = @{ $request->{arg} };
 | |
|     if ($#ARGV == -1) {
 | |
|         return 2;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     my $command = $request->{command}->[0];
 | |
|     my $rc;
 | |
| 
 | |
|     if ($command eq "bmcdiscover") {
 | |
|         $rc = bmcdiscovery($request, $callback, $request_command);
 | |
|     } else {
 | |
|         $callback->({ error => ["Error: $command not found in this module."], errorcode => [1] });
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3  bmcdiscover_usage
 | |
| 
 | |
|         Display the bmcdiscover usage
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| 
 | |
| sub bmcdiscovery_usage {
 | |
|     my $rsp;
 | |
|     push @{ $rsp->{data} }, "\nbmcdiscover - Discover BMC (Baseboard Management Controller) using the specified scan method\n";
 | |
|     push @{ $rsp->{data} }, "Usage:";
 | |
|     push @{ $rsp->{data} }, "\tbmcdiscover [-?|-h|--help]";
 | |
|     push @{ $rsp->{data} }, "\tbmcdiscover [-v|--version]";
 | |
|     push @{ $rsp->{data} }, "\tbmcdiscover [--sn <SN_nodename>] [-s scan_method] [-u bmc_user] [-p bmc_passwd] [-z] [-w] --range ip_range\n";
 | |
| 
 | |
|     xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3   bmcdiscovery_processargs
 | |
| 
 | |
|         Process the bmcdiscovery command line
 | |
|         Returns:
 | |
|                 0 - OK
 | |
|                 1 - just print version
 | |
|                 2 - just print help
 | |
|                 3 - error
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| sub bmcdiscovery_processargs {
 | |
| 
 | |
|     #if ( defined ($::args) && @{$::args} ){
 | |
|     #    @ARGV = @{$::args};
 | |
|     #}
 | |
|     my $request         = shift;
 | |
|     my $callback        = shift;
 | |
|     my $request_command = shift;
 | |
| 
 | |
|     my $rc = 0;
 | |
| 
 | |
| 
 | |
|     # parse the options
 | |
|     # options can be bundled up like -v, flag unsupported options
 | |
|     Getopt::Long::Configure("bundling", "no_ignore_case", "no_pass_through");
 | |
|     my $getopt_success = Getopt::Long::GetOptions(
 | |
|         'help|h|?'      => \$::opt_h,
 | |
|         's=s'           => \$::opt_M,
 | |
|         'm=s'           => \$::opt_M,
 | |
|         'range=s'       => \$::opt_R,
 | |
|         'bmcip|i=s'     => \$::opt_I,
 | |
|         'z'             => \$::opt_Z,
 | |
|         'w'             => \$::opt_W,
 | |
|         'check'         => \$::opt_C,
 | |
|         'bmcuser|u=s'   => \$::opt_U,
 | |
|         'bmcpasswd|p=s' => \$::opt_P,
 | |
|         'ipsource'      => \$::opt_S,
 | |
|         'version|v'     => \$::opt_v,
 | |
|         't'             => \$::opt_T,
 | |
|         'sn=s'          => \$::opt_SN,
 | |
|     );
 | |
| 
 | |
|     if (!$getopt_success) {
 | |
|         return 3;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     #########################################
 | |
|     # This command is for linux
 | |
|     #########################################
 | |
|     if ($^O ne 'linux') {
 | |
|         my $rsp = {};
 | |
|         push @{ $rsp->{data} }, "The bmcdiscovery command is only supported on Linux.\n";
 | |
|         xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     ##########################################
 | |
|     # Option -h for Help
 | |
|     ##########################################
 | |
|     if (defined($::opt_h)) {
 | |
|         bmcdiscovery_usage;
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     #########################################
 | |
|     # Option -v for version
 | |
|     #########################################
 | |
|     if (defined($::opt_v)) {
 | |
|         create_version_response('bmcdiscover');
 | |
| 
 | |
|         # no usage - just exit
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     ############################################
 | |
|     # Option -U and -P for bmc user and password
 | |
|     #
 | |
|     # Get the default bmc account from passwd table,
 | |
|     # this is only done for the discovery process
 | |
|     ############################################
 | |
|     ($bmc_user, $bmc_pass, $openbmc_user, $openbmc_pass) = bmcaccount_from_passwd();
 | |
|     # overwrite the default user and password if one is provided
 | |
|     if ($::opt_U) {
 | |
|         $bmc_user = $::opt_U;
 | |
|         $openbmc_user = $::opt_U;
 | |
|     } elsif ($::opt_P) {
 | |
|         # If password is provided, but no user, set the user to blank
 | |
|         # Support older FSP and Tuletta machines
 | |
|         $bmc_user = '';
 | |
|     }
 | |
|     if ($::opt_P) {
 | |
|         $bmc_pass = $::opt_P;
 | |
|         $openbmc_pass = $::opt_P;
 | |
|     }
 | |
|     if ($request->{sn}) {
 | |
|         $::opt_SN = $request->{sn}->[0];
 | |
|     } else {
 | |
|         $::opt_SN = '';
 | |
|     }
 | |
| 
 | |
|     #########################################
 | |
|     # Option -s -r should be together
 | |
|     ######################################
 | |
|     if (defined($::opt_R))
 | |
|     {
 | |
|         # Option -c should not be used with -r
 | |
|         if (defined($::opt_C)) {
 | |
|             my $msg = "The 'check' and 'range' option cannot be used together.";
 | |
|             my $rsp = {};
 | |
|             push @{ $rsp->{data} }, "$msg";
 | |
|             xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
 | |
|             return 2;
 | |
|         }
 | |
|         ######################################
 | |
|         # check if there is nmap or not
 | |
|         ######################################
 | |
|         if (-x '/usr/bin/nmap')
 | |
|         {
 | |
|             $nmap_path = "/usr/bin/nmap";
 | |
|         }
 | |
|         elsif (-x '/usr/local/bin/nmap')
 | |
|         {
 | |
|             $nmap_path = "/usr/local/bin/nmap";
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             my $rsp;
 | |
|             push @{ $rsp->{data} }, "\tThere is no nmap in /usr/bin/ or /usr/local/bin/. \n ";
 | |
|             xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
 | |
|             return 1;
 | |
|         }
 | |
| 
 | |
|         if ($::opt_T) {
 | |
|             my $msg = "The -t option is deprecated and will be ignored";
 | |
|             my $rsp = {};
 | |
|             push @{ $rsp->{data} }, "$msg";
 | |
|             xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
 | |
|         }
 | |
| 
 | |
|         scan_process($::opt_M, $::opt_R, $::opt_Z, $::opt_W, $request_command);
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     if (defined($::opt_C) && defined($::opt_S)) {
 | |
|         my $msg = "The 'check' and 'ipsource' option cannot be used together.";
 | |
|         my $rsp = {};
 | |
|         push @{ $rsp->{data} }, "$msg";
 | |
|         xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
 | |
|         return 2;
 | |
|     }
 | |
| 
 | |
|     #########################################################
 | |
|     # --check option, requires -i, -u, and -p to be specified
 | |
|     #########################################################
 | |
|     if (defined($::opt_C)) {
 | |
|         if (defined($::opt_P) && defined($::opt_U) && defined($::opt_I)) {
 | |
|             my $res = check_auth_process($::opt_I, $::opt_U, $::opt_P);
 | |
|             return $res;
 | |
|         }
 | |
|         else {
 | |
|             my $msg = "";
 | |
|             if (!defined($::opt_I)) {
 | |
|                 $msg = "The check option requires a BMC IP.  Specify the IP using the -i|--bmcip option.";
 | |
|             } elsif (!defined($::opt_U)) {
 | |
|                 $msg = "The check option requires a user.  Specify the user with the -u|--bmcuser option.";
 | |
|             } elsif (!defined($::opt_P)) {
 | |
|                 $msg = "The check option requires a password.  Specify the password with the -p|--bmcpasswd option.";
 | |
|             }
 | |
|             my $rsp = {};
 | |
|             push @{ $rsp->{data} }, "$msg";
 | |
|             xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
 | |
|             return 2;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     ####################################################
 | |
|     # --ipsource option, requires -i, -p to be specified
 | |
|     ####################################################
 | |
|     if (defined($::opt_S)) {
 | |
|         if (defined($bmc_pass) && defined($::opt_I)) {
 | |
|             my $res = get_bmc_ip_source($::opt_I, $bmc_user, $bmc_pass);
 | |
|             return $res;
 | |
|         }
 | |
|         else {
 | |
|             my $msg = "";
 | |
|             if (!defined($::opt_I)) {
 | |
|                 $msg = "The ipsource option requires a BMC IP.  Specify the IP using the -i|--bmcip option.";
 | |
|             } elsif (!defined($::opt_P)) {
 | |
|                 $msg = "The ipsource option requires a password.  Specify the password with the -p|--bmcpasswd option.";
 | |
|             } else {
 | |
|                 $msg = "Failed to process ipsource command for bmc ip=$::opt_I user=$bmc_user password=$bmc_pass";
 | |
|             }
 | |
|             my $rsp = {};
 | |
|             push @{ $rsp->{data} }, "$msg";
 | |
|             xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
 | |
|             return 2;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #########################################
 | |
|     # Other attributes are not allowed
 | |
|     #########################################
 | |
| 
 | |
|     return 4;
 | |
| }
 | |
| 
 | |
| my $bmc_str1  = "RAKP 2 message indicates an error : unauthorized name";
 | |
| my $bmc_resp1 = "Wrong BMC username";
 | |
| 
 | |
| my $bmc_str2  = "RAKP 2 HMAC is invalid";
 | |
| my $bmc_resp2 = "Wrong BMC password";
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3   get_bmc_ip_source
 | |
| 
 | |
|         get bmc ip address source
 | |
|         Returns:
 | |
|                 0 - OK
 | |
|                 2 - Error
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| 
 | |
| sub get_bmc_ip_source {
 | |
|     my $bmcip    = shift;
 | |
|     my $bmcuser  = shift;
 | |
|     my $bmcpw    = shift;
 | |
|     my $callback = $::CALLBACK;
 | |
|     my $pcmd;
 | |
| 
 | |
|     if ($bmcuser eq "none") {
 | |
|         $pcmd = "/opt/xcat/bin/ipmitool-xcat -vv -I lanplus -P $bmcpw -H $bmcip lan print ";
 | |
|     }
 | |
|     else {
 | |
|         $pcmd = "/opt/xcat/bin/ipmitool-xcat -vv -I lanplus -U $bmcuser -P $bmcpw -H $bmcip lan print ";
 | |
|     }
 | |
| 
 | |
|     my $output = xCAT::Utils->runcmd("$pcmd", -1);
 | |
| 
 | |
|     if ($output =~ "IP Address Source") {
 | |
|         # success case
 | |
|         my $rsp      = {};
 | |
|         my $ipsource = `echo "$output"|grep "IP Address Source"`;
 | |
|         chomp($ipsource);
 | |
|         push @{ $rsp->{data} }, "$ipsource";
 | |
|         xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
 | |
|         return 0;
 | |
|     }
 | |
|     else {
 | |
|         my $rsp = {};
 | |
|         if ($output =~ $bmc_str1) {
 | |
|             # Error: RAKP 2 message indicates an error : unauthorized name <== incorrect username
 | |
|             push @{ $rsp->{data} }, "$bmc_resp1";
 | |
|         } elsif ($output =~ $bmc_str2) {
 | |
|             # Error: RAKP 2 HMAC is invalid <== incorrect password
 | |
|             push @{ $rsp->{data} }, "$bmc_resp2";
 | |
|         } else {
 | |
|             my $error_msg = `echo "$output"|grep "Error" `;
 | |
|             if ($error_msg eq ""){
 | |
|                 $error_msg = "Can not find IP address Source";
 | |
|             }
 | |
|             # all other errors
 | |
|             push @{ $rsp->{data} }, "$error_msg";
 | |
|         }
 | |
|         xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
 | |
|         return 2;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3   check_auth_process
 | |
| 
 | |
|         check bmc user and password
 | |
|         Returns:
 | |
|                 0 - OK
 | |
|                 2 - Error
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| 
 | |
| sub check_auth_process {
 | |
|     my $bmcip    = shift;
 | |
|     my $bmcuser  = shift;
 | |
|     my $bmcpw    = shift;
 | |
|     my $bmc_str4 = "BMC Session ID";
 | |
| 
 | |
|     my $callback = $::CALLBACK;
 | |
|     my $icmd;
 | |
|     if ($bmcuser eq "none") {
 | |
|         $icmd = "/opt/xcat/bin/ipmitool-xcat -vv -I lanplus -P $bmcpw -H $bmcip chassis status ";
 | |
|     }
 | |
|     else {
 | |
|         $icmd = "/opt/xcat/bin/ipmitool-xcat -vv -I lanplus -U $bmcuser -P $bmcpw -H $bmcip chassis status ";
 | |
|     }
 | |
|     my $output = xCAT::Utils->runcmd("$icmd", -1);
 | |
| 
 | |
|     if ($output =~ "Set Session Privilege Level to ADMINISTRATOR") {
 | |
| 
 | |
|         # Success case
 | |
|         my $rsp = {};
 | |
|         push @{ $rsp->{data} }, "Correct ADMINISTRATOR";
 | |
|         xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
 | |
|         return 0;
 | |
|     } else {
 | |
| 
 | |
|         # handle the various error scenarios
 | |
|         my $rsp = {};
 | |
| 
 | |
|         if ($output =~ $bmc_str1) {
 | |
| 
 | |
|             # Error: RAKP 2 message indicates an error : unauthorized name <== incorrect username
 | |
|             push @{ $rsp->{data} }, "$bmc_resp1";
 | |
|         }
 | |
|         elsif ($output =~ $bmc_str2) {
 | |
| 
 | |
|             # Error: RAKP 2 HMAC is invalid <== incorrect password
 | |
|             push @{ $rsp->{data} }, "$bmc_resp2";
 | |
|         }
 | |
|         elsif ($output !~ $bmc_str4) {
 | |
| 
 | |
|             # Did not find "BMC Session ID" in the response
 | |
|             push @{ $rsp->{data} }, "Not a BMC, please verify the correct IP address";
 | |
|         }
 | |
|         else {
 | |
|             push @{ $rsp->{data} }, "Unknown Error: $output";
 | |
|         }
 | |
|         xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
 | |
|         return 2;
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub buildup_mtms_hash {
 | |
|     xCAT::MsgUtils->trace(0, "I", "Establish hash for vpd table with key=mtm*serial, value=node");
 | |
|     my %nodehash = ();
 | |
|     if (my $vpdtab = xCAT::Table->new("vpd")) {
 | |
|         my @entries = $vpdtab->getAllAttribs(qw/node serial mtm/);
 | |
|         foreach (@entries) {
 | |
|             unless ($_->{mtm} and $_->{serial}) { next; }
 | |
|             my $mtms = lc($_->{mtm}) . "*" . lc($_->{serial});
 | |
|             $nodehash{$_->{node}} = $mtms; 
 | |
|         }
 | |
|     }
 | |
|     my @nodes = keys %nodehash;
 | |
|     foreach my $tab (qw/ipmi openbmc/) {
 | |
|         my $tabfd = xCAT::Table->new($tab);
 | |
|         my $entries = $tabfd->getNodesAttribs(\@nodes,qw/bmc/);
 | |
|         foreach my $node (@nodes) {
 | |
|             my $bmc = $entries->{$node}->[0]->{bmc};
 | |
|             unless($bmc) { next; }
 | |
|             if (exists($nodehash{$node})) {
 | |
|                 my $mtmsip = $nodehash{$node}."-".$bmc;
 | |
|                 $nodehash{$node} = $mtmsip;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     my @tmp_bmc_nodes = ();
 | |
|     foreach my $node (keys %nodehash) {
 | |
|         my $mtmsip = $nodehash{$node};
 | |
|         if (exists($::VPDHASH{$mtmsip})) {
 | |
|             my $tmp_node = $::VPDHASH{$mtmsip};
 | |
|             if ($tmp_node =~ /node-.+/) {
 | |
|                 push @tmp_bmc_nodes, $tmp_node;
 | |
|             } elsif ($node =~ /node-.+/) {
 | |
|                 $::VPDHASH{$mtmsip} = $node;
 | |
|                 push @tmp_bmc_nodes, $node;
 | |
|             } else {
 | |
|                 xCAT::MsgUtils->message("W", { data => ["Node $node and $tmp_node have the same mtms-ip keys: $mtmsip"] }, $::CALLBACK);
 | |
|             }
 | |
|             next;
 | |
|         }
 | |
|         $::VPDHASH{$mtmsip} = $node;
 | |
|     }
 | |
|     if ($#tmp_bmc_nodes > 0) {
 | |
|         my $useless_nodes = join(',', @tmp_bmc_nodes);
 | |
|         xCAT::MsgUtils->message("W", { data => ["The nodes: $useless_nodes have normal nodes defined, please remove them"] }, $::CALLBACK);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3   scan_process
 | |
| 
 | |
|         Process the bmcdiscovery command line
 | |
|         Returns:
 | |
|                 0 - OK
 | |
|                 1 - just print version
 | |
|                 2 - just print help
 | |
|                 3 - error
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| 
 | |
| sub scan_process {
 | |
| 
 | |
|     my $method          = shift;
 | |
|     my $range           = shift;
 | |
|     my $opz             = shift;
 | |
|     my $opw             = shift;
 | |
|     my $request_command = shift;
 | |
|     my $callback        = $::CALLBACK;
 | |
|     my $children;       # The number of child process
 | |
|     my %sp_children;    # Record the pid of child process
 | |
|     my $bcmd;
 | |
| 
 | |
| 
 | |
|     if (!defined($method))
 | |
|     {
 | |
|         $method = "nmap";
 | |
|     }
 | |
| 
 | |
|     # Handle commas in $range for nmap
 | |
|     $range =~ tr/,/ /;
 | |
| 
 | |
|     ############################################################
 | |
|     # get live ip list
 | |
|     ###########################################################
 | |
|     if ($method ne "nmap") {
 | |
|         my $rsp = {};
 | |
|         push @{ $rsp->{data} }, "The bmcdiscover method should be nmap.\n";
 | |
|         xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
 | |
|         return 2;
 | |
|     }
 | |
| 
 | |
|     #check nmap version first
 | |
|     my $nmap_version = xCAT::Utils->get_nmapversion();
 | |
|     my $ip_info_list;
 | |
| 
 | |
|     #  the output of nmap is different for version under 5.10
 | |
|     if (xCAT::Utils->version_cmp($nmap_version, "5.10") < 0) {
 | |
|         $bcmd = join(" ", $nmap_path, " -sP -n $range");
 | |
|     } else {
 | |
|         $bcmd = join(" ", $nmap_path, " -sn -n $range");
 | |
|     }
 | |
| 
 | |
|     xCAT::MsgUtils->trace(0, "I", "$log_label Try to scan live IPs by command $bcmd ...");
 | |
|     $ip_info_list = xCAT::Utils->runcmd("$bcmd", -1);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         my $rsp = {};
 | |
|         push @{ $rsp->{data} }, "Nmap scan is failed.\n";
 | |
|         xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
 | |
|         return 2;
 | |
|     }
 | |
| 
 | |
|     my $ip_list;
 | |
|     my $mac_list;
 | |
|     if (xCAT::Utils->version_cmp($nmap_version, "5.10") < 0) {
 | |
|         $ip_list  = `echo -e "$ip_info_list" | grep \"appears to be up\" |cut -d ' ' -f2 |tr -s '\n' ' '`;
 | |
|         $mac_list = `echo -e "$ip_info_list" | grep -A1 up | grep "MAC Address" | cut -d ' ' -f3 | tr -s '\n' ' '`;
 | |
|     } else {
 | |
|         $ip_list  = `echo -e "$ip_info_list" | grep -B1 up | grep "Nmap scan report" |cut -d ' ' -f5 | tr -s '\n' ' '`;
 | |
|         $mac_list = `echo -e "$ip_info_list" | grep -A1 up | grep "MAC Address" | cut -d ' ' -f3 | tr -s '\n' ' '`;
 | |
|     }
 | |
| 
 | |
|     my $live_ip  = split_comma_delim_str($ip_list);
 | |
|     my $live_mac = split_comma_delim_str($mac_list);
 | |
|     my %pipe_map;
 | |
|     if (scalar(@{$live_ip}) > 0) {
 | |
|         
 | |
|         xCAT::MsgUtils->trace(0, "I", "$log_label Scaned live IPs " . scalar(@{$live_ip}) . " with mac " . scalar(@{$live_mac}));
 | |
|         foreach (@{$live_ip}) {
 | |
|             my $new_mac = lc(shift @{$live_mac});
 | |
|             $new_mac =~ s/\://g;
 | |
|             $ipmac{$_} = $new_mac;
 | |
|         }
 | |
| 
 | |
|         my $nodelisttab;
 | |
|         if ($nodelisttab = xCAT::Table->new("nodelist")) {
 | |
|             my @nodes_in_list = $nodelisttab->getAllAttribs("node");
 | |
|             foreach my $node (@nodes_in_list) {
 | |
|                 $node_in_list{$node->{node}} = 1;
 | |
|             }
 | |
|         } else {
 | |
|             xCAT::MsgUtils->message("E", " Could not read the nodelist table\n");
 | |
|             return 1;
 | |
|         }
 | |
| 
 | |
|         ###############################
 | |
|         # Set the signal handler for ^c
 | |
|         ###############################
 | |
|         $SIG{TERM} = $SIG{INT} = sub {
 | |
|             foreach (keys %sp_children) {
 | |
|                 kill 2, $_;
 | |
|             }
 | |
|             $SIG{ALRM} = sub {
 | |
|                 while (wait() > 0) {
 | |
|                     yield;
 | |
|                 }
 | |
|                 exit @_;
 | |
|             };
 | |
|             alarm(1);    # wait 1s for grace exit
 | |
|         };
 | |
| 
 | |
|         ######################################################
 | |
|         # Set the singal handler for child process finished it's work
 | |
|         ######################################################
 | |
|         $SIG{CHLD} = sub {
 | |
|             my $cpid;
 | |
|             while (($cpid = waitpid(-1, WNOHANG)) > 0) {
 | |
|                 if ($sp_children{$cpid}) {
 | |
|                     delete $sp_children{$cpid};
 | |
|                     $children--;
 | |
|                     forward_data($callback, $pipe_map{$cpid});
 | |
|                     close($pipe_map{$cpid});
 | |
|                     delete $pipe_map{$cpid};
 | |
|                 }
 | |
|             }
 | |
|         };
 | |
|         buildup_mtms_hash();
 | |
|         for (my $i = 0 ; $i < scalar(@{$live_ip}) ; $i++) {
 | |
| 
 | |
|             # fork a sub process to handle the communication with service processor
 | |
|             $children++;
 | |
|             my $cfd;
 | |
| 
 | |
|             # the $parent_fd will be used by &send_rep() to send response from child process to parent process
 | |
|             socketpair($parent_fd, $cfd, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!";
 | |
|             $cfd->autoflush(1);
 | |
|             $parent_fd->autoflush(1);
 | |
|             my $child = xCAT::Utils->xfork;
 | |
|             if ($child == 0) {
 | |
|                 close($cfd);
 | |
|                 $callback = \&send_rep;
 | |
| 
 | |
|                 # Set child process default, if not the function runcmd may return error
 | |
|                 $SIG{CHLD} = 'DEFAULT';
 | |
| 
 | |
|                 my $bmcusername;
 | |
|                 my $bmcpassword;
 | |
|                 $bmcusername = "-U $bmc_user" if ($bmc_user);
 | |
|                 $bmcpassword = "-P $bmc_pass" if ($bmc_pass);
 | |
| 
 | |
|                 my @mc_cmds = ("/opt/xcat/bin/ipmitool-xcat -I lanplus -H ${$live_ip}[$i] -P $openbmc_pass mc info -N 1 -R 1",
 | |
|                               "/opt/xcat/bin/ipmitool-xcat -I lanplus -H ${$live_ip}[$i] $bmcusername $bmcpassword mc info -N 1 -R 1");
 | |
|                 my $mc_info;
 | |
|                 my $is_openbmc = 0;
 | |
|                 foreach my $mc_cmd (@mc_cmds) {
 | |
|                     $mc_info = xCAT::Utils->runcmd($mc_cmd, -1);
 | |
|                     if ($::RUNCMD_RC != 0) {
 | |
|                         next;
 | |
|                     }
 | |
|                     if ($mc_info =~ /Manufacturer ID\s*:\s*(\d+)\s*Manufacturer Name.+\s*Product ID\s*:\s*(\d+)/) {
 | |
|                         xCAT::MsgUtils->trace(0, "D", "$log_label Found ${$live_ip}[$i] Manufacturer ID: $1 Product ID: $2");
 | |
|                         if ($1 eq $::P9_WITHERSPOON_MFG_ID and $2 eq $::P9_WITHERSPOON_PRODUCT_ID) {
 | |
|                             bmcdiscovery_openbmc(${$live_ip}[$i], $opz, $opw, $request_command,$parent_fd);
 | |
|                             $is_openbmc = 1;
 | |
|                             last;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 unless ($is_openbmc) {
 | |
|                     bmcdiscovery_ipmi(${$live_ip}[$i], $opz, $opw, $request_command,$parent_fd);
 | |
|                 }
 | |
|                 close($parent_fd);
 | |
|                 exit 0;
 | |
|             } else {
 | |
| 
 | |
|                 # in the main process, record the created child process and add parent fd for the child process to an IO:Select object
 | |
|                 # the main process will check all the parent fd and receive response
 | |
|                 $sp_children{$child} = 1;
 | |
|                 close($parent_fd);
 | |
|                 $pipe_map{$child} = $cfd;
 | |
|             }
 | |
| 
 | |
|             while ($children >= 32) {
 | |
|                 sleep(1);
 | |
|             }
 | |
|         }
 | |
|         while($children > 0) {
 | |
|             sleep(1);
 | |
|         }
 | |
|         unless ($done_num) {
 | |
|             my %rsp;
 | |
|             $rsp{data} = ["No bmc found.\n"];
 | |
|             xCAT::MsgUtils->message("W", \%rsp, $::CALLBACK);
 | |
|         }
 | |
| 
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         my $rsp = {};
 | |
|         push @{ $rsp->{data} }, "No bmc found.\n";
 | |
|         xCAT::MsgUtils->message("W", $rsp, $::CALLBACK);
 | |
|         return 2;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3  format_stanza
 | |
|       list the stanza format for node
 | |
|     Arguments:
 | |
|       bmc ip
 | |
|     Returns:
 | |
|       lists as stanza format for nodes
 | |
| =cut
 | |
| 
 | |
| #--------------------------------------------------------------------------------
 | |
| sub format_stanza {
 | |
|     my $node = shift;
 | |
|     my $data = shift;
 | |
|     my $mgt_type = shift;
 | |
|     my ($bmcip, $bmcmtm, $bmcserial, $bmcuser, $bmcpass, $nodetype, $hwtype, $mac, $sn, $conserver) = split(/,/, $data);
 | |
|     my $result;
 | |
|     if (defined($bmcip)) {
 | |
|         $result .= "$node:\n\tobjtype=node\n";
 | |
|         $result .= "\tgroups=all\n";
 | |
|         $result .= "\tbmc=$bmcip\n";
 | |
|         $result .= "\tcons=$mgt_type\n";
 | |
|         $result .= "\tmgt=$mgt_type\n";
 | |
|         if ($bmcmtm) {
 | |
|             $result .= "\tmtm=$bmcmtm\n";
 | |
|         }
 | |
|         if ($bmcserial) {
 | |
|             $result .= "\tserial=$bmcserial\n";
 | |
|         }
 | |
|         if ($bmcuser) {
 | |
|             $result .= "\tbmcusername=$bmcuser\n";
 | |
|         }
 | |
|         if ($bmcpass) {
 | |
|             $result .= "\tbmcpassword=$bmcpass\n";
 | |
|         }
 | |
|         if ($sn) {
 | |
|             $result .="\tservicenode=$sn\n";
 | |
|         }
 | |
|         if ($conserver) {
 | |
|             $result .= "\tconserver=$conserver\n";
 | |
|         }
 | |
|         my $rsp = {};
 | |
|         push @{ $rsp->{data} }, "$result";
 | |
|         xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
 | |
|     }
 | |
|     return ($result);
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3  write_to_xcatdb
 | |
|       write node definition into xcatdb
 | |
|     Arguments:
 | |
|       $node_stanza:
 | |
|     Returns:
 | |
| =cut
 | |
| 
 | |
| #--------------------------------------------------------------------------------
 | |
| sub write_to_xcatdb {
 | |
|     my $node = shift;
 | |
|     my $data = shift;
 | |
|     my $mgt_type = shift;
 | |
|     my ($bmcip, $bmcmtm, $bmcserial, $bmcuser, $bmcpass, $nodetype, $hwtype, $mac, $sn, $conserver) = split(/,/, $data);
 | |
|     my $request_command = shift;
 | |
|     my $ret;
 | |
| 
 | |
|     $ret = xCAT::Utils->runxcmd({ command => ['chdef'],
 | |
|                                   arg => [ '-t', 'node', '-o', $node, "bmc=$bmcip", "cons=$mgt_type",
 | |
|                                            "mgt=$mgt_type", "mtm=$bmcmtm", "serial=$bmcserial",
 | |
|                                            "bmcusername=$bmcuser", "bmcpassword=$bmcpass", "nodetype=$nodetype",
 | |
|                                            "servicenode=$sn", "conserver=$conserver",
 | |
|                                            "hwtype=$hwtype", "groups=all" ] },
 | |
|                                   $request_command, -1, 1);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         my $rsp = {};
 | |
|         push @{ $rsp->{data} }, "Failed to run chdef command for node=$node\n";
 | |
|         xCAT::MsgUtils->message("E", $rsp, $::CALLBACK);
 | |
|         return 2;
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3 send_rep
 | |
| 
 | |
|     DESCRIPTION:
 | |
|         Send date from forked child process to parent process.
 | |
|         This subroutine will be replace the original $callback in the forked child process
 | |
| 
 | |
|     ARGUMENTS:
 | |
|         $resp - The response which generated in xCAT::Utils->message();
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| sub send_rep {
 | |
|     my $resp = shift;
 | |
|     unless ($resp) { return; }
 | |
|     store_fd($resp, $parent_fd);
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3 forward_data
 | |
| 
 | |
|     DESCRIPTION:
 | |
|         Receive data from forked child process and call the original $callback to forward data to xcat client
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| sub forward_data {
 | |
|     my $callback  = shift;
 | |
|     my $cfd       = shift;
 | |
|     my $responses;
 | |
| 
 | |
|     if (!($@ and $@ =~ /^Magic number checking on storable file/)) { #this most likely means we ran over the end of available input
 | |
|         $callback->($responses);
 | |
|     }
 | |
|     eval {
 | |
|         $responses = fd_retrieve($cfd);
 | |
|         if ($responses->{data}) {
 | |
|             $done_num += $responses->{data};
 | |
|         }
 | |
|     };
 | |
| }
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3  split_comma_delim_str
 | |
| 
 | |
|         Split comma-delimited list of strings into an array.
 | |
| 
 | |
|         Arguments: comma-delimited string
 | |
|         Returns:   Returns list of strings (ref)
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| sub split_comma_delim_str {
 | |
|     my $input_str = shift;
 | |
| 
 | |
|     my @result = split(/ /, $input_str);
 | |
|     return \@result;
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3  create_version_response
 | |
| 
 | |
|         Create a response containing the command name and version
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| sub create_version_response {
 | |
|     my $command = shift;
 | |
|     my $rsp;
 | |
|     my $version = xCAT::Utils->Version();
 | |
|     push @{ $rsp->{data} }, "$command - xCAT $version";
 | |
|     xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
 | |
| }
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3  create_error_response
 | |
| 
 | |
|         Create a response containing a single error message
 | |
|         Arguments:  error message
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| sub create_error_response {
 | |
|     my $error_msg = shift;
 | |
|     my $rsp;
 | |
|     push @{ $rsp->{data} }, $error_msg;
 | |
|     xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
 | |
| }
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3  bmcdiscovery
 | |
| 
 | |
|         Support for discovering bmc
 | |
|         Returns:
 | |
|                 0 - OK
 | |
|                 1 - help
 | |
|                 2 - error
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| sub bmcdiscovery {
 | |
| 
 | |
|     my $request         = shift;
 | |
|     my $callback        = shift;
 | |
|     my $request_command = shift;
 | |
| 
 | |
|     my $rc = 0;
 | |
| 
 | |
|     ##############################################################
 | |
|     # process the command line
 | |
|     # 0=success, 1=version, 2=error for check_auth_, other=error
 | |
|     ##############################################################
 | |
|     $rc = bmcdiscovery_processargs($request, $callback, $request_command);
 | |
|     if ($rc != 0) {
 | |
|         if ($rc != 1)
 | |
|         {
 | |
|             if ($rc != 2)
 | |
|             {
 | |
|                 bmcdiscovery_usage(@_);
 | |
|             }
 | |
|         }
 | |
|         return ($rc - 1);
 | |
|     }
 | |
| 
 | |
|     #scan_process($::opt_M,$::opt_R);
 | |
| 
 | |
|     return $rc;
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3  get bmc account in passwd table
 | |
|         Returns:
 | |
|              username/password pair
 | |
|         Notes:
 | |
|              The default username/password is ADMIN/admin
 | |
| =cut
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| sub bmcaccount_from_passwd {
 | |
|     my $bmcusername = "ADMIN";
 | |
|     my $bmcpassword = "admin";
 | |
|     my $openbmcusername = "root";
 | |
|     my $openbmcpassword = "0penBmc";
 | |
|     my $passwdtab   = xCAT::Table->new("passwd", -create => 0);
 | |
|     if ($passwdtab) {
 | |
|         my $bmcentry = $passwdtab->getAttribs({ 'key' => 'ipmi' }, 'username', 'password');
 | |
|         if (defined($bmcentry)) {
 | |
|             $bmcusername = $bmcentry->{'username'};
 | |
|             $bmcpassword = $bmcentry->{'password'};
 | |
| 
 | |
|             # if username or password is undef or empty in passwd table, bmcusername or bmcpassword is empty
 | |
|             unless ($bmcusername) {
 | |
|                 $bmcusername = '';
 | |
|             }
 | |
|             unless ($bmcpassword) {
 | |
|                 $bmcpassword = '';
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         my $openbmcentry = $passwdtab->getAttribs({ 'key' => 'openbmc' }, 'username', 'password');
 | |
|         if (defined($openbmcentry)) {
 | |
|             $openbmcusername = $openbmcentry->{'username'};
 | |
|             $openbmcpassword = $openbmcentry->{'password'};
 | |
|             # if username or password is undef or empty in passwd table, openbmcusername or openbmcpassword is empty
 | |
|             unless ($openbmcusername) {
 | |
|                 $openbmcusername = '';
 | |
|             }
 | |
|             unless ($openbmcpassword) {
 | |
|                 $openbmcpassword = '';
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return ($bmcusername, $bmcpassword, $openbmcusername, $openbmcpassword);
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------------------
 | |
| 
 | |
| =head3  bmcdiscovery_ipmi
 | |
| 
 | |
|         Support for discovering bmc using ipmi
 | |
|         Returns:
 | |
|               if it is bmc, it returns bmc ip or host;
 | |
|               if it is not bmc, it returns nothing;
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| 
 | |
| sub bmcdiscovery_ipmi {
 | |
|     my $ip              = shift;
 | |
|     my $opz             = shift;
 | |
|     my $opw             = shift;
 | |
|     my $request_command = shift;
 | |
|     my $fd              = shift;
 | |
|     my $bmcstr          = "BMC Session ID";
 | |
|     my $bmcusername     = '';
 | |
|     my $bmcpassword     = '';
 | |
|     if ($bmc_user) {
 | |
|         $bmcusername = "-U $bmc_user";
 | |
|     }
 | |
|     if ($bmc_pass) {
 | |
|         $bmcpassword = "-P $bmc_pass";
 | |
|     }
 | |
| 
 | |
|     my $log_info = "$ip: Detected ipmi, attempting to obtain system information...";
 | |
|     xCAT::MsgUtils->trace(0, "D", "$log_label $log_info");
 | |
| 
 | |
|     my $mtms_node = "";
 | |
|     my $mac_node = "";
 | |
| 
 | |
|     my $node_data = $ip;
 | |
|     my $icmd = "/opt/xcat/bin/ipmitool-xcat -vv -I lanplus $bmcusername $bmcpassword -H $ip chassis status -R 1";
 | |
|     my $output = xCAT::Utils->runcmd("$icmd", -1);
 | |
|     if ($output =~ $bmcstr) {
 | |
|         store_fd({data=>1}, $fd);
 | |
| 
 | |
|         if ($output =~ /RAKP \d+ message indicates an error : (.+)\nError: (.+)/) {
 | |
|             xCAT::MsgUtils->message("W", { data => ["$2: $1 for $ip"] }, $::CALLBACK);
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         # The output contains System Power indicated the username/password is correct, then try to get MTMS
 | |
|         if ($output =~ /System Power\s*:\s*\S*/) {
 | |
|             my $mtm    = '';
 | |
|             my $serial = '';
 | |
| 
 | |
|             # For system X and Tuleta, the fru 0 will contain the MTMS; For firestone, fru 3; For habanero, fru 2
 | |
|             my @fru_num = (0, 2, 3);
 | |
|             foreach my $fru_cmd_num (@fru_num) {
 | |
|                 my $fru_cmd = "$::XCATROOT/bin/ipmitool-xcat -I lanplus $bmcusername $bmcpassword " .
 | |
|                   "\-H $ip fru print $fru_cmd_num";
 | |
|                 my @fru_output_array = xCAT::Utils->runcmd($fru_cmd, -1);
 | |
|                 if (($::RUNCMD_RC eq 0) && @fru_output_array) {
 | |
|                     my $fru_output = join(" ", @fru_output_array);
 | |
| 
 | |
|                     if (($fru_output =~ /Chassis Part Number\s*:\s*(\S*).*Chassis Serial\s*:\s*(\S*)/)) {
 | |
|                         $mtm    = $1;
 | |
|                         $serial = $2;
 | |
|                         last;
 | |
|                     }
 | |
| 
 | |
|                     if (($fru_output =~ /Product Part Number   :\s*(\S*).*Product Serial        :\s*(\S*)/)) {
 | |
|                         $mtm    = $1;
 | |
|                         $serial = $2;
 | |
|                         last;
 | |
|                     }
 | |
| 
 | |
|                     if (($fru_output =~ /Product Manufacturer\s+:\s+(.*?)\s+P.*?roduct Name\s+:\s+(.*?)\s+P.*?roduct Serial\s+:\s+(\S+)/)) {
 | |
|                         $mtm    = $1.":".$2;
 | |
|                         $serial = $3;
 | |
|                         last;
 | |
|                     }
 | |
| 
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             $mtm = '' if ($mtm =~ /^0+$/);
 | |
|             $serial = '' if ($serial =~ /^0+$/);
 | |
| 
 | |
|             unless (($mtm or $serial) or $ipmac{$ip}) {
 | |
|                 xCAT::MsgUtils->message("W", { data => ["BMC Type/Model and/or Serial and MAC Address is unavailable for $ip"] }, $::CALLBACK);
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             $node_data .= ",$mtm";
 | |
|             $node_data .= ",$serial";
 | |
|             if ($::opt_P) {
 | |
|                 if ($::opt_U) {
 | |
|                     $node_data .= ",$::opt_U,$::opt_P";
 | |
|                 } else {
 | |
|                     $node_data .= ",,$::opt_P";
 | |
|                 }
 | |
|             } else {
 | |
|                 $node_data .= ",,";
 | |
|             }
 | |
|             $node_data .= ",mp,bmc";
 | |
|             if ($mtm and $serial) {
 | |
|                 my $mtmsip = lc($mtm)."*".lc($serial)."-".$ip;
 | |
|                 if (exists($::VPDHASH{$mtmsip})) {
 | |
|                     my $pre_node = $::VPDHASH{$mtmsip};
 | |
|                     xCAT::MsgUtils->message("I", { data => ["Found match node $pre_node with bmc ip address: $ip, rsetboot/rpower $pre_node to continue hardware discovery."] }, $::CALLBACK);
 | |
|                     if ($opz) {
 | |
|                         $node_data .= ",";
 | |
|                         display_output($opz,undef,$pre_node,$mac_node,$node_data,"ipmi",$request_command);
 | |
|                     }
 | |
|                     return;
 | |
|                 }
 | |
|                 $mtms_node = "node-$mtm-$serial";
 | |
|                 $mtms_node =~ s/(.*)/\L$1/g;
 | |
|                 $mtms_node =~ s/[\s:\._]/-/g;
 | |
|                 $node_data .= ",";
 | |
|             } elsif ($ipmac{$ip}) {
 | |
|                 $mac_node = "node-$ipmac{$ip}";
 | |
|                 $node_data .= ",$ipmac{$ip}";
 | |
|             }
 | |
|             $node_data .= ",$::opt_SN,$::opt_SN";
 | |
|         } elsif ($output =~ /error : unauthorized name/) {
 | |
|             xCAT::MsgUtils->message("W", { data => ["BMC username is incorrect for $ip"] }, $::CALLBACK);
 | |
|             return;
 | |
|         } elsif ($output =~ /RAKP \S* \S* is invalid/) {
 | |
|             xCAT::MsgUtils->message("W", { data => ["BMC password is incorrect for $ip"] }, $::CALLBACK);
 | |
|             return;
 | |
|         } else {
 | |
|             xCAT::MsgUtils->message("W", { data => ["Unknown error get from $ip"] }, $::CALLBACK);
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         display_output($opz,$opw,$mtms_node,$mac_node,$node_data,"ipmi",$request_command);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| 
 | |
| =head3  bmcdiscovery_openbmc
 | |
| 
 | |
|         Support for discovering bmc using openbmc
 | |
|         Returns:
 | |
|               if it is openbmc, it returns bmc ip or host;
 | |
|               if it is not openbmc, it returns nothing;
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| sub bmcdiscovery_openbmc{
 | |
|     my $ip              = shift;
 | |
|     my $opz             = shift;
 | |
|     my $opw             = shift;
 | |
|     my $request_command = shift;
 | |
|     my $fd              = shift;
 | |
|     my $mtms_node       = "";
 | |
|     my $mac_node        = "";
 | |
| 
 | |
|     store_fd({data=>1}, $fd);
 | |
|     my $log_info = "$ip: Detected openbmc, attempting to obtain system information...";
 | |
|     print "$log_info\n";
 | |
|     xCAT::MsgUtils->trace(0, "D", "$log_label $log_info");
 | |
| 
 | |
|     my $http_protocol="https";
 | |
|     my $openbmc_project_url = "xyz/openbmc_project";
 | |
|     my $login_endpoint = "login";
 | |
|     my $system_endpoint = "inventory/system";
 | |
|     my $motherboard_boxelder_endpoint = "$system_endpoint/chassis/motherboard/boxelder/bmc";
 | |
| 
 | |
|     my $node_data = $ip;
 | |
|     my $brower = LWP::UserAgent->new( ssl_opts => { SSL_verify_mode => 0x00, verify_hostname => 0  }, );
 | |
|     my $cookie_jar = HTTP::Cookies->new();
 | |
|     my $header = HTTP::Headers->new('Content-Type' => 'application/json');
 | |
|     my $data = '{"data": [ "' . $openbmc_user .'", "' . $openbmc_pass . '" ] }';
 | |
|     $brower->cookie_jar($cookie_jar);
 | |
| 
 | |
|     my $url = "$http_protocol://$ip/$login_endpoint";
 | |
|     my $login_request = HTTP::Request->new( 'POST', $url, $header, $data );
 | |
|     my $login_response = $brower->request($login_request);
 | |
| 
 | |
|     if ($login_response->is_success) {
 | |
|         # attempt to find the system serial/model
 | |
|         $url = "$http_protocol://$ip/$openbmc_project_url/$system_endpoint";
 | |
|         my $req = HTTP::Request->new('GET', $url, $header);
 | |
|         my $req_output = $brower->request($req);
 | |
|         if ($req_output->is_error) {
 | |
|             # If the host system has not yet been powered on, check the boxelder bmc info for model/serial
 | |
|             $url = "$http_protocol://$ip/$openbmc_project_url/$motherboard_boxelder_endpoint";
 | |
|             $req = HTTP::Request->new('GET', $url, $header);
 | |
|             $req_output = $brower->request($req);
 | |
|             if ($req_output->is_error) {
 | |
|                 xCAT::MsgUtils->message("W", { data => ["$ip: Could not obtain system information from BMC."] }, $::CALLBACK);
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|         my $response = decode_json $req_output->content;
 | |
|         my $mtm;
 | |
|         my $serial;
 | |
| 
 | |
|         if (defined($response->{data})) {
 | |
|             if (defined($response->{data}->{Model}) and defined($response->{data}->{SerialNumber})) {
 | |
|                 $mtm = $response->{data}->{Model};
 | |
|                 $serial = $response->{data}->{SerialNumber};
 | |
|             }
 | |
| 
 | |
|         } else {
 | |
|             xCAT::MsgUtils->message("E", { data => ["Unable to connect to REST server at $ip"] }, $::CALLBACK);
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         # delete space before and after
 | |
|         $mtm =~ s/^\s+|\s+$|\.+//g;
 | |
|         $serial =~ s/^\s+|\s+$|\.+//g;
 | |
| 
 | |
|         $mtm = '' if ($mtm =~ /^0+$/);
 | |
|         $serial = '' if ($serial =~ /^0+$/);
 | |
| 
 | |
|         unless (($mtm or $serial) or $ipmac{$ip}) {
 | |
|             xCAT::MsgUtils->message("W", { data => ["Could not obtain Valid Model Type and/or Serial Number and MAC Address for BMC at $ip"] }, $::CALLBACK);
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         # format info string for format_stanza function
 | |
|         $node_data .= ",$mtm";
 | |
|         $node_data .= ",$serial";
 | |
|         if ($::opt_P) {
 | |
|             if ($::opt_U) {
 | |
|                 $node_data .= ",$::opt_U,$::opt_P";
 | |
|             } else {
 | |
|                 $node_data .= ",,$::opt_P";
 | |
|             }
 | |
|         } else {
 | |
|             $node_data .= ",,";
 | |
|         }
 | |
|         $node_data .= ",mp,bmc";
 | |
|         if ($mtm and $serial) {
 | |
|             my $mtmsip = lc($mtm)."*".lc($serial)."-".$ip;
 | |
|             if (exists($::VPDHASH{$mtmsip})) {
 | |
|                 my $pre_node = $::VPDHASH{$mtmsip};
 | |
|                 xCAT::MsgUtils->message("I", { data => ["Found match node $pre_node with bmc ip address: $ip, rsetboot/rpower $pre_node to continue hardware discovery."] }, $::CALLBACK);
 | |
|                 if ($opz) {
 | |
|                     $node_data .= ",";
 | |
|                     display_output($opz,undef,$pre_node,$mac_node,$node_data,"openbmc",$request_command);
 | |
|                 }
 | |
|                 return;
 | |
|             }
 | |
|             $mtms_node = "node-$mtm-$serial";
 | |
|             $mtms_node =~ s/(.*)/\L$1/g;
 | |
|             $mtms_node =~ s/[\s:\._]/-/g;
 | |
|             $node_data .= ",";
 | |
|         } elsif ($ipmac{$ip}) {
 | |
|             $mac_node = "node-$ipmac{$ip}";
 | |
|             $node_data .= ",$ipmac{$ip}";
 | |
|         }
 | |
|         $node_data .= ",$::opt_SN,$::opt_SN";
 | |
|     } else {
 | |
|         my $login_status;
 | |
|         eval { $login_status = $login_response->status_line };
 | |
|         if ($@) {
 | |
|             xCAT::MsgUtils->message("W", { data => ["Login failed for $ip and no status received from response"] }, $::CALLBACK);
 | |
|             return;
 | |
|         }
 | |
|         if ($login_status =~ /401 Unauthorized/) {
 | |
|             xCAT::MsgUtils->message("W", { data => ["Invalid username or password for $ip"] }, $::CALLBACK);
 | |
|         } else {
 | |
|             xCAT::MsgUtils->message("W", { data => ["Received response " . $login_response->status_line . " for $ip"] }, $::CALLBACK);
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     display_output($opz,$opw,$mtms_node,$mac_node,$node_data,"openbmc",$request_command);
 | |
| }
 | |
| 
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| 
 | |
| =head3  display_output
 | |
| 
 | |
|         Common code to print output of bmcdiscover
 | |
| 
 | |
| =cut
 | |
| 
 | |
| #-----------------------------------------------------------------------------
 | |
| sub display_output {
 | |
|     my $opz             = shift;
 | |
|     my $opw             = shift;
 | |
|     my $mtms_node       = shift;
 | |
|     my $mac_node        = shift;
 | |
|     my $node_data       = shift;
 | |
|     my $mgttype         = shift;
 | |
|     my $request_command = shift;
 | |
| 
 | |
|     my $node;
 | |
|     if (($node_in_list{$mac_node} and !$node_in_list{$mtms_node}) or (!$node_in_list{$mac_node} and !$mtms_node)) {
 | |
|         $node = $mac_node;
 | |
|     } else {
 | |
|         $node = $mtms_node;
 | |
|     }
 | |
| 
 | |
|     if (defined($opw)) {
 | |
|         my $rsp = {};
 | |
|         push @{ $rsp->{data} }, "Writing $node ($node_data) to database...";
 | |
|         xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
 | |
|         if (defined($opz)) {
 | |
|             format_stanza($node, $node_data, $mgttype);
 | |
|         }
 | |
|         write_to_xcatdb($node, $node_data, $mgttype, $request_command);
 | |
|     }
 | |
|     elsif (defined($opz)) {
 | |
|         format_stanza($node, $node_data, $mgttype);
 | |
|     } else {
 | |
|         my $rsp = {};
 | |
|         push @{ $rsp->{data} }, "$node_data";
 | |
|         xCAT::MsgUtils->message("I", $rsp, $::CALLBACK);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 1;
 |