#!/usr/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2006,2019 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 

#  sccsid = "@(#)16   1.7   src/rsct/rm/StorageRM/tools/srmdiag.perl, StorageRM, rsct_rady, rady2035a 6/16/15 08:10:29"

#------------------------------------------------------------------------------------------#
# Syntax:                                                                                  #
#     srmdiag [-h] [-r|-s|-d] -q query_string                                              #
#                                                                                          #
#     -h  -  Display the usage information of this command                                 #
#     -r  -  Display only LVM attribute information or the relationship                    #
#            of the LVM attributes collected by RSCT StoragRM.                             #
#     -s  -  Display only LVM attribute information or the relationship                    #
#            of the LVM attributes collected by system commands.                           #
#     -d  -  Display the diffrence of attribute information collected                      #
#            from StorageRM and system commands.                                           #
#     -q  -  The query string determines what kind of relationship will                    #
#            be displayed.                                                                 #
#            The following keywords can be used in the string with colon                   #
#            as delimiter.                                                                 #
#              pt       Partitiion                                                         #
#              dk       Disk                                                               #
#              pod      Partiition or Disk                                                 #
#              vg       Volume Group                                                       #
#              lv       Logical Volume                                                     #
#              fs       File System                                                        #
#                                                                                          #
#     Common Query String and explanation:                                                 #
#         fs            - all file systems;                                                #
#         lv            - all logical volumes;                                             #
#         pt            - all partitions;                                                  #
#         dk            - all disks;                                                       # 
#         pod           - all partitions or disks;                                         #
#         fs:lv         - the relationship of file system and logical volume;              #
#         fs:pt         - the relationship of file system and logical volume;              #
#         fs:dk         - the relationship of file system and disk;                        #
#         fs:pod        - the relationship of file system and partition or disk;           #
#         fs:pod:dk     - the relationship of file system, partition or disk and disk;     #
#         pt:dk/dk:pt   - the relationship of partition and disk;                          #
#         fs:lv:vg      - the relationship of file system, logical volume and volume group.#
#                         File system is the key word to get the relationship;             #
#         fs:lv:vg:pod  - the relationship of file system, logical volume.                 #
#                         Volume Group is the key word.                                    #
#                                                                                          #
#                                                                                          #
# Examples:                                                                                #
#         1. To display the file systems' information collected by rsct.                   #
#               srmdiag -r -q fs                                                           #
#                                                                                          #
#         2. Display the relationship among the file system, logical volume                #
#            and volume group information collected by system command line.                #
#               srmdiag -s -q fs:lv:vg                                                     #
#                                                                                          #
#         3. Display the differences information of filesystem,logical volume,             #
#            volume group and disk respectively which is collected from RSCT               #
#            StorageRM and system commands.                                                #
#               srmdiag -d -q fs:lv:vg:dk                                                  #
#                                                                                          #
#                                                                                          #
# Return code:                                                                             #
#                                                                                          #
#         0 - success, no difference found                                                 #
#         1 - success, difference between system and StorageRM was found                   #
#         2 - input error.   detail will be outputed to stderr                             #
#         3 - other errors.  detail will be outputed to stderr                             #
#                                                                                          #
#------------------------------------------------------------------------------------------#

#--------------------------------------------------------------------#
#                                                                    #
# External commands used:                                            #
#                                                                    #
#     fdisk                                                          #
#     pvdisplay                                                      #
#     vgdisplay                                                      #
#     lvdisplay                                                      #
#     bldid                                                          #
#     cat                                                            #
#     lspv                                                           #
#     getlvodm                                                       #
#     lsfs                                                           #
#     lsrsrc-api                                                     #
#                                                                    #
#--------------------------------------------------------------------#

BEGIN {
    $ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/sbin:' . $ENV{'PATH'};
}

##require 5.8.0;
                                 
use strict;
use warnings;
use locale;
use Getopt::Std;

package common;
#--------------------------------------------------------------------#
#                                                                    #
# common.pm :  include some common functions                         #
#                                                                    #
#--------------------------------------------------------------------#
# @EXPORT = qw(                                                      #
#        errlog                                                      #
#        trim                                                        #
#        is_blank_line                                               #
#        is_comment                                                  #
#        exit_with_msg                                               #
#        reverse_hash                                                #
#        reverse_hashlist                                            #
#        format_list                                                 #
#        repeat_list                                                 #
#        strlist_cmp                                                 #
#        differ                                                      #
# );                                                                 #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
#                                                                    #
# errlog : record the error messages                                 #
#                                                                    #
# Parameters :                                                       #
#    [Variation Parameters] String or Integer                        #
#                                                                    #
# Return :                                                           #
#    NULL                                                            #
#                                                                    #
#--------------------------------------------------------------------#
sub errlog
{
#   my @err = @_;
#   print $::log_handle ("@err\n");
}

#--------------------------------------------------------------------#
#                                                                    #
# trim : remove the blank symbols at begin or end                    #
#                                                                    #
# Parameters :                                                       #
#    [String] the string                                             #
#                                                                    #
# Return :                                                           #
#    [String] the trimed string                                      #
#--------------------------------------------------------------------#
sub trim
{
    my $line = shift;

    if ( $line =~ /^\s*(\S.*\S)\s*$/ ) {
        $line = $1;
    } elsif ( $line =~ /^\s*\S\s*$/ ) {
        $line = $1;
    }

    return $line;
}

#--------------------------------------------------------------------#
#                                                                    #
# is_blank_line : check if the input line is a blank line;           #
#                                                                    #
# Parameters :                                                       #
#       [string]                                                     #
#                                                                    #
# Return :                                                           #
#       [int] 1:SUCCESS; 0:FAIL;                                     #
#                                                                    #
#--------------------------------------------------------------------#
sub is_blank_line
{
    my ($line) = shift;

    if ( $line =~ /^\s*$/ ) {
        return 1;
    }
    return 0;
}

#--------------------------------------------------------------------#
# is_comment : check if the input line is a blank line or a comment  #
#              line ( start with '#' )                               #
#                                                                    #
# Parameters :                                                       #
#    [string] the input line                                         #
#                                                                    #
# Return :                                                           #
#    [int] 1:SUCCESS 0:FAIL;                                         #
#                                                                    #
#--------------------------------------------------------------------#
sub is_comment
{
    my $line = shift;

    if ( $line =~ /^[\s]*$/ || $line =~ /^\s*#/) {
        return 1;
    } else {
        return 0;
    }
}

#--------------------------------------------------------------------#
#                                                                    #
# exit_with_msg : print some msg into stderr device and return the   #
#                 specified value                                    #
#                                                                    #
# Parameters :                                                       #
#    [string] the error msg                                          #
#    [int] the return code                                           #
#                                                                    #
# Return :                                                           #
#    NULL                                                            #
#                                                                    #
#--------------------------------------------------------------------#
sub exit_with_msg
{
    my ($msg, $rc) = @_;

    print STDERR ($msg."\n");
#    errlog($msg);
#    close($::log_handle);

    exit($rc);
}

#--------------------------------------------------------------------#
#                                                                    #
# reverse_hash : reverse a hash's key and value and save the result  #
#                into a new hash;                                    #
#                                                                    #
# Parameters :                                                       #
#    [hash_handle] the original hash handle;                         #
#    [hash_handle] the target hash handle;                           #
#    [list_handle] the whole values list handle;                     #
#    [unkown] padding                                                #
#                                                                    #
# Return:                                                            #
#    NULL                                                            #
#                                                                    #
# Used Global Variables                                              #
#    $::nonexistent : the symbol for nonexistent relationship        #
#                                                                    #
#--------------------------------------------------------------------#
sub reverse_hash
{
    my ($hash1, $hash2, $list2) = @_;
    my $itr;

    # build keys
    # use $::nonexistent to fill all value position firstly.
    foreach $itr ( @$list2 ) {
        $$hash2{ $itr } = $::nonexistent;
    }

    foreach $itr ( keys(%$hash1) ) {
        $$hash2{ $$hash1{$itr} } = $itr;
    }
}

#--------------------------------------------------------------------#
#                                                                    #
# reverse_hashlist : reverse a hash's key and value, and save        #
#                    the result into a new hash_list;                #
#                                                                    #
#    WHAT IS HASH_LIST ?                                             #
#         normal hash struct:                                        #
#          ( key1 => value1, key2 => value2, ... )                   #
#         hash_list struct:                                          #
#          ( key1 => (value1, value2 .. ),key2 => (..) .. keyn=>() ) #
#                                                                    #
# Parameters :                                                       #
#    [hash_handle] the original hash handle;                         #
#    [hash_list_handle] the target hash_list handle;                 #
#    [list_handle] the whole value list handle;                      #
#                                                                    #
# Return :                                                           #
#    NULL                                                            #
#                                                                    #
# Used Global Variables                                              #
#    $::nonexistent : the symbol for nonexistent relationship        #
#                                                                    #
#--------------------------------------------------------------------#
sub reverse_hashlist
{
    my ($_n1_hash, $_1n_hash, $_1_list) = @_;

    my $itr;
    my $value;
    my $list_handle;

    # Build keys
    foreach $itr ( @$_1_list ) {
    # I have no idea is there any way to move the @null_list
    # definition out of here. :-(
        my @null_list = ();
        $$_1n_hash{$itr} = \@null_list;
    }

    foreach $itr ( keys(%$_n1_hash) ) {
        $value = $$_n1_hash{$itr};
        $list_handle = $$_1n_hash{$value};

        push( @$list_handle, $itr );
    }

    # se ($::nonexistent) to indicate a nonexistent relationship;
    foreach $itr ( keys(%$_1n_hash) ) {
        $list_handle = $$_1n_hash{$itr};

        if ( scalar(@$list_handle) == 0 ) {
            push (@$list_handle, $::nonexistent);
        }
    }
}

#--------------------------------------------------------------------#
# format_list : using a format list to format a input str list, and  #
#               return the result string line                        #
#                                                                    #
# Parameters :                                                       #
#       [strlist_handle] the input string  data list;                #
#       [intlist_handle] the field width list;                       #
#       [char] the padding character;                                #
#       [int] indicate if truncate the overflow part of a string;    #
#                                                                    #
# Return :                                                           #
#       [string] the final formatted string;                         #
#                                                                    #
#--------------------------------------------------------------------#
sub format_list
{
    my ($input_lst, $fw_lst, $padding, $truncate_flg) = @_;

    my $fw = 0;                    # field width
    my $diff = 0;
    my $offset = 0;
    my $data_len = 0;
    my $length = 0;

    my $data = '';
    my $output = '';


    $length = scalar(@$input_lst);

    while( $offset < $length ) {

        $data = $$input_lst[$offset];
        $fw = $$fw_lst[$offset];

        $data_len  = length($data);
        $diff = $fw - $data_len;

        if ( $diff > 0 ) {

            $output .= $data;
            $output .= $padding x $diff;

        } else {

             # truncate the overflow part of a string
            if ( $truncate_flg == 1 )
            {
                $output .= substr($data, 0, $fw - 4 );
                $output .= "...$padding";
            } else {
                $output .= $data;
                $output .= $padding;
            } # if ( $truncat_flg == 1 )

        } # if ( $diff > 0 )
           
        $offset++;
    } # while( $offset < $length )

    return $output;
}

#--------------------------------------------------------------------#
#                                                                    #
# repeat_list : repeatly fill some info into a list                  #
#                                                                    #
# Parameters :                                                       #
#    [list_handle] the target list handle                            #
#    [any] the padding                                               #
#    [int] length, how many push time                                #
#                                                                    #
# Return :                                                           #
#    NULL                                                            #
#                                                                    #
#--------------------------------------------------------------------#
sub repeat_list
{
    my ( $list_handle, $padding, $length ) = @_;

    my $offset = 0;
    while ( $offset < $length ) {
        push ( @$list_handle, $padding );
        $offset++;
    }
}

#--------------------------------------------------------------------#
#                                                                    #
# strlist_cmp : cmpare 2 string list                                 # 
#                                                                    #
# Parameters :                                                       #
#    [strlist_handle] 1st strlist handle                             #
#    [strlist_handle] 2nd strlist handle                             #
#                                                                    #
# Return :                                                           #
#    [int] 0: equal 1: non-equal                                     #
#                                                                    #
#--------------------------------------------------------------------#
sub strlist_cmp($$)
{

    my ( $l_strlist, $r_strlist ) = @_;

    my $ofst = 0;
    my $l_str = '';
    my $r_str = '';
    my $l_len = scalar(@$l_strlist);
    my $r_len = scalar(@$r_strlist);

    if ( $l_len != $r_len ) {
         return 1;
    }

    while ( $ofst < $l_len ) {
        $l_str = $$l_strlist[$ofst];
        $r_str = $$r_strlist[$ofst];

        if ( $l_str ne $r_str ) {
            return 1;
        }

        $ofst++;
    }

    return 0;
}

#--------------------------------------------------------------------#
# diff : diff 2 lists and get the common and different data.         #
#                                                                    #
# Parameters :                                                       #
#       [anylist_handle] the 1st list;                               #
#       [anylist_handle] the 2st list;                               #
#       [anylist_handle] the handle to refer the common data from    #
#                        1st list;                                   #
#       [anylist_handle] the handle to refer the common data from    #
#                        2nd list;                                   #
#       [anylist_handle] the handle to refer the special data from   #
#                        1st list;                                   #
#       [anylist_handle] the handle to refer the special data from   #
#                        2nd list;                                   #
#       [func_ptr] the function pointer, which refer the call back   #
#                  function to compare two elements                  #
#                  0 : specify equal                                 #
#                  1 : special not equal                             #
#                                                                    #
# Return :                                                           #
#       [int] 0: equal;  non-0: different;                           #
#--------------------------------------------------------------------#

sub differ
{
    my ( $lh_1, $lh_2, $lh_com1, $lh_com2,
            $lh_spe1, $lh_spe2, $cb_func ) = @_;

    my $ret = 0;
    my $loop_2 = 0;
    my $offset_1 = 0;
    my $offset_2 = 0;
    my @spe_1 = @$lh_1;
    my @spe_2 = @$lh_2;
    my $len_1 = scalar( @spe_1 );
    my $len_2 = scalar( @spe_2 );

    my $itor_1;
    my $itor_2;

    @$lh_com1  = ();
    @$lh_com2  = ();
    @$lh_spe1  = ();
    @$lh_spe2  = ();

    while ( $offset_1 < $len_1 ) {
        $itor_1 = $spe_1[$offset_1];

        $loop_2 = 1;
        $offset_2 = 0;

        while ( $loop_2 == 1 && $offset_2 < $len_2 ) {

            if ( defined( $spe_2[$offset_2] ) ) {

                $itor_2 = $spe_2[$offset_2];

                # Insert the comman elements into correspoing list;
                if ( $cb_func->($itor_1, $itor_2 ) == 0 ) {
                    push( @$lh_com1, $itor_1 );
                    push( @$lh_com2, $itor_2 );

                    delete $spe_1[$offset_1];
                    delete $spe_2[$offset_2];

                    # exit the inner loop
                    $loop_2 = 0;
                }
            }

            $offset_2++;
        }

        # Insert list1 special elements;
        if ( $loop_2 == 1 ) {
            push ( @$lh_spe1, $itor_1 );
            $ret++;
        }

        $offset_1++;
    }

    # Insert list2 special elements;
    $offset_2 = 0;
    while( $offset_2 < $len_2 ) {
        if ( defined( $spe_2[$offset_2] ) ) {
            push ( @$lh_spe2, $spe_2[$offset_2] );
            $ret++;
        }

        $offset_2++;
    }

    return $ret;
}


package wrapper;
#--------------------------------------------------------------------#
#                                                                    #
# wrapper.pm :  wrapper class                                        # 
#    wrapper::type |indicate the data type which is transparent      #
#                   to client programmer.                            #
#    wrapper::handle |indicate the handle which refer to the real    #
#                     date                                           #
#                                                                    #
# In other words, this class will provide a transparent data type    #
# information to client programmer.                                  #
#                                                                    #
#--------------------------------------------------------------------#
# @EXPORT = qw(                                                      #
#        get_type                                                    #
#        get_handle                                                  #
#--------------------------------------------------------------------#

#--------------------------------------------------------------------#
# new : constructor                                                  #
#                                                                    #
# Parameters :                                                       #
#    [String] %_{'type'} the data type                               #
#    [handle] %_{'handle'} the handle                                #
#                                                                    #
# Return :                                                           #
#    [handle] object reference                                       #
#                                                                    #
#--------------------------------------------------------------------#
sub new {
    my $this  = [];

    my %params= @_;

    $this->[0] = $params{'type'};
    $this->[1] = $params{'handle'};
    bless $this;

    return $this;
}

sub get_type
{
    my $this = shift;
    return $this->[0];
}

sub get_handle
{
    my $this  = shift;
    my $type = shift;

#   # Error Occured!!!
#   if ( common::is_blank_line( $this->[0] ) ) {
#       print 'This->[0] is null',"\m";
#   }

    if ( $this->[0] eq $type ) {
        return $this->[1];
    }

    return 0;
}

package reaper;
#--------------------------------------------------------------------#
#                                                                    #
# reaper.pm : reaper class                                           #
#                                                                    #
#     This class is a proxy to aix_reaper, lix_reaper, and srm_reaper#
#     the function 'checkup' and 'checkout' will be associated with  #
#     corresponding aix_reaper::checkup, aix_reaper::checkout,       #
#     lix_reaper::checkup, lix_reaper::checkout, srm_reaper::checkup #
#     srm_reaper::checkout.                                          #
#                                                                    #
#     This class help me to do sample dynamic-binding                #
#--------------------------------------------------------------------#

$reaper::rel_one2more =  0;
$reaper::rel_one2one  =  1;
$reaper::rel_unsupt   = -1;

#--------------------------------------------------------------------#
# new : constructor                                                  #
#                                                                    #
# Parameters :                                                       #
#    [String] %_{'type'} the data type                               #
#    [handle] %_{'handle'} the handle                                #
#                                                                    #
# Return :                                                           #
#    [handle] object reference                                       #
#                                                                    #
#--------------------------------------------------------------------#
sub new {
    my $this = [];
    my @params = ();

    shift;
    @params = @_;

    $this->[0] = $params[0];      # this
    $this->[1] = $params[1];      # checkup func
    $this->[2] = $params[2];      # checkout func

    bless $this;
    return $this;
}

sub checkup
{
    my $this = shift;

    my $inst = $this->[0];
    my $func = $this->[1];
    my @params = @_;

    return $func->($inst, @params);
}

sub checkout
{
    my $this = shift;

    my $inst = $this->[0];
    my $func = $this->[2];
    my @params = @_;

    return $func->($inst, @params);
}

package lix_reaper;
#--------------------------------------------------------------------#
#                                                                    #
# lix_reaper.pm : lix_reaper class                                   #
#                                                                    #
# This class will harvest LVM attributes and information from Linux  #
# platform.                                                          #
# This class isn't transparent to client programer.                  #
#--------------------------------------------------------------------#
# @EXPORT = qw(                                                      #
#              checkup                                               #
#              checkout                                              #
# );                                                                 #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# new : constructor                                                  #
#                                                                    #
# Parameters :                                                       #
#    NULL                                                            #
#                                                                    #
# Return :                                                           #
#    [handle] object reference                                       #
#                                                                    #
#--------------------------------------------------------------------#
sub new
{
    my $this = {};
    my $proxy;
    bless $this;

    $this->harvest_harddisk();
    $this->harvest_volumegroup();
    $this->harvest_logicalvolume();
    $this->harvest_filessystem();

    $proxy = reaper->new( $this,
                          \&lix_reaper::checkup,
                          \&lix_reaper::checkout );
    return $proxy;
}

sub checkup
{

    my ( $this, $query ) = @_;

    my $hdl;
    my $type=''; 

    if ( defined($this->{$query} ) ) {
        $hdl  = $this->{$query};
        $type = wrapper::get_type($hdl);
         
        if ( $type eq 'Hash_List' ) {
            return $reaper::rel_one2more;
        } else {
            return $reaper::rel_one2one;
        }       
    }
 
    return $reaper::rel_unsupt;
}

sub checkout
{
    my ($this, $query) = @_;
    my $relationship = $this->{$query};
    return $relationship;
}

# ------------------------------------------------------------------
# PRIVATE FUNCTIONS
# ------------------------------------------------------------------

sub support_dev($)
{
    my $device = shift;

    my $itor = '';
    my $dev_name = '';
    my $dev_type = '';

    if ( $device =~ /\/([^\/]+)$/ ) {
        $dev_name = $1;
    } else {
        $dev_name = $device;
    }

    if ( $dev_name =~ /s[gd]\S+$/ ) {
        $dev_type = 'SCSI';
    }

    if ( $dev_type eq '' ) {
        return 0;
    }

    foreach $itor ( @::Supported_DEV_Type_Linux_List ) {
        if ( $dev_type eq $itor ) {
            return 1;
        }
    }

    return 0;
}

sub support_fs_type($) {
    my $fs_type = shift;

    my $itor = '';

    foreach $itor ( @::Supported_FS_Type_Linux_List ) {
        if ( $fs_type eq $itor ) {
            return 1;
        }
    }

    return 0;
}

sub harvest_harddisk
{
my $this = shift;

my $disk_name = '';
my $part_name = '';
my $cmd = ''; 
my $rc = 0;

my %part_disk =();
my %part_part =();
my %disk_part =();
my %disk_disk =();
my %pod_dk =();
my %dk_pod =();
my %pod_pt =();
my %pt_pod =();
my %pod_pod =();
my @parts =();
my @disks =();
my @pods =();
my %tmp =();
my %mded = ();
my @output = ();

my $itor;

# harvest MD devices
my $md_name = '';
my @mds = ();
my %md_md = ();

$cmd = "/sbin/mdadm --version 2>&1";
@output = `$cmd`;
$cmd = "";
foreach (@output) {
    if ($_ =~ /^mdadm\s+-\s+v(\d+\.\d+)\.?.*/) {
        my $version = $1;
        if ($version < 2.0 ) {
            $cmd = "/sbin/mdadm --examine --brief --scan --config=partitions";
        } else {
            $cmd = "/sbin/mdadm --examine --brief --scan --config=partitions --verbose";
        }
        last;
    }
}
if (length($cmd) == 0) {
    common::exit_with_msg("System Error: can't detect the mdadm program version.\n", 3);
}

@output = `$cmd`;
foreach $itor (@output) {
    next if ( 1 == common::is_blank_line($itor) );

    if ( $itor =~ /^ARRAY\s*(\S+)\s*/ ) {
        $md_name = $1;
        push(@mds, $md_name);
        push(@disks, $md_name);

        $md_md{$md_name} = $md_name;
        $disk_disk{$md_name} = $md_name;
    }

    if ( $itor =~ /^\s+devices=(.*)\s*$/) {
        my @devs = split(/,/, $1);
        $mded{$_} = $md_name foreach (@devs);
#       push(@mded, @devs);
    }    
}

# harvest regular devices
$cmd = 'fdisk -l 2>/dev/null';
@output = `$cmd`;
$rc = $? >> 8;
if ( $rc != 0 ) {
    common::exit_with_msg("System Error: Invoke '$cmd' failed with RC=$rc\n", 3);
}
foreach $itor ( @output ) {
    next if ( 1 == common::is_blank_line($itor) );
	# if disk is supported but Disk identifier is 0x00000000
	# that means that its actually a logical volume.
	if ( $itor =~ /^\s*Disk\s*identifier\s*:\s*(0x[0]{8})/){
		if (scalar(@disks)){
			if ( support_dev($disk_name) == 1 ) {
			pop (@disks);
			} 
		}
    }elsif ( $itor =~ /^\s*Disk\s*(\S+)\s*:/ ) {
        $disk_name = $1;
        if ( support_dev($disk_name) == 1 ) {
            $disk_disk{$disk_name} = $disk_name;
            push ( @disks, $disk_name );
        } 
	else {
            $disk_name = '';
        }
    } elsif ( $itor =~ /^\s*(\/dev\S+).*/) {
        $part_name = $1;
        push ( @parts, $part_name );
        $part_part{$part_name} = $part_name;
	# Get the disk name printed in output just before the partition.
	$disk_name = $disks[-1];	
        $part_disk{$part_name} = $disk_name;
    }
}


@pods = (@disks, @parts);

%dk_pod = %disk_disk;
%pt_pod = %part_part;

%pod_pod = (%disk_disk, %part_part);
%pod_dk = (%disk_disk, %part_disk);

common::reverse_hashlist( \%part_part, \%tmp, \@parts );
common::reverse_hashlist( \%part_disk, \%disk_part, \@disks );

%pod_pt = (%disk_part, %tmp);

#print "size of \%md_md is " . scalar(keys(%md_md)) . "\n";
$this->{' | mded | '} = wrapper::new( 'type' => 'Hash', 'handle' => \%mded );
$this->{'md:md'} = wrapper::new( 'type' => 'Hash', 'handle' => \%md_md );
$this->{'pt:dk'} = wrapper::new( 'type' => 'Hash', 'handle' => \%part_disk );
$this->{'dk:dk'} = wrapper::new( 'type' => 'Hash', 'handle' => \%disk_disk );
$this->{'pt:pt'} = wrapper::new( 'type' => 'Hash', 'handle' => \%part_part );
$this->{'dk:pt'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%disk_part );
$this->{'dk:pod'} = wrapper::new( 'type' => 'Hash', 'handle' => \%dk_pod );
$this->{'pod:dk'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pod_dk );
$this->{'pt:pod'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pt_pod );
$this->{'pod:pt'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%pod_pt );
$this->{'pod:pod'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pod_pod );

$this->{'md'}    = wrapper::new( 'type' => 'List', 'handle' => \@mds );
$this->{'pt'}    = wrapper::new( 'type' => 'List', 'handle' => \@parts );
$this->{'dk'}    = wrapper::new( 'type' => 'List', 'handle' => \@disks );
$this->{'pod'}   = wrapper::new( 'type' => 'List', 'handle' => \@pods );

return 0;
}

sub harvest_volumegroup {

my $this = shift;

my $rc = 0;
my $pv_name_valid = 0;
my $lonely_vg = 0;
my $pv_name = '';
my $vg_name = '';
my $itor = '';
my $cmd = '';

my %pv_vg = ();                   # ParTition or Disk to VG
my %vg_pv = ();                   # VG to POD
my %vg_vg = ();                   # VG to VG
my %pt_vg = ();                   # ParTition to VG
my %vg_pt = ();                   # VG to ParTition
my %dk_vg = ();                   # DisK to VG
my %vg_dk = ();                   # VG to DisK
my %pt_dk = ();                   # ParTition to DisK
my %vg_md = ();                   # VG to MD
my %md_vg = ();                   # MD to VG
my @vgs = ();                     # VG List
my %pod_pod = ();                 # PoD to PoD
my %md_md = ();                   # MD to MD
my @output = ();

my $hash_handle; 

$hash_handle = wrapper::get_handle( $this->{'pod:pod'}, 'Hash');
%pod_pod = %$hash_handle;

$cmd = 'pvdisplay 2>/dev/null';
@output = `$cmd`;
$rc = $? >> 8;

if ( $rc != 0 ) {
    common::exit_with_msg("System Error: Invoke '$cmd' failed with RC=$rc\n", 3);
}


$hash_handle = wrapper::get_handle( $this->{'pt:dk'}, 'Hash' );
%pt_dk = %$hash_handle;

$hash_handle = wrapper::get_handle( $this->{'md:md'}, 'Hash' );
%md_md = %$hash_handle;

foreach $itor ( @output ) {
    next if ( common::is_blank_line($itor) == 1 );

    if ( $pv_name_valid == 0 && $itor =~ /^\s*PV\s*Name\s*(\S+)\s*$/ ) {
        $pv_name = $1;
        $pv_name_valid++;
        if ( !defined($pod_pod{$pv_name}) ) {
            $lonely_vg++;
        }
    } elsif ( $pv_name_valid != 0 && $itor =~ /^\s*VG\s*Name\s*(\S*)\s*$/ ) {

        $vg_name = $1;
        if ( $lonely_vg == 0 ) {
            if ( 1 != common::is_blank_line($vg_name) ) {

                $pv_vg{$pv_name}=$vg_name;

                if ( defined($vg_pv{$vg_name}) ) {
                    my $tmp_pv_list_handle = $vg_pv{$vg_name};
                    push ( @$tmp_pv_list_handle, $pv_name );
                } else {
                    my @tmp_pv_list = ( $pv_name );
                    push (@vgs, $vg_name); 

                    $vg_pv{$vg_name} = \@tmp_pv_list;
                    $vg_vg{$vg_name}=$vg_name;
                } # if ( defined($vg_pv{$vg_name}) )
            } # if ( 1 != common::is_blank_line($vg_name) ) 
        } else {
            $lonely_vg = 0;

            if ( 1 != common::is_blank_line($vg_name)) {
                if($vg_name ~~ @vgs) {print "Already exists... \n"; }
		else { push (@vgs, $vg_name);}
            } # if ( 1 != common::is_blank_line($vg_name) ) 
        } # if ( $pv_name_valid > 0 )

        $pv_name_valid = 0; 
    } # if ( $pv_name_valid == 0 && $itor =~ /^\s*PV\s*Name\s*(\S+)\s*$/ )
} # foreach $itor ( @output )

#@vgs = keys(%vg_pv);

foreach $itor ( keys(%pv_vg) ) {
    if ( defined($pt_dk{$itor}) ) {
        $pt_vg{$itor} = $pv_vg{$itor};
    } else {
        if ( defined($md_md{$itor}) ) {
            $md_vg{$itor} = $pv_vg{$itor};
        }

        $dk_vg{$itor} = $pv_vg{$itor};
    } #  if ( defined($pt_dk{$itor}) )
} # foreach $itor ( keys(%pv_vg) ) 


common::reverse_hashlist( \%pt_vg, \%vg_pt, \@vgs);
common::reverse_hashlist( \%dk_vg, \%vg_dk, \@vgs);
common::reverse_hashlist( \%md_vg, \%vg_md, \@vgs);

$this->{'pod:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pv_vg );
$this->{'vg:pod'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_pv );
$this->{'pt:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pt_vg );
$this->{'vg:pt'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_pt );
$this->{'dk:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%dk_vg );
$this->{'vg:dk'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_dk );
$this->{'md:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%md_vg );
$this->{'vg:md'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_md);
$this->{'vg:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%vg_vg );
$this->{'vg'} = wrapper::new( 'type' => 'List', 'handle' => \@vgs );

return 0;
}

sub harvest_logicalvolume
{

my $this = shift;

my $rc = 0;

my $cmd = '';
my $itor = '';
my $lv_name = '';
my $vg_name = '';
my $short_lvname = '';

my %lv_vg =();                         # LV to VG
my %vg_lv =();                         # VG to LV
my %lv_lv =();                         # LV to LV
my @lvs = ();                          # LV List
my @vgs = ();                          # VG List
my %lvname_long2short = ();            # LV : long name to short name
my @output = ();

my $list_handle;


$cmd = 'lvdisplay 2>/dev/null';
@output = `$cmd`;
$rc = $? >> 8;

if ( $rc != 0 ) {
    common::exit_with_msg("System Error: Invoke '$cmd' failed with RC=$rc\n", 3);
}

$list_handle = wrapper::get_handle( $this->{'vg'}, 'List');
@vgs = @$list_handle;

foreach $itor ( @output ) {
    next if ( 1 == common::is_blank_line($itor) );

    if ( length($lv_name) == 0 && $itor =~ /^\s*LV\s*Path\s*(\S+)\s*$/ ) {
        $lv_name = $1;
	}
 if ( length($lv_name) == 0 && $itor =~ /^\s*LV\s*Name\s*(\S+)\s*$/ ) {
		$lv_name = $1; 
    } elsif ( length($lv_name) > 0 && $itor =~ /^\s*VG\s*Name\s*(\S*)\s*$/ ) {
        $vg_name = $1;

        push( @lvs, $lv_name );

        if ( $lv_name =~ /\/([^\/]+)$/ ) {
            $short_lvname = $1;
            $lvname_long2short{$lv_name} = $short_lvname;
        } # if ( $lv_name =~ /\/([^\/]+)$/ )

        if ( 1 != common::is_blank_line($vg_name) ) {
            $lv_lv{$lv_name}=$lv_name; 
            $lv_vg{$lv_name}=$vg_name;
            $vg_name = '';
        } # if ( 1 != common::is_blank_line($vg_name) )

        $lv_name = '';
    } # if ( length($lv_name) == 0 && $itor =~ /^\s*LV\s*Name\s*(\S+)\s*$/ )
} # foreach $itor ( @output )

common::reverse_hashlist( \%lv_vg, \%vg_lv, \@vgs );

$this->{'lv:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%lv_vg );
$this->{'lv:lv'} = wrapper::new( 'type' => 'Hash', 'handle' => \%lv_lv );
$this->{'vg:lv'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_lv );
$this->{'lv'} = wrapper::new( 'type' => 'List', 'handle' => \@lvs );
$this->{'lv:short_lv'} = wrapper::new( 'type' => 'Hash', 'handle' => \%lvname_long2short );

return 0;
}

sub harvest_filessystem
{
my $this = shift;

my $rc = 0;
my $cmd = '';
my $line = '';
my $itor = '';
my @out = ();

my @lvs = ();               # LV List
my @pts = ();               # ParTition List
my @dks = ();               # DisK List
my %mds = ();               # MD Hash 
my %fs_fs = ();             # FS to FS
my %fs_lv = ();             # FS to LV
my %lv_fs = ();             # LV to FS
my %fs_pt = ();             # FS to ParTition
my %pt_fs = ();             # ParTition to FS
my %fs_dk = ();             # FS to DisK
my %dk_fs = ();             # DisK to FS
my %md_fs = ();             # MD to FS
my %fs_md = ();             # FS to MD
my %fs_pod = ();            # FS to ParTition or Disk
my %pod_fs = ();            # ParTition or Disk to FS
my @fs     = ();            # FS List
my %fs_mp  = ();            # FS : device name to mount point
my %fs_mnt = ();            # FS : device name to real mount point
my %fs_sysmnt = ();         # FS : device naem to system mount point information
my %mded   = ();            # FS : MD-ed deviced to MD deviced

my $list_handle;


$list_handle = wrapper::get_handle( $this->{'lv'}, 'List' );
@lvs = @$list_handle;
$list_handle = wrapper::get_handle( $this->{'pt'}, 'List' );
@pts = @$list_handle;
$list_handle = wrapper::get_handle( $this->{'dk'}, 'List' );
@dks = @$list_handle;
$list_handle = wrapper::get_handle( $this->{'md:md'}, 'Hash' );
%mds = %$list_handle;
$list_handle = wrapper::get_handle( $this->{' | mded | '}, 'Hash' );
%mded = %$list_handle;
# By using /bin/df to collection the MountPoint information

#Filesystem    Type 1024-blocks      Used Available Capacity Mounted on
#/dev/sda2 reiserfs    34506524   3552400  30954124      11% /
#proc          proc           0         0         0       -  /proc
#sysfs        sysfs           0         0         0       -  /sys
#tmpfs        tmpfs      257044        12    257032       1% /dev/shm
#devpts      devpts           0         0         0       -  /dev/pts
#/dev/hda     subfs           0         0         0       -  /media/cdrom
#/dev/fd0     subfs           0         0         0       -  /media/floppy
#usbfs        usbfs           0         0         0       -  /proc/bus/usb

#       $cmd = '/bin/df -alTP';
#       common::errlog("    RUN '$cmd'");
#       @out = `$cmd`;
#       $rc  = $? >> 8;

#       if ( $rc != 0 ) {
#               common::errlog(": failed with RC=$rc\n");
#               next;
#       } else {
#               common::errlog(": successfully.\n");
#       }
#
#
#       foreach my $line ( @out ) {
#               next if ( common::is_blank_line($line) == 1 );
#
#               if ( $line =~ /^(\S+)\s+(\S+)\s+\d+\s+.*\s+(\S+)\s*$/ ) {
#                       my ($dev_name, $fs_type, $mnt) = ($1, $2, $3);
#
#                       if ( support_fs_type($fs_type) == 1 ) {
#                               if ( $dev_name =~ /^\/dev\/mapper\/(\S+)\-(\S+)$/ ) {
#                                       $dev_name = "/dev/$1/$2";
#                               }
#
#                               $fs_mnt{ $dev_name } = $mnt;
#                       }
#               }
#       }
#
#
            #
            # By read /etc/fstab to collection the MountPoint information
            #


my @save_ARGV = @ARGV;
@ARGV = ('/etc/fstab'); 
#/dev/sda6       /       reiserfs        acl,user_xattr 1 1
##/dev/sda7      /data1  auto    noauto,user 0 0
#/dev/sda8       /data2  auto    noauto,user 0 0
##/dev/sda9      /data3  auto    noauto,user 0 0
#/dev/sda10      /data4  auto    noauto,user 0 0
#/dev/sda11      /data5  auto    noauto,user 0 0
#/dev/sda12      /data6  auto    noauto,user 0 0
#/dev/sda1       /windows/C      ntfs    ro,users,gid=users,umask=0002,nls=utf8 0 0
#/dev/sda2       /windows/D      vfat    users,gid=users,umask=0002,iocharset=utf8 0 0
#/dev/sda5       swap    swap    pri=42 0 0
#devpts  /dev/pts        devpts  mode=0620,gid=5 0 0
#proc    /proc   proc    defaults 0 0
#usbfs   /proc/bus/usb   usbfs   noauto 0 0
#sysfs   /sys    sysfs   noauto 0 0
#/dev/dvd        /media/dvd      subfs   fs=cdfss,ro,procuid,nosuid,nodev,exec,iocharset=utf8 0 0
#/dev/fd0        /media/floppy   subfs   fs=floppyfss,procuid,nodev,nosuid,sync 0 0
#
#LABEL=bbb       /data1  auto    noauto,user 0 0
#UUID=87b95f27-82a4-4abb-a2a3-1dc4a8e11eeb       /data3  auto    noauto,user 0 0
foreach $line ( <> ) {
    next if ( common::is_comment($line) == 1 );
    my ($dev_name, $sys_mnt, $fs_type, $option, $p1, $p2) = split(/\s+/, $line);

    next if ( length($p2) == 0 );

    if ( $dev_name =~ /^\s*LABEL=\"?(\S+)\"?\s*$/ ) {
        $dev_name = "L:$1";
    } elsif ( $dev_name =~ /^\s*UUID=\"?(\S+)\"?\s*$/ ) {
        $dev_name = "U:$1";
    } # if ( $dev_name =~ /^\s*LABEL=\"?(\S+)\"?\s*$/ )  

    # Comment the following line to support build
    # hash relationship for swap or orther filesytems
    # the name of which just used to describe the fucntion
    #if ( support_fs_type($fs_type) == 1 ) {

 	if ( $dev_name =~ /^\/dev\/mapper\/(\S+)\-(\S+)$/ ) {
		$dev_name = "/dev/$1/$2";
	}
	
	$fs_sysmnt{ $dev_name } = $sys_mnt;

	#}
}

@ARGV = @save_ARGV;
%fs_mp = (%fs_sysmnt, %fs_mnt);


# build the relationship for fs:lv
foreach $itor ( @lvs ) {
   
    # skip the MD-ed devices
    next if(defined($mded{$itor}));

    $cmd = "blkid $itor";
    @out = `$cmd`;
    $rc  = $? >> 8;

    if ( $rc != 0 ) {
        common::errlog("Invoke '$cmd' failed with RC=$rc\n");
        next;
    }

    foreach $line ( @out ) {
        next if ( common::is_blank_line($line) == 1 );

        my @fields = split( /\s+/, $line );
        my $dev_name   = $itor;
        my $label_name = '';
        my $uuid_name  = '';
        my $fs_type    = '';

        foreach my $field (@fields) {
            if ( $field =~ /LABEL=\"(\S+)\"/ ) {
                $label_name = "L:$1";
            } elsif ( $field =~ /UUID=\"(\S+)\"/ ) {
                $uuid_name = "U:$1";
            } elsif ( $field =~ /\s*TYPE=\"(\S+)\"/) {
                $fs_type = $1;
            }
        }

        if ( support_fs_type($fs_type) == 1 ) {
            my $fs_name = $dev_name;

            if ( defined($fs_mp{$dev_name}) ) {
                $fs_name = $fs_mp{$dev_name};
            } elsif ( defined($fs_mp{$label_name}) ){
                $fs_name = $fs_mp{$label_name};
            } elsif ( defined($fs_mp{$uuid_name}) ){
                $fs_name = $fs_mp{$uuid_name};
            }

            $fs_fs{$fs_name} = $fs_name;
            $fs_lv{$fs_name} = $dev_name;
            push ( @fs, $fs_name );
        }
    }
}

# build relationships for fs:dk
foreach my $itor ( @dks ) {
    # skip the MD-ed devices
    next if(defined($mded{$itor}));

    $cmd = "blkid $itor";
    @out = `$cmd`;
    $rc  = $? >> 8;

    if ( $rc != 0 ) {
        common::errlog("Invoke '$cmd' failed with RC=$rc\n");
        next;
    }

    foreach $line ( @out ) {
        next if ( common::is_blank_line($line) == 1 );

        my @fields = split( /\s+/, $line );
        my $dev_name   = $itor;
        my $label_name = '';
        my $uuid_name  = '';
        my $fs_type    = '';

        foreach my $field (@fields) {
            if ( $field =~ /LABEL=\"(\S+)\"/ ) {
                $label_name = "L:$1";
            } elsif ( $field =~ /UUID=\"(\S+)\"/ ) {
                $uuid_name = "U:$1";
            } elsif ( $field =~ /\s*TYPE=\"(\S+)\"/) {
                $fs_type = $1;
            }
        }

        if ( support_fs_type($fs_type) == 1 ) {
            my $fs_name = $dev_name;

            if ( defined($fs_mp{$dev_name}) ) {
                $fs_name = $fs_mp{$dev_name};
            } elsif ( defined($fs_mp{$label_name}) ){
                $fs_name = $fs_mp{$label_name};
            } elsif ( defined($fs_mp{$uuid_name}) ){
                $fs_name = $fs_mp{$uuid_name};
            }

            $fs_fs{$fs_name} = $fs_name;
            $fs_dk{$fs_name} = $dev_name;

            if ( defined($mds{$itor}) ) {
                $fs_md{$fs_name} = $fs_dk{$fs_name};
            }

            push ( @fs, $fs_name );
        }
    }
}

# build relationships for fs:pt
foreach my $itor ( @pts ) {
    # skip the MD-ed devices
    next if(defined($mded{$itor}));

    $cmd = "blkid $itor";
    @out = `$cmd`;
    $rc  = $? >> 8;

    if ( $rc != 0 ) {
        common::errlog("Invoke '$cmd' failed with RC=$rc\n");
        next;
    }

    foreach my $line ( @out ) {
        next if ( common::is_blank_line($line) == 1 );

        my @fields = split( /\s+/, $line );
        my $dev_name   = $itor;
        my $label_name = '';
        my $uuid_name  = '';
        my $fs_type    = '';

        foreach my $field (@fields) {
            if ( $field =~ /LABEL=\"(\S+)\"/ ) {
                $label_name = "L:$1";
            } elsif ( $field =~ /UUID=\"(\S+)\"/ ) {
                $uuid_name = "U:$1";
            } elsif ( $field =~ /\s*TYPE=\"(\S+)\"/) {
                $fs_type = $1;
            }
        }

        if ( support_fs_type($fs_type) == 1 ) {
            my $fs_name = $dev_name;

            if ( defined($fs_mp{$dev_name}) ) {
                $fs_name = $fs_mp{$dev_name};
            } elsif ( defined($fs_mp{$label_name}) ){
                $fs_name = $fs_mp{$label_name};
            } elsif ( defined($fs_mp{$uuid_name}) ){
                $fs_name = $fs_mp{$uuid_name};
            }

            $fs_fs{$fs_name} = $fs_name;
            $fs_pt{$fs_name} = $dev_name;

            push ( @fs, $fs_name );
        }
    }
}

#Harvest GPFS data
#File system attributes for /dev/crfs33on33Node:
#===============================================
#flag value            description
#---- ---------------- -----------------------------------------------------
# -o  none             Additional mount options
# -T  /gpfs/crfs33on33Node Default mount point
#
#File system attributes for /dev/f32f33Co:
#=========================================
#flag value            description
#---- ---------------- -----------------------------------------------------
# -o  none             Additional mount options
# -T  /gpfs/f32f33Co   Default mount point
#
$cmd = '/usr/lpp/mmfs/bin/mmlsfs all -o -T 2>/dev/null';
@out = `$cmd`;
my ($mmdev, $mmname) = ('', '');
foreach my $line ( @out )
{
    next if ( common::is_comment( $line )  == 1 );
    if ( $line  =~ /^File system attributes for\s*(\S+):\s*$/ ) {
        $mmdev = $1;
    }

    if ( $line =~ /\s*-T\s+(\S+)\s+/ ) {
        $mmname = $1;
        if (length($mmdev) != 0) {
#           $fs_lv{$mmname} = $mmdev;
            $fs_fs{$mmname} = $mmname;
            push ( @fs, $mmname );
        }
    }
}
%fs_pod = ( %fs_pt, %fs_dk );
common::reverse_hash( \%fs_lv, \%lv_fs, \@lvs);
common::reverse_hash( \%fs_pt, \%pt_fs, \@pts);
common::reverse_hash( \%fs_dk, \%dk_fs, \@dks);
my @tmplist = keys(%mds);
common::reverse_hash( \%fs_md, \%md_fs, \@tmplist);
%pod_fs = ( %pt_fs, %dk_fs );

$this->{'fs:lv'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_lv );
$this->{'lv:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%lv_fs );
$this->{'fs:pt'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_pt );
$this->{'pt:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pt_fs );
$this->{'fs:dk'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_dk );
$this->{'dk:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%dk_fs );
$this->{'md:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%md_fs );
$this->{'fs:md'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_md );
$this->{'fs:pod'}= wrapper::new( 'type' => 'Hash', 'handle' => \%fs_pod );
$this->{'pod:fs'}= wrapper::new( 'type' => 'Hash', 'handle' => \%pod_fs );
$this->{'fs'}    = wrapper::new( 'type' => 'List', 'handle' => \@fs );
$this->{'fs:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_fs );
$this->{'fs:mnt'}   = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_mnt );
$this->{'fs:sysmnt'}= wrapper::new( 'type' => 'Hash', 'handle' => \%fs_sysmnt );
return 0;
}

package srm_reaper;

# ------------------------------------------------------------------
# PUBLIC FUNCTIONS
# ------------------------------------------------------------------
sub new {

    my $this = {};
    my $proxy; 

    bless $this;

    $this->harvest_dk_pt_vg($this);
    $this->harvest_lv_fs($this);

    $proxy = reaper->new( $this, 
                          \&srm_reaper::checkup,
                          \&srm_reaper::checkout );
    return $proxy;
}

sub checkup
{

    my ( $this, $query ) = @_;

    my $hdl;
    my $type=''; 

    if ( defined($this->{$query} ) ) {
        $hdl  = $this->{$query};
        $type = wrapper::get_type($hdl);
         
        if ( $type eq 'Hash_List' ) {
            return $reaper::rel_one2more;
        } else {
            return $reaper::rel_one2one;
        }       
    }
 
    return $reaper::rel_unsupt;
}

sub checkout
{
    my ($this, $query) = @_;
    my $relationship = $this->{$query};
    return $relationship;
}

# ------------------------------------------------------------------
# PRIVATE FUNCTIONS
# ------------------------------------------------------------------

sub harvest_dk_pt_vg
{
        my $this = shift;

        my %pod_pt    =();
        my %pod_dk    =();
        my %pt_pod    =();
        my %dk_pod    =();
        my %dk_dk    =();
        my %pt_pt    =();
        my %md_md    =();
        my %vg_vg    =();
        my %pod_pod    =();

        my %pod_vg    =();
        my %vg_pod    =();

        my %pt_dk     =();
        my %dk_pt     =();

        my %dk_vg     =();
        my %vg_dk     =();

        my %md_vg     =();
        my %vg_md     =();

        my %pt_vg     =();
        my %vg_pt     =();

        my %vgid_vg   =();
        my %dkid_dk   =();
        my %ptid_pt   =();

        my %pt_vgid    =();
        my %dk_vgid    =();

        my %tmp        =();
        my @dks        =();
        my @mds        =();
        my @pts        =();
        my @pods       =();
        my @vgs        =();

        my $cmd = '';
        my @out = ();
        my $rc  = 0;
        my $mdflag = 0;

        ###########################################################################
        ##    DISK COLLECTION
        ###########################################################################

        $cmd = 'lsrsrc-api -D"::" -I"::" -s IBM.Disk::ResourceType==0::ResourceHandle::DeviceName::DependentResource::GhostDevice 2>/dev/null';
        @out = `$cmd`;
        $rc = $? >> 8;

       if ( $rc != 0 ) {
             # Run double check
            $cmd = 'lsrsrc IBM.Disk 2>&1 >/dev/null';
            $rc  = system($cmd) >> 8;

            if ( $rc != 0 ) {
                common::exit_with_msg("RSCT Error:RUN '$cmd' failed with RC=$rc\n", 3);
            } else {
                @out = ();
            }
        }


        foreach my $line ( @out ) {
                next if ( 1 == common::is_blank_line($line) );

                my ($dkid, $dk, $vgid, $ghost) = split('::', $line);
                next if ( length($ghost) == 0 );

                $mdflag = 0;
                $mdflag = 1 if ($dk =~ /^\/dev\/md/);

                $dk = "$::ghost_mark$dk" if ( $ghost != 0 );

                push( @dks, $dk );

                $dk_vgid{$dk}   = $vgid;
                $dkid_dk{$dkid} = $dk;
                $dk_dk{$dk} = $dk;

                if ($mdflag == 1) {
                    $md_md{$dk} = $dk;
                    push(@mds, $dk);
                }
        }

        ###########################################################################
        ##     PARTITION COLLECTION
        ###########################################################################

        $cmd = 'lsrsrc-api -D"::" -I"::" -s IBM.Partition::ResourceType==0::ResourceHandle::DeviceName::ContainerResource::DependentResource::GhostDevice 2>/dev/null';
        @out = `$cmd`;
        $rc = $? >> 8;


        if ( $rc != 0 ) {
            # Run double check
            $cmd = 'lsrsrc IBM.Partition 2>&1 >/dev/null';
            $rc  = system($cmd) >> 8;

            if ( $rc != 0 ) {
                common::exit_with_msg("RSCT Error:RUN '$cmd' failed with RC=$rc\n", 3);
            } else {
                @out = ();
            }
        }

        foreach my $itor ( @out ) {
                next if ( 1 == common::is_blank_line($itor) );

                my ($ptid, $pt, $dkid, $vgid, $ghost) = split('::', $itor);
                next if ( length($ghost) == 0 );

                my $dk = $dkid_dk{$dkid};

                $pt = "$::ghost_mark$pt" if ( $ghost != 0 );

                push ( @pts, $pt );    

                $ptid_pt{$ptid} = $pt;
                $pt_dk{$pt}     = $dk;
                $pt_vgid{$pt}   = $vgid;
                $pt_pt{$pt}     = $pt;
        }

        ###########################################################################
        ##    VOLUME GROUP COLLECTION
        ###########################################################################
        $cmd = 'lsrsrc-api -D"::" -I"::" -s IBM.VolumeGroup::ResourceType==0::ResourceHandle::VGName::GhostDevice 2>/dev/null';
        @out = `$cmd`;
        $rc = $? >> 8;


        if ( $rc != 0 ) {
                    # Run double check
                $cmd = 'lsrsrc IBM.VolumeGroup 2>&1 >/dev/null';
                $rc  = system($cmd) >> 8;

                if ( $rc != 0 ) {
                        common::exit_with_msg("RSCT Error:RUN '$cmd' failed with RC=$rc\n", 3);
                } else {
                        @out = ();
                }
        }

        foreach my $itor ( @out ) {
                next if ( 1 == common::is_blank_line($itor) );
                my ($vgid, $vg, $ghost) = split('::', $itor);
                next if ( length($ghost) == 0 );

                $vg = "$::ghost_mark$vg" if ( $ghost != 0 );


                push ( @vgs, $vg );
                $vgid_vg{$vgid} = $vg;
                $vg_vg{$vg} = $vg;
        }

        foreach my $itor ( keys(%dk_vgid) ) {

                my $value = $dk_vgid{$itor};
                if ( defined( $vgid_vg{$value} ) ) {

                        my $vg = $vgid_vg{$value};
                        $dk_vg{$itor} = $vg;

                        if (defined( $md_md{$itor} )) {
                            $md_vg{$itor} = $vg;
                        }
                }
        }

        foreach my $itor ( keys(%pt_vgid) ) {

                my $value = $pt_vgid{$itor};

                if ( defined($vgid_vg{$value}) ) {
                        my $vg = $vgid_vg{$value};
                        $pt_vg{$itor} = $vg;

                }
        }

        @pods = (@dks, @pts);
        %pod_vg = (%dk_vg, %pt_vg);
        %pt_pod = %pt_pt;
        %dk_pod = %dk_dk;
        %pod_pod = (%pt_pt, %dk_dk);
        %pod_dk = (%pt_dk, %dk_dk);
        common::reverse_hashlist( \%pt_vg,  \%vg_pt,  \@vgs );
        common::reverse_hashlist( \%dk_vg,  \%vg_dk,  \@vgs );
        common::reverse_hashlist( \%md_vg,  \%vg_md,  \@vgs );
        common::reverse_hashlist( \%pt_dk,  \%dk_pt,  \@dks );
        common::reverse_hashlist( \%pod_vg, \%vg_pod, \@vgs );
        common::reverse_hashlist( \%pt_pt,  \%tmp,  \@pods );
        %pod_pt = (%tmp, %dk_pt);
        $this->{' | vg_handle2vg_name | '} = wrapper::new( 'type' => 'Hash', 'handle' => \%vgid_vg );
        $this->{' | dk_handle2dk_name | '} = wrapper::new( 'type' => 'Hash', 'handle' => \%dkid_dk );
        $this->{' | pt_handle2pt_name | '} = wrapper::new( 'type' => 'Hash', 'handle' => \%ptid_pt );

        $this->{'pt:dk'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pt_dk );
        $this->{'dk:pt'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%dk_pt );

        $this->{'vg:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%vg_vg );
        $this->{'pt:pt'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pt_pt );
        $this->{'dk:dk'} = wrapper::new( 'type' => 'Hash', 'handle' => \%dk_dk );
        $this->{'md:md'} = wrapper::new( 'type' => 'Hash', 'handle' => \%md_md );
        $this->{'pt:pod'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pt_pod );
        $this->{'pod:pt'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%pod_pt );
        $this->{'dk:pod'} = wrapper::new( 'type' => 'Hash', 'handle' => \%dk_pod );
        $this->{'pod:dk'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pod_dk );
        $this->{'pod:pod'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pod_pod );

        $this->{'pt:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pt_vg );
        $this->{'vg:pt'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_pt );

        $this->{'dk:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%dk_vg );
        $this->{'vg:dk'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_dk );

        $this->{'md:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%md_vg );
        $this->{'vg:md'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_md );

        $this->{'pod:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pod_vg );
        $this->{'vg:pod'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_pod );

        $this->{'pt'}    = wrapper::new( 'type' => 'List', 'handle' => \@pts );
        $this->{'dk'}    = wrapper::new( 'type' => 'List', 'handle' => \@dks );
        $this->{'pod'}   = wrapper::new( 'type' => 'List', 'handle' => \@pods );
        $this->{'vg'}    = wrapper::new( 'type' => 'List', 'handle' => \@vgs );
        $this->{'md'}    = wrapper::new( 'type' => 'List', 'handle' => \@mds );

        return 0;
}

sub harvest_lv_fs
{
        my $this = shift;

        my %lv_lv        =();
        my %fs_fs        =();
        my %lv_vg        =();
        my %vg_lv        =();
        my %fs_lv        =();
        my %lv_fs        =();
        my %fs_pt        =();
        my %pt_fs        =();
        my %fs_dk        =();
        my %dk_fs        =();
        my %fs_md        =();
        my %md_fs        =();
        my %fs_pod       =();
        my %pod_fs       =();
        my @lvs          =();
        my @fss          =();
        my %lvid_lv      =();
        my %lvname_long2short = ();

        my $hash_handle = wrapper::get_handle( $this->{' | vg_handle2vg_name | '}, 'Hash' );
        my %vgid_vg     = %$hash_handle;
        $hash_handle    = wrapper::get_handle( $this->{' | pt_handle2pt_name | '}, 'Hash' );
        my %ptid_pt     = %$hash_handle;
        $hash_handle    = wrapper::get_handle( $this->{' | dk_handle2dk_name | '}, 'Hash' );
        my %dkid_dk     = %$hash_handle;

        my $list_handle = wrapper::get_handle ($this->{'dk'}, 'List');
        my @dks         = @$list_handle;
        $list_handle    = wrapper::get_handle ($this->{'pt'}, 'List');
        my @pts         = @$list_handle;
        $list_handle    = wrapper::get_handle ($this->{'vg'}, 'List');
        my @vgs         = @$list_handle;

        $list_handle    = wrapper::get_handle ($this->{'md:md'}, 'Hash');
        my %mds         = %$list_handle;

        my $cmd = '';
        my @out = ();
        my $rc  = 0;

        ###########################################################################
        ##    LOGICAL VOLUME COLLECTION
        ###########################################################################
        $cmd = 'lsrsrc-api -D"::" -I"::" -s IBM.LogicalVolume::ResourceType==0::ResourceHandle::DeviceName::ContainerResource::GhostDevice 2>/dev/null';
        @out = `$cmd`;
        $rc = $? >> 8;

        if ( $rc != 0 ) {
                    # Run double check
                $cmd = 'lsrsrc IBM.LogicalVolume 2>&1 >/dev/null';
                $rc  = system($cmd) >> 8;

                if ( $rc != 0 ) {
                        common::exit_with_msg("RSCT Error:RUN '$cmd' failed with RC=$rc\n", 3);
                } else {
                        @out = ();
                }
        }

        foreach my $itor ( @out ) {
                next if ( common::is_blank_line($itor) == 1 );

                my ($lvid, $lv, $vgid, $ghost) = split('::', $itor);
                next if ( length($ghost) == 0 );

                $lv="$::ghost_mark$lv" if ($ghost != 0);

                if ( $lv =~ /\/([^\/]+)$/ ) {
                        my $short_lvname = $1;
                        $short_lvname="$::ghost_mark$short_lvname" if ($ghost != 0);
                        $lvname_long2short{"$lv"}= $short_lvname;
                }
                push( @lvs, $lv );

                $lv_vg{$lv}= $vgid_vg{$vgid};


                $lvid_lv{$lvid} = $lv;
                $lv_lv{$lv} = $lv;
        }

        ###########################################################################
        ##    FILE SYSTEM COLLECTION
        ###########################################################################
        $cmd = 'lsrsrc-api -D"::" -I"::" -s IBM.AgFileSystem::ResourceType==0::DeviceName::SysMountPoint::MountPoint::ContainerResource::GhostDevice::UserControl::OpState  2>/dev/null';
        @out= `$cmd`;
        $rc = $? >> 8;

        if ( $rc != 0 ) {
             # Run double check
            $cmd = 'lsrsrc IBM.AgFileSystem 2>&1 >/dev/null';
            $rc  = system($cmd) >> 8;

            if ( $rc != 0 ) {
                common::exit_with_msg("RSCT Error:RUN '$cmd' failed with RC=$rc\n", 3);
            } else {
                @out = ();
            }

        }

        foreach my $itor ( @out ) {
                next if ( common::is_blank_line($itor) == 1 );

                my ( $fs, $sys_mnt, $mnt, $cont_id, $ghost, $usr_def, $state ) = split('::', $itor);
                next if ( length($usr_def) == 0 );

                if ( common::is_blank_line( $mnt ) == 1 ) {
                        if ( common::is_blank_line( $sys_mnt ) != 1 ) {
                                $fs = $sys_mnt;
                        }
                } else {
                        if ( $state  == 1 ) {
                                $fs = $mnt;
                        } elsif ( common::is_blank_line( $sys_mnt ) != 1 ) {
                                $fs = $sys_mnt;
                        }
                }

                if ( $ghost != 0 ) {
                        if ( $usr_def  != 0 ) {
                                $fs = "$::ghost_usr_def_mark$fs";
                        } else {
                                $fs = "$::ghost_mark$fs";
                        }
                } else {
                        if ( $usr_def  != 0 ) {
                                $fs = "$::usr_def_mark$fs";
                        }
                }

                push ( @fss, $fs );
                $fs_fs{$fs} = $fs;

                if ( defined( $lvid_lv{$cont_id} ) ) {
                        $fs_lv{$fs} = $lvid_lv{$cont_id};
                } elsif ( defined( $ptid_pt{$cont_id} ) ) {
                        $fs_pt{$fs} = $ptid_pt{$cont_id};
                } elsif ( defined( $dkid_dk{$cont_id} ) ) {
                        $fs_dk{$fs} = $dkid_dk{$cont_id};

                        if ( defined( $mds{$fs_dk{$fs}} )) {
                            $fs_md{$fs} = $fs_dk{$fs};
                        }
                }
        }

        %fs_pod=(%fs_pt, %fs_dk);
        ###########################################################################
        ##    SUMMARY
        ###########################################################################
        common::reverse_hashlist( \%lv_vg, \%vg_lv, \@vgs );
        common::reverse_hash( \%fs_lv, \%lv_fs, \@lvs);
        common::reverse_hash( \%fs_pt, \%pt_fs, \@pts );
        common::reverse_hash( \%fs_dk, \%dk_fs, \@dks );
        my @tmplist = keys(%mds);
        common::reverse_hash( \%fs_md, \%md_fs, \@tmplist );
        %pod_fs = ( %dk_fs, %pt_fs );

        $this->{'lv:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%lv_vg );
        $this->{'vg:lv'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_lv );

        $this->{'fs:lv'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_lv );
        $this->{'lv:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%lv_fs );

        $this->{'fs:pt'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_pt );
        $this->{'pt:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pt_fs );

        $this->{'fs:dk'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_dk );
        $this->{'dk:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%dk_fs );

        $this->{'fs:md'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_md );
        $this->{'md:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%md_fs );

        $this->{'fs:fs'}= wrapper::new( 'type' => 'Hash', 'handle' => \%fs_fs );
        $this->{'lv:lv'}= wrapper::new( 'type' => 'Hash', 'handle' => \%lv_lv );
        $this->{'fs:pod'}= wrapper::new( 'type' => 'Hash', 'handle' => \%fs_pod );
        $this->{'pod:fs'}= wrapper::new( 'type' => 'Hash', 'handle' => \%pod_fs );
        $this->{'lv'}    = wrapper::new( 'type' => 'List', 'handle' => \@lvs );
        $this->{'fs'}    = wrapper::new( 'type' => 'List', 'handle' => \@fss );

        $this->{'lv:short_lv'} = wrapper::new( 'type' => 'Hash', 'handle' => \%lvname_long2short);

        return 0;
}

package aix_reaper;
#--------------------------------------------------------------------#
# aix_reaper : harvest LVM relationships on AIX platformso           #
#              and supply the query function using uniform           #
#              query interface                                       #
#--------------------------------------------------------------------#

#require Exporter;
#require reaper;
#@ISA = qw(Exporter reaper);
#@EXPORT = qw(checkout  checkup);

#--------------------------------------------------------------------#
# Public functions                                                   #
#--------------------------------------------------------------------#
sub new
{
    # make an instance, and setup the virtual function pointer;
    my $this = {};
    my $proxy;

    bless $this;

    $this->harvest_physicalvolume($this);
    $this->harvest_logicalvolume($this);
    $this->harvest_filesystem($this);

    $proxy = reaper->new( $this,
                          \&aix_reaper::checkup,
                          \&aix_reaper::checkout );

    return $proxy;
}

sub checkup
{

    my ( $this, $query ) = @_;

    my $hdl;
    my $type=''; 

    if ( defined($this->{$query} ) ) {
        $hdl  = $this->{$query};
        $type = wrapper::get_type($hdl);
         
        if ( $type eq 'Hash_List' ) {
            return $reaper::rel_one2more;
        } else {
            return $reaper::rel_one2one;
        }       
    }
 
    return $reaper::rel_unsupt;
}

sub checkout
{
    my ($this, $query) = @_;
    my $relationship = $this->{$query};
    return $relationship;
}

#--------------------------------------------------------------------#
# Private functions                                                  #
#--------------------------------------------------------------------#

sub support_fs_type($) {
    my $fs_type = shift;

    my $itor = '';

    foreach $itor ( @::Supported_FS_Type_AIX_List ) {
        if ( $fs_type eq $itor ) {
            return 1;
        }
    }

    return 0;
}



#--------------------------------------------------------------------#
#                                                                    #
# harvest_physicalvolume : Harvest all valid relaitonships among     #
#                          PV, VG, PV to VG on AIX platform          #
#                                                                    #
# Parameters :                                                       #
#    [aix_reaper_handle] this pointer                                #
#                                                                    #
# Return :                                                           #
#    NULL                                                            #
#                                                                    #
#--------------------------------------------------------------------#

sub harvest_physicalvolume
{
my $this = shift;

my $cmd = '';
my $rc  = 0;
my @output = ();

my %part_disk = ();                      # Partition to Disk
my %disk_disk = ();                      # Disk to Disk
my %part_part = ();                      # ParTition to ParTition
my %pod_dk = ();                         # ParTition or DisK to DisK
my %dk_pod = ();                         # Disk to ParTition or DisK
my %pod_pt = ();                         # ParTition or DisK to ParTition
my %pt_pod = ();                         # ParTition to ParTition or DisK
my %pod_pod = ();                        # ParTition or DisK to ParTition or DisK
my %pod_vg = ();                         # ParTition or DisK to VG
my %vg_pod = ();                         # VG to ParTition or DisK 
my %vg_vg = ();                          # VG to VG
my %disk_part = ();                      # Disk to Partition
my %disk_vg = ();                        # Disk to VG
my %vg_disk = ();                        # VG to Disk
my %part_vg = ();                        # Partition to VG
my %vg_part = ();                        # VG to Partition
my @parts = ();                          # Partition List
my @vgs = ();                            # VG List
my @disks = ();                          # Disk List
my @pods = ();                           # Partition and Disk List
my @mds = ();
my %md_md = ();

my $disk_name = '';
my $part_name = '';
my $vg_name = '';
my $tmp_cmd ='';
my $line = '';
my $tmp_rc=0;
my $tmp_handle;
my $itor;
my $vg_valid_flag = 0;
my @tmp_out = ();

$cmd = '/usr/sbin/lspv -L 2>/dev/null';
#hdisk0          0024a09a1b6f2012                    rootvg          active
#hdisk1          0024a09a543cb76f                    None            
#hdisk2          0024a09a50cc0c7d                    baevg1          
#hdisk3          0024a09ad82a0789                    yuj_test_vg01   active
#hdisk4          0024a09ad82a3a3b                    yuj_test_vg02   
#hdisk5          0024a09ad82a5e81                    yuj_test_vg03   
#hdisk6          none                                None            
#hdisk7          none                                None            
#hdisk8          0024a15a54078137                    None            
#hdisk9          0024a15a540785ec                    None            
#hdisk10         0024a15a54078735                    None            active
#hdisk11         0024a15a540787f5                    None            
#hdisk12         0024a15a540788bf                    None            
#hdisk13         0024a15a5407897f                    None            
#hdisk14         0024a15a54078a4a                    None            
 
@output = `$cmd`;
$rc = $? >> 8;

if ( $rc != 0 ) {
    common::exit_with_msg("System Error: Invoke '$cmd' failed with RC=$rc\n", 3);
} 

foreach $itor ( @output ) {
    next if ( 1 == common::is_blank_line($itor) );    

    # From the src of IBM.StorageRM
    # A valid Disk resource should belong to a valid VG.
   if ( $itor =~ /^\s*(\S+)\s*\S+\s*(\S+)\s*\S*\s*$/ ) {
        $disk_name = $1;
        $vg_name = $2;

        $vg_valid_flag = 0;
        if ( $vg_name =~ /^none$/i ) {
            # The vg_name matchs /^none$/i, Then use command line 
            # lspv -L disk_name
            # To do check work. If there is no VG definition on this disk, 
            # It will return a non-zero value, or 0 will be returned.
            $tmp_cmd = "/usr/sbin/lspv -L $disk_name 2>/dev/null";
            @tmp_out = `$tmp_cmd`;
            $tmp_rc  = $? >> 8;

            if( $tmp_rc != 0 ) {
                common::errlog("Invoke '$tmp_cmd' failed with RC=[$tmp_rc]\n");
            }# if( $tmp_rc != 0 )

            foreach $line ( @tmp_out ) {
                if ( $line =~ /^\s*VG IDENTIFIER\s*\S+\s*$/ ) {
                    $vg_valid_flag++;
                    last;    
                } # if ( $line =~ /^\s*VG IDENTIFIER\s*\S+\s*$/ )
            } # foreach $line ( @tmp_out )
        } else { # if ( $vg_name =~ /^none$/i )
            # Just collect the disks belongs to some valid VG. Command Line:
            #     getlvodm -v vg_name 
            # Is used to do the check work.
            #
            $tmp_cmd = "/usr/sbin/getlvodm -v $vg_name 2>/dev/null";
            @tmp_out = `$tmp_cmd`;
            $tmp_rc  = $? >> 8;

            if ( $tmp_rc != 0 ) {
                common::errlog("Invoke '$tmp_cmd' failed with RC=[$tmp_rc]\n");
            }else {
                $vg_valid_flag++;
            } # if ( $tmp_rc != 0 )

            if ( $vg_valid_flag == 0 ) {
                foreach my $tmp_line ( @tmp_out ) {
                    if ( $tmp_line =~ /^\s*\S+\s*$/ ) {
                        $vg_valid_flag++;
                        last;
                    } # if ( $tmp_line =~ /^\s*\S+\s*$/ )
                } # foreach my $tmp_line ( @tmp_out )
            } # if ( $vg_valid_flag == 0 )
        } # if ( $vg_name =~ /^none$/i )

        if ( $vg_valid_flag != 0 ) {
            $disk_name = '/dev/'.$disk_name;

            push ( @disks, $disk_name );
            $vg_vg{$vg_name} = $vg_name;
            $disk_vg{$disk_name} = $vg_name;
            $disk_disk{$disk_name} = $disk_name;

            if ( !defined($vg_disk{$vg_name}) ){
                my @new_list = ($disk_name);
                $vg_disk{$vg_name} = \@new_list;
            } else { #if ( !defined($vg_disk{$vg_name}) )
                $tmp_handle = $vg_disk{$vg_name};
                push( @$tmp_handle, $disk_name );
            } # if ( !defined($vg_disk{$vg_name}) )
        } else { # if ( $vg_valid_flag != 0 )
            ;
        } # if ( $vg_valid_flag != 0 )
    } # if ( $itor =~ /^\s*(\S+)\s*\S+\s*(\S+)\s*\S*\s*$/ )
} # foreach $itor ( @output ) 

common::reverse_hashlist( \%part_disk, \%disk_part, \@disks );

@vgs  = keys(%vg_vg);
@pods = (@disks, @parts);

common::reverse_hashlist( \%part_vg, \%vg_part, \@vgs );

%pod_vg = (%disk_vg, %part_vg);
%vg_pod = %vg_disk;

%pod_pod = %disk_disk;
%pod_pt = %disk_part;
%dk_pod = %disk_disk;
%pod_dk = %disk_disk;

# Setup the query entry;
$this->{'md:md'} = wrapper::new( 'type' => 'Hash', 'handle' => {} );
$this->{'pt:dk'} = wrapper::new( 'type' => 'Hash', 'handle' => \%part_disk );
$this->{'dk:pt'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%disk_part );
$this->{'pt:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%part_vg );
$this->{'vg:pt'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_part );
$this->{'dk:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%disk_vg );
$this->{'vg:dk'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_disk );
$this->{'vg:pod'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_pod );
$this->{'pt'} = wrapper::new( 'type' => 'List', 'handle' => \@parts );
$this->{'dk'} = wrapper::new( 'type' => 'List', 'handle' => \@disks );
$this->{'vg'} = wrapper::new( 'type' => 'List', 'handle' => \@vgs );
$this->{'md'} = wrapper::new( 'type' => 'List', 'handle' => [] );
$this->{'pod'} = wrapper::new( 'type' => 'List', 'handle' => \@pods );
$this->{'pod:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pod_vg );
$this->{'pod:dk'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pod_dk );

$this->{'dk:pod'} = wrapper::new( 'type' => 'Hash', 'handle' => \%dk_pod );
$this->{'pod:pt'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%pod_pt );
$this->{'pt:pod'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pt_pod );
$this->{'pt:pt'} =wrapper::new( 'type' => 'Hash', 'handle' => \%part_part );
$this->{'dk:dk'} =wrapper::new( 'type' => 'Hash', 'handle' => \%disk_disk );
$this->{'vg:vg'} =wrapper::new( 'type' => 'Hash', 'handle' => \%vg_vg );
$this->{'pod:pod'} =wrapper::new( 'type' => 'Hash', 'handle' => \%pod_pod );
$this->{'md:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => {});
$this->{'vg:md'} = wrapper::new( 'type' => 'Hash_List', 'handle' => {});

return 0;
}


#--------------------------------------------------------------------#
#                                                                    #
# harvest_logicalvolume : Harvest all valid relaitonships among      #
#                         LV, VG, LV to VG on AIX platform           #
#                                                                    #
# Parameters :                                                       #
#    [aix_reaper_handle] this pointer                                #
#                                                                    #
# Return :                                                           #
#    NULL                                                            #
#                                                                    #
#--------------------------------------------------------------------#

sub harvest_logicalvolume
{
my $this = shift;

my $list_handle;
my @vgs = ();                     # VG list
my @lvs = ();                     # LV list
my %vg_lv = ();                   # VG to LV
my %lv_vg = ();                   # LV to VG
my %lv_lv = ();                   # LV to LV
my %lvname_long2short = ();       # LV : long name to short name

my $itor;
my $cmd = '';
my $line= '';
my $rc = 0;
my @out = ();

my $lv_name = '';
my $short_lvname = '';

$list_handle = wrapper::get_handle($this->{'vg'}, 'List');
@vgs = @$list_handle;

foreach $itor ( @vgs ) {
    $cmd = "/usr/sbin/getlvodm -L $itor 2>/dev/null";
    #[c48f1rp16][/]> /usr/sbin/getlvodm -L baevg1
    #loglv03 0024a09a00004c000000010d958c50d6.1
    #lv00 0024a09a00004c000000010d958c50d6.2
    #[c48f1rp16][/]> echo $?
    #0
    #[c48f1rp16][/]> /usr/sbin/getlvodm -L None
    #[c48f1rp16][/]> echo $?
    #0
    #[c48f1rp16][/]> /usr/sbin/getlvodm -L ABC 
    #[c48f1rp16][/]> echo $?
    #0
    @out = `$cmd`;
    $rc  = $? >> 8;
        
    if ( $rc != 0 ) {
        common::errlog("Invoke '$cmd' failed with RC=$rc.\n");
    }

    foreach $line ( @out ) {
        if ( $line =~ /\s*(\S+)\s*\S+\s*$/ ) {
            $short_lvname = $1;
            $lv_name = "/dev/$short_lvname";
                
            push ( @lvs, $lv_name );
            $lv_lv{$lv_name} = $lv_name;
            $lv_vg{$lv_name} = $itor;
            $lvname_long2short{$lv_name} = $short_lvname;
        }
    }
}

common::reverse_hashlist( \%lv_vg, \%vg_lv, \@vgs );

# Setup the query entry;
$this->{'lv:vg'} = wrapper::new( 'type' => 'Hash', 'handle' => \%lv_vg );
$this->{'lv:lv'} = wrapper::new( 'type' => 'Hash', 'handle' => \%lv_lv );
$this->{'vg:lv'} = wrapper::new( 'type' => 'Hash_List', 'handle' => \%vg_lv );
$this->{'lv'} = wrapper::new( 'type' => 'List', 'handle' => \@lvs );
$this->{'lv:short_lv'} = wrapper::new( 'type' => 'Hash', 'handle' => \%lvname_long2short);

return 0;
}

#--------------------------------------------------------------------#
#                                                                    #
# harvest_filesystem : Harvest all valid relaitonships among FS,     #
#                         FS to LV, FS to PV on AIX platform         #
#                                                                    #
# Parameters :                                                       #
#    [aix_reaper_handle] this pointer                                #
#                                                                    #
# Return :                                                           #
#    NULL                                                            #
#                                                                    #
#--------------------------------------------------------------------#

sub harvest_filesystem
{
my $this = shift;

my $list_handle;

my @lvs = ();                  # LV list
my @dks = ();                  # DisK list
my @pts = ();                  # ParTition List
my %lsfs = ();                 # FS list from instruction 'lsfs'
my %fs_lv = ();                # FS to LV
my %fs_fs = ();                # FS to FS
my %lv_fs = ();                # LV to FS
my %fs_pt = ();                # FS to ParTition
my %pt_fs = ();                # ParTition to FS
my %fs_dk = ();                # FS to Disk
my %dk_fs = ();                # DisK to FS
my %fs_pod = ();               # FS to Partition Or Disk
my %pod_fs = ();               # Partition Or Disk to FS
my %md_fs = ();                # MD to FS
my @fs = ();                   # FS List

my $cmd = '';
my $line = '';
my $fs_name = '';
my $mount_dir = '';
my $lv_name = '';
my $fs_type = '';
my $rc = 0;
my @out = ();

# Read harvested  LV, DK, PT List
$list_handle = wrapper::get_handle( $this->{'lv'}, 'List' );
@lvs = @$list_handle;

$list_handle = wrapper::get_handle( $this->{'dk'}, 'List' );
@dks = @$list_handle;

$list_handle = wrapper::get_handle( $this->{'pt'}, 'List' );
@pts = @$list_handle;

$cmd = '/usr/sbin/lsfs -ca 2>/dev/null';
#[c48f1rp16][/]> lsfs
#Name            Nodename   Mount Pt               VFS   Size    Options    Auto Accounting
#/dev/hd4        --         /                      jfs   262144  --         yes  no 
#/dev/hd1        --         /home                  jfs   131072  --         yes  no 
#/dev/hd2        --         /usr                   jfs   5111808 --         yes  no 
#/dev/hd9var     --         /var                   jfs   393216  --         yes  no 
#/dev/hd3        --         /tmp                   jfs   393216  --         yes  no 
#/proc           --         /proc                  procfs --      --         yes  no 
#/dev/hd10opt    --         /opt                   jfs   131072  --         yes  no 
#/dev/lv00       --         /baelv0                jfs   --      rw         no   no 
#/dev/yuj_test_lv31 --         /yuj_test_fs31         jfs   --      rw         no   no 
#/dev/yuj_test_lv21 --         /yuj_test_fs21         jfs   --      rw         no   no 
#/dev/yuj_test_lv11 --         /yuj_test_fs11         jfs   131072  rw         no   no 
#/dev/yuj_test_lv12 --         /yuj_test_fs12         jfs   131072  rw         no   no 
#/dev/yuj_test_lv13 --         /yuj_test_fs528        jfs   131072  rw         no   no 
#/dev/yuj_test_lv13 --         /yuj_test_fs13         jfs   131072  rw         no   no 
#[c48f1rp16][/]> echo $?
#0
@out = `$cmd`;
$rc  = $? >> 8;
        
if ( $rc != 0 ) {
    common::exit_with_msg("System Error: Invoke '$cmd' failed with RC=$rc\n", 3);
}

foreach my $line ( @out )
{
    next if ( common::is_comment( $line )  == 1 ); 

    if ( $line  =~ /^([^:]*):([^:]+):([^:]+)/ ) {
        $mount_dir = $1;
        $lv_name   = $2;
        $fs_type   = $3;

        if ( support_fs_type($fs_type) == 1 ) {
            # fs will use mount point directory as name, if the mount point
            # dir doesn't exist, the lv name will be used.
            $fs_name = $mount_dir;
            if ( 1 == common::is_blank_line( $mount_dir ) ) {
                $fs_name = $lv_name;
            }

            $fs_lv{$fs_name} = $lv_name;
            $fs_fs{$fs_name} = $fs_name;
            push ( @fs, $fs_name );
        }
    }
}

#Harvest GPFS data
#File system attributes for /dev/crfs33on33Node:
#===============================================
#flag value            description
#---- ---------------- -----------------------------------------------------
# -o  none             Additional mount options
# -T  /gpfs/crfs33on33Node Default mount point
#
#File system attributes for /dev/f32f33Co:
#=========================================
#flag value            description
#---- ---------------- -----------------------------------------------------
# -o  none             Additional mount options
# -T  /gpfs/f32f33Co   Default mount point
#
$cmd = '/usr/lpp/mmfs/bin/mmlsfs all -o -T 2>/dev/null';
@out = `$cmd`;
foreach my $line ( @out )
{
    next if ( common::is_comment( $line )  == 1 );
    if ( $line  =~ /^File system attributes for\s*(\S+):\s*$/ ) {
        $lv_name = $1;
    }
    
    if ( $line =~ /\s*-T\s+(\S+)\s+/ ) {
        $fs_name = $1;
        if (length($lv_name) != 0) {
#           $fs_lv{$fs_name} = $lv_name;
            $fs_fs{$fs_name} = $fs_name;
            push ( @fs, $fs_name );
        }
    }
}
%fs_pod = ( %fs_pt, %fs_dk );
common::reverse_hash( \%fs_lv, \%lv_fs, \@lvs );
common::reverse_hash( \%fs_dk, \%dk_fs, \@dks );
common::reverse_hash( \%fs_pt, \%pt_fs, \@pts );
common::reverse_hash( {}, \%md_fs, [] );
%pod_fs = ( %dk_fs, %pt_fs );

# Setup the query entry;
$this->{'fs:lv'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_lv );
$this->{'fs:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_fs );
$this->{'lv:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%lv_fs );
$this->{'fs:pt'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_pt );
$this->{'pt:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%pt_fs );
$this->{'fs:dk'} = wrapper::new( 'type' => 'Hash', 'handle' => \%fs_dk );
$this->{'dk:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%dk_fs );
$this->{'fs:md'} = wrapper::new( 'type' => 'Hash', 'handle' => {} );
$this->{'md:fs'} = wrapper::new( 'type' => 'Hash', 'handle' => \%md_fs );
$this->{'fs:pod'}= wrapper::new( 'type' => 'Hash', 'handle' => \%fs_pod );
$this->{'pod:fs'}= wrapper::new( 'type' => 'Hash', 'handle' => \%pod_fs );
$this->{'fs'}    = wrapper::new( 'type' => 'List', 'handle' => \@fs );

return 0;
}

package config;
#--------------------------------------------------------------------#
#                                                                    #
# config.pm : config class                                           #
#                                                                    #
# This class is uesed to read some system configure information.     #
# Currently just check unix type is supported.                       #
#                                                                    #
#--------------------------------------------------------------------#
# Exporter = qw (                                                    #
#                get_utype                                           #
# );                                                                 #
#--------------------------------------------------------------------#
use constant CFG_MEM_UNAME => 0;

sub new 
{
    my $this   = [];
    my @uname  = ();

    $this->[CFG_MEM_UNAME] = \@uname;

    bless $this;
    return $this;
}

sub get_utype
{
    my $this = shift;

    my $uname_handle = $this->[CFG_MEM_UNAME];

    if ( scalar( @$uname_handle ) == 0 ) {
        &__check_uname($this);
        return $$uname_handle[0];
    } else {
        return $$uname_handle[0];
    }
}

sub __check_uname
{
    my $this = shift;

    my $rc = 0;
    my $line = '';
    my $utype = '';
    my @out = ();
    my @uname_list = ();

    my $uname_handle;

    $uname_handle = $this->[CFG_MEM_UNAME];
    @out = `uname 2>/dev/null`;
    $rc  = $? >> 8;

    if ( $rc != 0 ) {
        common::exit_with_msg("System Error: Invoked 'uname' command failed with RC=$rc.\n", 3);
    }

    foreach $line ( @out ) {
        next if ( common::is_blank_line ( $line ) == 1 );

        if ( $line =~ /^\s*(\S+)\s*$/ ) {
            $utype = $1;
       }
    }

    push ( @uname_list, $utype );
    push ( @$uname_handle, @uname_list );
}

package dnode;
#--------------------------------------------------------------------#
#                                                                    #
# dnode.pm : dnode class                                             #
#                                                                    #
# dnode is a double link node, the query result will be dnode-tree   #
# list. each dnode will describe one field.                          #
#                                                                    #
#--------------------------------------------------------------------#
# Exporter = qw (                                                    #
#                [dnode list]    get_children()                      #
#                [void]          add_child([dnode])                  #
#                [dnode handle]  get_parent()                        #
#                [void]          set_parent([dnode])                 #
#                [any]           get_value()                         #
#                [void]          set_value()                         #
#                [integer]       children()                          #
#                [dnode handle]  parent()                            #
#                [dnode list]    vertical()                          # 
# );                                                                 #
#--------------------------------------------------------------------#
use constant DND_MEM_VALUE => 0;
use constant DND_MEM_CHILDREN => 1;
use constant DND_MEM_PARENT => 2;

sub new {
    my $this  = [];
    my $value = shift;
    my @children  = ();

    $this->[DND_MEM_VALUE] = $value;         # value;
    $this->[DND_MEM_CHILDREN] = \@children;  # children node list;
    $this->[DND_MEM_PARENT] = 0;             # parent node;

    bless $this;
    return $this;
}

sub get_children
{
    my $this = shift;

    return $this->[DND_MEM_CHILDREN];
}

sub get_parent
{ 
    my $this  = shift;

    return $this->[DND_MEM_PARENT];
}

sub get_value
{
    my $this = shift;

    return $this->[DND_MEM_VALUE];
}

sub set_value
{
    my $this  = shift;
    my $value = shift;

    $this->[DND_MEM_VALUE] = $value;
}

sub children
{
    my $this = shift;

    my $ary_handle = $this->[DND_MEM_CHILDREN];
    if ( scalar(@$ary_handle) == 0 ) {
        return 0;
    } else {
        return 1;
    }
}

sub parent 
{
    my $this = shift;

    return $this->[DND_MEM_PARENT];
}


sub add_child 
{
    my $this = shift;

    my $next = shift;
    my $ary_handle = $this->[DND_MEM_CHILDREN];

    $next->[DND_MEM_PARENT] = $this;
    push ( @$ary_handle, $next );
}

sub set_parent 
{
    my $this = shift;
    my $parent = shift;

    $this->[DND_MEM_PARENT] = $parent;
}


sub vertical {
    my $this = shift;
    my @matrix = ();

    do {
        unshift ( @matrix, $this->[DND_MEM_VALUE] );
    } while ( ($this = $this->[DND_MEM_PARENT] ) != 0 );

    return @matrix;
}

package ssession;
#--------------------------------------------------------------------#
#                                                                    #
# ssession.pm : ssession class                                       #
#                                                                    #
# ssession will hold an group of query list, can execute it or diff  #
# its result.                                                        #
#                                                                    #
#--------------------------------------------------------------------#
# @EXPORT = qw(                                                      #
#                get_query                                           #
#                get_tail                                            #
#                get_spec                                            #
#                get_head                                            #
#                execute                                             #
#                differ                                              #
#                vertical                                            #
# );                                                                 #
#--------------------------------------------------------------------#

use constant SSES_MEM_QUERY_LIST => 0;
use constant SSES_MEM_REAPER     => 1;
use constant SSES_MEM_HEAD       => 2;
use constant SSES_MEM_TAIL       => 3;
use constant SSES_MEM_SPEC       => 4;

sub new 
{
    my $this = [];

    my $spec = 0;
    my $qlist_len = 0;
    my $rel_type = '';
    my %params = @_;
    my $query_list;
    my $rel_hdl;

    $this->[SSES_MEM_QUERY_LIST] = $params{'Query'};
    $this->[SSES_MEM_REAPER] = $params{'Reaper'};
    $this->[SSES_MEM_HEAD] = $params{'Head'};          # Head string
    $this->[SSES_MEM_TAIL] = 0;  # Redundancy structure, tail nodes list;

    $query_list = $params{'Query'};
    $qlist_len  = scalar(@$query_list);
    $spec = $qlist_len;

    if ( $qlist_len > 0  ) {
        $rel_hdl = $params{'Reaper'}->reaper::checkout($$query_list[0]);
        $rel_type = $rel_hdl->get_type();

        if ( $rel_type ne 'List' ) {
            $spec++;
        } # if ( $rel_type ne 'List' )
    } # if ( $qlist_len > 0  )

    $this->[SSES_MEM_SPEC] = $spec;

    bless $this;
    return $this;
}

sub get_query 
{
    my $this = shift;
    return $this->[SSES_MEM_QUERY_LIST];
}

sub get_spec 
{
    my $this = shift;
    return $this->[SSES_MEM_SPEC];
}

sub get_head 
{
    my $this = shift;
    return $this->[SSES_MEM_HEAD];
}

sub get_tail 
{
    my $this = shift;
    return $this->[SSES_MEM_TAIL];
}

#--------------------------------------------------------------------#
#                                                                    #
# execute : query the query list on reaper, and collect the          #
#           result about head string;                                #
#                                                                    #
# Parameters :                                                       #
#    NULL                                                            #
#                                                                    #
# Return :                                                           #
#    NULL                                                            #
#                                                                    #
# Used Global Varibale :                                             #
#    $::nonexistent    # describe the non-existent relationship      #
#                                                                    #
#--------------------------------------------------------------------#
sub execute 
{
my $this = shift;

my $ret = 0;
my $dnode_value = '';
my $relation_type = '';
my $query_list_itor = '';
my @curr_dnode_list = ();
my @tail_dnode_list = ();

my $reaper = $this->[SSES_MEM_REAPER];
my $head_dnode = $this->[SSES_MEM_HEAD];
my $query_list = $this->[SSES_MEM_QUERY_LIST];

my $relation;
my $dnode_itor;
my $relation_handle;


@curr_dnode_list = ( $head_dnode );

foreach $query_list_itor ( @$query_list ) {

    $relation = $reaper->reaper::checkout($query_list_itor);
    $relation_type = wrapper::get_type($relation);

    @tail_dnode_list  = ();

    if ( $relation_type eq 'Hash_List' ) {
        $relation_handle =  wrapper::get_handle($relation, 'Hash_List');

        foreach $dnode_itor ( @curr_dnode_list ) {

            $dnode_value = $dnode_itor->get_value();

            if ( defined( $$relation_handle{ $dnode_value } ) ) {
                my $target_value_list_handle = $$relation_handle{ $dnode_value };

                foreach my $target_value ( @$target_value_list_handle ) {
                    my $target_dnode = dnode::new ( $target_value );
                    $dnode_itor->add_child( $target_dnode );

                     push( @tail_dnode_list, $target_dnode );
                } # foreach my $target_value ( @$target_value_list_handle )
            } else {

                my $target_dnode = dnode::new ( $::nonexistent );
                $dnode_itor->add_child( $target_dnode );

                push( @tail_dnode_list, $target_dnode );
            } # if ( defined( $$relation_handle{ $dnode_value } ) )
        } # foreach my $dnode_itor ( @curr_dnode_list )
    } # if ( $relation_type eq 'Hash_List' )
    elsif ( $relation_type eq 'Hash' ) {
        $relation_handle =  wrapper::get_handle($relation, 'Hash');

        foreach $dnode_itor ( @curr_dnode_list ) {

            $dnode_value = $dnode_itor->get_value();

            if ( defined( $$relation_handle{ $dnode_value } ) ) {
                my $target_value = $$relation_handle{ $dnode_value };
                my $target_dnode = dnode::new ( $target_value );

                $dnode_itor->add_child( $target_dnode );

                push( @tail_dnode_list, $target_dnode );
            } else {
                my $target_dnode = dnode::new ( $::nonexistent );

                $dnode_itor->add_child( $target_dnode );

                push( @tail_dnode_list, $target_dnode );
            } # if ( defined( $$relation_handle{ $dnode_value } ) )
        } # foreach $dnode_itor ( @curr_dnode_list )
    } # if ( $relation_type eq 'Hash_List' )
    elsif ( $relation_type eq 'List' ) {

        foreach $dnode_itor ( @curr_dnode_list ) {
            push ( @tail_dnode_list, $dnode_itor );
        } # foreach $dnode_itor ( @curr_dnode_list )
    } # elsif ( $relation_type eq 'List' )

    @curr_dnode_list = @tail_dnode_list;

} # foreach $query_list_itor ( @$query_list )

$this->[SSES_MEM_TAIL] = \@tail_dnode_list;

return $ret;
}

#--------------------------------------------------------------------#
#                                                                    #
# differ : diff tow session, and return back the common and different#
#          data;                                                     #
#                                                                    #
# Parameters :                                                       #
#    [ssession handle] other                                         #
#    [list_handle] the common data from this ssession                #
#    [list_handle] the common data from the other ssession           #
#    [list_handle] the special data from this ssession               #
#    [list_handle] the special data from the other ssession          #
#                                                                    #
# Return :                                                           #
#    [Integer] 0 : no difference, >0 : different                     #
#                                                                    #
#--------------------------------------------------------------------#
sub differ
{
    my ( $this, $other,
         $this_com_list, $other_com_list,
         $this_spe_list, $other_spe_list ) = @_;

    my $ret = 0;
    my $itor = '';

    my @this_nd_vtor  = ();
    my @other_nd_vtor = ();

    my $this_tail  = $this->[SSES_MEM_TAIL];
    my $other_tail = $other->[SSES_MEM_TAIL];

    foreach $itor ( @$this_tail ) {
        my @temp_list = dnode::vertical($itor);
        push ( @this_nd_vtor, \@temp_list );
    }

    foreach $itor ( @$other_tail ) {
        my @temp_list = dnode::vertical($itor);
        push ( @other_nd_vtor, \@temp_list );
    }

    $ret = common::differ(\@this_nd_vtor, \@other_nd_vtor,
                          $this_com_list, $other_com_list,
                          $this_spe_list, $other_spe_list,
                          \&common::strlist_cmp );

    return $ret;
}

#--------------------------------------------------------------------#
#                                                                    #
# vertical : vertical the result                                     #
#                                                                    #
# Parameters :                                                       #
#    [list_handle] result matrix handle                              #
#    [Integer] Begin Pos [Option]                                    #
#    [Integer] End Pos [Opition]                                     #
#                                                                    #
# Return :                                                           #
#    NULL                                                            #
#                                                                    #
#--------------------------------------------------------------------#
sub vertical 
{
    my $this = shift;
    my $matrix = shift;
    my $begin = shift || 0;
    my $end = shift || -1;

    my $offset = 0;
    my $length = 0;
    my @last_unrepeat_row = ();
    my $tail_list;

    @$matrix = ();

    $tail_list = $this->[SSES_MEM_TAIL];
    $length = scalar ( @$tail_list );

    $offset = $begin;
    if ( $end < 0 || $end > $length ) {
        $end = $length;
    }

    while ( $offset < $end ) {
        my $start_dnode = $$tail_list[$offset];
        my @curr_list = ();

        my $curr_list_offset = 0;
        my $insert_flag      = 1;
        my $loop_flag        = 1;

        while( $loop_flag == 1 ) {
            if ( $insert_flag == 1 && !defined($last_unrepeat_row[$curr_list_offset]) ) {
                $insert_flag = 1;
            } elsif ( $insert_flag == 1 && $start_dnode != $last_unrepeat_row[$curr_list_offset] ) {
                $insert_flag = 1; 
            } else {
                $insert_flag = 0;
            }

            if ( $insert_flag == 0 ) {
                push ( @curr_list, dnode::new( $::padding ) );
            } else {
                push ( @curr_list,  $start_dnode );
                $last_unrepeat_row[ $curr_list_offset ] = $start_dnode;
            }
                                                                                
            if ( $start_dnode->parent() != 0 ) {
                $start_dnode = $start_dnode->get_parent();
            } else {
                $loop_flag = 0;
            } 

            $curr_list_offset++;
        }

        push(@$matrix, \@curr_list);
        $offset++;
    }
}

package sgroup;
#--------------------------------------------------------------------#
#                                                                    #
# sgroup.pm : sgroup class                                           #
#                                                                    #
# sgroup manage 2 ssession: left ssession and right ssession.        #
# A query list will be divided into 2 parts: left query list and     #
# right query list.                                                  #
#                                                                    #
#--------------------------------------------------------------------#
# @EXPORT = qw(                                                      #
#                get_lquery                                          #
#                get_rquery                                          #
#                get_lsession                                        #
#                get_rsession                                        #
#                vertical                                            #
#                execute                                             #
#                superficially_differ                                #
#                differ                                              #
#                get_result                                          #
# );                                                                 #
#--------------------------------------------------------------------#
use constant SGRP_MEM_HEAD        => 0;
use constant SGRP_MEM_REAPER      => 1;
use constant SGRP_MEM_LEFTQ_LIST  => 2;
use constant SGRP_MEM_RIGHTQ_LIST => 3;
use constant SGRP_MEM_SESSIONS    => 4;
use constant SGRP_MEM_RESULT      => 5;


sub new {

    my $this   = [];

    my %params = @_;
    my @result = ();
    my @seses  = ();

    $this->[SGRP_MEM_HEAD]         = $params{'Head'};
    $this->[SGRP_MEM_REAPER]       = $params{'Reaper'};
    $this->[SGRP_MEM_LEFTQ_LIST]   = $params{'LeftQuery'}; 
    $this->[SGRP_MEM_RIGHTQ_LIST]  = $params{'RightQuery'}; 
    $this->[SGRP_MEM_SESSIONS]     = \@seses; # session list;
    $this->[SGRP_MEM_RESULT]       = \@result; # session list;

    bless $this;
    return $this;
}

sub get_lquery
{
    my $this = shift;

    return $this->[SGRP_MEM_LEFTQ_LIST];
}

sub get_rquery 
{
    my $this = shift;

    return $this->[SGRP_MEM_RIGHTQ_LIST];
}

sub get_lsession
{
    my $this = shift;

    my $session_list_hdl = $this->[SGRP_MEM_SESSIONS];
    return $$session_list_hdl[0];
}

sub get_rsession
{
    my $this = shift;
    my $session_list_hdl = $this->[SGRP_MEM_SESSIONS];

    return $$session_list_hdl[1];
}

sub vertical 
{
my $this = shift;

my $l_len = 0;
my $r_len = 0;
my $offset = 0;
my $length = 0;
my $spec_len = 0;
my $session_list_hdl = $this->[SGRP_MEM_SESSIONS];   # Session list handle
my $matrix = $this->[SGRP_MEM_RESULT];               # result list
my $l_session = $$session_list_hdl[0];               # Left query session
my $r_session = $$session_list_hdl[1];               # Right query session

my @l_matrix = ();
my @r_matrix = ();
my @blank_line = ();

@$matrix = ();

$l_session->ssession::vertical(\@l_matrix);
$r_session->ssession::vertical(\@r_matrix);

# The follow code will format the result

$l_len = scalar( @l_matrix );
$r_len = scalar( @r_matrix );

if ( $l_len > $r_len ) {
    $spec_len = $r_session->get_spec();
    $length = $l_len;
    common::repeat_list ( \@blank_line, dnode::new($::padding), $spec_len );
    common::repeat_list ( \@r_matrix, \@blank_line, $length - $r_len );
} else {
    $spec_len = $l_session->get_spec();
    $length = $r_len;
    common::repeat_list ( \@blank_line, dnode::new($::padding), $spec_len );
    common::repeat_list ( \@l_matrix, \@blank_line, $length - $l_len );
}

while ( $offset < $length ) {

    my @temp_line = ();
    my $l_part = $l_matrix[$offset];
    my $r_part = $r_matrix[$offset];

    @temp_line = @$l_part;
    pop( @temp_line );

    push ( @temp_line, reverse( @$r_part ) );
    push ( @$matrix, \@temp_line );

    $offset++;
}

return @$matrix;
}


sub execute {
my $this = shift;

my $ret_code = 0;
my $ret_msg = 0;
my @session_group = ();
my $left_session;
my $right_session;

my $str_head = $this->[SGRP_MEM_HEAD];
my $reaper = $this->[SGRP_MEM_REAPER];
my $left_qlist = $this->[SGRP_MEM_LEFTQ_LIST];
my $right_qlist = $this->[SGRP_MEM_RIGHTQ_LIST];

my $left_head_dnode  = dnode::new($str_head);
my $right_head_dnode = dnode::new($str_head);


# Setup the left session
$left_session = ssession::new( 'Reaper' => $reaper, 'Query' => $left_qlist, 'Head' => $left_head_dnode );
($ret_code, $ret_msg) = ssession::execute($left_session);
if ( $ret_code != 0 ) {
        return ( $ret_code, $ret_msg );
}
push ( @session_group, $left_session );


# Setup the right session
$right_session = ssession::new( 'Reaper' => $reaper, 'Query' => $right_qlist, 'Head' => $right_head_dnode );
($ret_code, $ret_msg) = ssession::execute($right_session);
if ( $ret_code != 0 ) {
    return ( $ret_code, $ret_msg );
}

# Setup session group
push ( @session_group, $right_session );
$this->[SGRP_MEM_SESSIONS] = \@session_group;

return ( $ret_code, $ret_msg );
}


sub superficially_differ
{
    my ( $this, $other ) = @_;

    if ( $this->[SGRP_MEM_HEAD] eq $other->[SGRP_MEM_HEAD] ) {
        return 0;
    } else {
        return 1;
    }
}

sub differ
{
my ( $this, $other, $com_list, $tspe_list, $ospe_list ) = @_;

my $tmp_ret = 0;
my $ret = 0;
my $left_spec_len = 0;
my @spec_blank = ();

my @tl_com = (); # This::left COMmon data list
my @tr_com = (); # This::Right ...
my @ol_com = (); # Other::Left ...
my @or_com = (); # Other::Right...
my @tl_spe = (); # This::left SPEcial data list
my @tr_spe = (); # This::Right ...
my @ol_spe = (); # Other::Left ...
my @or_spe = (); # Other::Right...

my $this_sessions  = $this->[SGRP_MEM_SESSIONS];
my $other_sessions = $other->[SGRP_MEM_SESSIONS];

my $itor;

my $this_left  = $$this_sessions[0];
my $this_right = $$this_sessions[1];
my $other_left  = $$other_sessions[0];
my $other_right = $$other_sessions[1];

$tmp_ret = ssession::differ($this_left, $other_left, \@tl_com, \@ol_com, \@tl_spe, \@ol_spe);
$ret += $tmp_ret;
$tmp_ret = ssession::differ($this_right, $other_right, \@tr_com, \@or_com, \@tr_spe, \@or_spe);
$ret += $tmp_ret;

@$com_list  = ();
@$tspe_list = ();
@$ospe_list = ();

foreach $itor ( @tl_com ) {
    my @temp = reverse ( @$itor );
    push ( @$com_list, \@temp );
}

foreach $itor ( @tl_spe ) {
    my @temp = reverse ( @$itor );
    push ( @$tspe_list, \@temp );
}

foreach $itor ( @ol_spe ) {
    my @temp = reverse ( @$itor );
    push ( @$ospe_list, \@temp );
}

$left_spec_len = $this_left->get_spec() ;
common::repeat_list(\@spec_blank, $::padding, $left_spec_len-1 );

foreach $itor ( @tr_com ) {
    my @temp = ( @spec_blank, @$itor );
    push ( @$com_list, \@temp );
}

foreach $itor ( @tr_spe ) {
    my @temp = ( @spec_blank, @$itor );
    push ( @$tspe_list, \@temp );
}

foreach $itor ( @or_spe ) {
    my @temp = ( @spec_blank, @$itor );
    push ( @$ospe_list, \@temp );
}

return $ret;
}

sub eval_dnode_list {
my ($dnode_list, $value_list) = @_;
my $nd;

foreach $nd ( @$dnode_list ) {
    if ( $nd !~ /^\s*$/ ) {
        push ( @$value_list, dnode::get_value($nd) );
    } else {
        push ( @$value_list, $nd );
    }
}

}

sub get_result
{
my $this = shift;

my @result = ();
my $matrix = $this->[SGRP_MEM_RESULT];
my $row;

foreach $row ( @$matrix ) {
    my @temp = ();
    &eval_dnode_list( $row, \@temp );
    push ( @result, \@temp );
}

return @result;
}

package smanager;
# @EXPORT = qw( 
#    get_key
#    has_one2more
#    query
#    get_result
#    diff_result
#    apply
#    check
# );

use constant SMGR_MEM_REAPER      => 0;
use constant SMGR_MEM_QUERY       => 1;
use constant SMGR_MEM_SGROUP      => 2;
use constant SMGR_MEM_KEY         => 3;
use constant SMGR_MEM_ONE2MORE    => 4;
use constant SMGR_MEM_LEFTQ_LIST  => 5;
use constant SMGR_MEM_RIGHTQ_LIST => 6;
use constant SMGR_MEM_RESULT      => 7;
use constant SMGR_MEM_KEY_STR     => 8;  # new 


sub new {
    my $this   = [];

    my %params = @_;
    my @s_grps = ();

    $this->[SMGR_MEM_REAPER] = $params{'Reaper'};      # reaper handle
    $this->[SMGR_MEM_QUERY] = $params{'Query List'};   # query list
    $this->[SMGR_MEM_SGROUP] = \@s_grps;               # session group list;
    $this->[SMGR_MEM_KEY] = 0;                         # Key position
    $this->[SMGR_MEM_KEY_STR] = '';                    # Key value
    $this->[SMGR_MEM_ONE2MORE] = 0;                    # if has 1-to-more rels;
    $this->[SMGR_MEM_LEFTQ_LIST] = 0;                  # Left part of query list;
    $this->[SMGR_MEM_RIGHTQ_LIST] = 0;                 # Right part of query list;
    $this->[SMGR_MEM_RESULT] = 0;                      # Result

    bless $this;
    return $this;
}

sub get_key {
    my $this = shift;        

    return $this->[SMGR_MEM_KEY];
}

sub has_one2more {
    my $this = shift;

    return $this->[SMGR_MEM_ONE2MORE];
}

sub query {

my $this = shift;

my $ret_code = 0;
my $ret_msg = '';

my $reaper               = $this->[SMGR_MEM_REAPER];           # reaper handle
my $s_grp_hdl            = $this->[SMGR_MEM_SGROUP];           # sgroup list handle
my $left_query_list_hdl  = $this->[SMGR_MEM_LEFTQ_LIST];       # left request list handle
my $right_query_list_hdl = $this->[SMGR_MEM_RIGHTQ_LIST];      # right request list handle

my $rel_hdl = $reaper->reaper::checkout( $$right_query_list_hdl[0] );  # key's relationship handle
my $rel_type = $rel_hdl->wrapper::get_type();                  # key's relationship type;

my @tmp = ();
my @keys = ();                                                 # key's value list;
my @result = ();
my $list_hdl;
my $hash_hdl;
my $itor;


if ( $rel_type eq 'List' ) {
    $list_hdl = wrapper::get_handle($rel_hdl, 'List');
    @keys = sort @$list_hdl;
}
elsif ( $rel_type eq 'Hash' or $rel_type eq 'Hash_List' ) {
    $hash_hdl = wrapper::get_handle($rel_hdl, $rel_type);
    @keys = sort (keys(%$hash_hdl));

    if ( scalar( @keys ) == 0 ) {
        $rel_hdl = $reaper->reaper::checkout( $this->[SMGR_MEM_KEY_STR] );  # key's relationship handle
        $list_hdl = wrapper::get_handle($rel_hdl, 'List');
        @keys = sort @$list_hdl;
    }
}

foreach $itor ( @keys ) {
    my @tmp_matrix = ();

    # make a sgroup to finish the query work;
    my $tmp_sgrp = sgroup::new('Head'=>$itor,
                               'Reaper'=>$reaper,
                               'LeftQuery'=>$left_query_list_hdl,
                               'RightQuery'=>$right_query_list_hdl);

    ($ret_code, $ret_msg) = $tmp_sgrp->sgroup::execute();
    if ( $ret_code != 0 ) {
        return ($ret_code, $ret_msg);
    }

    @tmp_matrix = $tmp_sgrp->sgroup::vertical();

    push ( @result, \@tmp_matrix );
    push ( @$s_grp_hdl, $tmp_sgrp );
}

$this->[SMGR_MEM_RESULT] = \@result;
}


# result_hdl will hold a matrix list;
# each matrix for one scope;
sub get_result {
my $this = shift;

my $sgrps = $this->[SMGR_MEM_SGROUP];
my @results = ();
my $itor;

foreach $itor (@$sgrps ) {
    my @tmp_result = sgroup::get_result($itor);
    push ( @results, \@tmp_result);
}

return @results;
}


# The head data will from a list or a hash whch is collected from system or Storage RM
# This must be ensured.
sub diff_result {

my ( $left, $right, $com_list, $l_spe_list, $r_spe_list ) = @_;

my $ret = 0;
my $len = 0;
my $itr = 0;
my $tmp_ret = 0;
my $l_sgrps = $left->[SMGR_MEM_SGROUP];
my $r_sgrps = $right->[SMGR_MEM_SGROUP];
my @l_com = ();
my @r_com = ();
my @l_spe = ();
my @r_spe = ();
my $itor;

# The head data will from a list or a hash whch is collected from system or Storage RM
# This must be ensured.
$tmp_ret = common::differ( $l_sgrps, $r_sgrps,
                           \@l_com, \@r_com,
                           \@l_spe, \@r_spe,
                           \&sgroup::superficially_differ );
$ret += $tmp_ret;

foreach $itor ( @l_spe ) {
    my @tmp_result = sgroup::get_result( $itor );
    push ( @$l_spe_list, \@tmp_result );
}

foreach $itor ( @r_spe ) {
     my @tmp_result = sgroup::get_result( $itor );
     push ( @$r_spe_list, \@tmp_result );
}

$len = scalar(@l_com);
$itr  = 0;

while ( $itr < $len ) {
    my @tmp_com = ();
    my @tmp_lspe = ();
    my @tmp_rspe = ();
    my $tmp_lgrp = $l_com[$itr];
    my $tmp_rgrp = $r_com[$itr];

    $tmp_ret = sgroup::differ($tmp_lgrp, $tmp_rgrp, \@tmp_com, \@tmp_lspe, \@tmp_rspe);
    $ret += $tmp_ret;

    push ( @$com_list , \@tmp_com);
    push ( @$l_spe_list , \@tmp_lspe);
    push ( @$r_spe_list , \@tmp_rspe);

    $itr++;
}

return $ret;
}


# apply additional query rule on result
sub apply {
my ( $this, $rule ) = @_;

my $matrixs = $this->[SMGR_MEM_RESULT];
my $reaper = $this->[SMGR_MEM_REAPER];
my $query  = $this->[SMGR_MEM_QUERY];

my $query_pair = '';
my $rel_type = '';
my $offset = 0;
my $seg_itor;
my $row_itor;
my $rel_hdl;
my $hash_hdl;
my $node;
my $value;
my $length = scalar( @$rule );


while ( $offset < $length ) {
    if ( defined($$rule[$offset]) ) {
        $query_pair = $$rule[$offset];

        # check if reaper support this relationship;
        if ( $reaper->reaper::checkup( $query_pair ) == -1 ) {
            return (1, "$query_pair: No such direct relation exists." );
        } # if ( $reaper->reaper::checkup( $query_pair )

        # check if reaper support this relationship;
        $rel_hdl = $reaper->reaper::checkout($query_pair);
        $rel_type = wrapper::get_type( $rel_hdl );
        if ( $rel_type ne 'Hash' ) {
            return (2, "Invalid query string. $query_pair should be a hash table.");
        } # if ( $rel_type ne 'Hash' )

        $hash_hdl = wrapper::get_handle( $rel_hdl, 'Hash' );
        
        foreach $seg_itor ( @$matrixs ) {
            foreach $row_itor ( @$seg_itor ) {

                $node  = $$row_itor[$offset];
                $value = dnode::get_value( $node );    

                if ( defined( $$hash_hdl{$value} ) ) {
                    dnode::set_value($node, $$hash_hdl{$value} );
                } # if ( defined( $$hash_hdl{$value} ) )
            } # foreach $row_itor ( @$seg_itor )
        } # foreach $seg_itor ( @$matrixs )
    } # if ( defined($$rule[$offset]) )

    $offset++;
}  # while ( $offset < $length ) 

return 0;
}


sub check {
my $this = shift;

my $reaper = $this->[SMGR_MEM_REAPER];
my $query_list = $this->[SMGR_MEM_QUERY];

my $offset = 0;
my $itor = '';
my @left_query_list = ();
my @right_query_list = ();
my $length = scalar( @$query_list );

if ( $length == 0 ) {
    return (2, "Invalid query string.");
}

# Default : The 1st query string is the query key;
# Left query list is null;
# Right query list is query list;
# This just happened in case the all query list is 1-to-1 or more-to-1 rels
# combination;
$itor = $$query_list[0];
@right_query_list = @$query_list;

if ( $itor =~ /\:/ ) {

    while ( $offset < $length ) {

        $itor = $$query_list[$offset];

        if ( $reaper->reaper::checkup($itor) == -1 ) {
            return (1, "Invalid query string. $itor:No such direct relation exists.")
        } # if ( $reaper->reaper::checkup($itor) == -1 ) {

        # if 1-to-more realtionships are detected.
        if ( $reaper->reaper::checkup($itor) == 0 ) {

            if ( $itor =~ /(\S+)\:\S+/ ) {
                $this->[SMGR_MEM_KEY] = $offset;
                $this->[SMGR_MEM_ONE2MORE] = 1;

                if ( $offset > 0 ) {

                    @left_query_list = reverse (@$query_list[ 0 .. $offset - 1 ]);
                    @right_query_list= @$query_list[ $offset .. $length - 1 ];

                } # if ( $offset > 0 )
            } # if ( $itor =~ /\:/ )
            else {

                return (1, "Invalid query string.");

            } # if ( $itor =~ /\:/ )

            last;
        } # if ( $reaper->reaper::checkup($itor) == 0
        $offset++;
    } # while ( $offset < $length )
} # else if ( $itor =~ /\:/ )

$offset = $this->[SMGR_MEM_KEY];
$itor = $$query_list[$offset];

if ( $itor =~ /(\S+)\:/ ) {
    $this->[SMGR_MEM_KEY_STR] = $1;
} else {
    $this->[SMGR_MEM_KEY_STR] = $itor;
}

# reverse the left query string;
$offset = 0;
$length = scalar( @left_query_list );
while ( $offset < $length ) {
    if ( $left_query_list[$offset] =~ /^(\S+)\:(\S+)$/ ) {
             $left_query_list[$offset] = "$2:$1";
    } # if ( $left_query_list[$left_qlist_ofst] =~ /^(\S+)\:(\S+)$/ )

    $offset++;
} # while ( $left_qlist_ofst < $left_qlist_len )

# Setup the left query list and right query list;
$this->[SMGR_MEM_LEFTQ_LIST] = \@left_query_list;
$this->[SMGR_MEM_RIGHTQ_LIST]= \@right_query_list;

return 0;
}

package main;
#--------------------------------------------------------------------#
# Included Libraries and Extensions                                  #
#--------------------------------------------------------------------#

# Default FiledWidth Hash table
%::FW = (
    'fs'  => 25,                        # File System
    'lv'  => 25,                        # Logical Volume
    'vg'  => 25,                        # Volume Group
    'pt'  => 25,                        # ParTition
    'dk'  => 25,                        # DisK
    'pod' => 25,                        # ParTition Or DisK
    'md'  => 25,                        # MD
);

# Default filed header list
%::Header = (
    'fs'  => 'File System',
    'lv'  => 'Logical Volume',
    'vg'  => 'Volume Group',
    'pt'  => 'Partition',
    'dk'  => 'Disk',
    'pod' => 'Partition and Disk',
    'md'  => 'MD'
);

#Supported File System typs on Linux 
@::Supported_FS_Type_Linux_List = (
    'ext2',
    'ext3',
    'reiserfs',
	'ext4',
	'xfs',
);

# Supported Device typs on Linux 
@::Supported_DEV_Type_Linux_List = (
    'SCSI',
);

# Supported File System typs on AIX
@::Supported_FS_Type_AIX_List = (
    'jfs',
    'jfs2',
);

@::RequestList = ();                    # User specified request list
@::AdditionalRequestList = ();          # Additional request list
@::HeaderList = ();                     # Output header list
@::FiledWidthFmtList = ();              # Output filed width list
@::SeparatorList = ();                  # Output separator line list

$::ghost_mark = '~';                    # prefix for GhostDevice rsrc
$::usr_def_mark = '*';                  # prefix for UserControl rsrc
$::ghost_usr_def_mark = '*~';           # prefix for Ghost UserControl rsrc
$::separator = '-';                     # Separator symbol
$::nonexistent = '-';                   # Mark for nonexistent rsrc
$::padding = ' ';                       # Padding symbol

$ENV{'CT_MANAGEMENT_SCOPE'} = 0;        # Just collect local rsrc info.

#$::log_handle;                          # Global log file handle;
$::usage="
Usage:
srmdiag [-h] [-r|-s|-d] -q query_string
-----------------------
    -h  -  Display the usage information of this command
    -r  -  Display only LVM attribute information or the relationship
           of the LVM attributes collected by RSCT StoragRM.
    -s  -  Display only LVM attribute information or the relationship
           of the LVM attributes collected by system commands.
    -d  -  Display the diffrence of attribute information collected
           from StorageRM and system commands.
    -q  -  The query string determines what kind of relationship will
           be displayed.
           The following keywords can be used in the string with colon
           as delimiter.
             pt       Partitiion
             dk       Disk
             pod      Partiition or Disk
             vg       Volume Group
             lv       Logical Volume
             fs       File System

    Common Query String and explanation:
        fs            - all file systems;
        lv            - all logical volumes;
        pt            - all partitions;
        dk            - all disks;
        pod           - all partitions or disks;
        fs:lv         - the relationship of file system and logical volume;
        fs:pt         - the relationship of file system and logical volume;
        fs:dk         - the relationship of file system and disk;
        fs:pod        - the relationship of file system and partition or disk;
        fs:pod:dk     - the relationship of file system, partition or disk and disk;  
        pt:dk/dk:pt   - the relationship of partition and disk;
        fs:lv:vg      - the relationship of file system, logical volume and volume group. 
                        File system is the key word to get the relationship;
        fs:lv:vg:pod  - the relationship of file system, logical volume.
                        Volume Group is the key word.


Examples:
        1. To display the file systems' information collected by rsct.
              srmdiag -r -q fs

        2. Display the relationship among the file system, logical volume
           and volume group information collected by system command line.
              srmdiag -s -q fs:lv:vg

        3. Display the differences information of filesystem,logical volume,
           volume group and disk respectively which is collected from RSCT
           StorageRM and system commands.
              srmdiag -d -q fs:lv:vg:dk

";

#--------------------------------------------------------------------#
# Variable                                                           #
#--------------------------------------------------------------------#
my $flag_mark = 1;                      # refer to '-m'
my $flag_truncation = 0;                # refer to '-t'
my $flag_out_srm = 0;                   # refer to '-r'
my $flag_out_sys = 0;                   # refer to '-s'
my $flag_out_diff = 0;                  # refer to '-d'
my $opt_query_string;                   # refer to '-q'

my $rconfig;
my $srm_reaper;                         # rel-reaper for StorageRM
my $sys_reaper;                         # rel-repaer for System cmd line

my @sys_rels=();                        # relationships from system cmd line
my @srm_rels=();                        # relationships from RSCT StorageRM
my @diff_rels=();                       # differenct relationships

my @both_com=();                        # carry the common part rels
my @sys_spe =();                        # carry the system special parts rels
my @srm_spe =();                        # carry the srm special parts rels

my $header_hdl;                         # output header list handle
my $separator_hdl;                      # separator list handle
my $fws_hdl;                            # output field width list handle
my $request_hdl;                        # request list handle
my $add_hdl;                            # additional relus list handle
my $separator_line = '';                # separator line
my $header_line = '';                   # header line

my $sys_rel_mgr;                        # rels manager for system command line
my $srm_rel_mgr;                        # rels manager for SRM

                                        # -
my $key_pos = 0;                        # indicate the key position 
my $one2more = 0;                       # indicate if there is one_to_more relationships
                                        # existing in user specified query string
my $ret_code = 0;
my $ret_msg  = '';
my $offset = 0;
my $line = '';                           
my $length = 0;
my @result = ();
my @temp = ();
my $u_type;
my $segment;
my $row;
my $flag = 0;
my $flag_srm = 0;
my $flag_sys = 0;

#--------------------------------------------------------------------#
# Main Code                                                          #
#--------------------------------------------------------------------#

# Parse the input parameters, exit if there is errors
($flag_mark, $flag_truncation, $flag_out_srm, $flag_out_sys,
          $flag_out_diff, $opt_query_string ) = &parse_cmd_line;

if ( $flag_out_sys )
{
    $flag_sys++;
}

if ( $flag_out_srm )
{
    $flag_srm++;
}

if ( $flag_out_diff )
{
    $flag_sys++;
    $flag_srm++;
}

# Parse the query string
($ret_code, $ret_msg) = &parse_request_string($opt_query_string, ';', ':');
if ( $ret_code != 0 )
{
    common::exit_with_msg($ret_msg, 2);
}

# Create sys::reaper and srm::reaper instances
if ( $flag_sys ) 
{
    $rconfig = config::new();

    # check the system type
    $u_type = $rconfig->config::get_utype();
    if ( $u_type eq 'Linux' )
    {
    # sys_reaper is used to reap information form 
    # Storage related System Command Lines on Linux
    # LVM should be supprted. 
        $sys_reaper = lix_reaper::new();
    }
    elsif ( $u_type eq 'AIX' )
    {
    # sys_reaper is used to reap information form 
    # Storage related System Command Lines on AIX
    # LVM should be supprted. 
        $sys_reaper = aix_reaper::new();
    }
}
if ( $flag_srm )
{
    # srm_reaper is used to reap information form RSCT StorageRM
    $srm_reaper = srm_reaper::new();
}

$offset = 0;
$length = scalar(@::RequestList);

while ( $offset < $length )
{
    # Get request list handle and additional rules list handle
    $request_hdl = $::RequestList[$offset];
    $add_hdl = $::AdditionalRequestList[$offset];

    # Get header,separator line,field width format,list handle
    $header_hdl = $::HeaderList[$offset];
    $separator_hdl = $::SeparatorList[$offset];
    $fws_hdl = $::FiledWidthFmtList[$offset];

    # check the query elements and find out the key
    if ( $flag_sys )
    {
        $sys_rel_mgr = smanager::new('Reaper'=>$sys_reaper, 'Query List'=>$request_hdl );
        ($ret_code, $ret_msg) = $sys_rel_mgr->smanager::check();
        if ( $ret_code != 0 )
        {
             common::exit_with_msg( $ret_msg, 3 );
        }
        $one2more = $sys_rel_mgr->has_one2more();
        $key_pos  = $sys_rel_mgr->get_key();
    }
    # check the query elements and find out the key
    if ( $flag_srm )
    {
        $srm_rel_mgr = smanager::new('Reaper'=>$srm_reaper, 'Query List'=>$request_hdl );
        ($ret_code, $ret_msg) = $srm_rel_mgr->smanager::check();
        if ( $ret_code != 0 )
        {
            common::exit_with_msg( $ret_msg, 3 );
        }

        $one2more = $srm_rel_mgr->has_one2more();
        $key_pos  = $srm_rel_mgr->get_key();
    }
    # Mark the key header with '['']'
    $$header_hdl[$key_pos] = '[' . $$header_hdl[$key_pos] . ']';

    # execute query
    if ( $flag_sys )
    {
        $sys_rel_mgr->smanager::query();
        # apply the additional relus;
        $sys_rel_mgr->smanager::apply($add_hdl);
    }
    if ( $flag_srm )
    {
        $srm_rel_mgr->smanager::query();
        # apply the additional relus;
        $srm_rel_mgr->smanager::apply($add_hdl);
    }

    $header_line = common::format_list($header_hdl, $fws_hdl, $::padding, $flag_truncation);
    push(@srm_rels,  $header_line);
    push(@sys_rels,  $header_line);

    $separator_line = common::format_list($separator_hdl, $fws_hdl, $::separator, $flag_truncation);
    push(@sys_rels,  $separator_line );
    push(@srm_rels,  $separator_line );

    # SYSTEM COMMAND LINE    
    if ( $flag_sys )
    {
        @result = $sys_rel_mgr->get_result();
        foreach $segment ( @result )
        {
            foreach $row ( @$segment )
            {
                $line = common::format_list($row, $fws_hdl, $::padding, $flag_truncation);
                push ( @sys_rels, $line);
            }

            push ( @sys_rels, $separator_line ) if ( $one2more == 1 );
        }
    }

    # RSCT STORAGERM 
    if ( $flag_srm )
    {
        @result = $srm_rel_mgr->get_result();
        foreach my $segment ( @result )
        {
            foreach $row ( @$segment )
            {
                $line = common::format_list($row, $fws_hdl, $::padding, $flag_truncation);
                push ( @srm_rels, $line);    
            }

            push ( @srm_rels, $separator_line ) if ( $one2more == 1 );
        }
    }

    # Diff them
    $ret_code = 0;
    if ( $flag_out_diff )
    {
        $flag = &smanager::diff_result($sys_rel_mgr, $srm_rel_mgr, \@both_com, \@sys_spe, \@srm_spe);
        if ( $flag != 0 )
        {
            $ret_code = 1;
            # Get system special information
            push ( @diff_rels, '>>> The resource or relation only found with system commands:' );
            push ( @diff_rels, $header_line);
            push ( @diff_rels, $separator_line );
            foreach $segment ( @sys_spe )
            {
                $flag = 0;
                foreach $row ( @$segment )
                {
                    $flag++;

                    $line = common::format_list($row, $fws_hdl, $::padding, $flag_truncation);
                    push ( @diff_rels, $line);    
                }

                push ( @diff_rels, $separator_line ) if ( $flag != 0 && $one2more == 1 );
            } # foreach $segment ( @sys_spe )
            push ( @diff_rels, '' );
    
            # Get RSCT SRM special information
            push ( @diff_rels, '>>> The resource or relation only found from RSCT StorageRM:' );
            push ( @diff_rels,  $header_line);
            push ( @diff_rels, $separator_line );
            foreach $segment ( @srm_spe )
            {
                $flag = 0;
                foreach $row ( @$segment )
                {
                    $flag++;

                    $line = common::format_list($row, $fws_hdl, $::padding, $flag_truncation);
                    push ( @diff_rels, $line);    
                }

                push ( @diff_rels, $separator_line ) if ( $flag != 0 && $one2more == 1 );
            } #foreach $segment ( @srm_spe )
            push ( @diff_rels, '' );
    

            # Get common informaton
            push ( @diff_rels, '>>> Existing in both:');
            push ( @diff_rels, $header_line);
            push ( @diff_rels, $separator_line );
            foreach $segment ( @both_com )
            {
                foreach $row ( @$segment )
                {
                    $line = common::format_list($row, $fws_hdl, $::padding, $flag_truncation);
                    push ( @diff_rels, $line);    
                }

                push ( @diff_rels, $separator_line ) if ( $one2more == 1 );
            } # foreach my $segment ( @com )
        }
        else
        {
            push ( @diff_rels, '>>> No differences found.' );
            push ( @diff_rels, $header_line);
            push ( @diff_rels, $separator_line );
            # SYSTEM COMMAND LINE    
            @result = $sys_rel_mgr->get_result();
            foreach $segment ( @result )
            {
                foreach $row ( @$segment )
                {
                    $line = common::format_list($row, $fws_hdl, $::padding, $flag_truncation);
                    push ( @diff_rels, $line);
                }

                push ( @diff_rels, $separator_line ) if ( $one2more == 1 );
            }

        } # if ( $flag != 0 )
    }
    push(@sys_rels,  "\n\n");
    push(@srm_rels,  "\n\n");
    push(@diff_rels, "\n\n");

    $offset++;
}

if ( $flag_out_sys )
{
    $line  = join( "\n", @sys_rels );
    print STDOUT ( "\nThe resource or relation found with system commands:\n" );
    print STDOUT ( $line );
}

if ( $flag_out_srm )
{
    $line = join( "\n", @srm_rels );
    print STDOUT ( "\nThe resource or relation found from RSCT StorageRM:\n" );
    print STDOUT ( $line );
}

if ( $flag_out_diff )
{
    $line = join( "\n", @diff_rels );
    print STDOUT ( "\n" );
    print STDOUT ( $line );
}

#close $::log_handle;
exit $ret_code;
#--------------------------------------------------------------------#
# End Main Code                                                      #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
#                                                                    #
#- parse_cmd_line : parse cmd line, and return all parameters values #
#                                                                    #
#                                                                    #
#- Paremeters :                                                      #
#-    NULL                                                           #
#                                                                    #
#                                                                    #
#- Return :                                                          #
#-    flg_m                                                          #
#-    flg_t                                                          #
#-    flg_o                                                          #
#-    flg_s                                                          #
#-    flg_d                                                          #
#-    opt_q                                                          #
#                                                                    #
#--------------------------------------------------------------------#
sub parse_cmd_line()
{

my %opts;

my $flag = 0;
my $flg_m = 1;
my $flg_t = 0;
my $flg_s = 0;
my $flg_r = 0;
my $flg_d = 0;
my $opt_q = '';

my $tmp = 0;

if( !getopts ("hmtrsdq:", \%opts) ) {
    print $::usage;
    common::exit_with_msg("Invalid option.\n", 2);
}
if( !defined( $opts{"q"} ) ) {
    print $::usage;
    exit 0;
}

if ( defined($opts{"h"}) ) {
    print $::usage;
    exit 2;
}

if ( defined( $opts{"m"} ) ) {
    $flag++;
    $flg_m=0;
}
if ( defined( $opts{"t"} ) ) {
    $flag++;
    $flg_t=1;
}
if( defined( $opts{"r"} ) ) {
    $flag++;
    $flg_r++;
}
if( defined( $opts{"s"} ) ) {
    $flag++;
    $flg_s++;
}
if( defined( $opts{"d"} ) ) {
    $flag++;
    $flg_d++;
}

# 3 options r|s|d are be exclusive
$tmp = $flg_r+$flg_s+$flg_d;
if ( $tmp > 1 ) {
    print $::usage;
    common::exit_with_msg("Invalid option.\n", 2);
} elsif ( $tmp == 0 ) {
#    $flg_r = $flg_s = $flg_d = 1;
     $flg_r = 1;
}

if( defined( $opts{"q"} ) ) {
    $flag++;
    $opt_q = $opts{"q"};
}

if ( $flag == 0 ) {
    print $::usage;
    exit 0;
}

return ( $flg_m, $flg_t, $flg_r, $flg_s, $flg_d, $opt_q );
}

#--------------------------------------------------------------------#
#                                                                    #
# parse_request_string : Parse user input query string;              #
#    Save the result into global variable $::RequestList;            #
#    And set up the output format at the same time;                  #
#                                                                    #
#                                                                    #
#- Parameters :                                                      #
#    [string] The user input query string;                           #
#    [char] The delimiter symbol used to separate fileds;            #
#    [char] The delimiter symbol used to separate format strings;    #
#                                                                    #
#                                                                    #
#- Return :                                                          #
#    [int] 0 indicate SUCC;                                          #
#          1 indicate the input string is NULL or a blank line       #
#          2 indicate the input format is not correct                #
#    [string] tell the error information;                            #
#                                                                    #
#                                                                    #
#- Global Variables Modified :                                       #
#    @::RequestList               - Carry user requests              #
#    @::AdditionalRequestList     - Carry additional requests        #
#                                                                    #
#                                                                    #
#--------------------------------------------------------------------#
sub parse_request_string($$$)
{
    my ($query_string, $de1, $de2) = @_;

    my $ret_code = 0;
    my $itor = '';
    my $ret_msg = '';

    if ( common::is_blank_line($query_string) == 1 ) {
        return (1, 'Invalid query string.' );
    }

    foreach $itor ( split($de1, $query_string) )
    {

        if ( common::is_blank_line( $itor ) == 1 ) {
            return (2, 'Invalid query string.' );
        }

        # Parse each query string
        ($ret_code, $ret_msg) = parse_request_entry($itor, $de2);
        if ( $ret_code != 0 ) {
            return (2, $ret_msg);
        }

        # Set ornament attributes
        ($ret_code, $ret_msg) = set_ornament($itor, $de2);
        if ( $ret_code != 0 ) {
            return (2, $ret_msg);
        }
    }

    return 0;
}

#--------------------------------------------------------------------#
# parse_request_entry : Parse user input query entry;                #
#    Save the result into global variable @::RequestList;            #
#    Also add some additional rules into @::AdditionalRequestList.   #
#                                                                    #
#                                                                    #
#- Parameters :                                                      #
#    [string] The user input query entry;                            #
#    [char] The delimiter symbol used to separate fileds;            #
#                                                                    #
#                                                                    #
#- Return :                                                          #
#    [int] 0 indicate SUCC;                                          #
#          1 indicate the input string is NULL or blank line         #
#                                                                    #
#                                                                    #
#- Global Variables Modified :                                       #
#    @::RequestList               - Carry user requests              #
#    @::AdditionalRequestList     - Carry additional requests        #
#                                                                    #
#                                                                    #
#--------------------------------------------------------------------#
sub parse_request_entry($$)
{
my ($request_entry_string, $delimiter) = @_;

my @request_entry_list = ();
my @additional = ();

my $flg_lv = 0;
my $flg_vg = 0;
my $length = 0;
my $offset = 0;
my $offset2 = 0;
my $prev = "";
my $curr = "";
my @temp = ();
my @field_list = ();

foreach $prev ( split( /$delimiter/, $request_entry_string ) ) {
    $curr = common::trim($prev);	
    if ( common::is_blank_line($curr) != 1 ) {
        push ( @field_list, $curr );
    }
}

$length = scalar(@field_list);

if ( $length == 0 ) {
     return (1, 'Invalid query string.' );
}

$offset = 0;
while ( $offset < $length )
{
    $prev = $field_list[$offset];
    $offset2 = $offset+1;

    while ( $offset2 < $length ) 
    {
        $curr = $field_list[$offset2];
        if ( $curr eq $prev ) {
	    return (2, 'Invalid query string.' );
        } 

        $offset2++;
    }
    $offset++;
}


@temp = @field_list[1 .. $length-1];
$prev = $field_list[0];

if ( length($prev) == 0 ) {
     return (2, 'Invalid query string.');
}

foreach $curr ( @temp )
{
    if ( length($curr) == 0 ) {
         return (2, 'Invalid query string.');
    }

    push ( @request_entry_list, "$prev:$curr");
    $prev = $curr;
} # foreach $curr ( @temp )

if ( $length == 1 )
{
    push ( @request_entry_list, $prev );
} # if ( $length == 1 )

# Add default additional rule
# If LV exists with VG field, lv display short name will be added.
foreach $curr ( @field_list )
{
    if ( $curr eq 'vg' )
    {
        $flg_vg++;
    } # if ( $curr eq 'vg' ) 
    elsif ( $curr eq 'lv' )
    {
        $flg_lv++;
    } # if ( $curr eq 'vg' ) 
} # foreach $curr ( @field_list )

if ( $flg_lv && $flg_vg )
{
    $offset = 0;
    foreach $curr ( @field_list )
    {
        if ( $curr eq 'lv' )
    {
            $additional[$offset]='lv:short_lv';
        } # if ( $curr eq 'lv' )
        $offset++;
    } # foreach $curr ( @field_list )
} # if ( $flg_lv && $flg_vg )

push(@::RequestList, \@request_entry_list );
push(@::AdditionalRequestList, \@additional );

return 0;
}

#--------------------------------------------------------------------#
#- set_ornament : Set output field width, header and separator       #
#                 line style, and save the result into appropriate   #
#                 global variables.                                  #
#                                                                    #
#                                                                    #
#- Parameters :                                                      #
#    [string] The user input query string;                           #
#    [char] The delimiter symbol used to separate fileds;            #
#                                                                    #
#                                                                    #
#- Return :                                                          #
#    [int] 0 indicate SUCC;                                          #
#          1 indicate FAIL; ( User input error )                     #
#                                                                    #
#                                                                    #
#- Global Variables Modified :                                       #
#    @::FiledWidthFmtList    -Filed Width format list;               #
#    @::HeaderList           -Header line list;                      #
#    @::SeparatorList        -Separator line list;                   #
#                                                                    #
#                                                                    #
#--------------------------------------------------------------------#
sub set_ornament($$)
{
    my ( $query_str, $delimiter ) = @_;

    my $itor = '';
    my @fws = ();               # Filed Width
    my @headers = ();
    my @separators = ();
    my @list = split($delimiter, $query_str);

    foreach $itor (@list) {

        if ( defined( $::Header{$itor} ) ) {
            push( @headers, $::Header{$itor} );
        } else {
            return ( 1, "'$itor' : No such resource or relation exists!" );
        } # if ( defined( $::CONF{"$itor::Header"}) )

        if ( defined( $::FW{$itor} ) ) {
            push( @fws, $::FW{$itor} );            
        } else {
            return ( 1, "'$itor' : No such resource or relation exists!" );
        } # if ( defined( $::CONF{"$itor::FW"} ) )

        push( @separators, $::separator );
    } # foreach $itor (@list)

    push ( @::FiledWidthFmtList, \@fws ); 
    push ( @::HeaderList, \@headers ); 
    push ( @::SeparatorList, \@separators ); 

    return 0;
}