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

package xCAT_plugin::lsslp;
use lib "/opt/xcat/lib/perl";
use strict;
use Getopt::Long;
use Socket;
use xCAT::Usage;
use POSIX "WNOHANG";
use Storable qw(freeze thaw);
use Time::HiRes qw(gettimeofday);
use xCAT::SvrUtils qw/sendmsg/;
use IO::Select;
use XML::Simple;
$XML::Simple::PREFERRED_PARSER='XML::Parser';
use xCAT::PPCdb;
use xCAT::NodeRange;
use xCAT::Utils;
use xCAT::MacMap;
use xCAT::IMMUtils;
use xCAT_plugin::blade;
use xCAT::SLP;
require xCAT::data::ibmhwtypes;


my $mpahash;
my $defaultbladeuser;
my $defaultbladepass;
my $currentbladepass;
my $currentbladeuser;
my %nodebymp;
my $macmap;
my %chassisbyuuid;
my %flexchassisuuid;
my %flexchassismap;
my %passwordmap;
my %doneaddrs;
my %btresult;
my $option_s;

#######################################
# Constants
#######################################
use constant {
    HARDWARE_SERVICE => "service:management-hardware.IBM",
    SOFTWARE_SERVICE => "service:management-software.IBM",
    WILDCARD_SERVICE => "service:management-*",
    SERVICE_FSP      => "cec-service-processor",
    SERVICE_BPA      => "bulk-power-controller",
    SERVICE_CEC      => "cec-service-processor",
    SERVICE_FRAME    => "bulk-power-controller",
    SERVICE_HMC      => "hardware-management-console",
    SERVICE_IVM      => "integrated-virtualization-manager",
    SERVICE_MM       => "management-module",
    SERVICE_CMM      => "chassis-management-module",
    SERVICE_IMM2     => "integrated-management-module2",
    SERVICE_RSA      => "remote-supervisor-adapter",
    SERVICE_RSA2     => "remote-supervisor-adapter-2",
    #SLP_CONF         => "/usr/local/etc/slp.conf",
    #SLPTOOL          => "/usr/local/bin/slptool",
    TYPE_MM          => "mm",
    TYPE_CMM         => "cmm",
    TYPE_IMM2        => "imm2",
    TYPE_RSA         => "rsa",
    TYPE_BPA         => "bpa",
    TYPE_HMC         => "hmc",
    TYPE_IVM         => "ivm",
    TYPE_FSP         => "fsp",
    TYPE_CEC         => "cec",
    TYPE_FRAME       => "frame",
    IP_ADDRESSES     => 4,
    TEXT             => 0,
    FORMAT           => 1,
    SUCCESS          => 0,
    RC_ERROR         => 1,
};

#######################################
# Globals
#######################################
my %service_slp = (
    @{[ SERVICE_FSP    ]} => TYPE_FSP,
    @{[ SERVICE_BPA    ]} => TYPE_BPA,
    @{[ SERVICE_CEC    ]} => TYPE_CEC,
    @{[ SERVICE_FRAME  ]} => TYPE_FRAME,
    @{[ SERVICE_HMC    ]} => TYPE_HMC,
    @{[ SERVICE_IVM    ]} => TYPE_IVM,
    @{[ SERVICE_MM     ]} => TYPE_MM,
    @{[ SERVICE_CMM    ]} => TYPE_CMM,
    @{[ SERVICE_IMM2   ]} => TYPE_IMM2,
    @{[ SERVICE_RSA    ]} => TYPE_RSA,
    @{[ SERVICE_RSA2   ]} => TYPE_RSA
);

#######################################
# SLP display header
#######################################
my @header = (
    ["device",        "%-8s" ],
    ["type-model",    "%-12s"],
    ["serial-number", "%-15s"],
    ["side",          "%-6s" ],
    ["ip-addresses",  "placeholder"],
    ["hostname",      "%s"]
);
my %headertoattr = (
    "device"        =>  "type",
    "type-model"    =>  "mtm",
    "serial-number" =>  "serial",
    "side"          =>  "side",
    "ip-addresses"  =>  "ip",
    "hostname"      =>  "hostname",
);

#######################################
# Invalid IP address list
#######################################
my @invalidiplist = (
        "192.168.2.144",
        "192.168.2.145",
        "192.168.2.146",
        "192.168.2.147",
        "192.168.2.148",
        "192.168.2.149",
        "192.168.3.144",
        "192.168.3.145",
        "192.168.3.146",
        "192.168.3.147",
        "192.168.3.148",
        "192.168.3.149",
        "169.254.",
        "127.0.0.0",
        "127",
        0,
        );

#######################################
# Power methods
#######################################


my %globalopt;
#these globals are only used in mn
my %ip_addr    = ();



#my $macmap;
my @filternodes;
my $TRACE = 0;
my $DEBUG_MATCH = 0;

my %globalhwtype = (
    fsp   => $::NODETYPE_FSP,
    bpa   => $::NODETYPE_BPA,
    lpar  => $::NODETYPE_LPAR,
    hmc   => $::NODETYPE_HMC,
    ivm   => $::NODETYPE_IVM,
    frame => $::NODETYPE_FRAME,
    cec   => $::NODETYPE_CEC,
    cmm   => $::NODETYPE_CMM,
    imm2   => $::NODETYPE_IMM2,
    pbmc  => "pbmc",
);
my %globalnodetype = (
    fsp   => $::NODETYPE_PPC,
    bpa   => $::NODETYPE_PPC,
    cec   => $::NODETYPE_PPC,
    frame => $::NODETYPE_PPC,
    hmc   => $::NODETYPE_PPC,
    ivm   => $::NODETYPE_PPC,
    cmm   => $::NODETYPE_MP,
    lpar  =>"$::NODETYPE_PPC,$::NODETYPE_OSI",
    pbmc  => $::NODETYPE_PPC,
);
my %globalmgt = (
    fsp   => "fsp",
    bpa   => "bpa",
    cec   => "fsp",
    frame => "bpa",
    mm    => "blade",
    ivm   => "ivm",
    rsa   => "blade",
    cmm   => "blade",
    imm2  => "blade",
    hmc   => "hmc",
    pbmc  => "ipmi",
);
my %globalid = (
     fsp   => "cid",
     cec   => "cid",
     bpa   => "fid",
     frame => "fid"
);
##########################################################################
# Command handler method from tables
##########################################################################
sub handled_commands {

    return( {lsslp=>"lsslp"} );
}

##########################################################################
# Invokes the callback with the specified message
##########################################################################
sub send_msg {

    my $request = shift;
    my $ecode   = shift;
    my $msg     = shift;
    my %output;

    #################################################
    # Called from child process - send to parent
    #################################################
    if ( exists( $request->{pipe} )) {
        my $out = $request->{pipe};

        $output{errorcode} = $ecode;
        $output{data} = \@_;
        print $out freeze( [\%output] );
        print $out "\nENDOFFREEZE6sK4ci\n";
    }
    #################################################
    # Called from parent - invoke callback directly
    #################################################
    elsif ( exists( $request->{callback} )) {
        my $callback = $request->{callback};
        $output{errorcode} = $ecode;
        $output{data} = $msg;
        $callback->( \%output );
    }
}


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

    my $request  = shift;
    my $args     = $request->{arg};
    my $cmd      = $request->{command};
    my %opt;
    my %services = (
        HMC   => SOFTWARE_SERVICE.":".SERVICE_HMC.":",
        IVM   => SOFTWARE_SERVICE.":".SERVICE_IVM.":",
        BPA   => HARDWARE_SERVICE.":".SERVICE_BPA,
        FSP   => HARDWARE_SERVICE.":".SERVICE_FSP,
        CEC   => HARDWARE_SERVICE.":".SERVICE_CEC,
        FRAME => HARDWARE_SERVICE.":".SERVICE_FRAME,
        RSA   => HARDWARE_SERVICE.":".SERVICE_RSA.":",
        CMM   => HARDWARE_SERVICE.":".SERVICE_CMM,
        IMM2  => HARDWARE_SERVICE.":".SERVICE_IMM2,
        MM    => HARDWARE_SERVICE.":".SERVICE_MM.":",
        PBMC  => HARDWARE_SERVICE.":".SERVICE_FSP
    );
    #############################################
    # Responds with usage statement
    #############################################
    local *usage = sub {
        my $usage_string = xCAT::Usage->getUsage($cmd);
        return( [$_[0], $usage_string] );
    };
    #############################################
    # No command-line arguments - use defaults
    #############################################
    if ( !defined( $args )) {
        return(0);
    }
    #############################################
    # 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" );

    #############################################
    # Process command-line flags
    #############################################
    if (!GetOptions( \%opt,
            qw(h|help V|Verbose v|version i=s x z w r s=s e=s t=s m c n C=s T=s I u range=s flexdiscover updatehosts vpdtable))) {
        return( usage() );
    }

    #############################################
    # Check for node range
    #############################################
    if ( scalar(@ARGV) eq 1 ) {
        my @nodes = xCAT::NodeRange::noderange( @ARGV );
        foreach (@nodes)  {
            push @filternodes, $_;
        }
        unless (@filternodes) {
            return(usage( "Invalid Argument: $ARGV[0]" ));
        }
    } elsif ( scalar(@ARGV) > 1 ) {
        return(usage( "Invalid flag, please check and retry." ));
    }

    #############################################
    # Option -V for verbose output
    #############################################
    if ( exists( $opt{V} )) {
        $globalopt{verbose} = 1;
    }

    #############################################
    # Check for mutually-exclusive formatting
    #############################################
    if ( (exists($opt{r}) + exists($opt{x}) + exists($opt{z}) + exists($opt{vpdtable}) ) > 1 ) {
        return( usage() );
    }
    #############################################
    # Command tries
    #############################################
    if ( exists( $opt{t} )) {
       $globalopt{maxtries} = $opt{t};

       if ( $globalopt{maxtries} !~ /^\d+$/ ) {
           return( usage( "Invalid command tries (1-9)" ));
       }
    }

    #############################################
    # Check for unsupported service type
    #############################################
    if ( exists( $opt{s} )) {
        if ( !exists( $services{$opt{s}} )) {
            return(usage( "Invalid service: $opt{s}" ));
        }
	$option_s = $opt{s};
        $globalopt{service} = $services{$opt{s}};
    }
    #############################################
    # Check the validation of -T option
    #############################################
    if ( exists( $opt{T} )) {
        $globalopt{T} = $opt{T};
        if ( $globalopt{T} !~ /^\d+$/ ) {
            return( usage( "Invalid timeout value, should be number" ));
        }
        #if (!exists( $opt{C} )) {
        #    return ( usage( "-T should be used with -C" ));
        #}
    }

    #############################################
    # Check the validation of -C option
    #############################################
    if ( exists( $opt{C} )) {
        $globalopt{C} = $opt{C};

        if ( $globalopt{C} !~ /^\d+$/ ) {
            return( usage( "Invalid expect entries, should be number" ));
        }
        if ( !exists($opt{i} )) {
            return( usage( "-C should be used with -i" ));
        }
    }

    #############################################
    # Check the validation of -i option
    #############################################
    if ( exists( $opt{i} )) {
        foreach ( split /,/, $opt{i} ) {
            my $ip = $_;
            ###################################
            # Length for IPv4 addresses
            ###################################
            my (@octets) = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
            if ( scalar(@octets) != 4 ) {
                return( usage("Invalid IP address: $ip") );
            }
            foreach my $octet ( @octets ) {
                if (( $octet < 0 ) or ( $octet > 255 )) {
                    return( usage( "Invalid IP address: $ip") );
                }
            }
        }
        $globalopt{i} = $opt{i};
    }

    #############################################
    # write to the database
    #############################################
    if ( exists( $opt{w} )) {
        $globalopt{w} = 1;
    }

    #############################################
    # list the raw information
    #############################################
    if ( exists( $opt{r} )) {
        $globalopt{r} = 1;
    }

    #############################################
    # list the xml formate data
    #############################################
    if ( exists( $opt{x} )) {
        $globalopt{x} = 1;
    }

    #############################################
    # list the stanza formate data
    #############################################
    if ( exists( $opt{z} )) {
        $globalopt{z} = 1;
    }

    #############################################
    # match vpd table
    #############################################
    if ( exists( $opt{vpdtable} )) {
        $globalopt{vpdtable} = 1;
    }
    #########################################################
    # only list the nodes that discovered for the first time
    #########################################################
    if ( exists( $opt{n} )) {
        $globalopt{n} = 1;
    }
    ##############################################
    # unicast
    ##############################################
    if ( exists( $opt{u} )) {
        $globalopt{u} = 1;
                unless (exists( $opt{s} ) and exists ($opt{range})) {
            return( usage( "-u should be used with -s and --range" ));
                }
                $globalopt{range} = $opt{range};
    }
    if (exists( $opt{range} )) {
        unless (exists( $opt{u} )) {
            return( usage( "range is used in unicast mode" ));
        }
    }

    ##############################################
    # warn for no discovered nodes in database
    ##############################################
    if ( exists( $opt{I} )) {
        $globalopt{I} = 1;
    }
    
    ##############################################
    # do slp and setup for cmm
    ##############################################
    if ( exists( $opt{flexdiscover} )) {
        $globalopt{flexdiscover} = 1;
    }
    
    return (0);
}


##########################################################################
# Verbose mode (-V)
##########################################################################
sub trace {

    my $request = shift;
    my $msg     = shift;
    my $sig     = shift;

    if ($sig) {
        if ($TRACE) {
            my ($sec,$min,$hour,$mday,$mon,$yr,$wday,$yday,$dst) = localtime(time);
            my $msg = sprintf "%02d:%02d:%02d %5d %s", $hour,$min,$sec,$$,$msg;
            send_msg( $request, 0, $msg );
        }
    } else {
        if ( $globalopt{verbose})  {
            my ($sec,$min,$hour,$mday,$mon,$yr,$wday,$yday,$dst) = localtime(time);
            my $msg = sprintf "%02d:%02d:%02d %5d %s", $hour,$min,$sec,$$,$msg;
            send_msg( $request, 0, $msg );
        }
    }
}
##########################################################################
# Forks a process to run the slp command (1 per adapter)
##########################################################################
sub fork_cmd {

    my $request  = shift;

    #######################################
    # Pipe childs output back to parent
    #######################################
    my $parent;
    my $child;
    pipe $parent, $child;
    my $pid = xCAT::Utils->xfork();

    if ( !defined($pid) ) {
        ###################################
        # Fork error
        ###################################
        send_msg( $request, 1, "Fork error: $!" );
        return undef;
    }
    elsif ( $pid == 0 ) {
        ###################################
        # Child process
        ###################################
        close( $parent );
        $request->{pipe} = $child;

        invoke_dodiscover($request);
        ########################################
        # Pass result array back to parent
        ########################################
        my @results = ("FORMATDATA6sK4ci");
        my $out = $request->{pipe};

        print $out freeze( \@results );
        print $out "\nENDOFFREEZE6sK4ci\n";
        exit(0);
    }
    else {
        ###################################
        # Parent process
        ###################################
        close( $child );
        return( $parent );
    }
    return(0);
}
##########################################################################
# Run the forked command and send reply to parent
##########################################################################
sub invoke_dodiscover {

    my $request  = shift;

    ########################################
    # SLP command
    ########################################
    my $services;
    my $maxt;
    if ($globalopt{service}) {
        $services = $globalopt{service};
    } else {
        $services = [WILDCARD_SERVICE,HARDWARE_SERVICE,SOFTWARE_SERVICE,SERVICE_IMM2];
    }
	#efix for hmc bug
	if ($services  =~ /hardware-management-console/)  {
            $services = [SOFTWARE_SERVICE];
    }		 
	 
    if ($globalopt{maxtries}) {
        $maxt = $globalopt{maxtries};
    } else {
        $maxt = 0;
    }


    my %arg;
   if ($globalopt{flexdiscover}) { #we do two SLP passes, one to hopefully catch the less numerous management modules reliably, a separate one to best-effort catch imms
       $arg{SrvTypes} = [ qw/service:management-hardware.IBM:chassis-management-module service:management-hardware.IBM:management-module/ ];
       my ($searchmacsref,$sendcount,$rsp) = xCAT::SLP::dodiscover(SrvTypes=>$arg{SrvTypes},Callback=>\&bt_handle_new_slp_entity);
       $arg{SrvTypes} = [ qw/service:management-hardware.IBM:integrated-management-module2/ ];
       my  ($newsearchmacsref,$newsendcount,$newrsp) = xCAT::SLP::dodiscover(SrvTypes=>$arg{SrvTypes},Callback=>\&bt_handle_new_slp_entity);
       foreach (keys %$newsearchmacsref) {
        $searchmacsref->{$_}=$newsearchmacsref->{$_};
       }
       $sendcount += $newsendcount;
       $rsp+=$newrsp;
       return ($searchmacsref,$sendcount,$rsp);
   } 
   $arg{SrvTypes} = $services;
   #$arg{Callback} = \&handle_new_slp_entity;
   $arg{Ip} = $globalopt{i} if($globalopt{i});
   $arg{Retry} = $maxt;
   $arg{Count} = $globalopt{C} if($globalopt{C});
   $arg{Time} = $globalopt{T} if($globalopt{T});
   $arg{nomsg} = 1 if($globalopt{z} or $globalopt{x});
   $arg{reqcallback} = $request->{callback} if($request->{callback});
   if ($globalopt{u}) {
       $arg{unicast} = 1;
           $arg{range} = $globalopt{range};
    }
    my ($searchmacsref,$sendcount,$rsp) = xCAT::SLP::dodiscover(%arg);


    #########################################
    ## Need to check if the result is enough
    #########################################
    #if ( $request->{C} != 0) {
    #    send_msg( $request, 0, "\n Begin to try again, this may takes long time \n" );
    #    my %val_tmp = %$values;
    #    my %found_cec;
    #    for my $v (keys %val_tmp) {
    #        $v =~ /type=([^\)]+)\)\,\(serial-number=([^\)]+)\)\,\(machinetype-model=([^\)]+)\)\,/;
    #        if ( $found_cec{$2.'*'.$3} ne 1 and $1  eq SERVICE_FSP)  {
    #            $found_cec{$2.'*'.$3} = 1;
    #        }
    #    }
    #
    #    my $rlt;
    #    my $val;
    #    my $start_time = Time::HiRes::gettimeofday();
    #    my $elapse;
    #    my $found = scalar(keys %found_cec);
    #    while ( $found < $globalopt{C} ) {
    #        $rlt = xCAT::SLP::dodiscover(SrvTypes=>$services,Callback=>sub { print Dumper(@_) });
    #        $val =  @$rlt[1];
    #        for my $v (keys %$val) {
    #            $v =~ /type=([^\)]+)\)\,\(serial-number=([^\)]+)\)\,\(machinetype-model=([^\)]+)\)\,/;
    #            if ( $found_cec{$2.'*'.$3} ne 1 and $1  eq SERVICE_FSP)  {
    #                $found_cec{$2.'*'.$3} = 1;
    #                $val_tmp{$v} = 1;
    #            }
    #        }
    #        $found = scalar(keys %val_tmp);
    #        $elapse = Time::HiRes::gettimeofday() - $start_time;
    #        if ( $elapse > $globalopt{time_out} ) {
    #            send_msg( $request, 0, "Time out, Force return.\n" );
    #            last;
    #        }
    #    }
    #    send_msg( $request, 0, "Discovered $found nodes \n" );
    #    $values = \%val_tmp;
    #}
    
    return ($searchmacsref,$sendcount,$rsp);
}


##########################################################################
# Formats slp responses
##########################################################################
sub format_output {

    my $request = shift;
    my $searchmacsref = shift;
    my %searchmacs = %$searchmacsref;
    my $length  = length( $header[IP_ADDRESSES][TEXT] );
    my $result;

    ###########################################
    # No responses
    ###########################################
    if ( keys %searchmacs  == 0 ){
        send_msg( $request, 0, "No responses" );
        return;
    }

    ###########################################
    # Check -C -T
    ###########################################
    if ($globalopt{C}){
        if (scalar(keys %searchmacs) ne $globalopt{C}) {
            send_msg( $request, 0, "Timeout...Fource to return" );
        }
    }
    ###########################################
    # Read table to get exists data
    ###########################################
    unless($globalopt{service} =~ /hardware-management-console/) {
        my $errcode = read_from_table();
        if ($errcode) {
            send_msg( $request, 0, "Can't open $errcode table" );
            return;
        }
    }    
    ###########################################
    # Parse responses and add to hash
    ###########################################
    my $outhash = parse_responses( $request, \$length, $searchmacsref );

	#hmc bug efix
	#my $newouthash;
	#if ($globalopt{service} =~ /hardware-management-console/) {
	#    for my $en ( keys %$outhash ) {
	#	    if (${$outhash->{$en}}{type} eq 'hmc') {
	#		    $newouthash->{$en} = $outhash->{$en};
	#		}
	#	}		
	#    $outhash =  $newouthash;
	#}
	
    ###########################################
    # filter the result in the same vlan
    ###########################################
    if ( exists( $globalopt{i} )) {
        my $outhash1 = filtersamevlan( $outhash );
        $outhash = $outhash1;
    }
    # filter the result and keep the specified nodes
    ###########################################
    if ( scalar(@filternodes)) {
        my $outhash1 = filter( $outhash );
        $outhash = $outhash1;
    }

    ###########################################
    # -w flag for write to xCat database
    ###########################################
    if ( $globalopt{w} ) {
        send_msg( $request, 0, "Begin to write into Database, this may change node name" );
        xCATdB( $outhash );
    }



    ###########################################
    # -r flag for raw response format
    ###########################################
    my %rawhash;
    if ( $globalopt{r} ) {
        foreach ( keys %$outhash ) {
            my $raw = ${$outhash->{$_}}{url};
            $rawhash{$raw} = 1;
        }
        foreach my $en ( keys %rawhash ) {
            if ($en =~ /(\(type.*\))/) {
                $result .= "$1\n";
            }
        }

        send_msg( $request, 0, $result );
        return;
    }
    ###########################################
    # -x flag for xml format
    ###########################################
    if ( $globalopt{x} ) {
        send_msg( $request, 0, format_xml( $outhash ));
        return;
    }
    ###########################################
    # -z flag for stanza format
    ###########################################
    if ( $globalopt{z} ) {
        send_msg( $request, 0, format_stanza( $outhash ));
        return;
    }

    ###########################################
    # -T flag for vpd table format
    ###########################################
    if ( $globalopt{vpdtable} ) {
        send_msg( $request, 0, format_table( $outhash ) );
        return;
    }

    ###########################################
    # Get longest IP for formatting purposes
    ###########################################
    my $format = sprintf "%%-%ds", ( $length + 2 );
    $header[IP_ADDRESSES][FORMAT] = $format;

    ###########################################
    # Display header
    ###########################################
    foreach ( @header ) {
        $result .= sprintf @$_[1], @$_[0];
    }
    $result .= "\n";

    ###########################################
    # Display response attributes
    ###########################################
    foreach my $nameentry ( sort keys %$outhash ) {
        my $hostname= ${$outhash->{$nameentry}}{hostname};

        foreach ( @header ) {
            my $attr = $headertoattr{@$_[0]};
            $result .= sprintf @$_[1], ${$outhash->{$nameentry}}{$attr};

        }
        $result .= "\n";
    }
    send_msg( $request, 0, $result );
}

##########################################################################
# Read the table and cache the data that will be used frequently
##########################################################################
sub read_from_table {
    my %vpdhash;
    my @nodelist;
    my %ppchash;
    my %iphash;

    if ( !(%::OLD_DATA_CACHE))
    {
        #find out all the existed nodes
        my $nodelisttab  = xCAT::Table->new('nodelist');
        if ( $nodelisttab ) {
            my @typeentries = $nodelisttab->getAllNodeAttribs( ['node'] );
            for my $typeentry ( @typeentries) {
                push @nodelist, $typeentry->{node};
            }
        } else {
            return "nodelist";
        }

        #find out all the existed nodes
        my $hoststab  = xCAT::Table->new('hosts');
        if ( $hoststab ) {
            my @hostsentries = $hoststab->getAllNodeAttribs( ['node','ip'] );
            for my $hostsentry ( @hostsentries) {
                $iphash{$hostsentry->{node}} = $hostsentry->{ip};
            }
        } else {
            return "hosts";
        }

        #find out all the existed nodes' type
        my $typehashref = xCAT::DBobjUtils->getnodetype(\@nodelist);

        # find out all the existed nodes' mtms and side
        my $vpdtab  = xCAT::Table->new( 'vpd' );
        if ( $vpdtab )  {
            my @vpdentries = $vpdtab->getAllNodeAttribs(['node','mtm','serial','side']);
            for my $entry ( @vpdentries ) {
                @{$vpdhash{$entry->{node}}}[0] = $entry->{mtm};
                @{$vpdhash{$entry->{node}}}[1] = $entry->{serial};
                @{$vpdhash{$entry->{node}}}[2] = $entry->{side};
            }
        } else {
            return "vpd";
        }

        # find out all the existed nodes' attributes
        my $ppctab  = xCAT::Table->new('ppc');
        if ( $ppctab ) {
            my @identries = $ppctab->getAllNodeAttribs( ['node','id','parent'] );
            for my $entry ( @identries ) {
                next if ($entry->{nodetype} =~ /lpar/);
                @{$ppchash{$entry->{node}}}[0] = $entry->{id};#id
                @{$ppchash{$entry->{node}}}[1] = $entry->{parent};#parent
            }
        } else {
            return "ppc";
        }

        foreach my $node (@nodelist) {
            my $type = $$typehashref{$node};
            my $mtm = @{$vpdhash{$node}}[0];
            my $sn = @{$vpdhash{$node}}[1];
            my $side = @{$vpdhash{$node}}[2];
            my $id = $ppchash{$node}[0];
            my $parent = $ppchash{$node}[1];
            my $pmtm = @{$vpdhash{$parent}}[0];
            my $psn = @{$vpdhash{$parent}}[1];
            my $ip = $iphash{$node};
            if ($type =~ /frame/){
                $::OLD_DATA_CACHE{"frame*".$mtm."*".$sn} = $node if (defined $mtm and defined $sn);
            }elsif ($type =~ /cec/) {
                $::OLD_DATA_CACHE{"cec*".$mtm."*".$sn} = $node if (defined $mtm and defined $sn);
                my $iid = int($id);
                $parent = 'Server-'.$pmtm.'-SN'.$psn;
                $::OLD_DATA_CACHE{"cec*".$parent."*".$iid} = $node if (defined $parent and defined $id);
            }elsif ($type =~ /^fsp|bpa$/) {
                $::OLD_DATA_CACHE{$type."*".$mtm."*".$sn."*".$side} = $node if (defined $mtm and defined $sn);;
            }elsif ($type =~ /hmc/) {
                $::OLD_DATA_CACHE{"hmc*".$ip} = $node if (defined $ip);
            }else {
                $::OLD_DATA_CACHE{$type."*".$mtm."*".$sn} = $node if (defined $mtm and defined $sn);;
            }
        }
    }
    return undef;
}
##########################################################################
# Makesure the ip in SLP URL is valid
# return 1 if valid, 0 if invalid
##########################################################################
sub check_ip {
    my $myip = shift;
    $myip =~ s/^(\d+)\..*/$1/;
    if ($myip >= 224 and $myip <= 239){
        return 0;
    }
    foreach (@invalidiplist){
        if ( $myip =~ /^($_)/ ){
            return 0;
        }
    }

    return 1;
}
##########################################################################
# Get hostname from SLP URL response
##########################################################################
sub get_host_from_url {

    my $request = shift;
    my $attr    = shift;
    my $vip;
    my $host;

    #######################################
    # Extract IP from URL
    #######################################
    my $nets = xCAT::NetworkUtils::my_nets();
    my $inc = $globalopt{i};
    my @ips = (exists $attr->{'ip-address'})? @{$attr->{'ip-address'}}: @{$attr->{'ipv4-address'}};

    my @ips2 = split /,/, $inc;
    my @validip;
    if ( $inc) {
        for my $net (keys %$nets) {
            my $fg = 1;
            for my $einc (@ips2) {
                if ( $nets->{$net} eq $einc) {
                    $fg = 0;
                }
            }
            delete $nets->{$net} if ($fg) ;
        }
    }
    #######################################
    # Check if valid IP
    #######################################
    for my $tip (@ips) {
        next if ( $tip =~ /:/); #skip IPV6 addresses
        for my $net ( keys %$nets) {
            my ($n,$m) = split /\//,$net;
            if ( #xCAT::NetworkUtils::isInSameSubnet($n, $tip, $m, 1) and
                 xCAT::NetworkUtils::isPingable($tip) and (length(inet_aton($tip)) == 4)) {
                push @validip, $tip;
            }
        }
    }


    if (scalar(@validip) == 0) {
        trace( $request, "Invalid IP address in URL" );
        return undef;
     }


    #######################################
    # Get Factory Hostname
    #######################################
    if ( ${$attr->{'hostname'}}[0] ) {
        $host = ${$attr->{'hostname'}}[0];

    } else {
        $host = "Server-".${$attr->{'machinetype-model'}}[0]."-SN".${$attr->{'serial-number'}}[0];
        foreach my $ip (@validip) {
            my $hname = gethostbyaddr( inet_aton($ip), AF_INET );
            if($hname) {
                $host = $hname;
                $vip = $ip;
                last;

            }
        }
        foreach my $ip (@validip) {
            my $hoststab = xCAT::Table->new( 'hosts' );
            my @entries = $hoststab->getAllNodeAttribs(['node','ip']);
            foreach my $entry ( @entries ) {
                if ( $entry->{ip} and $entry->{ip} eq $ip ) {
                    $host = $entry->{node};
                    $vip = $ip;

                }
            }
        }
    }
    if ( $host =~ /([^\.]+)\./ ) {
            $host = $1;
    }
    return $host;

}

##########################################################################
#
#########################################################################
sub parse_responses {

    my $request = shift;
    my $length  = shift;
    my $searchmacsref = shift;
    my $matchflag;
    my %outhash;
    my $host;
    my @matchnode;
    my @cmmnodes;
    my %searchmacs = %$searchmacsref;

   #get networks information for defining HMC
    my %net;
    my %addr;
    my $nettab = xCAT::Table->new('networks');
    my @nets = $nettab->getAllAttribs('netname', 'net','mask','mgtifname');
    if (scalar(@nets) == 0) {
        send_msg( $request, 0, "Can't get networks information from networks table" );
    } else {
        foreach my $enet (@nets) {
            next if ($enet->{'net'} =~ /:/);
            $net{$enet->{'mgtifname'}}{subnet} = $enet->{'net'};
            $net{$enet->{'mgtifname'}}{netmask} = $enet->{'mask'};
        }
    }
    my $netref = xCAT::NetworkUtils->get_nic_ip();
    for my $entry (keys %$netref) {
        $addr{$netref->{$entry}}{subnet} = $net{$entry}{subnet};
        $addr{$netref->{$entry}}{netmask} = $net{$entry}{netmask};
    }

    trace( $request, "Now lsslp begin to parse its response...");
    foreach my $rsp ( keys(%searchmacs) ) {
        ###########################################
        # attribute not found
        ###########################################
        if ( !exists(${$searchmacs{$rsp}}{attributes} )) {
            trace( $request, "Attribute not found for $rsp" );
            next;
        }
        ###########################################
        # Valid service-type attribute
        ###########################################
        my $attributes = ${$searchmacs{$rsp}}{attributes};
        my $type = ${$attributes->{'type'}}[0] ;
        if ( !exists($service_slp{$type} )) {
            trace( $request, "Discarding unsupported type  $type" );
            next;
        }
		

        ###########################################
        # Define nodes
        ###########################################
        my %atthash;
        if (( $type eq SERVICE_RSA ) or ( $type eq SERVICE_RSA2 ) or
            ( $type eq SERVICE_MM ) or ( $type eq SERVICE_IMM2 )) {
            $atthash{type} = $service_slp{$type};
            $atthash{mtm} = ${$attributes->{'enclosure-machinetype-model'}}[0];
            $atthash{serial} = ${$attributes->{'enclosure-serial-number'}}[0];
            $atthash{slot} = int(${$attributes->{'slot'}}[0]);
            if ( $type eq SERVICE_IMM2 ) { $atthash{ip} = ${$attributes->{'ipv4-address'}}[0]; }
            else { $atthash{ip} = ${$attributes->{'ip-address'}}[0]; }
            $atthash{mac} = $rsp;
            $atthash{hostname} = get_host_from_url($request, $attributes);
            $atthash{otherinterfaces} = ${$attributes->{'ip-address'}}[0];
            $atthash{url} =  ${$searchmacs{$rsp}}{payload};
            $outhash{'Server-'.$atthash{mtm}.'-SN'.$atthash{serial}} = \%atthash;
            $$length = length( $atthash{ip}) if ( length( $atthash{ip} ) > $$length );
            trace( $request, "Discover node $atthash{hostname}: type is $atthash{type}, \
			mtm is $atthash{mtm}, sn is $atthash{serial}, slot is $atthash{slot}, \
			ip is $atthash{ip}, mac is $atthash{mac}, otherinterfaces is $atthash{otherinterfaces}" );

        } elsif ($type eq SERVICE_CMM) {
            $atthash{type} = $service_slp{$type};
            $atthash{mtm} = ${$attributes->{'enclosure-mtm'}}[0];
            $atthash{serial} = ${$attributes->{'enclosure-serial-number'}}[0];
            $atthash{side} = int(${$attributes->{'slot'}}[0]);
            $atthash{ip} = ${$attributes->{'ipv4-address'}}[0];
            $atthash{mac} = $rsp;
            $atthash{mname} = ${$attributes->{'mm-name'}}[0];
            $atthash{url} =  ${$searchmacs{$rsp}}{payload};
            $atthash{hostname} = get_host_from_url($request, $attributes);
			$atthash{mpa} = $atthash{hostname};
            $atthash{otherinterfaces} = ${$attributes->{'ipv4-address'}}[0];
            $outhash{'Server-'.$atthash{mtm}.'-SN'.$atthash{serial}} = \%atthash;
            $$length = length( $atthash{ip}) if ( length( $atthash{ip} ) > $$length );
            if (exists($::OLD_DATA_CACHE{"mp*".$atthash{mtm}."*".$atthash{serial}})){
                $atthash{hostname} = $::OLD_DATA_CACHE{"mp*".$atthash{mtm}."*".$atthash{serial}};
                push  @matchnode,'Server-'.$atthash{mtm}.'-SN'.$atthash{serial};
            }
            push @cmmnodes, 'Server-'.$atthash{mtm}.'-SN'.$atthash{serial};
            trace( $request, "Discover node $atthash{hostname}: type is $atthash{type}, \
			mtm is $atthash{mtm}, sn is $atthash{serial}, side is $atthash{side}, \
			ip is $atthash{ip}, mac is $atthash{mac}, mname is $atthash{mname},\
			mpa is $atthash{mpa}, otherinterfaces is $atthash{otherinterfaces}" );

        } elsif ($type eq SERVICE_HMC) {
            $atthash{type} = $service_slp{$type};
            $atthash{mtm} = ${$attributes->{'machinetype-model'}}[0];
            $atthash{serial} = ${$attributes->{'serial-number'}}[0];
            $atthash{ip} = ${$attributes->{'ip-address'}}[0];
            $atthash{hostname} = get_host_from_url($request, $attributes);
            my @ips = @{$attributes->{'ip-address'}};
            foreach my $tmpip (@ips) {
                if (exists($::OLD_DATA_CACHE{"hmc*".$tmpip})){
                    $atthash{hostname} = $::OLD_DATA_CACHE{"hmc*".$tmpip};
                    push  @matchnode, 'Server-'.$atthash{mtm}.'-SN'.$atthash{serial};
                    $atthash{ip} = $tmpip;
                }
            }
            $atthash{mac} = $rsp;
            $atthash{url} =  ${$searchmacs{$rsp}}{payload};
            $atthash{otherinterfaces} = ${$attributes->{'ip-address'}}[0];
            $outhash{'Server-'.$atthash{mtm}.'-SN'.$atthash{serial}} = \%atthash;
            $$length = length( $atthash{ip}) if ( length( $atthash{ip} ) > $$length );
            trace( $request, "Discover node $atthash{hostname}: type is $atthash{type},\
			mtm is $atthash{mtm},sn is $atthash{serial},  ip is $atthash{ip},\
			mac is $atthash{mac}, otherinterfaces is $atthash{otherinterfaces}" );
       }  elsif ($type eq SERVICE_IVM) {
            $atthash{type} = $service_slp{$type};
            $atthash{mtm} = ${$attributes->{'machinetype-model'}}[0];
            $atthash{serial} = ${$attributes->{'serial-number'}}[0];
            $atthash{id} = ${$attributes->{'lparid'}}[0];
            $atthash{ip} = ${$attributes->{'ip-address'}}[0];
            $atthash{hostname} = get_host_from_url($request, $attributes);
            $atthash{hostname} =~ s/^Server/ivm/;
            my @ips = @{$attributes->{'ip-address'}};
            foreach my $tmpip (@ips) {
                if (exists($::OLD_DATA_CACHE{"ivm*".$atthash{mtm}."*".$atthash{serial}})){
                    $atthash{hostname} = $::OLD_DATA_CACHE{"ivm*".$atthash{mtm}."*".$atthash{serial}};
                    push  @matchnode, 'Server-'.$atthash{mtm}.'-SN'.$atthash{serial};
                    $atthash{ip} = $tmpip;
                }
            }
            $atthash{mac} = $rsp;
            $atthash{url} =  ${$searchmacs{$rsp}}{payload};
            $atthash{otherinterfaces} = ${$attributes->{'ip-address'}}[0];
            $outhash{'Server-'.$atthash{mtm}.'-SN'.$atthash{serial}} = \%atthash;
            $$length = length( $atthash{ip}) if ( length( $atthash{ip} ) > $$length );
            trace( $request, "Discover node $atthash{hostname}: type is $atthash{type},\
			mtm is $atthash{mtm},sn is $atthash{serial},  ip is $atthash{ip},\
			mac is $atthash{mac}, otherinterfaces is $atthash{otherinterfaces}" );
        } elsif (($type eq SERVICE_FSP) && ($option_s eq "PBMC")) {
            my %tmphash;
            $atthash{type} = "pbmc";
            $atthash{mtm} = ${$attributes->{'machinetype-model'}}[0];
            $atthash{serial} = ${$attributes->{'serial-number'}}[0];
            $atthash{ip} = ${$searchmacs{$rsp}}{peername};
            $atthash{url} =  ${$searchmacs{$rsp}}{payload};
            $atthash{hostname} =  'Server-'.$atthash{mtm}.'-SN'.$atthash{serial};
            $outhash{'Server-'.$atthash{mtm}.'-SN'.$atthash{serial}} = \%atthash;

        }elsif (($type eq SERVICE_FSP) && (${$attributes->{'machinetype-model'}}[0] =~ /^7895|1457|7954/ )) {
            # Skip this entry if "-s CEC" was specified - we do not list FSP entries for Flex when only CECs were requested
	    next unless ($option_s ne "CEC");  

            #begin to define fsp and bpa
            my %tmphash;
            $tmphash{type} = ($type eq SERVICE_BPA) ? TYPE_BPA : TYPE_FSP;
            $tmphash{mtm} = ${$attributes->{'machinetype-model'}}[0];
            $tmphash{serial} = ${$attributes->{'serial-number'}}[0];
            $tmphash{ip} = ${$searchmacs{$rsp}}{peername};
            my $loc = ($tmphash{ip} =~ ${$attributes->{'ip-address'}}[0]) ? 0:1; #every entry has two ip-addresses
            $tmphash{side} = (int(${$attributes->{'slot'}}[0]) == 0) ? 'B-'.$loc:'A-'.$loc;
            $tmphash{mac} = $rsp;
            $tmphash{parent} =  'Server-'.$tmphash{mtm}.'-SN'.$tmphash{serial};
            $tmphash{hostname} = $tmphash{ip};
            $tmphash{url} =  ${$searchmacs{$rsp}}{payload};
            $tmphash{otherinterfaces} = ${$searchmacs{$rsp}}{peername};
            $tmphash{bpcmtm} = ${$attributes->{'bpc-machinetype-model'}}[0];
            $tmphash{bpcsn} = ${$attributes->{'bpc-serial-number'}}[0];
            $tmphash{fid} = int(${$attributes->{'frame-number'}}[0]);
            $tmphash{cid} = int(${$attributes->{'cage-number'}}[0]);
            $outhash{$tmphash{ip}} = \%tmphash;
            $$length = length( $tmphash{ip}) if ( length( $tmphash{ip} ) > $$length );
            trace( $request, "Discover node $tmphash{hostname}:type is $tmphash{type}, mtm is $tmphash{mtm}, \
			sn is $tmphash{serial}, side is $tmphash{side},parent is $tmphash{parent},ip is $tmphash{ip}, \
			cec id is $tmphash{cid} , frame id is $tmphash{fid},mac is $tmphash{mac}, \
			otherinterfaces is $tmphash{otherinterfaces}" );
            
            #####################################################################
            #define another side to fix the issue that the result is imcomplete
            #####################################################################
            my %tmphash1;
            $tmphash1{ip} = (${$searchmacs{$rsp}}{peername} =~ ${$attributes->{'ip-address'}}[0])?${$attributes->{'ip-address'}}[1]:${$attributes->{'ip-address'}}[0]; 
            unless ($outhash{$tmphash1{ip}}) {
                my $validflag = 1;
                foreach (@invalidiplist){
                    if ( $tmphash1{ip} =~ /^($_)/ ){
                        $validflag = 0;
                        last;
                    }
                }    
                if ($validflag == 1) {
                    $tmphash1{type} = ($type eq SERVICE_BPA) ? TYPE_BPA : TYPE_FSP;
                    $tmphash1{mtm} = ${$attributes->{'machinetype-model'}}[0];
                    $tmphash1{serial} = ${$attributes->{'serial-number'}}[0];
                    my $loc = ($tmphash1{ip} =~ ${$attributes->{'ip-address'}}[0]) ? 0:1; #every entry has two ip-addresses
                    $tmphash1{side} = (int(${$attributes->{'slot'}}[0]) == 0) ? 'B-'.$loc:'A-'.$loc;
                    $tmphash1{mac} = xCAT::SLP::get_mac_for_addr($tmphash1{ip});
                    $tmphash1{parent} =  'Server-'.$tmphash1{mtm}.'-SN'.$tmphash1{serial};
                    $tmphash1{hostname} = $tmphash1{ip};
                    $tmphash1{otherinterfaces} = ${$searchmacs{$rsp}}{peername};
                    $tmphash1{bpcmtm} = ${$attributes->{'bpc-machinetype-model'}}[0];
                    $tmphash1{bpcsn} = ${$attributes->{'bpc-serial-number'}}[0];
                    $tmphash1{fid} = int(${$attributes->{'frame-number'}}[0]);
                    $tmphash1{cid} = int(${$attributes->{'cage-number'}}[0]);
                    $outhash{$tmphash1{ip}} = \%tmphash1;
                    $$length = length( $tmphash1{ip}) if ( length( $tmphash1{ip} ) > $$length );
                    trace( $request, "Discover another node $tmphash1{hostname}:type is $tmphash1{type}, mtm is $tmphash1{mtm}, \
			        sn is $tmphash1{serial}, side is $tmphash1{side},parent is $tmphash1{parent},ip is $tmphash1{ip}, \
			        cec id is $tmphash1{cid} , frame id is $tmphash1{fid},mac is $tmphash1{mac}, \
			        otherinterfaces is $tmphash1{otherinterfaces}" );
                }
	}
            }else  {
            #begin to define fsp and bpa
            my %tmphash;
            $tmphash{type} = ($type eq SERVICE_BPA) ? TYPE_BPA : TYPE_FSP;
            $tmphash{mtm} = ${$attributes->{'machinetype-model'}}[0];
            $tmphash{serial} = ${$attributes->{'serial-number'}}[0];
            $tmphash{ip} = ${$searchmacs{$rsp}}{peername};
            my $loc = ($tmphash{ip} =~ ${$attributes->{'ip-address'}}[0]) ? 0:1; #every entry has two ip-addresses
            $tmphash{side} = (int(${$attributes->{'slot'}}[0]) == 0) ? 'B-'.$loc:'A-'.$loc;
            $tmphash{mac} = $rsp;
            $tmphash{parent} =  'Server-'.$tmphash{mtm}.'-SN'.$tmphash{serial};
            $tmphash{hostname} = $tmphash{ip};
            $tmphash{otherinterfaces} = ${$searchmacs{$rsp}}{peername};
            $tmphash{bpcmtm} = ${$attributes->{'bpc-machinetype-model'}}[0];
            $tmphash{bpcsn} = ${$attributes->{'bpc-serial-number'}}[0];
            $tmphash{fid} = int(${$attributes->{'frame-number'}}[0]);
            $tmphash{cid} = int(${$attributes->{'cage-number'}}[0]);
            $outhash{$tmphash{ip}} = \%tmphash;
            $$length = length( $tmphash{ip}) if ( length( $tmphash{ip} ) > $$length );
            trace( $request, "Discover node $tmphash{hostname}:type is $tmphash{type}, mtm is $tmphash{mtm}, \
			sn is $tmphash{serial}, side is $tmphash{side},parent is $tmphash{parent},ip is $tmphash{ip}, \
			cec id is $tmphash{cid} , frame id is $tmphash{fid},mac is $tmphash{mac}, \
			otherinterfaces is $tmphash{otherinterfaces}" );
            
            #####################################################################
            #define another side to fix the issue that the result is imcomplete
            #####################################################################
            my %tmphash1;
            $tmphash1{ip} = (${$searchmacs{$rsp}}{peername} =~ ${$attributes->{'ip-address'}}[0])?${$attributes->{'ip-address'}}[1]:${$attributes->{'ip-address'}}[0]; 
            unless ($outhash{$tmphash1{ip}}) {
                my $validflag = 1;
                foreach (@invalidiplist){
                    if ( $tmphash1{ip} =~ /^($_)/ ){
                        $validflag = 0;
                        last;
                    }
                }    
                if ($validflag == 1) {
                    $tmphash1{type} = ($type eq SERVICE_BPA) ? TYPE_BPA : TYPE_FSP;
                    $tmphash1{mtm} = ${$attributes->{'machinetype-model'}}[0];
                    $tmphash1{serial} = ${$attributes->{'serial-number'}}[0];
                    my $loc = ($tmphash1{ip} =~ ${$attributes->{'ip-address'}}[0]) ? 0:1; #every entry has two ip-addresses
                    $tmphash1{side} = (int(${$attributes->{'slot'}}[0]) == 0) ? 'B-'.$loc:'A-'.$loc;
                    $tmphash1{mac} = xCAT::SLP::get_mac_for_addr($tmphash1{ip});
                    $tmphash1{parent} =  'Server-'.$tmphash1{mtm}.'-SN'.$tmphash1{serial};
                    $tmphash1{hostname} = $tmphash1{ip};
                    $tmphash1{otherinterfaces} = ${$searchmacs{$rsp}}{peername};
                    $tmphash1{bpcmtm} = ${$attributes->{'bpc-machinetype-model'}}[0];
                    $tmphash1{bpcsn} = ${$attributes->{'bpc-serial-number'}}[0];
                    $tmphash1{fid} = int(${$attributes->{'frame-number'}}[0]);
                    $tmphash1{cid} = int(${$attributes->{'cage-number'}}[0]);
                    $outhash{$tmphash1{ip}} = \%tmphash1;
                    $$length = length( $tmphash1{ip}) if ( length( $tmphash1{ip} ) > $$length );
                    trace( $request, "Discover another node $tmphash1{hostname}:type is $tmphash1{type}, mtm is $tmphash1{mtm}, \
			        sn is $tmphash1{serial}, side is $tmphash1{side},parent is $tmphash1{parent},ip is $tmphash1{ip}, \
			        cec id is $tmphash1{cid} , frame id is $tmphash1{fid},mac is $tmphash1{mac}, \
			        otherinterfaces is $tmphash1{otherinterfaces}" );
                }
            }
            # this part of code is used to avoid two messages sent from different ports of fsp give different info. Although this hasn't showed.
            #else {
              #  ${$outhash{$tmphash1{ip}}{fid} = int(${$attributes->{'frame-number'}}[0]) if(int(${$attributes->{'frame-number'}}[0]) != 0);
              #  ${$outhash{$tmphash1{ip}}{cid} = int(${$attributes->{'cage-number'}}[0]) if(int(${$attributes->{'cage-number'}}[0]) != 0);
              #  trace( $request, "change frame id to ${$outhash{$tmphash1{ip}}{fid}, change cec id to ${$outhash{$tmphash1{ip}}{cid} \n");
            #}        
            ######################################################################
            
            #begin to define frame and cec
            $atthash{type} = $service_slp{$type};
            $atthash{mtm} = ${$attributes->{'machinetype-model'}}[0];
            $atthash{serial} = ${$attributes->{'serial-number'}}[0];
            my $name = 'Server-'.$atthash{mtm}.'-SN'.$atthash{serial};
            unless (exists $outhash{$name} ){
                $atthash{slot} = '';
                $atthash{ip} = '';
                $atthash{hostname} = 'Server-'.$atthash{mtm}.'-SN'.$atthash{serial};;
                $atthash{mac} = "";
                $atthash{bpcmtm} = ${$attributes->{'bpc-machinetype-model'}}[0];
                $atthash{bpcsn} = ${$attributes->{'bpc-serial-number'}}[0];
                $atthash{fid} = int(${$attributes->{'frame-number'}}[0]);
                $atthash{cid} = int(${$attributes->{'cage-number'}}[0]);
                $atthash{parent} = 'Server-'.$atthash{bpcmtm}.'-SN'.$atthash{bpcsn} if ($type eq SERVICE_FSP);
                $atthash{children} = ${$attributes->{'ip-address'}}[0].",".${$attributes->{'ip-address'}}[1];
                $atthash{url} =  ${$searchmacs{$rsp}}{payload};
                $outhash{'Server-'.$atthash{mtm}.'-SN'.$atthash{serial}} = \%atthash;
			    trace( $request, "Discover node $atthash{hostname}: type is $atthash{type},  mtm is $atthash{mtm},\
			    sn is $atthash{serial},  mac is $atthash{mac}, children is $atthash{children}, frame id is $atthash{fid}, \
			    cec id is $atthash{cid}, otherinterfaces is $atthash{otherinterfaces}, parent is $atthash{parent}" );
            } else {
                #update frameid and cageid to fix the firmware mistake
                ${$outhash{$name}}{fid} = int(${$attributes->{'frame-number'}}[0]) if(int(${$attributes->{'frame-number'}}[0]) != 0);
                ${$outhash{$name}}{cid} = int(${$attributes->{'cage-number'}}[0]) if(int(${$attributes->{'cage-number'}}[0]) != 0);
                ${$outhash{$name}}{bpcmtm} = ${$attributes->{'bpc-machinetype-model'}}[0] if(int(${$attributes->{'bpc-machinetype-model'}}[0]) != 0);
                ${$outhash{$name}}{bpcsn} = ${$attributes->{'bpc-serial-number'}}[0] if(int(${$attributes->{'bpc-serial-number'}}[0]) != 0);
                $atthash{parent} = 'Server-'.${$outhash{$name}}{bpcmtm}.'-SN'.${$outhash{$name}}{bpcsn} if ($type eq SERVICE_FSP);
                $outhash{$name}{children} .= ",".${$attributes->{'ip-address'}}[0].",".${$attributes->{'ip-address'}}[1];# at most save 8 ips and have redendant
                trace( $request, "adjust frame id to ${$outhash{$name}}{fid}, cec id to  ${$outhash{$name}}{cid}, children to $outhash{$name}{children},\
				bpcmtm to ${$outhash{$name}}{bpcmtm}, bpcsn to ${$outhash{$name}}{bpcsn}");
            }

        }
    }

    ###########################################################
    # find frame's hostname first, then use find the cec's parent
    # until then can begin with finding cec's hostname
    # the order of finding PPC nodes' hostname can't be wrong
    # and can't be done together
    ###########################################################
    my $newhostname;
    trace( $request, "\n\n\nBegin to find find frame's hostname");
    foreach my $h ( keys %outhash ) {
        if(${$outhash{$h}}{type} eq TYPE_FRAME) {
            $newhostname = $::OLD_DATA_CACHE{"frame*".${$outhash{$h}}{mtm}."*".${$outhash{$h}}{serial}};
            if ($newhostname) {
                ${$outhash{$h}}{hostname} = $newhostname ;
				trace ( $request, "$h found hostname $newhostname");
                push  @matchnode, $h;
            }
        }
    }
    trace( $request, "\n\n\nBegin to find cec's parent");
    foreach my $h ( keys %outhash ) {
        next unless (${$outhash{$h}}{type} eq TYPE_CEC);
        my $parent;
        #find parent in the discovered nodes
        foreach my $h1 ( keys %outhash ) {
            if (${$outhash{$h1}}{type} eq "frame" and ${$outhash{$h}}{bpcmtm} eq ${$outhash{$h1}}{mtm} and ${$outhash{$h}}{bpcsn} eq ${$outhash{$h1}}{serial} ) {
                $parent = ${$outhash{$h1}}{hostname};
                last;
            }
        }
        #find parent in database
        if (!defined($parent)) {
            my $existing_node = $::OLD_DATA_CACHE{"frame*".${$outhash{$h}}{bpcmtm}.'*'.${$outhash{$h}}{bpcsn}};
            $parent = $existing_node if ($existing_node);
        }
        ${$outhash{$h}}{parent} = $parent;
		trace( $request, "$h found parent $parent") if ($parent); 
    }

    trace( $request, "\n\n\nBegin to find cec hostname");
    foreach my $h ( keys %outhash ) {
        if(${$outhash{$h}}{type} eq TYPE_CEC) {
            my $newhostname1 = $::OLD_DATA_CACHE{"cec*".${$outhash{$h}}{mtm}.'*'.${$outhash{$h}}{serial}};
            if ($newhostname1) {
                trace( $request, "$h found hostname $newhostname1 with mtms");
                ${$outhash{$h}}{hostname} = $newhostname1;
                push  @matchnode, $h;
            }
            my $tp = 'Server-'.${$outhash{$h}}{bpcmtm}.'-SN'.${$outhash{$h}}{bpcsn} ;
            trace( $request, "$h begin to find hostname with parent $tp and id ${$outhash{$h}}{cid}");
            my $newhostname2 = $::OLD_DATA_CACHE{"cec*".$tp.'*'.${$outhash{$h}}{cid}};
            if ($newhostname2) {
                ${$outhash{$h}}{hostname} = $newhostname2;
                trace( $request, "$h found hostname $newhostname2 with parent and id");
                push  @matchnode, $h;
            }
        }
    }

    trace( $request, "\n\n\nBegin to find fsp/bpa's hostname and parent");
    foreach my $h ( keys %outhash ) {
	# Added a skip if processing Flex blades
        if(${$outhash{$h}}{type} eq TYPE_FSP or ${$outhash{$h}}{type} eq TYPE_BPA) {
            $newhostname = $::OLD_DATA_CACHE{${$outhash{$h}}{type}."*".${$outhash{$h}}{mtm}.'*'.${$outhash{$h}}{serial}.'*'.${$outhash{$h}}{side}};
            if ($newhostname){
                ${$outhash{$h}}{hostname} = $newhostname ;
                trace( $request, "$h found hostname $newhostname");
                push  @matchnode, $h;
            }
            my $ptmp = ${$outhash{$h}}{parent};
            ${$outhash{$h}}{parent} = ${$outhash{$ptmp}}{hostname} unless((${$outhash{$h}}{type} eq TYPE_FSP) && ${$outhash{$h}}{mtm} =~ /^7895|1457|7954/ );
			trace( $request, "$h found parent ${$outhash{$ptmp}}{hostname}");
            #check if fsp/bpa's ip is valid
            my $vip = check_ip(${$outhash{$h}}{ip});
            unless ( $vip )   { #which means the ip is a valid one
                delete $outhash{$h};
            }
        }
    }
	trace( $request, "\n\n\nBegin to adjust fsp/bpa's id");
    foreach my $h ( keys %outhash ) {
        if(${$outhash{$h}}{type} eq TYPE_CEC or ${$outhash{$h}}{type} eq TYPE_FRAME) {
		    my @children = split /,/, ${$outhash{$h}}{children};
			foreach my $child (@children) {
                ${$outhash{$child}}{fid} = ${$outhash{$h}}{fid};
                ${$outhash{$child}}{cid} = ${$outhash{$h}}{cid};
		        trace( $request, "child is $child, fid is ${$outhash{$child}}{fid}, cid is ${$outhash{$child}}{cid}");
    }	

        } 
    } 
    
    
	trace( $request, "\n\n\nBegin to find cmm hostname in switch table");
    $macmap = xCAT::MacMap->new();
	$macmap->refresh_table();
    foreach my $cmmnode (@cmmnodes) {
        my $macvalue = ${$outhash{$cmmnode}}{mac};
        my $hostn = $macmap->find_mac($macvalue, 1);
        if ($hostn) {
            ${$outhash{$cmmnode}}{hostname} = $hostn;
            trace($request, "cmmnode $cmmnode find hostname $hostn");
        }    
    }
    
    

    ##########################################################
    # If there is -n flag, skip the matched nodes
    ##########################################################
    if (exists($globalopt{n})) {
        trace( $request, "\n\n\nThere is -n flag, skip these nodes:\n");
        for my $matchednode (@matchnode) {
            if ($outhash{$matchednode}) {
                trace( $request, "skip the node $matchednode\n");
                delete $outhash{$matchednode};
            }
        }
    }
    if (exists($globalopt{I})) {
        my %existsnodes;
        my $nodelisttab = xCAT::Table->new('nodelist');
        unless ( $nodelisttab ) {
            return( "Error opening 'nodelisttable'" );
        }
        my @nodes = $nodelisttab->getAllNodeAttribs([qw(node)]);
        my $notdisnode;
        for my $enode (@nodes) {
            for my $mnode (@matchnode) {
                if ($enode->{node} eq ${$outhash{$mnode}}{hostname}) {
                    $existsnodes{$enode->{node}} = 1;
                    last;
                }
            }
        }
        
        for my $enode (@nodes) {
            unless ($existsnodes{$enode->{node}}) {
                $notdisnode .= $enode->{node}.",";
            }
        }
        send_msg ( $request, 0, "These nodes defined in database but can't be discovered: $notdisnode  \n");
    }
    foreach my $no(keys %outhash) {
	    delete $outhash{$no} unless ( ${$outhash{$no}}{hostname} );
    }		
    return \%outhash;
}
##########################################################################
# Write result to xCat database
##########################################################################
sub xCATdB {
    my $outhash = shift;

    ########################################
    # Begin to collect attributes for each node
    ########################################
    my %nodelisthash;
    my %ppchash;
    my %vpdhash;
    my %nodehmhash;
    my %nodetypehash;
    my %ppcdirecthash;
    my %hostshash;
    my %machash;
    my %mphash;
    my %ipmihash;
    foreach my $nodeentry ( keys %$outhash ) {
        my $type       = ${$outhash->{$nodeentry}}{type};
        my $model      = ${$outhash->{$nodeentry}}{mtm};
        my $serial     = ${$outhash->{$nodeentry}}{serial};
        my $side       = ${$outhash->{$nodeentry}}{side};
        my $ip         = ${$outhash->{$nodeentry}}{ip};
        my $frameid    = ${$outhash->{$nodeentry}}{fid};
        my $cageid     = ${$outhash->{$nodeentry}}{cid};
        my $parent     = ${$outhash->{$nodeentry}}{parent};
        my $mac        = ${$outhash->{$nodeentry}}{mac};
        my $otherif    = ${$outhash->{$nodeentry}}{otherinterfaces};
        my $hostname   = ${$outhash->{$nodeentry}}{hostname};

        my $id = ($type =~ /bpa|frame/) ? $frameid:$cageid;
        my $hidden = ($type =~ /bpa|fsp/)? 1:0;
        my $groups = lc($type).",all";
        my $tmp_pre = xCAT::data::ibmhwtypes::parse_group($model);
        if (defined($tmp_pre)) {
            $groups .= ",$tmp_pre";
        }
        ########################################
        # Write result to every tables,
        ########################################
        if ( $type =~ /^bpa|fsp|cec|frame$/ ) {
            #$nodelisthash{$hostname} = {groups=>"$type,all", hidden=>$hidden};
            $nodelisthash{$hostname} = {groups=>$groups, hidden=>$hidden};
            $ppchash{$hostname} = {id=>$id, parent=>$parent, hcp=>$hostname, nodetype=>$globalhwtype{$type}};
            $vpdhash{$hostname} = {mtm=>$model, serial=>$serial, side=>$side};
            $nodehmhash{$hostname} = {mgt=>$globalmgt{$type}};
            $nodetypehash{$hostname} = {nodetype=>$globalnodetype{$type}};
            $hostshash{$hostname} = {otherinterfaces=>$otherif} if ($type =~ /fsp|bpa/);
            $machash{$hostname} = {mac=>$mac} if ($type =~ /^fsp|bpa$/);           
        } elsif ( $type =~ /^pbmc$/) {
            $nodelisthash{$hostname} = {groups=>$groups, hidden=>$hidden};
            $ppchash{$hostname} = {nodetype=>$globalhwtype{$type}};
            $vpdhash{$hostname} = {mtm=>$model, serial=>$serial};
            $nodehmhash{$hostname} = {mgt=>$globalmgt{$type}};
            $nodetypehash{$hostname} = {nodetype=>$globalnodetype{$type}};
            $ipmihash{$hostname} = {bmc=>$ip};
        } elsif ( $type =~ /^(rsa|mm)$/ ) {
            my @data = ($type, $model, $serial, $side, $ip, $frameid, $cageid, $parent, $mac);
            xCAT::PPCdb::add_systemX( $type, $hostname, \@data );
        } elsif ( $type =~ /^(hmc|ivm)$/ ) {
            $nodelisthash{$hostname} = {groups=>$groups, hidden=>$hidden};
            $ppchash{$hostname} = {nodetype=>$globalhwtype{$type}};
            $vpdhash{$hostname} = {mtm=>$model, serial=>$serial};
            $nodetypehash{$hostname} = {nodetype=>$globalnodetype{$type}};
            $nodehmhash{$hostname} = {mgt=>$globalmgt{$type}};
            $hostshash{$hostname} = {ip=>$ip};
            $machash{$hostname} = {mac=>$mac};
        }elsif ($type =~ /^cmm$/){
            $nodelisthash{$hostname} = {groups=>$groups, hidden=>$hidden};
            $vpdhash{$hostname} = {mtm=>$model, serial=>$serial, side=>$side};
            $nodetypehash{$hostname} = {nodetype=>$globalnodetype{$type}};
            $nodehmhash{$hostname} = {mgt=>"blade"};
            $mphash{$hostname} = {nodetype=>$globalhwtype{$type}, mpa=>$hostname};
            $hostshash{$hostname} = {otherinterfaces=>$otherif};
        }
    }
    ########################################
    # Update database 
    ########################################
    my %dbhash;
    $dbhash{nodelist} = \%nodelisthash, if (%nodelisthash);
    $dbhash{ppc} = \%ppchash, if (%ppchash);
    $dbhash{vpd} = \%vpdhash, if (%vpdhash);
    $dbhash{nodehm} = \%nodehmhash, if (%nodehmhash);
    $dbhash{nodetype} = \%nodetypehash, if (%nodetypehash);
    $dbhash{ppcdirect} = \%ppcdirecthash, if (%ppcdirecthash);
    $dbhash{hosts} = \%hostshash, if (%hostshash);
    $dbhash{mac} = \%machash, if (%machash);
    $dbhash{mp} = \%mphash, if (%mphash);
    $dbhash{ipmi} = \%ipmihash, if (%ipmihash);
    
    for my $tab (keys %dbhash) {
        my $db = xCAT::Table->new($tab);
        if ( !$db ) {
            return( "Error opening $db" );
        }
        $db->setNodesAttribs($dbhash{$tab});
        $db->close();
    }    
}
##########################################################################
# Stanza formatting
##########################################################################
sub format_stanza {

    my $outhash = shift;
    my $result;
    #####################################
    # Write attributes
    #####################################
    foreach my $name ( keys %$outhash ) {
        my $hostname = ${$outhash->{$name}}{hostname};
        my $ip = ${$outhash->{$name}}{ip};
        if ( $hostname =~ /^([^\(]+)\(([^\)]+)\)$/) {
            $hostname = $1;
            $ip  = $2;
        }
        my $type = ${$outhash->{$name}}{type};
        my $groups = "$type,all";
        my $tmp_pre = xCAT::data::ibmhwtypes::parse_group(${$outhash->{$name}}{mtm});
        if (defined($tmp_pre)) {
            $groups .= ",$tmp_pre";
        }

        #################################
        # Node attributes
        #################################
        $result .= "$hostname:\n\tobjtype=node\n";
        if ($type =~ /^cmm$/){
            $result .= "\tmpa=${$outhash->{$name}}{hostname}\n";
        } elsif ($type =~ /^pbmc$/) {
            $result .= "\tbmc=${$outhash->{$name}}{ip}\n";
        }else{
            $result .= "\thcp=${$outhash->{$name}}{hostname}\n";
        }
        $result .= "\tnodetype=$globalnodetype{$type}\n";
        $result .= "\tmtm=${$outhash->{$name}}{mtm}\n";
        $result .= "\tserial=${$outhash->{$name}}{serial}\n";
        if ($type =~ /^fsp|bpa|cmm$/) {
            $result .= "\tside=${$outhash->{$name}}{side}\n";
        }
        #$result .= "\tgroups=$type,all\n";
        $result .= "\tgroups=$groups\n";
        $result .= "\tmgt=$globalmgt{$type}\n";
        if ($type =~ /^fsp|bpa|frame|cec$/) {
            $result .= "\tid=${$outhash->{$name}}{$globalid{$type}}\n";
        }
        if ($type =~ /^fsp|bpa|cec$/ and exists(${$outhash->{$name}}{parent})) {
            $result .= "\tparent=${$outhash->{$name}}{parent}\n";
        }
        unless ($type =~ /^frame|cec$/ or !exists(${$outhash->{$name}}{mac})){
            $result .= "\tmac=${$outhash->{$name}}{mac}\n";
        }
        if ($type =~ /^fsp|bpa$/){
            $result .= "\thidden=1\n";
        }else {
            $result .= "\thidden=0\n";
        }
        #unless ($type =~ /^cmm$/) {
        #    $result .= "\tip=$ip\n";
        #}
        if ($type =~ /^fsp|bpa|cmm$/){
            $result .= "\totherinterfaces=${$outhash->{$name}}{otherinterfaces}\n";
        }
        if ($type eq "ivm") {
            $result .= "\tip=${$outhash->{$name}}{ip}\n";
        }
        $result .= "\thwtype=$globalhwtype{$type}\n";
    }
    return( $result );
}



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

    my $outhash = shift;
    my $xml;

    my $result = format_stanza($outhash);
    my @nodeentry = split 'objtype=', $result;
    foreach my $entry (@nodeentry) {
        my $href = {
            Node => { }
        };
        my @attr = split '\\n\\t', $entry;
        $href->{Node}->{node} = $attr[0];
        for (my $i = 1; $i < scalar(@attr); $i++ ){
            if( $attr[$i] =~ /(\w+)\=(.*)/){
                $href->{Node}->{$1} = $2;
            }
        }
        $xml.= XMLout($href,
                     NoAttr   => 1,
                     KeyAttr  => [],
                     RootName => undef );
    }
    return( $xml );
}

##########################################################################
# VPD table formatting
##########################################################################
sub format_table {

    my $outhash = shift;
    my $result;

    #####################################
    # Create XML formatted attributes
    #####################################
    foreach my $name ( keys %$outhash ) {
        my $type = ${$outhash->{$name}}{type};
        next if ($type =~ /^(fsp|bpa)$/);
        $result .= "${$outhash->{$name}}{hostname}:\n";
        $result .= "\tobjtype=node\n";
        $result .= "\tmtm=${$outhash->{$name}}{mtm}\n";
        $result .= "\tserial=${$outhash->{$name}}{serial}\n";
    }
    return( $result );
}

##########################################################################
# Collect output from the child processes
##########################################################################
sub child_response {

    my $callback = shift;
    my $fds = shift;
    my @ready_fds = $fds->can_read(1);

    foreach my $rfh (@ready_fds) {
        my $data = <$rfh>;

        #################################
        # Read from child process
        #################################
        if ( defined( $data )) {
            while ($data !~ /ENDOFFREEZE6sK4ci/) {
                $data .= <$rfh>;
            }
            my $responses = thaw($data);

            #############################
            # Formatted SLP results
            #############################
            if ( @$responses[0] =~ /^FORMATDATA6sK4ci$/ ) {
                my $result = @$responses[1];
                foreach ( keys %$result ) {
                    #$slp_result{$_} = 1;
                }
                next;
            }

            #############################
            # Message or verbose trace
            #############################
            foreach ( @$responses ) {
                $callback->( $_ );
            }
            next;
        }
        #################################
        # Done - close handle
        #################################
        $fds->remove($rfh);
        close($rfh);
    }
}

#############################################################################
# Preprocess request from xCAT daemon and send request to service nodes
#############################################################################
sub preprocess_request {

    my $req = shift;
    if ($req->{_xcatpreprocessed}->[0] == 1) { return [$req]; }
    my $callback=shift;
    my @requests;

    my $command = $req->{command}->[0];
    my $extrargs = $req->{arg};
    my @exargs=($req->{arg});
    if (ref($extrargs)) {
        @exargs=@$extrargs;
    }
    my $usage_string=xCAT::Usage->parseCommand($command, @exargs);
    if ($usage_string) {
        $callback->({data=>[$usage_string]});
        $req = {};
        return;
    }
    ###########################################
    # find all the service nodes for xCAT cluster
    # build an individual request for each service node
    ###########################################
    my %sv_hash=();
    #my @all = xCAT::Utils::getAllSN();
    #foreach (@all) {
    #    $sv_hash{$_}=1;
    #}
    ###########################################
    # build each request for each service node
    ###########################################
    my @result = ();
    my $mncopy = {%$req};
    push @result, $mncopy;
    #foreach my $sn (keys (%sv_hash)) {
    #  my $reqcopy = {%$req};
    #  $reqcopy->{_xcatdest} = $sn;
    #  $reqcopy->{_xcatpreprocessed}->[0] = 1;
    #  push @result, $reqcopy;
    #}
    return \@result;
}

##########################################################################
# Process request from xCat daemon
##########################################################################
sub process_request {

    my $req      = shift;
    my $callback = shift;
    #unless ($macmap) { $macmap = xCAT::MacMap->new(); }

    ###########################################
    # Build hash to pass around
    ###########################################
    my %request;
    $request{arg}      = $req->{arg};
    $request{callback} = $callback;
    $request{command}  = $req->{command}->[0];

    ####################################
    # Process command-specific options
    ####################################
    my $result = parse_args( \%request );

    ####################################
    # Return error
    ####################################
    if ( ref($result) eq 'ARRAY' ) {
        send_msg( \%request, 1, @$result );
        return(1);
    }

    #######################################
    # Write header for trace
    #######################################
    my $tm  = localtime( time );
    my $msg = "\n--------  $tm\nTime     PID";
    trace( \%request, $msg );


    ###########################################
    # Record begin time
    ###########################################
    my $start = Time::HiRes::gettimeofday();
    ############################################
    ## Fork one process per adapter
    ############################################
    #my $children = 0;
    #$SIG{CHLD} = sub {
    #   my $rc_bak = $?;
    #   while (waitpid(-1, WNOHANG) > 0) { $children--; }
    #   $? = $rc_bak;
    #};
    #my $fds = new IO::Select;
    #
    #foreach ( keys %ip_addr ) {
    #    my $pipe = fork_cmd( $req, $_);
    #    if ( $pipe ) {
    #        $fds->add( $pipe );
    #        $children++;
    #    }
    #}
    ############################################
    ## Process slp responses from children
    ############################################
    #while ( $children > 0 ) {
    #    child_response( $callback, $fds );
    #}
    #while (child_response($callback,$fds)) {}

    my ($searchmacsref,$sendcount,$rspc) = invoke_dodiscover(\%request);

    if ($globalopt{flexdiscover}) {
        bt_process($req, $callback, $searchmacsref);
        return ( SUCCESS );
    }
    ###########################################
    # Record ending time
    ###########################################

    my $elapsed = Time::HiRes::gettimeofday() - $start;
    my $msg2 = sprintf( "Total SLP Time: %.3f sec\n", $elapsed );
    trace( \%request, $msg2 );
    ###########################################
    # Combined responses from all children
    ###########################################
    my $num = keys %$searchmacsref;
	my $min;
	if ($num < 500) {
	    $min = "0-1";
	} elsif (500 < $num and $num < 1000) {
	    $min = "1-2";
	} else {
	    $min = "more than 2";
    }	
    #my $start1 = Time::HiRes::gettimeofday();
    send_msg( \%request, 0, "$sendcount requests with $rspc responses.  Now processing responses.  This will take $min minutes...") unless ($globalopt{x} or $globalopt{z});
    format_output( \%request, $searchmacsref);
    #my $elapsed1 = Time::HiRes::gettimeofday() - $start1;
    #send_msg( \%request, 0, "$num nodes takes $elapsed1");
    return( SUCCESS );
}



##########################################################################
# Filter nodes the user specified
##########################################################################
sub filter {
    my $oldhash = shift;
    my $newhash;
    # find HMC/CEC/Frame that the user want to find
    foreach my $n(@filternodes) {
        for my $foundnode ( keys %$oldhash ) {
            if ( ${$oldhash->{$foundnode}}{hostname} =~ /^(\w+)\(.*\)/ )  {
                if ( $1 eq $n ) {
                    $newhash->{$foundnode} = $oldhash->{$foundnode};
                    if (${$oldhash->{$foundnode}}{type} eq TYPE_CEC or ${$oldhash->{$foundnode}}{type} eq  TYPE_FRAME) {
                        my @ips =  split /,/, ${$oldhash->{$foundnode}}{children};
                        for (my $i=0; $i<scalar(@ips); $i++) {
                            $newhash->{$ips[$i]} = $oldhash->{$ips[$i]};
                        }
                    }
                }
            } elsif ( ${$oldhash->{$foundnode}}{hostname} eq $n )  {
                $newhash->{$foundnode} = $oldhash->{$foundnode};
                if (${$oldhash->{$foundnode}}{type} eq TYPE_CEC or ${$oldhash->{$foundnode}}{type} eq  TYPE_FRAME) {
                    my @ips =  split /,/, ${$oldhash->{$foundnode}}{children};
                    for (my $i=0; $i<scalar(@ips); $i++) {
                        $newhash->{$ips[$i]} = $oldhash->{$ips[$i]};
                    }
                }
            }
        }
    }
    return $newhash;
}
##########################################################################
# Filter nodes not in the user specified vlan
##########################################################################
sub filtersamevlan {
    my $oldhash = shift;
    my $newhash;
    my $nets = xCAT::NetworkUtils::my_nets();
    my $validnets;
    for my $net ( keys %$nets) {
        for my $nic ( split /,/, $globalopt{i} ) {
            if ( $nets->{$net} eq $nic ) {
                $validnets->{$net} = $nic;
            }
        }
    }
    foreach my $name ( keys %$oldhash ) {
        if (${$oldhash->{$name}}{type} =~ /^(fsp|bpa)$/) {
            my $ip = ${$oldhash->{$name}}{ip};
            for my $net ( keys %$validnets){
                my ($n,$m) = split /\//,$net;
                if ( xCAT::NetworkUtils::isInSameSubnet( $n, $ip, $m, 1)) {#and xCAT::NetworkUtils::isPingable( $ip)) {
                    $newhash->{$name} = $oldhash->{$name};
                }
            }
        } else {
            $newhash->{$name} = $oldhash->{$name};
        }
    }
    return $newhash;
}
##########################################################################
# This is the function that merged in from slpdiscover
##########################################################################
sub bt_process {
	my $request = shift;
	my $callback = shift;
        my $searef = shift;


	
	my $mpatab=xCAT::Table->new("mpa",-create=>0);
	my @mpaentries;
	$mpahash={};
	if (ref $request->{environment} and ref $request->{environment}->[0]->{XCAT_CURRENTPASS}) {
		$currentbladepass=$request->{environment}->[0]->{XCAT_CURRENTPASS}->[0];
	} else {
		$currentbladepass="PASSW0RD";
	}
	if (ref $request->{environment} and ref $request->{environment}->[0]->{XCAT_CURRENTUSER}) {
		$currentbladeuser=$request->{environment}->[0]->{XCAT_CURRENTUSER}->[0];
	} else {
		$currentbladeuser="USERID";
	}
	if ($mpatab) {
		@mpaentries = $mpatab->getAllNodeAttribs([qw/mpa username password/]);
		foreach (@mpaentries) {
			$mpahash->{$_->{mpa}}=$_;
		}
	}
	my $passwdtab=xCAT::Table->new("passwd",-create=>0);
	$defaultbladeuser="USERID";
	$defaultbladepass="";
	if ($passwdtab) {
		my @ents = $passwdtab->getAttribs({key=>'blade'},'username','password');
		foreach (@ents) {
			if ($_->{username} eq "HMC") { next; }
			if ($_->{username}) { $defaultbladeuser=$_->{username}; }
			if ($_->{password}) { $defaultbladepass=$_->{password}; }
		}
	}
	my $mactab = xCAT::Table->new("mac");
	my %machash;
	my %node2machash;
	my %macuphash;
	my @maclist = $mactab->getAllNodeAttribs([qw/node mac/]);
	foreach (@maclist) {
		$machash{$_->{node}}=$_->{mac};
		$node2machash{$_->{mac}} = $_->{node};
	}
		

	
	my $mptab = xCAT::Table->new('mp');
    my $nodecandidates;
	if ($mptab) {
		my @mpents = $mptab->getAllNodeAttribs(['node','mpa','id']);
		foreach (@mpents) {
			$nodebymp{$_->{mpa}}->{$_->{id}}=$_->{node};
		}
	}

	$macmap = xCAT::MacMap->new();
	$macmap->refresh_table();
	my @toconfig;
	foreach my $mac (keys(%btresult)) {
		my $node = $macmap->find_mac($mac,1);
		unless ($node) {
			if (defined $node2machash{$mac}) {
				$node = $node2machash{$mac};
			} else {
				next;
			}
		}
		my $data = $btresult{$mac};
		$data->{nodename}=$node;
		$data->{macaddress}=$mac;
		$chassisbyuuid{$data->{attributes}->{"enclosure-uuid"}->[0]}=$node;
		push @toconfig,$data;
	}

	foreach my $data (@toconfig) {
		my $mac = $data->{macaddress};
		my $nodename = $data->{nodename};
		my $addr = $data->{peername}; #todo, use sockaddr and remove the 427 port from it instead?
		if ($addr =~ /^fe80/) { #Link local address requires scope index
			$addr .= "%".$data->{scopeid};
		}
		$flexchassisuuid{$nodename}=$data->{attributes}->{"enclosure-uuid"}->[0];
		
		if ($data->{SrvType} eq "service:management-hardware.IBM:chassis-management-module") {
			sendmsg(":Found ".$data->{SrvType}." at address $addr",$callback,$nodename);
			
			setup_cmm_pass($nodename);
			if ($machash{$nodename} =~ /$mac/i) { #ignore prospects already known to mac table
				configure_hosted_elements($nodename, $callback);
				next;
			}
		
			unless (do_blade_setup($data,$callback,curraddr=>$addr)) {
				next;
			}
			configure_hosted_elements($nodename, $callback);
			unless (do_blade_setup($data,$callback,curraddr=>$addr,pass2=>1)) {
				next;
			}
			sendmsg(":Configuration complete, configuration may take a few minutes to take effect",$callback,$nodename);
			$macuphash{$nodename} = { mac => $mac };
		}
	}
	$mactab->setNodesAttribs(\%macuphash);
}


sub configure_hosted_elements {
	my $cmm = shift;
    my $callback = shift;
	my $uuid=$flexchassisuuid{$cmm};
	my $node;
	my $immdata;
	my $slot;
        my $user = $passwordmap{$cmm}->{username};
        my $pass = $passwordmap{$cmm}->{password};
	foreach $immdata (values %{$flexchassismap{$uuid}}) {
		$slot=$immdata->{attributes}->{slot}->[0];
        if (defined $immdata->{attributes}->{'chassis-sub-slot'}) {
            $slot .= ":".$immdata->{attributes}->{'chassis-sub-slot'}->[0];
        }
		if ($node = $nodebymp{$cmm}->{$slot}) {
			my $addr = $immdata->{peername}; #todo, use sockaddr and remove the 427 port from it instead?
			if ($addr =~ /^fe80/) { #Link local address requires scope index
				$addr .= "%".$immdata->{scopeid};
			}
			if ($doneaddrs{$node}) { next; }
			$doneaddrs{$node}=1;
			xCAT::IMMUtils::setupIMM($node,nodedata=>$immdata,curraddr=>$addr,cliusername=>$user,clipassword=>$pass,callback=>$callback);
		} else {
			sendmsg(": Ignoring target in bay $slot, no node found with mp.mpa/mp.id matching",$callback,$cmm);
		}
	
	}
	while (wait() > 0) {}
}

sub setup_cmm_pass {
	my $nodename = shift;
	my $localuser=$defaultbladeuser;
	my $localpass=$defaultbladepass;
	if ($mpahash->{$nodename}) {
		if ($mpahash->{$nodename}->{username}) {
			$localuser = $mpahash->{$nodename}->{username};
		}
		if ($mpahash->{$nodename}->{password}) {
			$localpass = $mpahash->{$nodename}->{password};
		}
	}
        $passwordmap{$nodename}->{username}=$localuser;
        $passwordmap{$nodename}->{password}=$localpass;
}
sub do_blade_setup {
	my $data = shift;
    my $callback = shift;
	my %args = @_;
	my $addr = $args{curraddr};
	my $nodename = $data->{nodename};
	my $localuser=$passwordmap{$nodename}->{username};
	my $localpass=$passwordmap{$nodename}->{password};
	if (not $localpass or $localpass eq "PASSW0RD") {
		sendmsg([1,":Password for blade must be specified in either mpa or passwd tables, and it must not be PASSW0RD"],$callback,$nodename);
		return 0;
	}
	require xCAT_plugin::blade;
	my @cmds;
	my %exargs;
	if ($args{pass2}) {
	  @cmds = qw/initnetwork=*/; 
	  %exargs = ( nokeycheck=>1 ); #still not at the 'right' ip, so the known hosts shouldn't be bothered
	} else {
	  @cmds = qw/snmpcfg=enable sshcfg=enable textid=*/; # initnetwork=*/; defer initnetwork until after chassis members have been configured
	  %exargs = ( curruser=>$currentbladeuser, currpass=>$currentbladepass );
        }
	my $result;
        $@="";
	my $rc = eval { $result = xCAT_plugin::blade::clicmds(
						 $nodename,
						 $localuser,
						 $localpass,
						 $nodename,
						 0,
						 curraddr=>$addr,
						 %exargs,
						 cmds=>\@cmds );
		1;
	};
	my $errmsg=$@;
        if ($errmsg) {
		if ($errmsg =~ /Incorrect Password/) {
			sendmsg([1,"Failed to set up Management module due to Incorrect Password (You may try the environment variables XCAT_CURRENTUSER and/or XCAT_CURRENTPASS to try a different value)"],$callback,$nodename);
		}else {
			sendmsg([1,"Failed to set up Management module due to $errmsg"],$callback,$nodename);
		}
		return 0;
	}
	if ($result) {
		if ($result->[0]) {
			if ($result->[2] =~ /Incorrect Password/) {
				sendmsg([1,"Failed to set up Management module due to Incorrect Password (You may try the environment variables XCAT_CURRENTUSER and/or XCAT_CURRENTPASS to try a different value)"],$callback,$nodename);
				return 0;
			}
			my $errors = $result->[2];
			if (ref $errors) { 
				foreach my $error (@$errors) {
					sendmsg([$result->[0],$error],$callback,$nodename);
				}
			} else {
				sendmsg([$result->[0],$result->[2]],$callback,$nodename);
			}
			return 0;
		}
	}
	return $rc;
}
sub bt_handle_new_slp_entity {
	my $data = shift;
	delete $data->{sockaddr}; #won't need it
	my $mac = xCAT::SLP::get_mac_for_addr($data->{peername});
	if ($data->{SrvType} eq "service:management-hardware.IBM:integrated-management-module2" and $data->{attributes}->{"enclosure-form-factor"}->[0] eq "BC2") {
		$data->{macaddress}=$mac;
		#this is a Flex ITE, don't go mac searching for it, but remember the chassis UUID for later
		if ($flexchassismap{$data->{attributes}->{"chassis-uuid"}->[0]}->{$mac} and $data->{peername} !~ /fe80/) {
			return;
		}
		$flexchassismap{$data->{attributes}->{"chassis-uuid"}->[0]}->{$mac}=$data;
		return;
	}
	unless ($mac) { return; }
	$btresult{$mac} = $data;
}
1;


