# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html

package xCAT::FSPflash;
use strict;
use lib "/opt/xcat/lib/perl";
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 xCAT::PPCrflash;

#use Data::Dumper;
use xCAT::FSPUtils;

my $packages_dir = ();
my $activate     = ();
my $verbose      = 0;
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 $req = shift;
    $req->{mgt} = __PACKAGE__;
    my $opt = xCAT::PPCrflash::parse_args($req);
    delete($req->{mgt});
    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);
    }
}


#-------------------------------------------------------------------------#
# 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!";
            if ($activate !~ /^(disruptive|deferred)$/) {
                $msg = "Option --activate's value shouldn't be $activate, and it must be disruptive or deferred";
                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);

}



##########################
#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 $subreq  = $request->{subreq};
    my $hwtype  = @$exp[2];
    my @result;
    my $timeout      = $request->{ppctimeout};
    my $housekeeping = $request->{housekeeping};
    $packages_dir = $request->{opt}->{p};
    $activate     = $request->{opt}->{activate};
    print "housekeeping:$housekeeping\n";
    my $mtms;
    my $h;
    my $user;
    my $action;

    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;
    my %infor;
    my $role;   #0x01: BPC A, BPC B; 0x01: Primary or only FSP, 0x02: Backup FSP

    #print "in Directflash \n";
    #print Dumper($request);
    #print Dumper($hash);

    ####################################
    # Power commands are grouped by hardware control point
    # In Direct attach support, the hcp is the related fsp.
    ####################################

    # Example of $hash.
    #VAR1 = {
    #	          '9110-51A*1075ECF' => {
    #                                   'Server-9110-51A-SN1075ECF' => [
    #    	                                                          0,
    #		                                                          0,
    #									  '9110-51A*1075ECF',
    #									  'Server-9110-51A-SN1075ECF',
    #									  'fsp',
    #									  0
    #									  ]
    #					 }
    # 	   };
    my $flag  = 0;
    my $flag2 = 0;
    while (my ($mtms, $h) = each(%$hash)) {
        #
        #For one mtms, it just needs to do the operation one time.
        #
        $flag += 1;
        if ($flag > 1) {
            last;
        }

        $mtms =~ /(\w+)-(\w+)\*(\w+)/;
        my $mtm    = "$1-$2";
        my $serial = $3;


        while (my ($name, $d) = each(%$h)) {
            $flag2 += 1;
            if ($flag2 > 1) {
                last;
            }

            if (!defined($housekeeping) && ($$d[4] =~ /^fsp$/ || $$d[4] =~ /^lpar$/ || $$d[4] =~ /^cec$/)) {
                $action = "get_compatible_version_from_rpm";
                my $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, $action, 0, $request->{opt}->{d});
                my $Rc = @$values[2];
                my $v  = @$values[1];
                if ($Rc != 0) {
                    push @value, [ $name, $v, -1 ];
                    return (\@value);
                }

                #if( $v !~ "nocheckversion") {
                my @levels = split(/,/, $v);

                my $frame = $$d[5];
                my $type  = xCAT::DBobjUtils->getnodetype($frame);
                if (($frame ne $name) && ($type eq "frame") && $activate !~ /^deferred$/) {

                    my @frame_d = (0, 0, 0, $frame, "frame", 0);
                    $action = "list_firmware_level";
                    $values = xCAT::FSPUtils::fsp_api_action($request, $frame, \@frame_d, $action);
                    $Rc = @$values[2];
                    my $frame_firmware_level = @$values[1];
                    if ($Rc != 0) {
                        push @value, [ $frame, $frame_firmware_level, -1 ];
                        return (\@value);
                    }

                    my $level_a;
                    my $level_b;
                    if ($frame_firmware_level =~ /curr_level_a=(\d{3}),curr_ecnumber_a=02(\w{5})/) {
                        $level_a = "$2_$1";
                    }

                    if ($frame_firmware_level =~ /curr_level_b=(\d{3}),curr_ecnumber_b=02(\w{5})/) {
                        $level_b = "$2_$1";
                    }

                    #print "frame_firmware_level=$frame_firmware_level,level_a=$level_a,level_b=$level_b\n";
                    foreach my $l (@levels) {

                        #print "rpm requires: $l\n"	;
                        if ((defined($level_a) && ($l gt $level_a)) || (defined($level_b) && ($l gt $level_b))) {
                            my $res = "New Managed System level for $name is not compatible with current Power Subsystem level 02$level_a on $frame.\nPower Subsystem level 02$l or later is required.";

                            push @value, [ $name, $res, -1 ];
                            return (\@value);
                        }

                    }
                }

                #}

            }

            if (!defined($housekeeping)) {
                my $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "list_firmware_level");
                my $Rc    = @$values[2];
                my $level = @$values[1];
                #####################################
                # Return error
                #####################################
                if ($Rc != SUCCESS) {
                    push @value, [ $name, $level, $Rc ];
                    next;
                }

                if ($level =~ /ecnumber=(\w+)/) {
                    $release_level = $1;
                    &dpush(\@value, [ $name, "$mtms :release level:$1" ]);
                }

                if ($level =~ /activated_level=(\w+)/) {
                    $active_level = $1;
                    &dpush(\@value, [ $name, "$mtms :activated level:$1" ]);
                }

            }



            if ($housekeeping =~ /^commit$/)   { $action = "code_commit" }
            if ($housekeeping =~ /^recover$/)  { $action = "code_reject" }
            if ($housekeeping =~ /^bpa_acdl$/) { $action = "acdl" }
            if ($activate eq "disruptive") {
                $action = "code_update";
            } elsif ($activate eq "deferred") {
                $action = "code_updateD";
            } elsif (defined($activate)) {

                #if($activate =~ /^concurrent$/) {
                my $res = "\'$activate\' option not supported in FSPflash. Please use disruptive or deferred mode";
                push @value, [ $name, $res, -1 ];
                next;
            }

            my $msg;
            if (!defined($housekeeping)) {
                my $flag = 0;
                ($rpm_file, $xml_file, $upgrade_required, $msg, $flag) = &get_lic_filenames($mtms);
                if ($flag == -1) {
                    push(@value, [ $name, "$mtms: $msg" ]);
                    push(@value, [ $name, "Failed to upgrade the firmware of $name" ]);
                    return (\@value);
                }
                dpush(\@value, [ $name, $msg ]);
            }

            my $res = xCAT::FSPUtils::fsp_api_action($request, $name, $d, $action, 0, $request->{opt}->{d});
            if ($action eq "acdl" && @$res[2] eq '0') {
                push(@value, [ $name, "Success", '0' ]);
            } else {
                push(@value, [ $name, @$res[1], @$res[2] ]);
            }
            return (\@value);

        }
    }
    push(@value, @result);
    return (\@value);


}

1;


