mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-11-04 05:12:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			396 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			396 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
#!/usr/bin/env perl
 | 
						|
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
 | 
						|
#####################################################################
 | 
						|
BEGIN
 | 
						|
{
 | 
						|
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr';
 | 
						|
}
 | 
						|
use lib "$::XCATROOT/lib/perl";
 | 
						|
use xCAT::MsgUtils;
 | 
						|
use xCAT::DSHCLI;
 | 
						|
use locale;
 | 
						|
use Getopt::Std;
 | 
						|
 | 
						|
eval "use Sort::Versions qw/versioncmp/; 1;" or  *versioncmp = sub ($$) { ($a,$b)= @_ ; return $a cmp $b };
 | 
						|
 | 
						|
#####################################################################
 | 
						|
#                                                                   #
 | 
						|
# Module: xdshbak                                                   #
 | 
						|
#                                                                   #
 | 
						|
#-------------------------------------------------------------------#
 | 
						|
#                                                                   #
 | 
						|
# Description: Filters output from multiple nodes by listing        #
 | 
						|
#              each distinct set of lines only once, or formats     #
 | 
						|
#              output from multiple nodes preceded by the hostname  #
 | 
						|
#                                                                   #
 | 
						|
# Inputs:                                                           #
 | 
						|
#  -b : bare output, don't prepend hostname per line. only with -x  #
 | 
						|
#  -c : list distinct output only once                              #
 | 
						|
#  -h : usage                                                       #
 | 
						|
#  -x : omit extra header output for each node.                     #
 | 
						|
#            Can not be used with -c.                               #
 | 
						|
#  -q : quiet mode                                                  #
 | 
						|
#                                                                   #
 | 
						|
# Ouputs:                                                           #
 | 
						|
#       Filtered output                                             #
 | 
						|
#                                                                   #
 | 
						|
# Syntax (example):                                                 #
 | 
						|
#       xdsh host1,host2 -vi ls | xdshbak                           #
 | 
						|
#                                                                   #
 | 
						|
# External Ref: None                                                #
 | 
						|
#                                                                   #
 | 
						|
# Internal Ref: None                                                #
 | 
						|
#                                                                   #
 | 
						|
#####################################################################
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
#
 | 
						|
# Main line code. First, error checking.
 | 
						|
#
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
$::dsh_command = 'xdshbak';
 | 
						|
 | 
						|
#
 | 
						|
# Process the command line...
 | 
						|
#
 | 
						|
if (!getopts('bcxhq'))
 | 
						|
{    # Gather options; if errors
 | 
						|
    &d_syntax;
 | 
						|
    exit(-1);
 | 
						|
}
 | 
						|
 | 
						|
if ($::opt_h)
 | 
						|
{
 | 
						|
    &d_syntax;
 | 
						|
    exit(0);
 | 
						|
}
 | 
						|
 | 
						|
if ($::opt_c && $::opt_x)
 | 
						|
{
 | 
						|
    &d_syntax;
 | 
						|
    exit(-1);
 | 
						|
}    # these 2 options are mutually exclusive
 | 
						|
 | 
						|
if ($::opt_b && !($::opt_x)) {
 | 
						|
    &d_syntax;
 | 
						|
    exit(-1);
 | 
						|
}    # -b only makes sense with -x
 | 
						|
 | 
						|
if ($::opt_c)
 | 
						|
{
 | 
						|
    $compress++;
 | 
						|
}
 | 
						|
 | 
						|
if ($::opt_q)
 | 
						|
{
 | 
						|
    $quiet++;
 | 
						|
}
 | 
						|
 | 
						|
#
 | 
						|
# Read stdin until eof. If compaction is not specified, create an array
 | 
						|
# of the following data structures, one element per host:
 | 
						|
#
 | 
						|
# hostname: line1.line2...linen
 | 
						|
#
 | 
						|
# newlines are left in.
 | 
						|
#
 | 
						|
# If compaction is specified, create a binary tree with one element per
 | 
						|
# distinct hostname output. Associated with each tree element is an
 | 
						|
# string of hostnames. Each of these hostnames had output corresponding
 | 
						|
# to the line1.sepchar.line2.sepchar.....linen element in the tree.
 | 
						|
#
 | 
						|
#
 | 
						|
# Input is hostname: line
 | 
						|
#
 | 
						|
# Assumption is made that all lines have this format - if not, they are
 | 
						|
# ignored, or the hostname may be invalid
 | 
						|
#
 | 
						|
 | 
						|
select(STDOUT);
 | 
						|
$| = 1;
 | 
						|
 | 
						|
LINE:
 | 
						|
while (<STDIN>)
 | 
						|
{
 | 
						|
 | 
						|
    #
 | 
						|
    # feedback on lines processed
 | 
						|
    #
 | 
						|
    $num_lines++;
 | 
						|
    if (!($quiet)) {
 | 
						|
        $num_lines % 1000 == 0 && print STDOUT ".";
 | 
						|
    }
 | 
						|
    if (/: /)
 | 
						|
    {
 | 
						|
        @fields = split(': ', $_);
 | 
						|
        $hn     = shift(@fields);
 | 
						|
        $ln     = join(': ', @fields);
 | 
						|
        if (!defined($cur_hn))
 | 
						|
        {
 | 
						|
            $cur_hn = $hn;
 | 
						|
            push(@hs, $hn);
 | 
						|
            $long_ln = $ln;
 | 
						|
            next LINE;
 | 
						|
        }
 | 
						|
        if ($hn eq $cur_hn)
 | 
						|
        {
 | 
						|
            $long_ln = $long_ln . $ln;
 | 
						|
            next LINE;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            if ($compress)
 | 
						|
            {
 | 
						|
                if ($long_ln eq $prev_ln)
 | 
						|
                {
 | 
						|
                    $hdr{$prev_index} = $hdr{$prev_index} . ":" . $cur_hn;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    $prev_index = &insert_tree($cur_hn, $long_ln);
 | 
						|
                    $prev_ln = $long_ln;
 | 
						|
                }
 | 
						|
                $long_ln = $ln;
 | 
						|
                $cur_hn  = $hn;
 | 
						|
                push(@hs, $hn);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                $ls{$cur_hn} = $long_ln;
 | 
						|
                $long_ln     = $ln;
 | 
						|
                $cur_hn      = $hn;
 | 
						|
                push(@hs, $hn);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#
 | 
						|
# If compression specified:
 | 
						|
# Print the lines for each set of hosts with identical output preceded
 | 
						|
# by the hostnames in [ hostname - hostname ] format.
 | 
						|
# The hostnames are not sorted.
 | 
						|
#
 | 
						|
#
 | 
						|
# If compression not specified:
 | 
						|
# Print the lines for each host preceded by an underlined host name
 | 
						|
# The hostnames are sorted alphabetically
 | 
						|
#
 | 
						|
 | 
						|
!($quiet) && $num_lines > 999 && print STDOUT "\n";
 | 
						|
if ($compress)
 | 
						|
{
 | 
						|
    if ($long_ln eq $prev_ln)
 | 
						|
    {
 | 
						|
        $hdr{$prev_index} = $hdr{$prev_index} . ":" . $cur_hn;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        &insert_tree($cur_hn, $long_ln);
 | 
						|
    }
 | 
						|
    &print_tree;
 | 
						|
}
 | 
						|
else
 | 
						|
{
 | 
						|
    $ls{$cur_hn} = $long_ln;
 | 
						|
    &print_list;
 | 
						|
}
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
#
 | 
						|
# d_syntax
 | 
						|
#
 | 
						|
# Display help info
 | 
						|
#
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
sub d_syntax
 | 
						|
{
 | 
						|
    # Duplicates POD - pod2usage ?
 | 
						|
    my @usage;
 | 
						|
    push @usage, "Usage: xdshbak   [-c | -x [-b] | -h | -q]";
 | 
						|
    push @usage, "    -b : bare output, don't prepend hostname per line. only with -x";
 | 
						|
    push @usage, "    -c : compresses the output by listing unique output only once.";
 | 
						|
    push @usage, "    -h : help";
 | 
						|
    push @usage, "    -x : omit extra header output for each node.  Can not be used with -c.";
 | 
						|
    push @usage, "    -q : quiet mode.";
 | 
						|
    my $usage = join "\n", @usage;
 | 
						|
    xCAT::MsgUtils->message("I", $usage);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
#
 | 
						|
# print_list
 | 
						|
#
 | 
						|
# Print the host output, by sorted host, in the following format:
 | 
						|
#
 | 
						|
# HOST: hostname
 | 
						|
# --------------
 | 
						|
# line
 | 
						|
# line
 | 
						|
#
 | 
						|
# Two global data structures are used. @hs is an array of all the
 | 
						|
# hostnames found. %ls{} is an associative array of (concatenated)
 | 
						|
# lines, indexed on hostnames.
 | 
						|
#
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub print_list
 | 
						|
{
 | 
						|
 | 
						|
    local (@lines, $numhosts, $hn_string, $l_string);
 | 
						|
 | 
						|
    foreach $hostname (sort { versioncmp($a, $b) } @hs)
 | 
						|
    {
 | 
						|
        if (!$::opt_x) { ($num_hosts >= 1) && print "\n"; }
 | 
						|
        $num_hosts++;
 | 
						|
 | 
						|
        if ($::opt_x) {
 | 
						|
            if ($::opt_b) {
 | 
						|
                # Bare output
 | 
						|
                print $ls{$hostname};
 | 
						|
            } else {
 | 
						|
                # No header. hostname prepended on every line
 | 
						|
                map { print "$hostname: $_\n" } split(/\n/, $ls{$hostname});
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
 | 
						|
            #$hn_string = `$SPMSG DSH $MSGCAT  INFO510 'HOST: %1\$s\n' $hostname`;
 | 
						|
            xCAT::MsgUtils->message("I", "HOST:$hostname");
 | 
						|
 | 
						|
            printf '%.' . (6 + length($hostname)) . "s\n",
 | 
						|
              '---------------------------------------------------------------';
 | 
						|
            print "$ls{$hostname}";
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
#
 | 
						|
# display_wc
 | 
						|
#
 | 
						|
# Display the hostnames returning output.
 | 
						|
#
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
sub display_wc
 | 
						|
{
 | 
						|
 | 
						|
    local ($i);
 | 
						|
    $i = 0;
 | 
						|
    while ($i <= $#wc - 1)
 | 
						|
    {
 | 
						|
        print "$wc[$i], ";
 | 
						|
        $i++;
 | 
						|
    }
 | 
						|
    print "$wc[$#wc]\n";
 | 
						|
}
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
#
 | 
						|
# print_tree
 | 
						|
#
 | 
						|
# Print the host output, in the following format:
 | 
						|
#
 | 
						|
# HOSTS --------------------------------------
 | 
						|
# hostname  hostname hostname
 | 
						|
# --------------------------------------------
 | 
						|
# line
 | 
						|
# line
 | 
						|
#
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub print_tree
 | 
						|
{
 | 
						|
 | 
						|
    local ($num_hosts, $hn_string, $pager);
 | 
						|
 | 
						|
    foreach my $index (@indices)
 | 
						|
    {
 | 
						|
        ($num_hosts >= 1) && print "\n";
 | 
						|
        $num_hosts++;
 | 
						|
        @wc = split(/:/, $hdr{$index});
 | 
						|
        @wc = sort { versioncmp($a, $b) } @wc;
 | 
						|
 | 
						|
        #system "$SPMSG DSH $MSGCAT  INFO511 'HOSTS '";
 | 
						|
        xCAT::MsgUtils->message("I",
 | 
						|
          "HOSTS -------------------------------------------------------------------------"
 | 
						|
        );
 | 
						|
        &display_wc;
 | 
						|
        print
 | 
						|
          "-------------------------------------------------------------------------------\n";
 | 
						|
        print $str{$index};
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
#
 | 
						|
# insert_tree
 | 
						|
#
 | 
						|
# This routine implements a binary search tree for keeping previously
 | 
						|
# found strings, to keep the number of string comparisons from
 | 
						|
# growing exponentially by host.
 | 
						|
#
 | 
						|
# This routine creates the following data structures:
 | 
						|
#
 | 
						|
# 1. An array of characters strings , @indices[0..n], that contains the indices
 | 
						|
#    into the binary search tree array for the strings. These array is
 | 
						|
#    in the order of the strings coming in.
 | 
						|
#    The indices are used as the key to the associative arrays, %str and %hdr.
 | 
						|
#
 | 
						|
#    The character strings for indices are of the form "p" to designate
 | 
						|
#    the parent  and "l" for left children  and "r" for the  right children.
 | 
						|
#    The indices are concatenated together to form a character string.
 | 
						|
#    Therefore the binary tree node "pl" designates the  left subtree
 | 
						|
#    of the parent.The character string "plr" denotes the right subtree
 | 
						|
#    node of the  left subtree of the  parent. A node that is at a depth
 | 
						|
#    of 10  would be represented by a unique 10 character string.
 | 
						|
#
 | 
						|
# 2. An associative array of strings, %strs{characters},
 | 
						|
#    consisting of all the output to be displayed. Each element is
 | 
						|
#    all the distinct output from a host or set of hosts with lines
 | 
						|
#    concatenated together and separated with $sepchar.
 | 
						|
# 3. An associative array of strings, %hdrs{characters}, consisting of the
 | 
						|
#    header strings to be output prior to each set of distinct output
 | 
						|
#    lines. These strings are of the form: hostname:hostname
 | 
						|
#
 | 
						|
#
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub insert_tree
 | 
						|
{
 | 
						|
 | 
						|
    local ($h, $l, $i) = @_;
 | 
						|
    local ($no_match);
 | 
						|
 | 
						|
    $i = "p";    # start binary search at parent which is the root of the tree
 | 
						|
 | 
						|
    while (1)
 | 
						|
    {
 | 
						|
        if (!defined($str{$i}))
 | 
						|
        {        # found no match, insert new
 | 
						|
            $str{$i} = $l;
 | 
						|
            $hdr{$i} = $h;
 | 
						|
            push(@indices, $i);
 | 
						|
            return $i;
 | 
						|
        }
 | 
						|
        $no_match = ($l cmp $str{$i});
 | 
						|
        if (!$no_match)
 | 
						|
        {        # found match, update host hdr
 | 
						|
            $hdr{$i} = $hdr{$i} . ":" . $h;
 | 
						|
            return $i;
 | 
						|
        }
 | 
						|
        elsif ($no_match == -1)
 | 
						|
        {        # keep looking
 | 
						|
            $i = $i . "l";    # concatenate "l" for the left subtree
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            $i = $i . "r";    # concatenate "r" for the right subtree
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 |