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

package xCAT::FSPscan;
use strict;
use Getopt::Long;
use Socket;
use XML::Simple;
$XML::Simple::PREFERRED_PARSER = 'XML::Parser';
use xCAT::PPCcli qw(SUCCESS EXPECT_ERROR RC_ERROR NR_ERROR);
use xCAT::PPCdb;
use xCAT::PPCscan;
use xCAT::GlobalDef;
use xCAT::Usage;
use xCAT::NetworkUtils;
use xCAT::FSPUtils;
require xCAT::data::ibmhwtypes;

#use Data::Dumper;

##############################################
# Globals
##############################################
my @header = (
    [ "type",          "%-8s" ],
    [ "name",          "placeholder" ],
    [ "id",            "%-8s" ],
    [ "type-model",    "%-12s" ],
    [ "serial-number", "%-15s\n" ]);

#,
#   ["side",          "%-8s" ],
#    ["address",       "%-20s\n" ]);

my @attribs = qw(nodetype node id mtm serial side hcp pprofile parent groups mgt cons hwtype);
my %globalnodetype = (
    fsp   => $::NODETYPE_PPC,
    bpa   => $::NODETYPE_PPC,
    cec   => $::NODETYPE_PPC,
    frame => $::NODETYPE_PPC,
    lpar  => "$::NODETYPE_PPC,$::NODETYPE_OSI"
);
my %globalhwtype = (
    fsp   => $::NODETYPE_FSP,
    bpa   => $::NODETYPE_BPA,
    lpar  => $::NODETYPE_LPAR,
    cec   => $::NODETYPE_CEC,
    frame => $::NODETYPE_FRAME,
);


##########################################################################
# Parse the command line for options and operands
##########################################################################
sub parse_args {
    xCAT::PPCscan::parse_args(@_);
}



##########################################################################
# Returns short-hostname given an IP
##########################################################################
sub getshorthost {

    my $ip = shift;

    my $host = xCAT::NetworkUtils->gethostname($ip);
    if ($host and !$!) {
        ##############################
        # Get short-hostname
        ##############################
        if ($host =~ /([^\.]+)\./) {
            return ($1);
        }
    }
    ##################################
    # Failed
    ##################################
    return undef;
}


##########################################################################
# Returns I/O bus information
##########################################################################
sub enumerate {

    my $request = shift;
    my $hash    = shift;
    my $exp     = shift;
    my $hwtype  = ();
    my $server;
    my @values = ();
    my $cageid;
    my $server;
    my $prof;
    my $fname;
    my %cage   = ();
    my %hwconn = ();
    my $Rc;
    my $filter;
    my $data;
    my @output;
    my $ips;
    my $fsp;
    my $model;
    my $serial;
    my $side;
    my $ips;
    my $line;

    foreach my $cec_bpa (keys %$hash)
    {

        my $node_hash = $hash->{$cec_bpa};
        for my $node_name (keys %$node_hash)
        {
            my $d = $node_hash->{$node_name};
            if ($$d[4] =~ /^lpar$/ || $$d[4] =~ /^bpa$/ || $$d[4] =~ /^frame$/) {
                $data = "please check the $node_name; the noderange of rscan couldn't be LPAR or BPA, or frame. ";

                #push @output, [$node_name,$data,$Rc];
                push @values, $data;
                next;
            }
            my $stat = xCAT::FSPUtils::fsp_api_action($request, $node_name, $d, "query_connection");
            my $Rc   = @$stat[2];
            my $data = @$stat[1];

            ##################################
            # Output error
            ##################################
            if ($Rc != SUCCESS) {

                #push @output, [$node_name,$data,$Rc];
                push @values, $data;
                next;
            }
            my @data_a = split("\n", $data);
            foreach $line (@data_a) {
                if ($line !~ "Connected" && $line !~ "LINE UP") {
                    next;
                }

                #########################################
                # GET CEC's information
                #########################################
                #$data =~ /state=([\w\s]+),\(type=([\w-]+)\),\(serial-number=([\w]+)\),\(machinetype-model=([\w-]+)\),sp=([\w]+),\(ip-address=([\w.]+),([\w.]+)\)/ ;
                $line =~ /state=([\w\s]+), type=([\w-]+), MTMS=([\w-]+)\*([\w-]+), sp=([\w=]+), slot=([\w]+), ipadd=([\w.]+), alt_ipadd=([\w.]+)/;

                #print "parsing: $1,$2,$3,$4,$5,$6,$7,$8\n";

                $fsp    = $node_name;
                $model  = $3;
                $serial = $4;
                $side   = $6;
                $server = $fsp;
                $fname  = $fsp;
                my $ip   = $7;
                my $ip_s = $8;
                if (!defined($ips)) {

                    if ($ip_s =~ /unavailable/) {
                        $ips = "$ip";
                    } else {
                        $ips = "$ip;$ip_s";
                    }
                } else {
                    if ($ip_s =~ /unavailable/) {
                        $ips .= ";$ip";
                    } else {
                        $ips .= ";$ip;$ip_s";
                    }
                }
            }
            if (!defined($fsp)) {
                my $msg = "please check if the $node_name is coneected to the hardware server";
                push @values, $msg;
                next;
            }
            if ($$d[4] =~ /^cec$/) {
                $side = "";
            }
            push @values, join(",",
                $$d[4], $node_name, $cageid, $model, $serial, $side, $server, $prof, $fname);

            #$$d[4],$node_name,$cageid,$model,$serial,$side, $server,$prof,$fname, $ips, $$d[4]);
            # $$d[4],$node_name,$cageid,$model,$serial,$side, $server,$prof,$fname, $7);
            # "fsp",$node_name,$cageid,$model,$serial,$side, $server,$prof,$fname, $7);
            #"fsp",$fsp,$cageid,$model,$serial,$side,$server,$prof,$fname,$ips );

            #####################################
            # Enumerate LPARs
            #####################################
            $stat = xCAT::FSPUtils::fsp_api_action($request, $node_name, $d, "get_lpar_info");
            $Rc   = @$stat[2];
            $data = @$stat[1];

            ##################################
            # Output error
            ##################################
            if ($Rc != SUCCESS) {

                #push @output, [$node_name,$data,$Rc];
                push @values, $data;
                next;
            }
            my @list = split(/\n/, $data);

            #print "list\n";
            #print Dumper(\@list);
            foreach my $lpar (@list) {
                $lpar =~ /lparname:\s+([\w\-]+),\s+lparid:\s+(\d+),\s+state:/;
                my $name   = $1;
                my $lparid = $2;
                my $prof   = "";     # No profile for Power 775
                my $server = $fsp;
                my $ips    = "";
                my $port   = "";

                #            $name =~ s/\-//g;
                #            $name =~ tr/A-Z/a-z/;

                #####################################
                # Save LPAR information
                #####################################
                push @values, join(",",
"lpar", $name, $lparid, $model, $serial, $port, $server, $prof, $fsp);

                #"lpar",$name,$lparid,$model,$serial,$port,$server,$prof,$fsp,$ips,"lpar" );

            }

        }

        #return(\@values);
    }


    return (\@values);
}



##########################################################################
# Format responses
##########################################################################
sub format_output {

    my $request    = shift;
    my $exp        = shift;
    my $values     = shift;
    my $opt        = $request->{opt};
    my %output     = ();
    my $hwtype     = "fsp";
    my $max_length = 0;
    my $result;

    #print "In format output\n";
    #print Dumper($request);
    #print Dumper($exp);
    #print Dumper($values);
    ###########################################
    # -w flag for write to xCat database
    ###########################################
    if (exists($opt->{w})) {
        my $server = @$exp[3];
        my $uid    = @$exp[4];
        my $pw     = @$exp[5];

        #######################################
        # Strip errors for results
        #######################################
        my @val = grep(!/^#.*: ERROR /, @$values);

        #xCAT::PPCdb::add_ppc( $hwtype, \@val );
        $values = xCAT::PPCdb::update_lpar($hwtype, \@val, "write");
    }

    ###########################################
    # -u flag for write to xCat database
    ###########################################
    if (exists($opt->{u})) {
        #######################################
        # Strip errors for results
        #######################################
        my @val = grep(!/^#.*: ERROR /, @$values);

        #$values = xCAT::PPCdb::update_ppc( $hwtype, \@val );
        $values = xCAT::PPCdb::update_lpar($hwtype, \@val);
        if (exists($opt->{x}) or exists($opt->{z}))
        {
            unshift @$values, "hmc";
        }
    }

    ###########################################
    # -x flag for xml format
    ###########################################
    if (exists($opt->{x})) {
        $result .= format_xml($hwtype, $values);
    }
    ###########################################
    # -z flag for stanza format
    ###########################################
    elsif (exists($opt->{z})) {
        $result .= format_stanza($hwtype, $values);
    }
    else {
        $result = sprintf("#Updated following nodes:\n") if (exists($opt->{u}));
        #######################################
        # Get longest name for formatting
        #######################################
        foreach (@$values) {
            ###################################
            # Skip error message
            ###################################
            if (/^#.*: ERROR /) {
                next;
            }
            /[^\,]+,([^\,]+),/;
            my $length = length($1);
            $max_length = ($length > $max_length) ? $length : $max_length;
        }
        my $format = sprintf("%%-%ds", ($max_length + 2));
        $header[1][1] = $format;

        #######################################
        # Add header
        #######################################
        foreach (@header) {
            $result .= sprintf(@$_[1], @$_[0]);
        }
        #######################################
        # Add node information
        #######################################
        my @errmsg;
        foreach (@$values) {
            my @data = split /,/;
            my $i    = 0;

            ###################################
            # Save error messages for last
            ###################################
            if (/^#.*: ERROR /) {
                push @errmsg, $_;
                next;
            }
            foreach (@header) {
                my $d = $data[ $i++ ];

                ###############################
                # Use IPs instead of
                # hardware control address
                ###############################
                if (@$_[0] eq "address") {
                    if ($data[0] !~ /^(hmc|ivm)$/) {
                        $d = $data[9];
                    }
                }
                $result .= sprintf(@$_[1], $d);
            }
        }
        #######################################
        # Add any error messages
        #######################################
        foreach (@errmsg) {
            $result .= "\n$_";
        }
    }
    $output{data} = [$result];
    return ([ \%output ]);
}



##########################################################################
# Stanza formatting
##########################################################################
sub format_stanza {

    my $hwtype = shift;
    my $values = shift;

    my $result;

    #####################################
    # Skip hardware control point
    #####################################
    shift(@$values);

    foreach (sort @$values) {
        my @data = split /,/;
        my $type = $data[0];
        my $i    = 0;

        #################################
        # Skip error message
        #################################
        if (/^#.*: ERROR /) {
            next;
        }
        #################################
        # Node attributes
        #################################
        $result .= "$data[1]:\n\tobjtype=node\n";

        #################################
        # Add each attribute
        #################################
        my $mtm = undef;
        foreach (@attribs) {
            my $d = $data[ $i++ ];

            if (/^node$/) {
                next;
            } elsif (/^nodetype$/) {
                $d = $globalnodetype{$type};
            } elsif (/^hwtype$/) {
                $d = $globalhwtype{$type};
            } elsif (/^groups$/) {
                next;

                #$d = "$type,all";
            } elsif (/^mgt$/) {
                $d = $hwtype;
            } elsif (/^cons$/) {
                if ($type eq "lpar") {
                    $d = $hwtype;
                } else {
                    $d = undef;
                }

            } elsif (/^(mtm|serial)$/) {
                if ($type eq "lpar") {
                    $d = undef;
                } elsif (/^mtm$/) {
                    $mtm = $d;
                }
            } elsif (/^side$/) {
                unless ($type =~ /^fsp|bpa$/) {
                    next;
                }
            }
            $result .= "\t$_=$d\n";
        }
        my $tmp_groups = "$type,all";
        if (defined($mtm)) {
            my $tmp_pre = xCAT::data::ibmhwtypes::parse_group($mtm);
            if (defined($tmp_pre)) {
                $tmp_groups .= ",$tmp_pre";
            }
        }
        $result .= "\tgroups=$tmp_groups\n";

    }
    return ($result);
}


##########################################################################
# XML formatting
##########################################################################
sub format_xml {

    my $hwtype = shift;
    my $values = shift;
    my $xml;

    #####################################
    # Skip hardware control point
    #####################################
    #shift(@$values);

    #####################################
    # Create XML formatted attributes
    #####################################
    foreach (@$values) {
        my @data = split /,/;
        my $type = $data[0];
        my $i    = 0;

        #################################
        # Skip error message
        #################################
        if (/^#.*: ERROR /) {
            next;
        }
        #################################
        # Initialize hash reference
        #################################
        my $href = {
            Node => {}
        };
        #################################
        # Add each attribute
        #################################
        my $mtm = undef;
        foreach (@attribs) {
            my $d = $data[ $i++ ];

            if (/^nodetype$/) {
                $d = $globalnodetype{$type};
            } elsif (/^hwtype$/) {
                $d = $globalhwtype{$type};
            } elsif (/^groups$/) {
                next;

                #$d = "$type,all";
            } elsif (/^mgt$/) {
                $d = $hwtype;
            } elsif (/^cons$/) {
                if ($type eq "lpar") {
                    $d = $hwtype;
                } else {
                    $d = undef;
                }
            } elsif (/^(mtm|serial)$/) {
                if ($type eq "lpar") {
                    $d = undef;
                } elsif (/^mtm$/) {
                    $mtm = $d;
                }
            } elsif (/^side$/) {
                unless ($type =~ /^fsp|bpa$/) {
                    next;
                }
            }
            $href->{Node}->{$_} = $d;
        }
        my $tmp_groups = "$type,all";
        if (defined($mtm)) {
            my $tmp_pre = xCAT::data::ibmhwtypes::parse_group($mtm);
            if (defined($tmp_pre)) {
                $tmp_groups .= ",$tmp_pre";
            }
        }
        $href->{Node}->{groups} = $tmp_groups;

        #print Dumper($href);
        #################################
        # XML encoding
        #################################
        $xml .= XMLout($href,
            NoAttr   => 1,
            KeyAttr  => [],
            RootName => undef);
    }
    return ($xml);
}



##########################################################################
# Returns I/O bus information
##########################################################################
sub rscan {

    my $request = shift;
    my $hash    = shift;
    my $exp     = shift;
    my $args    = $request->{arg};
    my $server  = @$exp[3];

    #print "in rscan,";
    #print Dumper($request);
    #print Dumper($hash);
    #print Dumper($exp);

    ###################################
    # Enumerate all the hardware
    ###################################
    my $values = enumerate($request, $hash);

    #print "In rscan:\n";
    #print Dumper($values);
    if (ref($values) ne 'ARRAY') {
        return ([ [ $server, $values, 1 ] ]);
    }
    ###################################
    # Success
    ###################################
    my $result = format_output($request, $exp, $values);
    unshift @$result, "FORMATDATA6sK4ci";
    return ($result);

}



1;







