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

BEGIN
{
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
}


package xCAT::FSPvm;
use lib "$::XCATROOT/lib/perl";
use strict;
use Getopt::Long;
use xCAT::PPCdb;
use xCAT::PPCcli qw(SUCCESS EXPECT_ERROR RC_ERROR NR_ERROR);
use xCAT::Usage;
use xCAT::NodeRange;
use xCAT::FSPUtils;
use xCAT::VMCommon;
use Data::Dumper;
use xCAT::MsgUtils qw(verbose_message);
##############################################
# Globals
##############################################
my %method = (
    mkvm => \&mkvm_parse_args,
    lsvm => \&lsvm_parse_args,
    rmvm => \&rmvm_parse_args,
    chvm => \&chvm_parse_args
);


##########################################################################
# Parse the command line for options and operands
##########################################################################
sub parse_args {

    my $request = shift;
    my $cmd     = $request->{command};

    ###############################
    # Invoke correct parse_args
    ###############################
    my $result = $method{$cmd}($request);
    return ($result);
}


##########################################################################
# Parse the chvm command line for options and operands
##########################################################################
my @query_array    = ();
my %param_list_map = (
    'vmcpus'        => 'part_get_lpar_processing',
    'vmmemory'      => 'part_get_lpar_memory',
    'add_physlots'  => 'part_get_all_io_bus_info',
    'del_physlots'  => 'part_get_all_io_bus_info',
    'add_vmnics'    => 'part_get_all_vio_info',
    'add_vmstorage' => 'part_get_all_vio_info',
    'del_vadapter'  => 'part_get_all_vio_info'
);

sub chvm_parse_extra_options {
    my $args = shift;
    my $opt  = shift;

    # Partition used attributes #
    my @support_ops = qw(vmcpus vmmemory add_physlots vmothersetting add_vmstorage add_vmnics del_vadapter del_physlots);
    if (ref($args) ne 'ARRAY') {
        return "$args";
    }
    my %tmp_hash = ();
    foreach (@$args) {
        my ($cmd, $value) = split(/\=/, $_);
        if (!defined($value)) {
            return "no value specified";
        }
        if ($cmd =~ /^lparname$/) {
            if ($value ne '*' && $value !~ /^[a-zA-Z0-9-_]+$/) {
                return "'$value' invalid";
            }
            my $len = rindex $value . "\$", "\$";
            if ($len > '47') {
                return "'$value' is too long, max 47 characters";
            }

            #       } elsif ($cmd =~ /^huge_page$/) {
            #			if ($value !~ /^\d+\/\d+\/\d+$/) {
            #				return "'$value' invalid";
            #			}
        } elsif (grep(/^$cmd$/, @support_ops)) {
            if (exists($param_list_map{$cmd})) {
                $tmp_hash{ $param_list_map{$cmd} } = 1;
            }
            if (exists($opt->{p775})) {
                return "'$cmd' doesn't work for Power 775 machines.";
            } elsif ($cmd eq "del_vadapter") {
                if ($value !~ /^\d+$/) {
                    return "Invalid param '$value', only one slot id can be specified";
                }
            } elsif ($cmd eq "del_physlots") {
                my @tmp_array = split ",", $value;
                foreach (@tmp_array) {
                    unless (/(0x\w{8})/) {
                        return "'$_' is invalid";
                    }
                }
            } elsif ($cmd eq "vmothersetting") {
                if ($value =~ /hugepage:\s*(\d+)/i) {
                    $opt->{huge_page} = $1;
                    $tmp_hash{'get_huge_page'} = 1;
                }
                if ($value =~ /bsr:\s*(\d+)/i) {
                    $opt->{bsr} = $1;
                    $tmp_hash{'get_cec_bsr'} = 1;
                }
                next;
            } elsif ($cmd eq "add_vmstorage") {
                if (exists($opt->{vios})) {
                    if ($value !~ /\d+/) {
                        return "'$value' is invalid, must be numbers";
                    } else {
                        my @array = ();
                        for (1 .. $value) {
                            push @array, 0;
                        }
                        $value = \@array;
                    }
                } else {
                    if ($value =~ /^([\w_-]*):(\d+)$/) {
                        $value = ["0,$1:$2"];
                    } else {
                        return "'$value' is invalid, must be in form of 'Server_name:slotnum'";
                    }
                }
            } elsif ($cmd eq "vmcpus") {
                if ($value =~ /^(\d+)\/(\d+)\/(\d+)$/) {
                    unless ($1 <= $2 and $2 <= $3) {
                        return "'$value' is invalid, must be in order";
                    }
                } else {
                    return "'$value' is invalid, must be integer";
                }
            } elsif ($cmd eq "vmmemory") {
                if ($value =~ /^([\d|.]+)([G|M]?)\/([\d|.]+)([G|M]?)\/([\d|.]+)([G|M]?)$/i) {
                    my ($mmin, $mcur, $mmax);
                    if ($2 eq "G" or $2 eq '') {
                        $mmin = $1 * 1024;
                    }
                    if ($4 eq "G" or $4 eq '') {
                        $mcur = $3 * 1024;
                    }
                    if ($6 eq "G" or $6 eq '') {
                        $mmax = $5 * 1024;
                    }
                    unless ($mmin <= $mcur and $mcur <= $mmax) {
                        return "'$value' is invalid, must be in order";
                    }
                } else {
                    return "'$value' is invalid";
                }
            } elsif ($cmd eq "add_physlots") {
                my @tmp_array = split ",", $value;
                foreach (@tmp_array) {
                    unless (/(0x\w{8})/) {
                        return "'$_' is invalid";
                    }
                }
            } elsif ($cmd eq "vmothersetting") {
                my @tmp_array = split ",", $value;
                foreach (@tmp_array) {
                    unless (/^(bsr|hugepage):\d+$/) {
                        return "'$_' is invalid";
                    }
                }
            } elsif ($cmd eq "add_vmnics") {
                my @tmp_array = split ",", $value;
                foreach (@tmp_array) {
                    unless (/^vlan\d+$/i) {
                        return "'$_' is invalid";
                    }
                }
            }

        } else {
            return "'$cmd' not support";
        }
        $opt->{$cmd} = $value;
    }
    @query_array = keys(%tmp_hash);
    return undef;
}

sub chvm_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)) {

        #$request->{method} = $cmd;
        #return( \%opt );
        return (usage());
    }
    #############################################
    # 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(V|verbose p=s i=s m=s r=s p775 vios))) {
        return (usage());
    }
    ####################################
    # Check for "-" with no option
    ####################################
    if (grep(/^-$/, @ARGV)) {
        return (usage("Missing option: -"));
    }
    ####################################
    # Configuration file required
    ####################################
    #if ( !exists( $opt{p}) ) {
    #    if ( !defined( $request->{stdin} )) {
    #        return(usage( "Configuration file or attributes not specified" ));
    #    }
    #}
    if (exists($opt{p775})) {
        my @cfgdata;
        if ((exists($opt{p}) || defined($request->{stdin})) && !exists($opt{p775})) {
            return (usage("Profile just work for Power 775"));
        }
        if (exists($opt{p})) {

            if (exists($opt{i}) || exists($opt{r}) || exists($opt{m})) {
                return (usage("-p should NOT  be used with -i, -r or -m."));
            }

            $opt{p} = $request->{cwd}->[0] . '/' . $opt{p} if ($opt{p} !~ /^\//);
            return (usage("Profile $opt{p} cannot be found")) if (!-f $opt{p});
            open(PROFFILE, "<$opt{p}") or return (usage("Cannot open profile $opt{p}"));
            while (<PROFFILE>) {
                chomp;
                if ($_ =~ /(\d+):(\s+)(\d+)\/([\w\.\-]+)\/(\w+)\//) {
                    push @cfgdata, $_;
                } else {
                    return (usage("Invalid line in profile: $_"));
                }
            }
            $opt{profile} = \@cfgdata;
        }

        if (defined($request->{stdin})) {
            $opt{p} = 1;
            if (exists($opt{i}) || exists($opt{r}) || exists($opt{m})) {
                return (usage("When the profile is piped into the chvm command, the -i, -r and -m could NOT be used."));
            }
        }

        #if (defined( $request->{stdin} )) {
        #     my $p =  $request->{stdin};
        #     my @io = split(/\n/, $p) ;
        #     foreach (@io) {
        #         chomp;
        #         if( $_ =~ /(\d+):(\s+)(\d+),([\w\.\-]+),(\w+),/) {
        #            push @cfgdata, $_;
        #         } else {
        #             return ( usage( "Invalid line in profile: $_"));
        #         }

        #     }

        #    $opt{profile} = \@cfgdata;
        #}
        #print "in parse args:\n";
        #print Dumper(\%opt);



        if (exists($opt{i})) {
            if (!exists($opt{r})) {
                return (usage("Option -i should be used with option -r ."));
            }

            if ($opt{i} !~ /^([1-9]{1}|[1-9]{1}[0-9]+)$/) {
                return (usage("Invalid entry: $opt{i}"));
            }
            my @id = (1, 5, 9, 13, 17, 21, 25, 29);
            my @found = grep(/^$opt{i}$/, @id);
            if (@found != 1) {
                return (usage("Invalid entry: $opt{i}.\n For Power 775, starting numeric id of the newly created partitions only could be 1, 5, 9, 13, 17, 21, 25 and 29."));
            }

            #if ( !exists($opt{o})  ) {
            #    return(usage("For Power 775, -i should be used with -o"));
            #}

            #my @value = (1, 2, 3, 4, 5);
            #if ( grep(/^$opt{i}$/, @id ) != 1) {
            #    return(usage( "Invalid entry: $opt{o}.\n For Power 775, octant configuration values only could be 1, 2, 3, 4, 5. Please see the details in manpage of mkvm." ));
            #}


        }


        # pending memory interleaving mode  (1- interleaved, 2- non-interleaved)
        # non-interleaved mode means the memory cannot be shared across the processors in an octant.
        # interleaved means the memory can be shared.
        if (exists($opt{m})) {
            if ($opt{m} =~ /^interleaved$/ || $opt{m} =~ /^1$/) {
                $opt{m} = 1;
            } elsif ($opt{m} =~ /^non-interleaved$/ || $opt{m} =~ /^2$/) {
                $opt{m} = 2;
            } else {
                return (usage("Invalid entry: $opt{m}.\n For Power 775, the pending memory interleaving mode only could be interleaved(or 1), or non-interleaved(or 2)."));
            }
        } else {
            $opt{m} = 1;    # interleaved, which is the default
        }

        my @ratio = (1, 2, 3, 4, 5);
        my %octant_cfg = ();
        if (exists($opt{r})) {

            if (!exists($opt{i})) {
                return (usage("Option -r should be used with option -i ."));
            }

            my @elems = split(/\,/, $opt{r});
            my $range = "";
            while (my $elem = shift @elems) {
                if ($elem !~ /\:/) {
                    return (usage("Invalid argument $elem.\n The input format for 'r' should be like this: \"-r Octant_id:Value\"."))
                }
                if ($elem !~ /\-/) {
                    my @subelems = split(/\:/, $elem);
                    if ($subelems[0] < 0 || $subelems[0] > 7) {
                        return (usage("Octant ID only could be 0 to 7 in the octant configuration value $elem"));
                    }
                    if (grep(/^$subelems[1]$/, @ratio) != 1) {
                        return (usage("Invalid octant configuration value in $elem.\n For Power 775, octant configuration values only could be 1, 2, 3, 4, 5. Please see the details in manpage of chvm."));
                    }
                    if (exists($octant_cfg{ $subelems[0] }) && $octant_cfg{ $subelems[0] } == $subelems[1]) {
                        return (usage("In the octant configuration rule, same octant with different octant configuration value. Error!"));
                    }
                    $octant_cfg{ $subelems[0] } = $subelems[1];
                    $range .= "$elem,";
                } else {
                    my @subelems = split(/\:/, $elem);
                    my ($left, $right) = split(/\-/, $subelems[0]);
                    if ($left < 0 || $left > 7 || $right < 0 || $right > 7) {
                        return (usage("Octant ID only could be 0 to 7 in the octant configuration rule $elem"));
                    }
                    if ($left == $right) {
                        if (grep(/^$subelems[1]$/, @ratio) != 1) {
                            return (usage("Invalid octant configuration value in $elem.\n For Power 775, octant configuration values only could be 1, 2, 3, 4, 5. Please see the details in manpage of chvm."));
                        }
                        if (exists($octant_cfg{$left}) || $octant_cfg{$left} == $subelems[1]) {
                            return (usage("In the octant configuration rule, same octant with different octant configuration value. Error!"));
                        }
                        $octant_cfg{$left} = $subelems[1];
                        $range .= "$left:$subelems[1],"
                    } elsif ($left < $right) {
                        my $i = $left;
                        for ($i ; $i <= $right ; $i++) {
                            if (exists($octant_cfg{$i}) || $octant_cfg{$i} == $subelems[1]) {
                                return (usage("In the octant configuration rule, same octant with different octant configuration value. Error!"));
                            }
                            $octant_cfg{$i} = $subelems[1];

                            $range .= "$i:$subelems[1],";
                        }
                    } else {
                        return (usage("In the octant configuration rule $elem, the left octant ID could NOT be bigger than the right octant ID"));
                    }
                }    # end of "if .. else.."
            }    # end of while
        }    #end of if

        if (exists($opt{i}) && exists($opt{r})) {
            $opt{octant_cfg}{octant_cfg_value}  = (\%octant_cfg);
            $opt{octant_cfg}{memory_interleave} = $opt{m};

            $opt{target} = \@{ $request->{node} };
            my $ppctab = xCAT::Table->new('ppc');
            unless ($ppctab) {
                return (usage("Cannot open ppc table"));
            }

            my $other_p;
            foreach my $node (@{ $request->{node} }) {
                my $parent_hash = $ppctab->getNodeAttribs($node, [qw(parent)]);
                my $p = $parent_hash->{parent};
                if (!$p) {
                    return (usage("Not found the parent of $node"));
                }
                if (!defined($other_p)) {
                    $other_p = $p;
                }
                if ($other_p ne $p) {
                    return (usage("For Power 775, please make sure the noderange are in one CEC "));
                }
            }
            $request->{node}      = [$other_p];
            $request->{noderange} = $other_p;
        }
    }
    ####################################
    # Check for an extra argument
    ####################################
    if (defined($ARGV[0])) {
        my $check_chvm_arg = chvm_parse_extra_options(\@ARGV, \%opt);
        if (defined($check_chvm_arg)) {
            return (usage("Invalid argument: $check_chvm_arg"));
        } elsif (($opt{lparname}) && ($opt{lparname} ne '*') && (scalar(@{ $request->{node} }) > '1')) {
            return (usage("Invalid argument: must specify '*' for more than one node"));
        }
        if ((exists($opt{lparname}) || exists($opt{huge_page})) &&
            (exists($opt{p}) || exists($opt{i}) || exists($opt{r}))) {
            return (usage("lparname should NOT be used with -p, -i or -r."));
        }
    }

    ####################################
    # No operands - add command name
    ####################################
    $request->{method} = $cmd;
    return (\%opt);
}


##########################################################################
# Parse the mkvm command line for options and operands
##########################################################################
sub mkvm_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 command specified" ));
    #}
#############################################
    # Checks case in GetOptions, allows opts
    # to be grouped (e.g. -vx), and terminates
    # at the first unrecognized option.
#############################################
    if (defined($args)) {
        @ARGV = @$args;
    }
    $Getopt::Long::ignorecase = 0;
    Getopt::Long::Configure("bundling");

    #    if ( !GetOptions( \%opt, qw(V|verbose ibautocfg ibacap=s i=s l=s c=s p=s m=s r=s full) )) {
    #        return( usage() );
    #    }
    if (!GetOptions(\%opt, qw(V|verbose full vios))) {
        return (usage());
    }
####################################
    # Check for "-" with no option
####################################
    if (grep(/^-$/, @ARGV)) {
        return (usage("Missing option: -"));
    }
    if (!exists($opt{p775})) {
        my @unsupport_ops = ();
        foreach my $tmpop (keys %opt) {
            if ($tmpop !~ /full|vios|V/) {
                push @unsupport_ops, $tmpop;
            }
        }
        my @support_ops = qw(vmcpus vmmemory vmphyslots vmothersetting vmnics vmstorage);
        if (defined(@ARGV[0]) and defined($opt{full})) {
            return (usage("Option 'full' shall be used alone."));
        } elsif (defined(@ARGV[0])) {
            foreach my $arg (@ARGV) {
                my ($cmd, $val) = split(/=/, $arg);
                if (!grep(/^$cmd$/, @support_ops)) {
                    push @unsupport_ops, $cmd;
                } elsif (!defined($val)) {
                    return (usage("The option $cmd need specific parameters."));
                } else {
                    $opt{$cmd} = $val;
                }
            }
        }

        if (@unsupport_ops) {
            my $tmpops = join(",", @unsupport_ops);
            return (usage("The options $tmpops can only work(s) with Power 775 machines."));
        }
    } else {
        if (exists($opt{full}) or exists($opt{vios})) {
            return (usage("Option 'p775' only works for Power 775 machines."));
        }
####################################
        # Check for non-zero integer
####################################
        if (exists($opt{i})) {
            if ($opt{i} !~ /^([1-9]{1}|[1-9]{1}[0-9]+)$/) {
                return (usage("Invalid entry: $opt{i}"));
            }
            my @id = (1, 5, 9, 13, 17, 21, 25, 29);
            my @found = grep(/^$opt{i}$/, @id);
            if (@found != 1) {
                return (usage("Invalid entry: $opt{i}.\n For Power 775, starting numeric id of the newly created partitions only could be 1, 5, 9, 13, 17, 21, 25 and 29."));
            }

            #if ( !exists($opt{o})  ) {
            #    return(usage("For Power 775, -i should be used with -o"));
            #}

            #my @value = (1, 2, 3, 4, 5);
            #if ( grep(/^$opt{i}$/, @id ) != 1) {
            #    return(usage( "Invalid entry: $opt{o}.\n For Power 775, octant configuration values only could be 1, 2, 3, 4, 5. Please see the details in manpage of mkvm." ));
            #}


        }


        # pending memory interleaving mode  (1- interleaved, 2- non-interleaved)
        # non-interleaved mode means the memory cannot be shared across the processors in an octant.
        # interleaved means the memory can be shared.
        if (exists($opt{m})) {
            if ($opt{m} =~ /^interleaved$/ || $opt{m} =~ /^1$/) {
                $opt{m} = 1;
            } elsif ($opt{m} =~ /^non-interleaved$/ || $opt{m} =~ /^2$/) {
                $opt{m} = 2;
            } else {
                return (usage("Invalid entry: $opt{m}.\n For Power 775, the pending memory interleaving mode only could be interleaved(or 1), or non-interleaved(or 2)."));
            }
        } elsif (exists($opt{p775})) {
            $opt{m} = 2;    # non-interleaved, which is the default
        }

        if (exists($opt{r})) {
            my @ratio = (1, 2, 3, 4, 5);
            my %octant_cfg = ();

            my @elems = split(/\,/, $opt{r});
            my $range = "";
            while (my $elem = shift @elems) {
                if ($elem !~ /\-/) {
                    my @subelems = split(/\:/, $elem);
                    if ($subelems[0] < 0 || $subelems[0] > 7) {
                        return (usage("Octant ID only could be 0 to 7 in the octant configuration value $elem"));
                    }
                    if (grep(/^$subelems[1]$/, @ratio) != 1) {
                        return (usage("Invalid octant configuration value in $elem.\n For Power 775, octant configuration values only could be 1, 2, 3, 4, 5. Please see the details in manpage of mkvm."));
                    }
                    if (exists($octant_cfg{ $subelems[0] }) && $octant_cfg{ $subelems[0] } == $subelems[1]) {
                        return (usage("In the octant configuration rule, same octant with different octant configuration value. Error!"));
                    }
                    $octant_cfg{ $subelems[0] } = $subelems[1];
                    $range .= "$elem,";
                } else {
                    my @subelems = split(/\:/, $elem);
                    my ($left, $right) = split(/\-/, $subelems[0]);
                    if ($left < 0 || $left > 7 || $right < 0 || $right > 7) {
                        return (usage("Octant ID only could be 0 to 7 in the octant configuration rule $elem"));
                    }
                    if ($left == $right) {
                        if (grep(/^$subelems[1]$/, @ratio) != 1) {
                            return (usage("Invalid octant configuration value in $elem.\n For Power 775, octant configuration values only could be 1, 2, 3, 4, 5. Please see the details in manpage of mkvm."));
                        }
                        if (exists($octant_cfg{$left}) || $octant_cfg{$left} == $subelems[1]) {
                            return (usage("In the octant configuration rule, same octant with different octant configuration value. Error!"));
                        }
                        $octant_cfg{$left} = $subelems[1];
                        $range .= "$left:$subelems[1],"
                    } elsif ($left < $right) {
                        my $i = $left;
                        for ($i ; $i <= $right ; $i++) {
                            if (exists($octant_cfg{$i}) || $octant_cfg{$i} == $subelems[1]) {
                                return (usage("In the octant configuration rule, same octant with different octant configuration value. Error!"));
                            }
                            $octant_cfg{$i} = $subelems[1];

                            $range .= "$i:$subelems[1],";
                        }
                    } else {
                        return (usage("In the octant configuration rule $elem, the left octant ID could NOT be bigger than the right octant ID"));
                    }
                }    # end of "if .. else.."
            }    # end of while
            $opt{octant_cfg}{octant_cfg_value}  = (\%octant_cfg);
            $opt{octant_cfg}{memory_interleave} = $opt{m};
        }    #end of if


        if ((!exists($opt{i}) || !exists($opt{r}))) {
            return (usage());
        }
        $opt{target} = \@{ $request->{node} };
        my $ppctab = xCAT::Table->new('ppc');
        unless ($ppctab) {
            return (usage("Cannot open ppc table"));
        }

        my $other_p;
        foreach my $node (@{ $request->{node} }) {
            my $parent_hash = $ppctab->getNodeAttribs($node, [qw(parent)]);
            my $p = $parent_hash->{parent};
            if (!$p) {
                return (usage("Not found the parent of $node"));
            }
            if (exists($opt{full}) and defined($other_p) and $other_p eq $p) {
                return (usage("Only one full partition can be created in one CEC"));
            }

            if (!defined($other_p)) {
                $other_p = $p;
            }
            if ($other_p ne $p) {
                return (usage("For Power 775, please make sure the noderange are in one CEC "));
            }
        }

        #if (exists($opt{p775})) {
        $request->{node}      = [$other_p];
        $request->{noderange} = $other_p;

        #}

    }
    ####################################
    # No operands - add command name
    ####################################
    $request->{method} = $cmd;
    return (\%opt);
}



##########################################################################
# Parse the rmvm command line for options and operands
##########################################################################
sub rmvm_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)) {
        $request->{method} = $cmd;
        return (\%opt);
    }
    #############################################
    # 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(V|verbose service r p775))) {
        return (usage());
    }

    if (exists($opt{p775})) {
        return (usage("rmvm doesn't support for Power 775."));
    }

    ####################################
    # 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]"));
    }
    ####################################
    # No operands - add command name
    ####################################
    $request->{method} = $cmd;
    return (\%opt);
}


##########################################################################
# Parse the lsvm command line for options and operands
##########################################################################
sub lsvm_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)) {
        $request->{method} = $cmd;
        return (\%opt);
    }
    #############################################
    # 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(V|verbose l|long p775 updatedb))) {
        return (usage());
    }
    if (exists($opt{l}) && !exists($opt{p775})) {
        return (usage("option 'l' only works for Power 775"));
    }
    ####################################
    # 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]"));
    }
    ####################################
    # No operands - add command name
    ####################################
    $request->{method} = $cmd;
    return (\%opt);
}


##########################################################################
# Changes the configuration of an existing partition
##########################################################################
sub modify {
    my $request      = shift;
    my $hash         = shift;
    my $usage_string = xCAT::Usage->getUsage($request->{command});
    return modify_by_prof($request, $hash) if (exists($request->{opt}->{p775}) and ($request->{opt}->{p} || $request->{stdin}));
    return create($request, $hash) if (exists($request->{opt}->{p775}) and $request->{opt}->{i});
    return op_extra_cmds($request, $hash) if (!exists($request->{opt}->{p775}));
    return op_extra_cmds($request, $hash) if ($request->{opt}->{lparname} || $request->{opt}->{huge_page});
    return ([ [ "Error", "Miss argument\n" . $usage_string, 1 ] ]);
}

sub do_op_extra_cmds {
    my $request   = shift;
    my $hash      = shift;
    my @values    = ();
    my %lpar_hash = ();
    while (my ($mtms, $h) = each(%$hash)) {
        my $memhash;
        while (my ($name, $d) = each(%$h)) {
            foreach my $op (keys %{ $request->{opt} }) {
                my $action;
                my $param = $request->{opt}->{$op};
                if ($op eq "lparname") {
                    $action = "set_lpar_name";
                } elsif ($op eq "huge_page") {
                    my @td = @$d;
                    @td[0] = 0;
                    my $tmphash = &query_cec_info_actions($request, $name, \@td, 1, ["get_huge_page"]);
                    if ($tmphash->{huge_page_avail}) {
                        if ($param > $tmphash->{huge_page_avail}) {
                            push @values, [ $name, "No enough huge pages, only $tmphash->{huge_page_avail} pages available", 0 ];
                            $param = $tmphash->{huge_page_avail};
                        }
                        $param = "1/$param/$param";
                    } else {
                        push @values, [ $name, "No huge page available to configure", 0 ];
                        next;
                    }
                    $action = "set_huge_page";
                } elsif ($op eq "vmcpus") {
                    $action = "part_set_lpar_pending_proc";
                } elsif ($op eq "add_physlots" or $op eq "del_physlots") {
                    $action = "set_io_slot_owner_uber";
                } elsif ($op eq "del_vadapter") {
                    $action = "part_clear_vslot_config";
                } elsif ($op eq "add_vmnics") {
                    my @vlans = split /,/, $param;
                    foreach (@vlans) {
                        if (/vlan(\d+)/i) {
                            my $vlanid = $1;
                            my $mac    = lc(xCAT::VMCommon::genMac($name));
                            if ($mac =~ /(..):(..):(..):(..):(..):(..)/) {
                                my $tail = hex($6) + $vlanid;
                                $mac = sprintf("$1$2$3$4$5%02x", $tail);
                            }
                            my $value = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_veth_slot_config", 0, "0,$vlanid,$mac");
                            if (@$value[1] && ((@$value[1] =~ /Error/i) && (@$value[2] ne '0'))) {
                                return ([ [ $name, @$value[1], '1' ] ]);
                            } else {
                                push @values, [ $name, "Success", '0' ];
                            }
                        }
                    }
                    next;
                } elsif ($op eq "add_vmstorage") {
                    foreach my $v_info (@$param) {
                        if ($v_info =~ /(\d+),([\w_-]*):(\d+)/) {
                            my $vios = &find_lpar_id($request, @$d[3], $2);
                            my $r_slotid = $3;
                            if (!defined($vios)) {
                                return ([ [ $name, "Cannot find lparid for Server lpar:$1", '1' ] ]);
                            }
                            $v_info = "$1,$vios,$r_slotid";
                        }
                        my $value = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_vscsi_slot_config", 0, $v_info);
                        if (@$value[1] && ((@$value[1] =~ /Error/i) && (@$value[2] ne '0'))) {
                            return ([ [ $name, @$value[1], '1' ] ]);
                        } else {
                            push @values, [ $name, "Success", '0' ];
                        }
                    }
                    next;
                } elsif ($op eq "vmmemory") {
                    my @td = @$d;
                    @td[0] = 0;
                    $memhash = &query_cec_info_actions($request, $name, \@td, 1, ["part_get_hyp_process_and_mem"]);
                    unless (scalar keys(%$memhash)) {
                        push @values, [ $mtms, "Can not get hypervisor information", 1 ];
                        next;
                    }
                    if (!exists($memhash->{run})) {
                        if ($param =~ /(\d+)([G|M]?)\/(\d+)([G|M]?)\/(\d+)([G|M]?)/i) {
                            my $memsize = $memhash->{mem_region_size};
                            my $min     = $1;
                            if ($2 eq "G" or $2 eq '') {
                                $min = $min * 1024;
                            }
                            $min = int($min / $memsize);
                            my $cur = $3;
                            if ($4 eq "G" or $4 eq '') {
                                $cur = $cur * 1024;
                            }
                            $cur = int($cur / $memsize);
                            my $max = $5;
                            if ($6 eq "G" or $6 eq '') {
                                $max = $max * 1024;
                            }
                            $max                   = int($max / $memsize);
                            $request->{opt}->{$op} = "$min/$cur/$max";
                            $param                 = $request->{opt}->{$op};
                        } else {
                            return ([ [ $name, "The format of param:$param is incorrect.", 1 ] ]);
                        }
                        $memhash->{run} = 1;
                    }
                    $memhash->{memory}            = $param;
                    $memhash->{lpar_used_regions} = 0;
                    my $ret = &deal_with_avail_mem($request, $name, $d, $memhash);
                    if (ref($ret) eq "ARRAY") {
                        if (@$ret[2]) {
                            return ([ [@$ret] ]);
                        } else {
                            push @values, $ret;
                        }
                    }
                    $param  = $memhash->{memory};
                    $action = "part_set_lpar_pending_mem";
                } elsif ($op eq "bsr") {
                    $action = "set_lpar_bsr";
                } elsif ($op eq "vios") {
                    print __LINE__. "=========>op=vios===\n";
                    next;
                } else {
                    last;
                }
                my $tmp_value = ($param eq '*') ? $name : $param;
                xCAT::MsgUtils->verbose_message($request, "$request->{command} $action for node:$name, parm:$tmp_value.");
                my @tmpd = @$d;
                if ($op eq "del_physlots") {
                    @tmpd[0] = "-1";
                }
                my $value = xCAT::FSPUtils::fsp_api_action($request, $name, \@tmpd, $action, 0, $tmp_value);
                if (@$value[1] && ((@$value[1] =~ /Error/i) && (@$value[2] ne '0'))) {
                    return ([ [ $name, @$value[1], '1' ] ]);
                } else {
                    push @values, [ $name, "Success", '0' ];
                }
            }
            if (@query_array) {
                my $rethash = query_cec_info_actions($request, $name, $d, 1, \@query_array);
                unless (scalar keys(%$rethash)) {
                    push @values, [ $mtms, "Can not get hypervisor information", 1 ];
                    next;
                }

                # need to add update db here
                $lpar_hash{$name} = $rethash;
                $lpar_hash{$name}->{parent} = @$d[3];
            }
        }
    }
    if (%lpar_hash) {
        update_vm_db($request, \%lpar_hash);
    }
    return \@values;
}

sub check_node_info {
    my $hash     = shift;
    my $not_lpar = undef;
    while (my ($mtms, $h) = each(%$hash)) {
        while (my ($name, $d) = each(%$h)) {
            my $node_type = @$d[4];
            if ($node_type !~ /^lpar$/) {
                $not_lpar = $name;
                last;
            }
        }
    }
    return $not_lpar;
}

sub op_extra_cmds {
    my $request   = shift;
    my $hash      = shift;
    my $node      = $request->{node};
    my $lpar_flag = &check_node_info($hash);
    if (defined($lpar_flag)) {
        return ([ [ $lpar_flag, "Node must be LPAR", 1 ] ]);
    }
    return &do_op_extra_cmds($request, $hash);
}


##########################################################################
# Changes the configuration of an existing
# partition based on the profile specified
##########################################################################
sub modify_by_prof {
    my $request = shift;
    my $hash    = shift;
    my $name    = @{ $request->{node} }[0];
    my $opt     = $request->{opt};
    my @values;
    my $cfgdata = $opt->{profile};
    my $profile;
    my $cec_name;
    my $td;
    my %io         = ();
    my %lpar_state = ();
    my @result;
    xCAT::MsgUtils->verbose_message($request, "$request->{command} START.");

    if (defined($request->{stdin})) {
        my $p = $request->{stdin};
        my @io = split(/\n/, $p);
        foreach (@io) {
            chomp;
            if ($_ =~ /(\d+):(\s+)(\d+)\/([\w\.\-]+)\/(\w+)\//) {
                push @$cfgdata, $_;
            } else {
                return (\[ "Error", "Invalid line in profile: $_", -1 ]);
            }

        }
    }

    #print Dumper($cfgdata);
    while (my ($cec, $h) = each(%$hash)) {
        while (my ($lpar, $d) = each(%$h)) {
            $td = $d;

            #@$td[4] = "fsp";
            $cec_name = @$d[3];
        }
        $td->[4] = "cec";

        #get the current I/O slot information
        xCAT::MsgUtils->verbose_message($request, "$request->{command} :get_io_slot_info for node:$cec_name.");
        my $action = "get_io_slot_info";
        my $values = xCAT::FSPUtils::fsp_api_action($request, $cec_name, $td, $action);
        my $Rc = $$values[2];
        if ($Rc != 0) {
            push @result, [ $cec_name, $$values[1], $Rc ];
            return (\@result);
        }
        my @data = split(/\n/, $$values[1]);
        foreach my $v (@data) {
            my ($lparid, $busid, $location, $drc_index, $owner_type, $owner, $descr) = split(/,/, $v);
            $io{$drc_index}{lparid}     = $lparid;
            $io{$drc_index}{owner_type} = $owner_type;
            $io{$drc_index}{owner}      = $owner;
        }

        #get all the nodes state in the same cec
        xCAT::MsgUtils->verbose_message($request, "$request->{command} :get all the nodes state for CEC:$cec_name.");
        $action = "all_lpars_state";
        undef($values);
        my $values = xCAT::FSPUtils::fsp_state_action($request, $cec_name, $td, $action);
        $Rc = shift(@$values);
        if ($Rc != 0) {
            push @result, [ $cec_name, $$values[0], $Rc ];
            return (\@result);
        }
        foreach (@$values) {
            my ($state, $lparid) = split /,/;
            $lpar_state{$lparid} = $state;
        }
    }
    ##################################
    # Check if LPAR profile exists
    ###################################
    while (my ($cec, $h) = each(%$hash)) {
        while (my ($lpar, $d) = each(%$h)) {
            my $id = @$d[0];

            #print Dumper($cfgdata);
            my @found = grep(/^$id:/, @$cfgdata);

            #print Dumper(\@found);
            my $action   = "set_io_slot_owner";
            my $tooltype = 0;
            foreach my $f (@found) {

                #'1: 514/U78A9.001.0123456-P1-C17/0x21010202/2/1'
                my ($bus, $location, $drc_index, @t) = split(/\//, $f);
                my $orig_id = $io{$drc_index}{lparid};

                # the current owning lpar and the new owning lpar must be in power off  state
                if (($lpar_state{$orig_id} ne "Not Activated") || ($lpar_state{$id} ne "Not Activated")) {
                    push @result, [ $lpar, "For the I/O $location, the current owning lpar(id=$orig_id) of the I/O  and the new owning lpar(id=$id) must be in Not Activated state at first. And then run chvm again", -1 ];
                    return (\@result);
                }

                xCAT::MsgUtils->verbose_message($request, "$request->{command} :set_io_slot_owner io_slot_info:$f,owner:$lpar.");
                my $values = xCAT::FSPUtils::fsp_api_action($request, $lpar, $d, $action, $tooltype, $drc_index);

                #my $Rc = shift(@$values);
                my $Rc = pop(@$values);
                if ($Rc != 0) {
                    push @result, [ $lpar, $$values[1], $Rc ];
                    next;
                }
            }

        }
    }
    xCAT::MsgUtils->verbose_message($request, "$request->{command} END.");
    return (\@result);
}


sub enumerate {

    my $request = shift;
    my $h       = shift;
    my $mtms    = shift;
    my %outhash = ();
    my $cec;
    my $type;
    my @td;

    xCAT::MsgUtils->verbose_message($request, "lsvm :enumerate START for mtms:$mtms.");
    while (my ($name, $d) = each(%$h)) {
        $cec  = @$d[3];
        $type = @$d[4];
        @td   = @$d;
    }

    $td[4] = "cec";
    xCAT::MsgUtils->verbose_message($request, "lsvm :enumerate get_io_slot_info for node:$cec.");
    my $action = "get_io_slot_info";
    my $values = xCAT::FSPUtils::fsp_api_action($request, $cec, \@td, $action);

    #my $Rc = shift(@$values);
    my $Rc = $$values[2];
    if ($Rc != 0) {
        $outhash{1} = "The LPARs' I/O slots information could NOT be listed  because the cec is in power off state";
    } else {
        $outhash{0} = $$values[1];
    }

    #my @t;
    #foreach my $value ( @$values ) {
    #    my ($lparid, $busid, $slot_location_code, $drc_index,@t ) = split (/,/, $value);
    #    push (@{$outhash{$lparid}}, $value);
    #}

    if ($type =~ /^(fsp|cec)$/) {
        xCAT::MsgUtils->verbose_message($request, "lsvm :enumerate query_octant_cfg for node:$cec.");
        $action = "query_octant_cfg";
        my $values = xCAT::FSPUtils::fsp_api_action($request, $cec, \@td, $action);
        my $Rc = pop(@$values);
        if ($Rc != 0) {
            return ([ $Rc, $$values[1] ]);
        }

        #$outhash{ $cec } = @$values[0];
        my $data            = $$values[1];
        my @value           = split(/:/, $data);
        my $pendingpumpmode = $value[0];
        my $currentpumpMode = $value[1];
        my $octantcount     = $value[2];
        my $j               = 3;
        my $res = "PendingPumpMode=$pendingpumpmode,CurrentPumpMode=$currentpumpMode,OctantCount=$octantcount:\n";
        for (my $i = 0 ; $i < $octantcount ; $i++) {
            $res = $res . "OctantID=" . $value[ $j++ ] . ",PendingOctCfg=" . $value[ $j++ ] . ",CurrentOctCfg=" . $value[ $j++ ] . ",PendingMemoryInterleaveMode=" . $value[ $j++ ] . ",CurrentMemoryInterleaveMode=" . $value[ $j++ ] . ";\n";
        }
        $outhash{$cec} = $res;
    }

    xCAT::MsgUtils->verbose_message($request, "lsvm :enumerate END for mtms:$mtms.");
    return ([ 0, \%outhash ]);
}

sub get_cec_attr_info {
    my $request = shift;
    my $name    = shift;
    my $attr    = shift;
    my $op      = shift;
    my %op_hash = (
        lpar_info => "get_lpar_info",
        bsr       => "get_cec_bsr",
        huge_page => "get_huge_page"
    );
    my $action = $op_hash{$op};
    my $values = xCAT::FSPUtils::fsp_api_action($request, $name, $attr, $action);
    if (@$values[1] && ((@$values[1] =~ /Error/i) && @$values[2] ne '0')) {
        return ([ [ $name, @$values[1], '1' ] ]);
    }
    return @$values[1];
}

sub get_cec_lpar_hugepage {
    my $name      = shift;
    my $huge_info = shift;
    my $lparid    = shift;
    my $lparname  = shift;
    my @value     = split(/\n/, $huge_info);
    foreach my $v (@value) {
        if ($v =~ /\s*([^\s]+)\s*:\s*([\d|\/]+)/) {
            my $tmp_name = $1;
            my $tmp_num  = $2;
            if ($tmp_name =~ /^$lparname$/) {
                return $tmp_num;
            }
        }
    }
    return ([ [ $name, "can not get huge page info for lpar id $lparid", '1' ] ]);

}

sub get_cec_lpar_name {
    my $name      = shift;
    my $lpar_info = shift;
    my $lparid    = shift;
    my @value     = split(/\n/, $lpar_info);
    foreach my $v (@value) {
        if ($v =~ /lparname:\s*([^\,]*),\s*lparid:\s*([\d]+),/) {
            if ($2 eq $lparid) {
                return $1;
            }
        }
    }
    return ([ [ $name, "can not get lparname for lpar id $lparid", '1' ] ]);
}

sub get_cec_lpar_bsr {
    my $name      = shift;
    my $lpar_info = shift;
    my $lparid    = shift;
    my $lparname  = shift;
    my @value     = split(/\n/, $lpar_info);
    foreach my $v (@value) {
        if ($v =~ /\s*([^\s]+)\s*:\s*([\d]+)/) {
            my $tmp_name = $1;
            my $tmp_num  = $2;
            if ($tmp_name =~ /^$lparname$/) {
                return $tmp_num;
            }
        }
    }
    return ([ [ $name, "can not get BSR info for lpar id $lparid", '1' ] ]);
}

sub get_cec_cec_bsr {
    my $name      = shift;
    my $lpar_info = shift;
    my $index     = 0;
    my @value     = split(/\n/, $lpar_info);
    my $cec_bsr   = "";
    foreach my $v (@value) {
        if ($v =~ /(Number of BSR arrays:)\s*(\d+)/i) {
            $cec_bsr .= "$1 $2,";
            $index++;
        } elsif ($v =~ /(Bytes per BSR array:)\s*(\d+)/i) {
            $cec_bsr .= "$1 $2,";
            $index++;
        } elsif ($v =~ /(Available BSR array:)\s*(\d+)/i) {
            $cec_bsr .= "$1 $2;\n";
            $index++;
        }
    }
    if ($index != 3) {
        return undef;
    } else {
        return $cec_bsr;
    }
}

sub get_cec_cec_hugepage {
    my $name         = shift;
    my $huge_info    = shift;
    my $index        = 0;
    my @value        = split(/\n/, $huge_info);
    my $cec_hugepage = "";
    foreach my $v (@value) {
        if ($v =~ /(Available huge page memory\(in pages\):)\s*(\d+)/i) {
            my $tmp = sprintf "%-40s %s;\n", $1, $2;
            $cec_hugepage .= $tmp;
            $index++;
        } elsif ($v =~ /(Configurable huge page memory\(in pages\):)\s*(\d+)/i) {
            my $tmp = sprintf "%-40s %s;\n", $1, $2;
            $cec_hugepage .= $tmp;
            $index++;
        } elsif ($v =~ /(Page Size\(in GB\):)\s*(\d+)/i) {
            my $tmp = sprintf "%-40s %s;\n", $1, $2;
            $cec_hugepage .= $tmp;
            $index++;
        } elsif ($v =~ /(Maximum huge page memory\(in pages\):)\s*(\d+)/i) {
            my $tmp = sprintf "%-40s %s;\n", $1, $2;
            $cec_hugepage .= $tmp;
            $index++;
        } elsif ($v =~ /(Requested huge page memory\(in pages\):)\s*(\d+)/i) {
            my $tmp = sprintf "%-40s %s;\n", $1, $2;
            $cec_hugepage .= $tmp;
            $index++;
        }
    }
    if ($index != 5) {
        return undef;
    }
    return $cec_hugepage;
}

##########################################################################
# Lists logical partitions
##########################################################################
sub list {
    my $request = shift;
    my $hash    = shift;
    my $args    = $request->{opt};
    my $values  = ();
    my @value   = ();
    my $node_name;
    my $d;
    my @result;
    my $lpar_infos;
    my $bsr_infos;
    my $huge_infos;
    my %lpar_huges = ();
    my $l_string   = "\n";

    #print Dumper($hash);
    xCAT::MsgUtils->verbose_message($request, "lsvm START");
    while (my ($mtms, $h) = each(%$hash)) {
        my $info = enumerate($request, $h, $mtms);
        my $Rc   = shift(@$info);
        my $data = @$info[0];

        while (($node_name, $d) = each(%$h)) {
            my $cec  = @$d[3];
            my $type = @$d[4];

            my $id = @$d[0];

            if ($Rc != SUCCESS) {
                push @result, [ $node_name, $data, $Rc ];
                next;
            }
            my $values = $data->{0};
            my $msg    = $data->{1};

            # if ( !exists( $data->{$id} )) {
            #     push @result, [$node_name, "Node not found",1];
            # 	next;
            # }
            xCAT::MsgUtils->verbose_message($request, "lsvm :parse io info for node:$node_name.");
            if (defined($msg)) {
                push @result, [ $node_name, $msg, 0 ];
            } else {

                # get the I/O slot information
                if ($request->{opt}->{l}) {
                    if ($type =~ /^(fsp|cec)$/) {
                        $bsr_infos = get_cec_attr_info($request, $node_name, $d, "bsr");
                        if (ref($bsr_infos) eq 'ARRAY') {
                            return $bsr_infos;
                        }
                        $huge_infos = get_cec_attr_info($request, $node_name, $d, "huge_page");
                        if (ref($huge_infos) eq 'ARRAY') {
                            return $huge_infos;
                        }
                    }
                    $lpar_infos = get_cec_attr_info($request, $node_name, $d, "lpar_info");
                    if (ref($lpar_infos) eq 'ARRAY') {
                        return $lpar_infos;
                    }
                }
                my $v;
                my @t;
                my @value = split(/\n/, $values);
                foreach my $v (@value) {
                    my ($lparid, @t) = split(/,/, $v);
                    my $ios = join('/', @t);
                    if ($request->{opt}->{l}) {
                        my $lparname = get_cec_lpar_name($node_name, $lpar_infos, $lparid);
                        my $hugepage;
                        if ($type =~ /^(fsp|cec)$/) {
                            my $lpar_bsr = get_cec_lpar_bsr($node_name, $bsr_infos, $lparid, $lparname);
                            if (ref($lpar_bsr) eq 'ARRAY') {
                                return $lpar_bsr;
                            }
                            $ios .= ": " . $lpar_bsr;
                            $hugepage = get_cec_lpar_hugepage($node_name, $huge_infos, $lparid, $lparname);
                            if (ref($hugepage) eq 'ARRAY') {
                                return $hugepage;
                            }
                        } else {
                            if ($lparid ne $id) {
                                next;
                            }
                            if (defined($lpar_huges{$lparid})) {
                                $hugepage = $lpar_huges{$lparid};
                            } else {
                                $hugepage = get_cec_attr_info($request, $node_name, $d, "huge_page");
                                if (ref($hugepage) eq 'ARRAY') {
                                    return $hugepage;
                                }
                                $lpar_huges{$lparid} = $hugepage;
                            }
                        }
                        $ios .= ": " . $hugepage;
                        if (ref($lparname) eq 'ARRAY') {
                            return $lparname;
                        } else {
                            $lparname = "$lparname: $lparid";
                        }
                        $l_string .= "$lparname: " . $ios . "\n";
                    } else {
                        if ($type =~ /^(fsp|cec)$/) {
                            push @result, [ $lparid, $ios, $Rc ];
                        } else {
                            if ($lparid eq $id) {
                                push @result, [ $lparid, $ios, $Rc ];
                            }
                        }
                    }
                }
            }

            # get the octant configuration value
            if ($type =~ /^(fsp|cec)$/) {
                xCAT::MsgUtils->verbose_message($request, "lsvm :parse octant info for $type:$node_name.");
                my $value = $data->{$cec};
                if ($request->{opt}->{l}) {
                    my $cec_bsr = get_cec_cec_bsr($node_name, $bsr_infos);
                    my $cec_hugepage = get_cec_cec_hugepage($node_name, $huge_infos);
                    $l_string .= $value . $cec_bsr;
                    $l_string .= $cec_hugepage;
                } else {
                    $l_string = $value;
                }
            }
            if ($l_string =~ /^\n$/) {
                next;
            }
            push @result, [ $node_name, $l_string, $Rc ];
            $l_string = "\n";
        }    # end of while
    }    # end of while
    xCAT::MsgUtils->verbose_message($request, "lsvm END.");
    return (\@result);
}



##########################################################################
# Lists logical partitions
##########################################################################
sub list_orig {

    my $request = shift;
    my $hash    = shift;
    my $args    = $request->{opt};
    my $values  = ();
    my @value   = ();
    my $node_name;
    my $d;
    my @result;

    while (my ($mtms, $h) = each(%$hash)) {
        while (($node_name, $d) = each(%$h)) {
            my $lparid = @$d[0];
            my $mtms   = @$d[2];
            my $type   = @$d[4];
            my $pprofile;

            ####################################
            # Must be CEC or LPAR
            ####################################
            if ($type !~ /^(lpar|fsp|cec)$/) {

                #$values->{$lpar} = [$lpar,"Node must be LPAR or CEC",RC_ERROR];
                return ([ $node_name, "Node must be LPAR or CEC", RC_ERROR ]);

                #next;
            }

            ####################################
            # This is a single LPAR
            ####################################
            if ($type eq "lpar") {

                #$lpars[0] = "$lpar,$lparid";

            }
            ####################################
            # This is a CEC
            ####################################
            else {
                my $values = xCAT::FSPUtils::fsp_api_action($request, $node_name, $d, "query_octant_cfg");
                my $Rc   = @$values[2];
                my $data = @$values[1];
                if ($Rc != SUCCESS) {
                    push @result, [ $node_name, $data, $Rc ];
                } else {
                    my @value           = split(/,/, $data);
                    my $pendingpumpmode = $value[0];
                    my $currentpumpMode = $value[1];
                    my $octantcount     = $value[2];
                    my $j               = 3;
                    my $res = "PendingPumpMode=$pendingpumpmode,CurrentPumpMode=$currentpumpMode,OctantCount=$octantcount:";
                    for (my $i = 0 ; $i < $octantcount ; $i++) {
                        $res = $res . "OctantID=" . $value[ $j++ ] . ",PendingOctCfg=" . $value[ $j++ ] . ",CurrentOctCfg=" . $value[ $j++ ] . ",PendingMemoryInterleaveMode=" . $value[ $j++ ] . ",CurrentMemoryInterleaveMode" . $value[ $j++ ] . ";";
                    }
                    push @result, [ $node_name, $res, $Rc ];
                }
            }
        }
    }
    return (\@result);
}



##########################################################################
# Creates/changes logical partitions
##########################################################################
sub create {

    my $request = shift;
    my $hash    = shift;
    my $opt     = $request->{opt};
    my @values  = ();
    my @result;
    my $cec_name;
    my $d;
    my $lparid;
    my $mtms;
    my $type;
    my $profile;
    my $starting_lpar_id = $opt->{i};
    my $octant_cfg       = $opt->{octant_cfg};
    my $node_number      = @{ $opt->{target} };
    my %node_id          = ();
    my @nodes            = @{ $opt->{target} };

    xCAT::MsgUtils->verbose_message($request, "$request->{command} START.");

    #print Dumper($request);
    #####################################
    # Get source node information
    #####################################
    while (my ($cec, $h) = each(%$hash)) {
        while (my ($name, $data) = each(%$h)) {
            $d        = $data;
            $lparid   = @$d[0];
            $mtms     = @$d[2];
            $type     = @$d[4];
            $cec_name = $name;
            #####################################
            # Must be LPAR
            #####################################
            if ($type !~ /^(fsp|cec)$/) {
                return ([ [ $cec_name, "Node's parent must be fsp or CEC", RC_ERROR ] ]);
            }
        }

        xCAT::MsgUtils->verbose_message($request, "$request->{command} :query_octant_cfg for CEC:$cec_name.");
        my $values = xCAT::FSPUtils::fsp_api_action($request, $cec_name, $d, "query_octant_cfg");
        my $Rc = shift(@$values);
        if ($Rc != 0) {
            return ([ [ $cec_name, $$values[0], $Rc ] ]);
        }
        my @v = split(/:/, $$values[0]);
        $octant_cfg->{pendingpumpmode} = $v[0];


        my $number_of_lpars_per_octant;
        my $octant_num_needed;
        my $starting_octant_id;
        my $octant_conf_value;
        my $octant_cfg_value            = $octant_cfg->{octant_cfg_value};
        my $new_pending_interleave_mode = $octant_cfg->{memory_interleave};


        $starting_octant_id = int($starting_lpar_id / 4);
        my $lparnum_from_octant   = 0;
        my $new_pending_pump_mode = $octant_cfg->{pendingpumpmode};
        my $parameters;

        #my $parameters = "$new_pending_pump_mode:$octant_num_needed";
        my $octant_id = $starting_octant_id;
        my $i         = 0;
        my $res;
        for ($i = 0 ; $i < (keys %$octant_cfg_value) ; $i++) {
            if (!exists($octant_cfg_value->{$octant_id})) {
                $res = "starting LPAR id is $starting_lpar_id, starting octant id is $starting_octant_id. The octants should be used continuously. Octant $octant_id  configuration value isn't provided. Wrong plan.";
                return ([ [ $cec_name, $res, -1 ] ]);

            }
            my $octant_conf_value = $octant_cfg_value->{$octant_id};

            #octant configuration values could be 1,2,3,4,5 ; AS following:
            #  1 - 1 partition with all cpus and memory of the octant
            #  2 - 2 partitions with a 50/50 split of cpus and memory
            #  3 - 3 partitions with a 25/25/50 split of cpus and memory
            #  4 - 4 partitions with a 25/25/25/25 split of cpus and memory
            #  5 - 2 partitions with a 25/75 split of cpus and memory
            if ($octant_conf_value == 1) {
                $number_of_lpars_per_octant = 1;
            } elsif ($octant_conf_value == 2) {
                $number_of_lpars_per_octant = 2;
            } elsif ($octant_conf_value == 3) {
                $number_of_lpars_per_octant = 3;
            } elsif ($octant_conf_value == 4) {
                $number_of_lpars_per_octant = 4;
            } elsif ($octant_conf_value == 5) {
                $number_of_lpars_per_octant = 2;
            } else {
                $res = "octant $i, configuration values: $octant_conf_value. Wrong octant configuration values!\n";
                return ([ [ $cec_name, $res, -1 ] ]);
            }
            my $j;
            for ($j = 1 ; $j < $number_of_lpars_per_octant + 1 ; $j++) {
                if (@nodes) {
                    my $node = shift(@nodes);
                    $node_id{$node} = $j + $octant_id * 4;
                }
            }

            $lparnum_from_octant += $number_of_lpars_per_octant;
            $octant_num_needed++;
            $parameters .= ":$octant_id:$octant_conf_value:$new_pending_interleave_mode";
            $octant_id++;

        }
        $parameters = "$new_pending_pump_mode:$octant_num_needed" . $parameters;
        ##if($node_number != $lparnum_from_octant ) {##
        if ($node_number > $lparnum_from_octant) {
            $res = "According to the partition split rule and the starting LPAR id, $lparnum_from_octant LPARs will be gotten. But the noderange has $node_number node.  Wrong plan.\n";
            return ([ [ $cec_name, $res, -1 ] ]);
        }




        xCAT::MsgUtils->verbose_message($request, "$request->{command} :set_octant_cfg for CEC:$cec_name,param:$parameters.");

        #$values = xCAT::FSPUtils::fsp_api_create_parttion( $starting_lpar_id, $octant_cfg, $node_number, $d, "set_octant_cfg");
        $values = xCAT::FSPUtils::fsp_api_action($request, $cec_name, $d, "set_octant_cfg", 0, $parameters);
        my $Rc   = $$values[2];
        my $data = $$values[1];
        if ($Rc != SUCCESS) {
            push @result, [ $cec_name, $data, $Rc ];
        } else {
            foreach my $name (@{ $opt->{target} }) {
                push @result, [ $name, "Success", $Rc ];
                xCAT::FSPvm::xCATdB("mkvm", $name, "", $node_id{$name}, $d, "fsp", $name);
            }
            push @result, [ $cec_name, "Please reboot the CEC $cec_name before using chvm to assign the I/O slots to the LPARs", "mkvm" ];

            #$request->{callback}->({info => ["Please reboot the CEC $cec_name before using chvm to assign the I/O slots to the LPARs"]});
        }

    }

    xCAT::MsgUtils->verbose_message($request, "$request->{command} END.");
    return (\@result);
}


##########################################################################
# Adds/removes LPARs from the xCAT database
##########################################################################
sub xCATdB {

    my $cmd     = shift;
    my $name    = shift;
    my $profile = shift;
    my $lparid  = shift;
    my $d       = shift;
    my $hwtype  = shift;
    my $lpar    = shift;
    my $parent  = shift;

    #######################################
    # Remove entry
    #######################################
    if ($cmd eq "rmvm") {
        return (xCAT::PPCdb::rm_ppc($name));
    }
    #######################################
    # Change entry
    #######################################
    elsif ($cmd eq "chvm") {
        my $ppctab = xCAT::Table->new("ppc", -create => 1, -autocommit => 1);

        ###################################
        # Error opening ppc database
        ###################################
        if (!defined($ppctab)) {
            return ("Error opening 'ppc' database");
        }
        $ppctab->setNodeAttribs($name, { pprofile => $profile });
    }
    #######################################
    # Add entry
    #######################################
    else {
        if (!defined($profile)) {
            $profile = $name;
        }
        my ($model, $serial) = split /\*/, @$d[2];
        my $server = @$d[3];
        my $fsp    = @$d[2];

        ###################################
        # Find FSP name in ppc database
        ###################################
        my $tab = xCAT::Table->new("ppc");

        ###################################
        # Error opening ppc database
        ###################################
        if (!defined($tab)) {
            return ("Error opening 'ppc' database");
        }
        ###################################
        # If there is no parent provided
        # this lpar should be the cloned
        # in the same cec
        # Otherwise it should be cloned
        # between cecs
        ###################################
        if (!$parent)
        {
            my ($ent) = $tab->getNodeAttribs($lpar, ['parent']);

            ###################################
            # Node not found
            ###################################
            if (!defined($ent)) {
                return ("'$lpar' not found in 'ppc' database");
            }
            ###################################
            # Attributes not found
            ###################################
            if (!exists($ent->{parent})) {
                return ("'parent' attribute not found in 'ppc' database");
            }
            $parent = $ent->{parent};
        }

        my $values = join(",",
            "lpar",
            $name,
            $lparid,
            $model,
            $serial,
            "",
            $server,
            $profile,
            $parent);

        return (xCAT::PPCdb::add_ppc($hwtype, [$values], '', '', "FSP"));
    }
    return undef;
}
########################
#***** partition related
########################

#my @partition_query_actions = qw(part_get_partition_cap part_get_num_of_lpar_slots part_get_hyp_config_process_and_mem part_get_hyp_avail_process_and_mem part_get_service_authority_lpar_id part_get_shared_processing_resource part_get_all_vio_info lpar_lhea_mac part_get_all_io_bus_info part_get_lpar_processing part_get_lpar_memory get_huge_page get_cec_bsr);
my @partition_query_actions = qw(part_get_partition_cap part_get_hyp_process_and_mem part_get_all_io_bus_info get_huge_page get_cec_bsr);

sub parse_part_get_info {
    my $hash   = shift;
    my $data   = shift;
    my $lparid = shift;
    my @array  = split /\n/, $data;
    foreach my $line (@array) {
        chomp($line);
        if ($line =~ /Num of lpar slots: (\d+)/i) {
            $hash->{num_of_lpars} = $1;
        } elsif ($line =~ /HYP Configurable Memory[^\(]*\((\d+)\s*regions\)/i) {
            $hash->{hyp_config_mem} = $1;
        } elsif ($line =~ /HYP Available Memory[^\(]*\((\d+)\s*regions\)/i) {
            $hash->{hyp_avail_mem} = $1;
        } elsif ($line =~ /HYP Memory Region Size[^\(]*\((\d+)\s*MB\)/i) {
            $hash->{mem_region_size} = $1;
        } elsif ($line =~ /HYP Configurable Processors: (\d+),\s*Avail Processors: (\d+)/i) {
            $hash->{process_units_config} = $1;
            $hash->{process_units_avail}  = $2;
        } elsif ($line =~ /Authority Lpar id:(\w+)/i) {
            $hash->{service_lparid} = $1;
        } elsif ($line =~ /(\d+),(\d+),[^,]*,(\w+),\w*\(([\w| |-|_|\/]*)\)/) {
            $hash->{bus}->{$3}->{cur_lparid} = $1;
            $hash->{bus}->{$3}->{bus_slot}   = $2;
            $hash->{bus}->{$3}->{des}        = $4;
            if ($lparid and $lparid eq $1) {
                push @{ $hash->{lpar_phy_bus} }, $3;
            }
        } elsif ($line =~ /Phy drc_index:(\w+), Port group: (\w+), Phy port id: (\w+)/) {
            $hash->{phy_drc_group_port}->{$1}->{$2}->{$3} = '1';

            #} elsif ($line =~ /adapter_id=(\w+),lpar_id=([\d|-]+).*port_group=(\d+),phys_port_id=(\d+).*drc_index=(\w+),.*/) {
            if (($2 == -1) && ($4 == 255)) {
                $hash->{logic_drc_phydrc}->{$3}->{$5} = $1;

                #$hash->{logic_drc_phydrc}->{$5}->{$1} = [$2,$3,$4];
            }

            #} elsif ($line =~ /lpar 0:: Curr  Memory::min: 1,cur: (\d+),max:/i) {
        } elsif ($line =~ /HYP Reserved Memory Regions:([-]?)(\d+), Min Required Regions:(\d+)/i) {
            if ($1 eq '-') {
                $hash->{lpar0_used_dec} = 1;
            }
            $hash->{lpar0_used_mem}  = $2;
            $hash->{phy_min_mem_req} = $3;

            #print "===>lpar0_used_mem:$hash->{lpar0_used_mem}.\n";
        } elsif ($line =~ /Curr Memory (Min|Req|Max):\s*([\d]*)[^\(]*\((\d+)\s*regions\)/) {
            if ($1 eq 'Min') {
                $hash->{lpar_mem_min} = $2
            } elsif ($1 eq 'Max') {
                $hash->{lpar_mem_max} = $2;
            } else {
                $hash->{lpar_mem_req}      = $2;
                $hash->{lpar_used_regions} = $3;
            }
        } elsif ($line =~ /Curr Processor (Min|Req|Max):\s*(\d+)/) {
            if ($1 eq 'Min') {
                $hash->{lpar_cpu_min} = $2;
            } elsif ($1 eq 'Max') {
                $hash->{lpar_cpu_max} = $2;
            } else {
                $hash->{lpar_cpu_req} = $2;
            }
        } elsif ($line =~ /\s*lpar_id=(\d+),type=vSCSI,slot=(\d+),attr=(\d+).*remote_lpar_id=0x(\w+),remote_slot_num=0x(\w+)/) {
            if ($3 eq '0') {
                my $lparid = hex($4);
                my $slotid = hex($5);
                push @{ $hash->{lpar_vmstorage_client} }, "$lparid:$slotid";
            } else {
                if (exists($hash->{lpar_vmstorage_server})) {
                    $hash->{lpar_vmstorage_server}++;
                } else {
                    $hash->{lpar_vmstorage_server} = 1;
                }
            }
        } elsif ($line =~ /\s*lpar_id=(\d+),type=(vEth),slot=(\d+).*port_vlan_id=(\d+),mac_addr=(\w+)/) {
            push @{ $hash->{lpar_vmnics} }, "vlan$4";
        } elsif ($line =~ /Available huge page memory\(in pages\):\s*(\d+)/) {
            $hash->{huge_page_avail} = $1;
        } elsif ($line =~ /Available BSR array:\s*(\d+)/) {
            $hash->{cec_bsr_avail} = $1;
        } elsif ($line =~ /^\d+\/(\d+)\/\d+$/) {
            if ($1 ne 0) {
                push @{ $hash->{lpar_othersetting} }, "hugepage:$1";
            }
        } elsif ($line =~ /^(\d+)\.$/) {
            if ($1 ne 0) {
                push @{ $hash->{lpar_othersetting} }, "bsr:$1";
            }
        }
    }
}

sub query_cec_info_actions {
    my $request      = shift;
    my $name         = shift;
    my $td           = shift;
    my $usage        = shift;
    my $action_array = shift;
    my $lpar_hash    = shift;
    my $lparid       = @$td[0];
    my $data;
    my @array = ();
    my %hash  = ();

    if (!defined($action_array) or ref($action_array) ne "ARRAY") {
        $action_array = \@partition_query_actions;
    }

    foreach my $action (@$action_array) {

        #$data .= "======> ret info for $action:\n";
        my $values = xCAT::FSPUtils::fsp_api_action($request, $name, $td, $action);
        chomp(@$values[1]);

        #if ($action eq "part_get_partition_cap" and (@$values[1] =~ /Error:/i or @$values[2] ne 0)) {
        if (@$values[1] =~ /Error:/i or @$values[2] ne 0) {
            next;    #return ([[@$values]]);
        }
        if (@$values[1] =~ /^$/) {
            next;
        }
        if ($usage == 1 or $usage == 2) {
            &parse_part_get_info(\%hash, @$values[1], $lparid);
        }

        if ($usage == 0 or $usage == 2) {
            if ($lparid) {
                if ($action eq "lpar_lhea_mac") {
                    my @output = split /\n/, @$values[1];
                    foreach my $line (@output) {
                        if ($line =~ /adapter_id=\w+,lpar_id=$lparid,type=hea/) {

                            #$data .= "$line\n";
                            push @array, [ $name, $line, 0 ];
                        }
                    }

                    #$data .= "\n";
                    next;
                }
                if ($action eq "part_get_all_io_bus_info") {
                    my @output = split /\n/, @$values[1];
                    foreach my $line (@output) {
                        if ($line =~ /^$lparid,/) {

                            #$data .= "$line\n";
                            push @array, [ $name, $line, 0 ];
                        }
                    }

                    #$data .= "\n";
                    next;
                }
                if ($action eq "part_get_all_vio_info") {
                    my @output = split /\n/, @$values[1];
                    my ($drc_index, $drc_name);
                    foreach my $line (@output) {
                        chomp($line);
                        if ($line =~ /Index:.*drc_index:([^,]*),\s*drc_name:(.*)$/) {
                            $drc_index = $1;
                            $drc_name  = $2;
                            next;
                        } elsif ($line =~ /\s*lpar_id=(\d+),type=(vSCSI|vSerial),slot=(\d+),attr=(\d+).*remote_lpar_id=(0x\w+),remote_slot_num=(0x\w+)/) {
                            if ($4 eq '0') {
                                push @array, [ $name, "$1,$3,$drc_name,$drc_index,$2 Client(Server_lparid=$5,Server_slotid=$6)", 0 ];
                            } else {
                                push @array, [ $name, "$1,$3,$drc_name,$drc_index,$2 Server", 0 ];
                            }
                        } elsif ($line =~ /\s*lpar_id=(\d+),type=(vEth),slot=(\d+).*port_vlan_id=(\d+),mac_addr=(\w+)/) {
                            push @array, [ $name, "$1,$3,$drc_name,$drc_index,$2 (port_vlanid=$4,mac_addr=$5)", 0 ];

                            #} elsif ($line =~ /\s*lpar_id=(\d+),type=(\w+),slot=(\d+)/) {
                            #    push @array, [$name, "$1,$3,$drc_name,$drc_index,$2", 0];
                            #} else {
                            #print "=====>line:$line\n";
                            #push @array, [$name, $line, 0];
                        }
                        $drc_index = '';
                        $drc_name  = '';
                    }
                    next;
                }
            }

            #$data .= "@$values[1]\n\n";
            push @array, [ $name, @$values[1], @$values[2] ];
        }
    }
    if ($usage == 0 or $usage == 2) {

        #return $data;
        if ($usage == 2) {
            %$lpar_hash = %hash;
        }
        return \@array;
    } else {
        return \%hash;
    }
}

sub update_vm_db {
    my $request     = shift;
    my $lpar_hash   = shift;
    my $vm_hd       = xCAT::Table->new('vm');
    my %name_id_map = ();
    my $commit      = 0;
    foreach (keys(%$lpar_hash)) {
        my %db_update = ();
        my $node_hash = $lpar_hash->{$_};
        if (exists($node_hash->{lpar_cpu_min})) {
            $db_update{cpus} = "$node_hash->{lpar_cpu_min}/$node_hash->{lpar_cpu_req}/$node_hash->{lpar_cpu_max}";
        }
        if (exists($node_hash->{lpar_mem_min})) {
            $db_update{memory} = "$node_hash->{lpar_mem_min}/$node_hash->{lpar_mem_req}/$node_hash->{lpar_mem_max}";
        }
        if (exists($node_hash->{lpar_vmstorage_server})) {
            $db_update{storage} = $node_hash->{lpar_vmstorage_server};
        } elsif (exists($node_hash->{lpar_vmstorage_client})) {
            my @tmp_array = ();
            foreach (@{ $node_hash->{lpar_vmstorage_client} }) {
                if (/(\d+):(\d+)/) {
                    if (exists($name_id_map{$1})) {
                        push @tmp_array, "$name_id_map{$1}:$2";
                    } else {
                        my $vios_name = &find_lpar_name($request, $node_hash->{parent}, $1);
                        if (defined($vios_name)) {
                            $name_id_map{$1} = $vios_name;
                            push @tmp_array, "$vios_name:$2";
                        }
                    }
                }
            }
            $db_update{storage} = join(",", @tmp_array);
        }
        if (exists($node_hash->{lpar_vmnics})) {
            $db_update{nics} = join(",", @{ $node_hash->{lpar_vmnics} });
        }
        if (exists($node_hash->{lpar_phy_bus})) {
            $db_update{physlots} = join(",", @{ $node_hash->{lpar_phy_bus} });
        }
        if (exists($node_hash->{lpar_othersetting})) {
            $db_update{othersettings} = join(",", @{ $node_hash->{lpar_othersetting} });
        }
        if (%db_update) {
            $vm_hd->setNodeAttribs($_, \%db_update);
            $commit = 1;
        }
    }
    if ($commit) {
        $vm_hd->commit;
    }
}

#my @partition_query_actions = qw(part_get_partition_cap part_get_num_of_lpar_slots part_get_hyp_config_process_and_mem part_get_hyp_avail_process_and_mem part_get_service_authority_lpar_id part_get_shared_processing_resource part_get_all_vio_info lpar_lhea_mac part_get_all_io_bus_info part_get_lpar_processing part_get_lpar_memory get_huge_page get_cec_bsr);
sub query_cec_info {
    my $request   = shift;
    my $hash      = shift;
    my $args      = $request->{opt};
    my @td        = ();
    my @result    = ();
    my $usage     = 0;
    my %lpar_hash = ();

    #print Dumper($request);
    #print Dumper($hash);
    while (my ($mtms, $h) = each(%$hash)) {
        while (my ($name, $d) = each(%$h)) {
            my %tmp_hash = ();
            @td = @$d;
            if (@$d[0] == 0 && @$d[4] !~ /lpar|vios/) {
                last;
            }

            #my $rethash = query_cec_info_actions($request, $name, $d, 0, ["part_get_lpar_processing","part_get_lpar_memory","part_get_all_vio_info","lpar_lhea_mac","part_get_all_io_bus_info","get_huge_page","get_cec_bsr"]);
            if ($args->{updatedb}) {
                $usage = 2;
            }
            my $rethash = query_cec_info_actions($request, $name, $d, $usage, [ "part_get_lpar_processing", "part_get_lpar_memory", "part_get_all_io_bus_info", "part_get_all_vio_info", "get_huge_page", "get_cec_bsr" ], \%tmp_hash);

            #push @result, [$name, $rethash, 0];
            #push @result, @$rethash;
            if (scalar(@$rethash)) {
                push @result, @$rethash;
            } else {
                push @result, [ $name, "No information obtained.", -1 ];
                last;
            }
            $lpar_hash{$name} = \%tmp_hash;
            $lpar_hash{$name}->{parent} = @$d[3];
        }
        if (@td[0] == 0) {
            my $rethash = query_cec_info_actions($request, @td[3], \@td, $usage);
            if (scalar(@$rethash)) {
                push @result, @$rethash;
            } else {
                push @result, [ @td[3], "No information obtained.", -1 ];
                last;
            }

            #push @result, [@td[3], $rethash, 0];
            #push @result, @$rethash;
        }
    }
    if ($args->{updatedb} and %lpar_hash) {
        update_vm_db($request, \%lpar_hash);
    }
    return \@result;
}

########################
#***** partition related
########################

my @partition_config_actions = qw/part_set_lpar_def_state part_set_lpar_pending_proc part_set_lpar_pending_mem part_set_pending_max_vslots part_set_lpar_shared_pool_util_auth part_set_lpar_group_id part_set_lpar_avail_priority part_set_partition_placement part_set_lhea_assign_info part_set_phea_port_info part_set_lhea_port_info part_set_veth_slot_config part_set_vscsi_slot_config part_set_vfchan_slot_config part_clear_vslot_config set_huge_page set_lpar_name/;

sub set_lpar_undefined {
    my $request = shift;
    my $name    = shift;
    my $attr    = shift;
    my $values = xCAT::FSPUtils::fsp_api_action($request, $name, $attr, "part_set_lpar_def_state", 0, 0x0);
    if (!@$values[2]) {
        return ([ $name, "Done", 0 ]);
    }
    return $values;
}

sub clear_service_authority_lpar {
    my $request = shift;
    my $name    = shift;
    my $attr    = shift;
    my $values = xCAT::FSPUtils::fsp_api_action($request, $name, $attr, "part_get_service_authority_lpar_id");
    my @array          = split /\n/, @$values[1];
    my $service_lparid = undef;
    foreach my $line (@array) {
        if ($line =~ /Authority Lpar id:([-|\d]+)./i) {
            $service_lparid = $1;
        }
    }
    if (defined($service_lparid) and $service_lparid == @$attr[0]) {
        xCAT::FSPUtils::fsp_api_action($request, $name, $attr, "part_set_service_authority_lpar_id");
    }
}

sub remove {
    my $request = shift;
    my $hash    = shift;
    my @result  = ();
    while (my ($mtms, $h) = each(%$hash)) {
        while (my ($name, $d) = each(%$h)) {
            if (@$d[4] ne "lpar") {
                push @result, [ $name, "Node must be LPAR", 1 ];
                last;
            }
            &clear_service_authority_lpar($request, $name, $d);
            my $values = &set_lpar_undefined($request, $name, $d);
            push @result, $values;
        }
    }
    return \@result;
}

sub deal_with_avail_mem {
    my $request  = shift;
    my $name     = shift;
    my $d        = shift;
    my $lparhash = shift;
    my $max_required_regions;
    if ($lparhash->{memory} =~ /(\d+)\/(\d+)\/(\d+)/) {
        my ($min, $cur, $max);
        my $used_regions = 0;
        my $cur_avail    = 0;
        $min = $1;
        $cur = $2;
        $max = $3;
        my %tmphash;
        my $values;

        if (exists($lparhash->{lpar_used_regions})) {
            $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_get_lpar_memory");
            &parse_part_get_info(\%tmphash, @$values[1]);
            if (exists($tmphash{lpar_used_regions})) {
                $used_regions = $tmphash{lpar_used_regions};
            }
        }
        $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_get_hyp_res_mem_regions", 0, $3);
        &parse_part_get_info(\%tmphash, @$values[1]);
        if (exists($tmphash{lpar0_used_mem}) && exists($tmphash{phy_min_mem_req})) {
            if ($min < $tmphash{phy_min_mem_req}) {
                $min = $tmphash{phy_min_mem_req};
            }

            if (exists($lparhash->{lpar0_used_dec})) {
                $cur_avail = $lparhash->{hyp_avail_mem} + $used_regions + $tmphash{lpar0_used_mem};
            } else {
                $cur_avail = $lparhash->{hyp_avail_mem} + $used_regions - $tmphash{lpar0_used_mem};
            }

            #xCAT::MsgUtils->verbose_message($request, "====****====used:$used_regions,avail:$cur_avail,($min:$cur:$max).");
            if ($cur_avail < $min) {
                my $cur_mem_in_G = $lparhash->{hyp_avail_mem} * $lparhash->{mem_region_size} * 1.0 / 1024;
                return ([ $name, "Parse reserverd regions failed, no enough memory, available:$cur_mem_in_G GB.", 1 ]);
            }
            if (($cur_avail > 0) and ($cur > $cur_avail)) {
                my $cur_avail_in_G = $cur_avail * $lparhash->{mem_region_size} * 1.0 / 1024;
                $lparhash->{memory} = "$min/$cur_avail/$max";
                unless ($lparhash->{full_par}) {
                    return ([ $name, "Available memory is less than required, allocate $cur_avail_in_G GB.", 0 ]);
                }
            }
        } else {
            return ([ $name, "Failed to get hypervisor reserved memory regions.", 1 ]);
        }
    }
    return 0;
}

sub find_lpar_id {
    my $request = shift;
    my $parent  = shift;
    my $name    = shift;
    my %mapping = %{ $request->{ppc}->{$parent}->{mapping} };
    if (exists($mapping{$name})) {
        return $mapping{$name};
    }
    return undef;
}

sub find_lpar_name {
    my $request = shift;
    my $parent  = shift;
    my $id      = shift;
    my %mapping = %{ $request->{ppc}->{$parent}->{mapping} };
    foreach (keys %mapping) {
        if ($mapping{$_} eq $id) {
            return $_;
        }
    }
    return undef;
}

sub create_lpar {
    my $request  = shift;
    my $name     = shift;
    my $d        = shift;
    my $lparhash = shift;
    my @ret      = ();
    my $values;
    if (exists($request->{opt}->{vios})) {
        $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_lpar_def_state", 0, 0x03);
    } else {
        $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_lpar_def_state", 0, 0x01);
    }
    if (@$values[2] ne 0) {
        return ([ [ $name, @$values[1], @$values[0] ] ]);
    }
    $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "set_lpar_name", 0, $name);
    if (@$values[2] ne 0) {
        &set_lpar_undefined($request, $name, $d);
        return ([ [ $name, @$values[1], @$values[0] ] ]);
    }
    xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_lpar_shared_pool_util_auth");
    xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_lpar_group_id");
    xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_lpar_avail_priority");

    #print "======>physlots:$lparhash->{physlots}.\n";
    if (exists($lparhash->{physlots})) {
        $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "set_io_slot_owner_uber", 0, $lparhash->{physlots});

        #$values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "set_io_slot_owner", 0, join(",",@phy_io_array));
        if (@$values[2] ne 0) {
            &set_lpar_undefined($request, $name, $d);
            return ([ [ $name, @$values[1], @$values[2] ] ]);
        }
    }
    if (exists($lparhash->{nics})) {
        my @vlans = split /,/, $lparhash->{nics};
        foreach (@vlans) {
            if (/vlan(\d+)/i) {
                my $vlanid = $1;
                my $mac    = lc(xCAT::VMCommon::genMac($name));
                if ($mac =~ /(..):(..):(..):(..):(..):(..)/) {
                    my $tail = hex($6) + $vlanid;
                    $mac = sprintf("$1$2$3$4$5%02x", $tail);
                }
                $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_veth_slot_config", 0, "0,$vlanid,$mac");
                if (@$values[2] ne 0) {
                    &set_lpar_undefined($request, $name, $d);
                    return ([ [ $name, @$values[1], @$values[2] ] ]);
                }
            }
        }
    }
    if (exists($lparhash->{storage})) {
        foreach my $v_info (@{ $lparhash->{storage} }) {
            $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_vscsi_slot_config", 0, $v_info);
            if (@$values[2] ne 0) {
                &set_lpar_undefined($request, $name, $d);
                return ([ [ $name, @$values[1], @$values[2] ] ]);
            }
        }
    }

    # ======  ====== #
    if (exists($lparhash->{phy_hea})) {
        my $phy_hash = $lparhash->{phy_hea};
        foreach my $phy_drc (keys %$phy_hash) {

            #print "======> set_lhea_assign_info: drc_index:$phy_drc.\n";
            xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_lhea_assign_info", 0, $phy_drc);
            my $group_hash = $phy_hash->{$phy_drc};
            foreach my $group_id (keys %$group_hash) {
                my @lhea_drc = (keys %{ $lparhash->{logic_drc_phydrc}->{$group_id} });
                foreach my $phy_port_id (keys %{ $group_hash->{$group_id} }) {
                    my $tmp_param = "$phy_drc,$group_id,$phy_port_id";

                    #print "======> set_phea_port_info: $tmp_param.\n";
                    xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_phea_port_info", 0, $tmp_param);
                    my $tmp_lhea_param = $lhea_drc[$phy_port_id] . ",$phy_port_id";

                    #print "======> set_lhea_port_info: $tmp_lhea_param.\n";
                    xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_lhea_port_info", 0, $tmp_lhea_param);
                }
                delete($lparhash->{logic_drc_phydrc}->{$group_id}->{ $lhea_drc[0] });
                delete($lparhash->{logic_drc_phydrc}->{$group_id}->{ $lhea_drc[1] });
            }
        }
    }

    #print "======>cpus:$lparhash->{cpus}.\n";
    $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_lpar_pending_proc", 0, $lparhash->{cpus});
    if (@$values[2] ne 0) {
        &set_lpar_undefined($request, $name, $d);
        return ([ [ $name, @$values[1], @$values[2] ] ]);
    }
    $values = &deal_with_avail_mem($request, $name, $d, $lparhash);
    if (ref($values) eq "ARRAY") {
        if (@$values[2]) {
            &set_lpar_undefined($request, $name, $d);
            return ([ [@$values] ]);
        } else {
            push @ret, $values;
        }
    }

    #print "======>memory:$lparhash->{memory}.\n";
    $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_lpar_pending_mem", 0, $lparhash->{memory});
    if (@$values[2] ne 0) {
        &set_lpar_undefined($request, $name, $d);
        return ([ [ $name, @$values[1], @$values[2] ] ]);
    }

    xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_lpar_comp_modes");

    #print "======>memory:$lparhash->{huge_page}.\n";
    xCAT::FSPUtils::fsp_api_action($request, $name, $d, "set_huge_page", 0, $lparhash->{huge_page});

    #print "======>bsr:$lparhash->{bsr_num}.\n";
    xCAT::FSPUtils::fsp_api_action($request, $name, $d, "set_lpar_bsr", 0, $lparhash->{bsr_num});
    xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_partition_placement");
    if (exists($request->{opt}->{vios})) {
        $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_lpar_def_state", 0, 0x04);
    } else {
        $values = xCAT::FSPUtils::fsp_api_action($request, $name, $d, "part_set_lpar_def_state", 0, 0x02);
    }
    if (@$values[2] ne 0) {
        return ([ [ $name, @$values[1], @$values[2] ] ]);
    }
    push @ret, [ $name, "Done", 0 ];

    #return ([$name, "Done", 0]);
    return \@ret;
}

sub mkspeclpar {
    my $request = shift;
    my $hash    = shift;
    my $opt     = $request->{opt};
    my $values;
    my @result    = ();
    my %lpar_hash = ();
    my $vmtab     = xCAT::Table->new('vm');
    unless ($vmtab) {
        return ([ [ "Error", "Cannot open vm table", 1 ] ]);
    }
    while (my ($mtms, $h) = each(%$hash)) {
        my $memhash = undef;
        my @nodes   = keys(%$h);
        my $ent = $vmtab->getNodesAttribs(\@nodes, [ 'cpus', 'memory', 'physlots', 'othersettings', 'storage', 'nics' ]);
        while (my ($name, $d) = each(%$h)) {
            if (@$d[4] ne 'lpar') {
                push @result, [ $name, "Node must be LPAR", 1 ];
                last;
            }

            #if (!exists($memhash->{run}))
            #{
            my @td = @$d;
            @td[0] = 0;
            $memhash = &query_cec_info_actions($request, $name, \@td, 1, [ "part_get_hyp_process_and_mem", "lpar_lhea_mac", "part_get_all_io_bus_info" ]);
            unless (scalar keys(%$memhash)) {
                push @result, [ $mtms, "Can not get hypervisor information", 1 ];
                last;
            }

            #$memhash->{run} = 1;
            #}

            my $tmp_ent = $ent->{$name}->[0];
            if (exists($opt->{vmcpus})) {
                $tmp_ent->{cpus} = $opt->{vmcpus};
            }
            if (exists($opt->{vmmemory})) {
                $tmp_ent->{memory} = $opt->{vmmemory};
            }
            if (exists($opt->{vmphyslots})) {
                $tmp_ent->{physlots} = $opt->{vmphyslots};
            }

            if (exists($opt->{vmothersetting})) {
                $tmp_ent->{othersettings} = $opt->{vmothersetting};
            }
            if (exists($opt->{vmstorage})) {
                $tmp_ent->{storage} = $opt->{vmstorage};
            }
            if (exists($opt->{vmnics})) {
                $tmp_ent->{nics} = $opt->{vmnics};
            }


            if (!defined($tmp_ent)) {
                return ([ [ $name, "Not find params", 1 ] ]);

                #} elsif (!exists($tmp_ent->{cpus}) || !exists($tmp_ent->{memory}) || !exists($tmp_ent->{physlots})) {
            } elsif (!exists($tmp_ent->{cpus}) || !exists($tmp_ent->{memory})) {
                return ([ [ $name, "The attribute 'vmcpus', 'vmmemory' are needed to be specified.", 1 ] ]);
            }

            # FIX bug 3873 [FVT]DFM illegal action could work
            #

            if ($tmp_ent->{cpus} =~ /^(\d+)\/(\d+)\/(\d+)$/) {
                unless ($1 <= $2 and $2 <= $3) {
                    return ([ [ $name, "Parameter for 'vmcpus' is invalid", 1 ] ]);
                } elsif ($memhash->{process_units_avail} eq '0') {
                    push @result, [ $name, "No process available", 1 ];
                    next;
                } elsif ($2 > $memhash->{process_units_avail}) {
                    my $cur = $memhash->{process_units_avail};
                    my $min = $1 > $cur ? $cur : $1;
                    $tmp_ent->{cpus} = "$min/$cur/$3";
                    push @result, [ $name, "Available processor is less than required, allocate $cur processors.", 0 ];
                }
            } else {
                return ([ [ $name, "Parameter for 'vmcpus' is invalid", 1 ] ]);
            }
            if ($tmp_ent->{memory} =~ /^([\d|.]+)([G|M]?)\/([\d|.]+)([G|M]?)\/([\d|.]+)([G|M]?)$/i) {
                if ($memhash->{hyp_avail_mem} eq '0') {
                    push @result, [ $name, "No memory available", 1 ];
                    next;
                }
                my ($mmin, $mcur, $mmax);
                if ($2 eq "G" or $2 eq '') {
                    $mmin = $1 * 1024;
                }
                if ($4 eq "G" or $4 eq '') {
                    $mcur = $3 * 1024;
                }
                if ($6 eq "G" or $6 eq '') {
                    $mmax = $5 * 1024;
                }
                unless ($mmin <= $mcur and $mcur <= $mmax) {
                    return ([ [ $name, "Parameter for 'vmmemory' is invalid", 1 ] ]);
                }
                my $memsize = $memhash->{mem_region_size};
                $mmin              = int(($mmin + $memsize - 1) / $memsize);
                $mcur              = int(($mcur + $memsize - 1) / $memsize);
                $mmax              = int(($mmax + $memsize - 1) / $memsize);
                $tmp_ent->{memory} = "$mmin/$mcur/$mmax";
                $tmp_ent->{mem_region_size} = $memsize;
            } else {
                return ([ [ $name, "Parameter for 'vmmemory' is invalid", 1 ] ]);
            }

            if (exists($tmp_ent->{physlots})) {
                my @tmp_array = split ",", $tmp_ent->{physlots};
                foreach (@tmp_array) {
                    unless (/(0x\w{8})/i) {
                        return ([ [ $name, "Parameter:$_ for 'vmphyslots' is invalid", 1 ] ]);
                    }
                }
            }
            if (exists($tmp_ent->{othersettings})) {
                my @tmp_array = split ",", $tmp_ent->{othersettings};
                foreach (@tmp_array) {
                    unless (/^(bsr|hugepage):\d+$/) {
                        return ([ [ $name, "Parameter:$_ for 'vmothersetting' is invalid", 1 ] ]);
                    }
                }
            }

            if (exists($tmp_ent->{nics})) {
                my @tmp_array = split ",", $tmp_ent->{nics};
                foreach (@tmp_array) {
                    unless (/^vlan\d+$/i) {
                        return ([ [ $name, "Parameter:$_ for 'vmnics' is invalid", 1 ] ]);
                    }
                }
            }


            if (exists($opt->{vios})) {
                if (!exists($tmp_ent->{physlots})) {
                    my @phy_io_array = keys(%{ $memhash->{bus} });
                    $tmp_ent->{physlots} = join(",", @phy_io_array);
                }
                if (exists($tmp_ent->{storage}) and $tmp_ent->{storage} !~ /^\d+$/) {
                    return ([ [ $name, "Parameter for 'vmstorage' is invalid", 1 ] ]);
                } elsif (exists($tmp_ent->{storage})) {
                    my $num   = $tmp_ent->{storage};
                    my @array = ();
                    for (1 .. $num) {
                        push @array, '0';
                    }
                    $tmp_ent->{storage} = \@array;
                }
            } else {
                if (exists($tmp_ent->{storage})) {
                    my @tmp_array = split ",", $tmp_ent->{storage};
                    my $storage_array = undef;
                    foreach (@tmp_array) {
                        if (/([\w_-]*):(\d+)/) {
                            my $vios = &find_lpar_id($request, @$d[3], $1);
                            my $r_slotid = $2;
                            if (defined($vios)) {
                                push @$storage_array, "0,$vios,$r_slotid";
                            } else {
                                return ([ [ $name, "Cannot find lparid for Server lpar:$1" ] ]);
                            }
                        } else {
                            return ([ [ $name, "Parameter for 'vmstorage' is invalid", 1 ] ]);
                        }
                    }
                    $tmp_ent->{storage} = $storage_array;

                }
            }

            $tmp_ent->{hyp_config_mem} = $memhash->{hyp_config_mem};
            $tmp_ent->{hyp_avail_mem}  = $memhash->{hyp_avail_mem};
            if (exists($tmp_ent->{othersettings})) {
                my $setting = $tmp_ent->{othersettings};
                if ($setting =~ /hugepage:(\d+)/) {
                    my $tmp = $1;
                    if ($tmp >= 1) {
                        $tmp_ent->{huge_page} = "1/" . $tmp . "/" . $tmp;
                    }
                }
                if ($setting =~ /bsr:(\d+)/) {
                    if ($1 >= 1) {
                        $tmp_ent->{bsr_num} = $1;
                    }
                }
            }
            $tmp_ent->{phy_hea}          = $memhash->{phy_drc_group_port};
            $tmp_ent->{logic_drc_phydrc} = $memhash->{logic_drc_phydrc};
            $values = &create_lpar($request, $name, $d, $tmp_ent);
            push @result, @$values;

            #need to add update db here
            my $rethash = query_cec_info_actions($request, $name, $d, 1, [ "part_get_lpar_processing", "part_get_lpar_memory", "part_get_all_vio_info", "part_get_all_io_bus_info", "get_huge_page", "get_cec_bsr" ]);
            unless (scalar keys(%$rethash)) {
                push @result, [ $mtms, "Can not get hypervisor information", 1 ];
                next;
            }
            $lpar_hash{$name} = $rethash;
            $lpar_hash{$name}->{parent} = @$d[3];

            $name = undef;
            $d    = undef;
        }
    }
    if (%lpar_hash) {
        update_vm_db($request, \%lpar_hash);
    }
    return \@result;
}

sub mkfulllpar {
    my $request = shift;
    my $hash    = shift;
    my $values;
    my @result = ();
    while (my ($mtms, $h) = each(%$hash)) {
        my $rethash;
        while (my ($name, $d) = each(%$h)) {
            if (@$d[4] ne 'lpar') {
                push @result, [ $name, "Node must be LPAR", 1 ];
                last;
            }
            if (!exists($rethash->{run})) {
                my @td = @$d;
                @td[0] = 0;
                $rethash = query_cec_info_actions($request, $name, \@td, 1);
                unless (scalar keys(%$rethash)) {
                    push @result, [ $mtms, "Can not get hypervisor information", 1 ];
                    next;
                }
                $rethash->{run} = 1;

                #print Dumper($rethash);
            }
            my %lpar_param = ();
            $lpar_param{cpus} = "1/" . $rethash->{process_units_avail} . "/" . $rethash->{process_units_config};
            $lpar_param{memory} = "1/" . $rethash->{hyp_avail_mem} . "/" . $rethash->{hyp_config_mem};
            $lpar_param{hyp_config_mem}  = $rethash->{hyp_config_mem};
            $lpar_param{hyp_avail_mem}   = $rethash->{hyp_avail_mem};
            $lpar_param{mem_region_size} = $rethash->{mem_region_size};
            my @phy_io_array = keys(%{ $rethash->{bus} });
            $lpar_param{physlots} = join(",", @phy_io_array);
            $lpar_param{huge_page} = "1/" . $rethash->{huge_page_avail} . "/" . $rethash->{huge_page_avail};
            $lpar_param{bsr_num}          = $rethash->{cec_bsr_avail};
            $lpar_param{phy_hea}          = $rethash->{phy_drc_group_port};
            $lpar_param{logic_drc_phydrc} = $rethash->{logic_drc_phydrc};
            $lpar_param{full_par}         = 1;
            $values = &create_lpar($request, $name, $d, \%lpar_param);
            $rethash->{logic_drc_phydrc} = $lpar_param{logic_drc_phydrc};
            push @result, @$values;
            $name = undef;
            $d    = undef;
        }
    }
    return \@result;
}

##########################################################################
# Creates logical partitions
##########################################################################
sub mkvm {
    my $request = $_[0];
    my $opt     = $request->{opt};

    # decide if issuing mkvm with the option '-f'.
    # if yes, mklpar will be invoked to
    # create a full system partition for each CECs managed by the HMC.
    if (exists($opt->{p775})) {
        return (create(@_));
    }
    if (exists($opt->{full})) {
        return (mkfulllpar(@_));
    } else {
        return (mkspeclpar(@_));
    }
}

##########################################################################
# Change logical partition
##########################################################################
sub chvm {
    return (modify(@_));
}


##########################################################################
# No rmvm for Power 775
##########################################################################
sub rmvm {
    my $request = $_[0];
    my $opt     = $request->{opt};
    if (exists($opt->{p775})) {
        return ([ [ "lpar", "rmvm only support Power Partitioning.", 1 ] ]);
    } else {
        return (remove(@_));
    }

    #    return( remove(@_) );
}

##########################################################################
# Lists logical partition profile
##########################################################################
sub lsvm {
    my $request = shift;
    my $hash    = shift;
    my $args    = $request->{opt};
    if (exists($args->{p775})) {
        return (list($request, $hash));
    } else {
        return (query_cec_info($request, $hash));
    }
}

1;
