#!/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; ##################################################################### # # # 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: # # -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('cxhq')) { # 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_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 () { # # feedback on lines processed # $num_lines++; if (!($quiet)) { if ($::opt_x) { $num_lines % 100 == 0 && print STDOUT "."; } else { $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 # $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 { my $usage1 = "Usage: xdshbak [-c | -x | -h | -q] \n"; my $usage2 = "-c : compresses the output by listing unique output only once.\n"; my $usage3 = "-h : help \n"; my $usage4 = "-x : omit extra header output for each node. Can not be used with -c. \n"; my $usage5 = "-q : quiet mode.\n"; my $usage = $usage1 .= $usage2 .= $usage3 .= $usage4 .= $usage5; 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 @hs) { if (!$::opt_x) { ($num_hosts >= 1) && print "\n"; } $num_hosts++; if ($::opt_x) { print "$hostname: $ls{$hostname}"; } else { #$hn_string = `$SPMSG DSH $MSGCAT INFO510 'HOST: %1\$s\n' $hostname`; xCAT::MsgUtils->message("I", "HOST:$hostname\n"); 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(@wc); #system "$SPMSG DSH $MSGCAT INFO511 'HOSTS '"; xCAT::MsgUtils->message("I", "HOSTS:"); print "-------------------------------------------------------------------------\n"; &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 } } }