mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-31 11:22:27 +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
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 |