# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html package xCAT::PPCrflash; use strict; use Getopt::Long; use xCAT::PPCcli qw(SUCCESS EXPECT_ERROR RC_ERROR NR_ERROR); use xCAT::Usage; use xCAT::PPCinv; use xCAT::DSHCLI; use xCAT::Table; use Getopt::Long; use File::Spec; use POSIX qw(tmpnam); my $packages_dir= (); my $activate = (); my $verbose = 0; $::POWER_DEST_DIR = "/tmp"; my $release_level; my $active_level; my @dirlist; ####################################### # This flag tracks the operation to be performed. If set, it means we need # to commit a previously applied update or else recover from one. ####################################### my $housekeeping = undef; ##################################### #For -V|--verbose,put the $msg into @value ################################### sub dpush { my $value = shift; my $msg = shift; if($verbose == 1) { push(@$value,$msg); } } ########################################################################## # Parse the command line for options and operands ########################################################################## sub parse_args { my $request = shift; my %opt = (); my $cmd = $request->{command}; my $args = $request->{arg}; ############################################# # Responds with usage statement ############################################# local *usage = sub { my $usage_string = xCAT::Usage->getUsage($cmd); return( [ $_[0], $usage_string] ); }; ############################################# # Process command-line arguments ############################################# if ( !defined( $args )) { return(usage( "No arguments specified" )); } ############################################# # Checks case in GetOptions, allows opts # to be grouped (e.g. -vx), and terminates # at the first unrecognized option. ############################################# @ARGV = @$args; $Getopt::Long::ignorecase = 0; Getopt::Long::Configure( "bundling" ); if ( !GetOptions( \%opt, qw(h|help v|version V|verbose p=s activate=s commit recover) )) { return( usage() ); } #################################### # Option -v for version #################################### if ( exists( $opt{v} )) { return( \$::VERSION ); } if ( exists( $opt{h}) || $opt{help}) { return( usage()); } ################################# #Option --activate not valid with --commit or --recover ################################# if( exists( $opt{activate} ) && (exists( $opt{commit}) || exists( $opt{recover}))) { return( usage("Option --activate not valid with --commit or --recover ") ); } ################################# #Option -p not valid with --commit or --recover ################################# if( exists( $opt{p} ) && (exists( $opt{commit}) || exists( $opt{recover} ))) { return( usage("Option -p not valid with --commit or --recover ") ); } ################################# #Option -p required ################################# if( exists( $opt{p} ) && (!exists( $opt{activate}) )) { return( usage("Option -p must be used with --activate ") ); } ############################### #--activate's value only can be concurrent and disruptive ################################ if(exists($opt{activate})) { if( ($opt{activate} ne "concurrent") && ($opt{activate} ne "disruptive")) { return (usage("--activate's value can only be concurrent or disruptive")); } } #################################### # Check for "-" with no option #################################### if ( grep(/^-$/, @ARGV )) { return(usage( "Missing option: -" )); } #################################### # Check for an extra argument #################################### if ( defined( $ARGV[0] )) { return(usage( "Invalid Argument: $ARGV[0]" )); } #check to see if we are housekeeping or updating # if( defined( $opt{commit}) ) { print "commit flag\n"; $housekeeping = "commit"; } elsif( defined( $opt{ recover }) ) { print "recover flag\n"; $housekeeping = "recover"; } else { print "no housekeeping - update mode\n"; $housekeeping = undef; } ############################################# # Option -V for verbose output ############################################# if ( exists( $opt{V} )) { $verbose = 1; } #################### #suport for "rflash", copy the rpm and xml packages from user-spcefied-directory to /install/packages_fw ##################### if ( (!exists($opt{commit})) && (!exists($opt{ recover }))) { if( preprocess_for_rflash($request, \%opt) == -1) { return( usage() ); } } #################################### # No operands - add command name #################################### $request->{method} = $cmd; return( \%opt ); } ########################################################################## # Invokes the callback with the specified message ########################################################################## sub send_msg { my $request = shift; my $ecode = shift; my %output; ################################################# # Called from child process - send to parent ################################################# if ( exists( $request->{pipe} )) { my $out = $request->{pipe}; $output{errorcode} = $ecode; $output{data} = \@_; print $out freeze( [\%output] ); print $out "\nENDOFFREEZE6sK4ci\n"; } ################################################# # Called from parent - invoke callback directly ################################################# elsif ( exists( $request->{callback} )) { my $callback = $request->{callback}; $output{errorcode} = $ecode; $output{data} = \@_; $callback->( \%output ); } } sub preprocess_for_rflash { my $request = shift; my $opt = shift; my $packages_fw = "/install/packages_fw"; my $c = 0; my $packages_d; # foreach (@$exargs) { # $c++; # if($_ eq "-p") { # $packages_d = $$exargs[$c]; # last; # } # } $packages_d = $$opt{p}; if($packages_d ne $packages_fw ) { $$opt{p} = $packages_fw; if(! -d $packages_d) { send_msg($request, 1, "The directory $packages_d doesn't exist!"); $request = (); return -1; } #print "opening directory and reading names\n"; opendir DIRHANDLE, $packages_d; my @dirlist= readdir DIRHANDLE; closedir DIRHANDLE; @dirlist = File::Spec->no_upwards( @dirlist ); # Make sure we have some files to process # if( !scalar( @dirlist ) ) { send_msg($request, 1, "The directory $packages_d is empty !"); $request = (); return -1; } #Find the rpm lic file my @rpmlist = grep /\.rpm$/, @dirlist; my @xmllist = grep /\.xml$/, @dirlist; if( @rpmlist == 0 | @xmllist == 0) { send_msg($request, 1, "There isn't any rpm and xml files in the directory $packages_d!"); $request = (); return -1; } my $rpm_list = join(" ", @rpmlist); my $xml_list = join(" ", @xmllist); my $cmd; if( -d $packages_fw) { $cmd = "rm -rf $packages_fw"; xCAT::Utils->runcmd($cmd, 0); if ($::RUNCMD_RC != 0) { send_msg($request, 1, "Failed to remove the old packages in $packages_fw."); $request = (); return -1; } } $cmd = "mkdir $packages_fw"; xCAT::Utils->runcmd("$cmd", 0); if ($::RUNCMD_RC != 0) { send_msg($request, 1, "$cmd failed."); $request = (); return; } $cmd = "cp $packages_d/*.rpm $packages_d/*.xml $packages_fw"; xCAT::Utils->runcmd($cmd, 0); if ($::RUNCMD_RC != 0) { send_msg($request, 1, "$cmd failed."); $request = (); return -1; } # $req->{arg} = $exargs; } return 0; } sub print_var { my $j = shift; my $msg = shift; my $var = "+++++++++$msg start--++++\n"; if(ref($j) eq "ARRAY") { my $t; foreach $t(@$j) { if(ref($t) eq "ARRAY") { my $t0 = join(" ", @$t); # if(ref($t0) eq "SCALAR") { $var = $var."\t$t0(array)\n"; # } else { # &print_var($t0); # } } elsif( ref($t) eq "HASH" ) { my $t12; my $t23; while(($t12, $t23) = each(%$t)) { $var = $var. "\t$t12 => \n"; # if(ref($t23) eq "SCALAR") { $var = $var. "\t$t23(hash)\n"; # } else { # &print_var($t23); # } } }else { $var = $var. "$t\n"; } } } elsif (ref($j) eq "HASH") { my $t1; my $t2; while(($t1, $t2) =each (%$j)) { $var = $var. "$t1 =>"; if(ref($t2) eq "HASH") { my $t12; my $t23; while(($t12, $t23) = each(%$t2)) { $var = $var. "\t$t12 => $t23\n"; } } elsif(ref($t2) eq "ARRAY") { my $t = join(" ", @$t2); $var = $var. "$t (array)\n"; } else { $var = $var. "$t2\n"; } } } else { $var = $var. "$j(scalar)\n"; } $var = $var. "+++++++++++$msg end+++++++++++\n"; return $var; } #-------------------------------------------------------------------------# # get_lic_filenames - construct and validate the lup filenames for each # # each node # #-------------------------------------------------------------------------# # sub get_lic_filenames { my $mtms = shift; my $upgrade_required = 0; my $msg = undef; my $filename; if(! -d $packages_dir) { $msg = "The directory $packages_dir doesn't exist!"; return ("","","", $msg, -1); } #print "opening directory and reading names\n"; opendir DIRHANDLE, $packages_dir; @dirlist= readdir DIRHANDLE; closedir DIRHANDLE; @dirlist = File::Spec->no_upwards( @dirlist ); # Make sure we have some files to process # if( !scalar( @dirlist ) ) { $msg = "directory $packages_dir is empty"; return ("","","",$msg, -1); } $release_level =~/(\w{4})(\d{3})/; my $pns = $1; my $fff = $2; #Find the latest version lic file @dirlist = grep /\.rpm$/, @dirlist; @dirlist = grep /$1/, @dirlist; if( !scalar( @dirlist ) ) { $msg = "There isn't a package suitable for $mtms"; return ("","","",$msg, -1); } if( scalar(@dirlist) > 1) { # Need to find the latest version package. @dirlist =reverse sort(@dirlist); my $t = "\n"; foreach $t(@dirlist) { $msg =$msg."$t\t"; } } $filename = File::Spec->catfile( $packages_dir, $dirlist[0] ); $dirlist[0] =~ /(\w{4})(\d{3})_(\w{3})_(\d{3}).rpm$/; ############## #If the release levels are different, it will be upgrade_required. ############# if($fff ne $2) { $upgrade_required = 1; } else { if(($pns eq $1) && ($4 <= $active_level)) { $msg = $msg. "Upgrade $mtms $activate!"; # if($activate ne "concurrent") { # $msg = "Option --actviate's value should be disruptive"; # return ("", "","", $msg, -1); # } } else { $msg = $msg . "Upgrade $mtms disruptively!"; if($activate ne "disruptive") { $msg = "Option --activate's value shouldn't be concurrent, and it must be disruptive"; return ("", "","", $msg, -1); } } } #print "filename is $filename\n"; my $xml_file_name = $filename; $xml_file_name =~ s/(.+\.)rpm/\1xml/; #print "check_licdd_update: source xml file is $xml_file_name\n"; if( ( -z $filename)|| ( -z $xml_file_name) ) { $msg = "The package $filename or xml $xml_file_name is empty" ; return ("", "", "", $msg, -1); } return ($filename, $xml_file_name ,$upgrade_required, $msg, 0); } sub get_one_mtms { my $exp = shift; my $bpa = shift; my $cmd = "lssyscfg -r cage -e $bpa"; my $mtms; my $msg; my $values = xCAT::PPCcli::send_cmd( $exp, $cmd ); my $Rc = shift(@$values); ##################################### # Return error ##################################### if ( $Rc != SUCCESS ) { $msg = "ERROR: Failed to find a CEC managed by $bpa on the HMC"; return ("", $msg); } foreach (@$values) { if( $_ =~ /cage_num=(\w*),contents=sys,type_model_serial_num=(\w+)-(\w+)\*(\w+),loc_code=(\w+).(\w+).(\w+)/) { $mtms = "$2-$3*$4"; last; } } # print "the managed system is $mtms!\n"; return ($mtms, $msg); } ########################## #Performs Licensed Internal Code (LIC) update support for HMC-attached POWER5 and POWER6 Systems ########################### sub rflash { my $request = shift; my $hash = shift; my $exp = shift; my $hwtype = @$exp[2]; my @result; my $timeout = $request->{ppctimeout}; $packages_dir = $request->{opt}->{p}; $activate = $request->{opt}->{activate}; my $hmc; my $mtms; my $component; # system or power my $h; my $user; my $tmp_file; #the file handle of the stanza my $rpm_file; my $xml_file; my @rpm_files; my @xml_files; my $upgrade_required; my $stanza = undef; my $mtms_t; my @value; $hmc = @$exp[3]; dpush(\@value, [$hmc, "In rflash()"]); dpush(\@value,[$hmc, print_var($request, "request")]); dpush(\@value,[$hmc, print_var($hash, "hash")]); dpush(\@value,[$hmc, print_var($exp, "exp")]); # print_var($t); ######################## # Now build a temporary file containing the stanzas to be run on the HMC ################### my $tmp_file = tmpnam();# the file handle of the stanza ############################## # Open the temp file ########################## dpush(\@value,[$hmc, "opening file $tmp_file"]); unless( open TMP, ">$tmp_file" ) { push (@value,[ $hmc, "cannot open $tmp_file, $!\n"]); return (\@value); } while(($mtms,$h) = each(%$hash)) { dpush(\@value,[$hmc, "mtms:$mtms"]); $mtms_t = "$mtms_t $mtms"; my $lflag = 0; my $managed_system = $mtms; if( defined( $housekeeping ) ) { #$hmc_has_work = 1; #$::work_flag = 1; &dpush(\@value,[$hmc,"$mtms:creating stanza for housekeeping operation\n"]); $stanza = "updlic::" . $managed_system . "::" . $housekeeping . "::::"; &dpush(\@value,[$hmc, "$mtms:Writing $stanza to file\n"]); push(@result,[$hmc,"$mtms:$housekeeping successfully!"]); print TMP "$stanza\n"; } else { while(my ($name, $d) = each(%$h)) { if ( @$d[4] !~ /^(fsp|bpa|lpar)$/ ) { push @value, [$name,"Information only available for LPAR/CEC/BPA",RC_ERROR]; next; } ############### #If $name is a Lpar, the flag will be changed from "lpar" to "fsp" ####################### if ( @$d[4] =~ /^lpar$/ ) { @$d[4] = "fsp"; $lflag = 1; push (@value, [$hmc,"$name is a Lpar on MTMS $mtms", 1]); } if( @$d[4] eq "fsp" ) { $component = "system"; } else { $component = "power"; } dpush(\@value, [$hmc,"$mtms:component:$component!"]); my $values = xCAT::PPCcli::lslic( $exp, $d, $timeout ); my $Rc = shift(@$values); ##################################### # Return error ##################################### if ( $Rc != SUCCESS ) { push @value, [$name,@$values[0],$Rc]; next; } if ( @$values[0] =~ /ecnumber=(\w+)/ ) { $release_level = $1; &dpush( \@value, [$hmc,"$mtms :release level:$1"]); } if ( @$values[0] =~ /activated_level=(\w+)/ ) { $active_level = $1; &dpush( \@value, [$hmc,"$mtms :activated level:$1"]); } my $msg; my $flag = 0; ($rpm_file, $xml_file, $upgrade_required,$msg, $flag) = &get_lic_filenames($mtms); if( $flag == -1) { push (@value, [$hmc,"$mtms: $msg"]); push (@value, [$hmc,"Failed to upgrade the firmware of $mtms on $hmc"]); return (\@value); } dpush ( \@value, [$hmc, $msg]); # If we get to this point, the HMC has to attempt an update on the # managed system, so set the flag. # #$hmc_has_work = 1; #::work_flag = 1; # Collect the rpm and xml file names in a list so we can dcp then # in one call. # if( scalar( grep /$rpm_file/, @rpm_files ) == 0 ) { push @rpm_files, $rpm_file; push @xml_files, $xml_file; } my ($volume,$directories,$file) = File::Spec->splitpath($rpm_file); push(@result,[$hmc, "Upgrade $mtms from release level:$release_level activated level:$active_level to $file successfully"]); #If mtms is a bpa, we should change the managed_system to a cec whose parent is a bpa. if($component eq "power") { ($managed_system, $msg)= &get_one_mtms($exp, $managed_system); if($managed_system eq "") { push(@value, [$hmc, $msg]); return (\@value); } dpush(\@value,[$hmc, $msg]); } } my $rpm_dest = $::POWER_DEST_DIR."/".$dirlist[0]; # The contents of the stanza file are slightly different depending # on the operation being performed. # # $managed_system = "9125-F2A*0262652"; if( $upgrade_required ) { $stanza = "updlic::" . $managed_system . "::upgrade::::$rpm_dest"; } else { $stanza = "updlic::" . $managed_system . "::activate::" . $component . "::" .$rpm_dest; } dpush(\@value,[$hmc, "Writing $stanza to file"]); print TMP "$stanza\n"; @dirlist = (); $rpm_file = (); $xml_file = (); } } # Close the file. dcp the stanza file, rpm update and xml file to the # target HMC # close TMP; ################################## # Get userid/password ################################## my $cred = $request->{$hmc}{cred}; $user = @$cred[0]; dpush(\@value, [$hmc,"user: $user"]);; # $password = @$cred[1] my $rpm_file_list = join(" ", @rpm_files); my $xml_file_list = join(" ", @xml_files); ############################### #Prepare for "xdcp"-----runDcp_api ############################## my %options = (); $options{ 'user' } = $user; $options{ 'nodes' } = $hmc; $options{ 'exit-status' } = 1; $options{ 'preserve' } = 1; $options{ 'source' } = "$tmp_file $rpm_file_list $xml_file_list"; $options{ 'target' } = "/tmp"; my @res = xCAT::DSHCLI->runDcp_api( \%options, 0); my $Rc = shift(@res); if ($::RUNCMD_RC || $Rc =~ /denied/) { # error from dcp my $rsp={}; dpush(\@value, [$hmc,"invoking RunDcp_api()"]); $rsp->{data}->[0] = "Error from dsh. Return Code = $::RUNCMD_RC"; xCAT::MsgUtils->message("E", $rsp, $::CALLBACK, 1); push(@value,[$hmc,$rsp->{data}->[0]]); push(@value,[$hmc,"Failed to copy $tmp_file $rpm_file_list $xml_file_list to $hmc"]); #push(@value,[$hmc,"Please check whether the HMC $hmc is configured to allow remote ssh connections"]); push (@value, [$hmc,"Failed to upgrade the firmware of $mtms_t on $hmc"]); return(\@value); } my $r = (); foreach $r (@res){ push(@value, [$r]); } # print_var(@res); %options = (); push(@value,[$hmc, "copy files to $hmc complete"]); #`$::XDSH $hmc -K`; ############################################### # Now that all the stanzas files have been built and copied to the HMCs, # we can use a single dsh command to invoke them all. ################################################ $options{ 'user' } = $user; $options{ 'nodes' } = $hmc; $options{ 'exit-status' } = 1; $options{ 'stream' } = 1; $options{ 'preserve' } = 1; $options{ 'command' } = "csmlicutil $tmp_file"; # $options{ 'command' } = "ls -al"; @res = xCAT::DSHCLI->runDsh_api(\%options, 0); if ($::RUNCMD_RC ) { # error from dsh my $rsp={}; $rsp->{data}->[0] = "Error from dsh. Return Code = $::RUNCMD_RC"; xCAT::MsgUtils->message("S", $rsp, $::CALLBACK, 1); dpush(\@value,[$hmc,"failed to run xCAT::DSHCLI->runDsh_api()"]); push(@value,[$hmc,$rsp->{data}->[0]]); #push(@value,[$hmc,"Please check whether the HMC $hmc is configured to allow remote ssh connections"]); #push (@value, [$hmc,"Failed to run the upgrade command \"rflash\" on $hmc"]); push (@value, [$hmc,"Failed to upgrade the firmware of $mtms_t on $hmc"]); return(\@value); } %options = (); my $r = (); foreach $r (@res){ push(@value, [$r]); } push(@value, @result); return (\@value); } 1;