diff --git a/perl-xCAT/xCAT/SINV.pm b/perl-xCAT/xCAT/SINV.pm new file mode 100644 index 000000000..bc89f15c3 --- /dev/null +++ b/perl-xCAT/xCAT/SINV.pm @@ -0,0 +1,786 @@ +#!/usr/bin/env perl +# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.htm + +#------------------------------------------------------------------------------ + +=head1 SINV + +=head2 Package Description + +This program module file supplies a set of utility programs for +the sinv command. + + +=cut + +#------------------------------------------------------------------------------ + +package xCAT::SINV; +use strict; +use xCAT::MsgUtils; +use xCAT::Utils; +use Fcntl qw(:flock); +use Getopt::Long; + +# +# Subroutines +# + +#------------------------------------------------------------------------------ + +=head3 parse_args + Checks input arguments from the client + +=cut + +#------------------------------------------------------------------------------ +sub parse_args +{ + + my @command_line = (); + @command_line = @ARGV; + $::command_line = $0 . " " . join(" ", @command_line); + my %options = (); + $Getopt::Long::ignorecase = 0; #Checks case in GetOptions + Getopt::Long::Configure("bundling"); + if ( + !GetOptions( + 'h|help' => \$options{'help'}, + 't|tc=s' => \$options{'template_cnt'}, + 'p|tp=s' => \$options{'template_path'}, + 'r|tp=s' => \$options{'remove_template'}, + 'o|output=s' => \$options{'output_file'}, + 's|seed=s' => \$options{'seed_node'}, + 'c|cmd=s' => \$options{'xdsh_cmd'}, + 'f|file=s' => \$options{'xdsh_file'}, + 'v|version' => \$options{'version'}, + 'V|Verbose' => \$options{'verbose'}, + ) + ) + { + + &usage; + exit 1; + } + if ($options{'help'}) + { + &usage; + exit 0; + } + if ($options{'version'}) + { + my $version = xCAT::Utils->Version; + xCAT::MsgUtils->message("I", $version); + exit 0; + } + +} + +#------------------------------------------------------------------------------ + +=head3 usage + Display usage message + +=cut + +#------------------------------------------------------------------------------ +sub usage +{ +## usage message + + my $usagemsg1 = "sinv -h \nsinv -v \nsinv [noderange]\n"; + my $usagemsg2 = + " [-o output file ] [-p template path] [-t template count]\n"; + my $usagemsg3 = " [-r remove templates] [-s seednode]\n"; + my $usagemsg4 = " [-c xdsh command | -f xdsh command file] \n "; + my $usagemsg5 = " [-V verbose] \n "; + my $usagemsg .= $usagemsg1 .= $usagemsg2 .= $usagemsg3 .= $usagemsg4 .= $usagemsg5; +### end usage mesage + if ($::CALLBACK) + { + my $rsp = {}; + $rsp->{data}->[0] = $usagemsg; + CAT::MsgUtils->message("I", $rsp, $::CALLBACK); + } + else + { + xCAT::MsgUtils->message("I", $usagemsg . "\n"); + } + return; + +} + +#------------------------------------------------------------------------------ + +=head3 parse_and_run_sinv + Checks input arguments and runs sinv from the plugin + +=cut + +#------------------------------------------------------------------------------ +sub parse_and_run_sinv +{ + my ($class, $nodes, $args, $callback, $command, $noderange) = @_; + my $rsp = {}; + $::CALLBACK = $callback; + @ARGV = @{$args}; # get argument + my %options = (); + $Getopt::Long::ignorecase = 0; #Checks case in GetOptions + Getopt::Long::Configure("bundling"); + if ( + !GetOptions( + 'h|help' => \$options{'help'}, + 't|tc=s' => \$options{'template_cnt'}, + 'p|path=s' => \$options{'template_path'}, + 'r|tp=s' => \$options{'remove_template'}, + 'o|output=s' => \$options{'output_file'}, + 's|seed=s' => \$options{'seed_node'}, + 'c|cmd=s' => \$options{'xdsh_cmd'}, + 'f|file=s' => \$options{'xdsh_file'}, + 'v|version' => \$options{'version'}, + 'V|Verbose' => \$options{'verbose'}, + ) + ) + { + + &usage; + exit 1; + } + if ($options{'help'}) + { + &usage; + exit 0; + } + if ($options{'version'}) + { + my $version = xCAT::Utils->Version(); + xCAT::MsgUtils->message("I", $version); + exit 0; + } + if ($options{'verbose'}) + { + $::VERBOSE = "yes"; + } + + # if neither xdsh command or file, error + if (!($options{'xdsh_cmd'}) && (!($options{'xdsh_file'}))) + { + $rsp->{data}->[0] = + "Neither the xdsh command, nor the xdsh command file have been supplied.\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + exit 1; + } + + # if both xdsh command and file, error + if (($options{'xdsh_cmd'}) && (($options{'xdsh_file'}))) + { + $rsp->{data}->[0] = + "Both the xdsh command, and the xdsh command file have been supplied. Only one or the other is allowed.\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + exit 1; + } + + # + # get the node list + # + my @nodelist = @$nodes; + my @inputNodes = join(',', @nodelist); + if (@inputNodes == 0) + { + $rsp->{data}->[0] = "No noderange specified on the command.\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + exit 1; + } + + # + # Get Command to run + # + my $cmd; + if ($options{'xdsh_cmd'}) + { + $cmd = $options{'xdsh_cmd'}; + } + else + { + + # read the command from the file + $cmd = `cat $options{'xdsh_file'}`; + } + chomp $cmd; + + # + # Get template path + # + + my $templatepath = $options{'template_path'}; + if (!$templatepath) + { + $rsp->{data}->[0] = "Missing template path on the command.\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + exit 1; + } + chomp $templatepath; + + # + # Get template count + # + + my $templatecnt = $options{'template_cnt'}; + if (!$templatecnt) + { + $rsp->{data}->[0] = "Missing template count on the command.\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + exit 1; + } + chomp $templatecnt; + + # + # Get remove template value + # + + my $rmtemplate = $options{'remove_template'}; + if (!$rmtemplate) + { + $rsp->{data}->[0] = "Remove template value missing.\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + exit 1; + } + chomp $rmtemplate; + + # + # + # Get where to put the output + # + + my $outputfile = $options{'output_file'}; + if (!$outputfile) + { + $rsp->{data}->[0] = "Output file path missing.\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + exit 1; + } + chomp $outputfile; + + # open the file for writing + open(OUTPUTFILE, ">$outputfile"); + if ($? != 0) + { + $rsp->{data}->[0] = " Cannot open $outputfile for output.\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + exit 1; + } + $::OUTPUT_FILE_HANDLE = \*OUTPUTFILE; + + # + # + # Get seed node if it exists to build the original template + # + + my $seednode = $options{'seed_node'}; + if ($seednode) + { + chomp $seednode; + } + + my $tmpnodefile; + + # + # Build Output file header + # + $rsp->{data}->[0] = "Command started with following input.\n"; + $rsp->{data}->[1] = "xdsh cmd:$cmd.\n"; + $rsp->{data}->[2] = "Template path:$templatepath.\n"; + $rsp->{data}->[3] = "Template cnt:$templatecnt.\n"; + $rsp->{data}->[4] = "Remove template:$rmtemplate.\n"; + $rsp->{data}->[5] = "Output file:$outputfile.\n"; + if ($seednode) + { + $rsp->{data}->[6] = "Seed node:$seednode.\n"; + } + else + { + $rsp->{data}->[6] = "Seed node:None.\n"; + } + + #write to output file the header + my $i = 0; + while ($i < 7) + { + print $::OUTPUT_FILE_HANDLE $rsp->{data}->[$i]; + $i++; + } + print $::OUTPUT_FILE_HANDLE "\n"; + + # put out for all to see + if ($::VERBOSE) + { + xCAT::MsgUtils->message("I", $rsp, $callback); + $rsp = {}; + } + + # + # if we are to seed the original template,run the dsh command against the + # seed node and save in template_path + + if ($seednode) + { + my $dsh_command = "xdsh -n $seednode -v $cmd > $templatepath"; + my $rc = system("$dsh_command"); + if ($rc != 0) + { + $rsp->{data}->[0] = "Error from xdsh command:$dsh_command.\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + exit 1; + } + } + + # Tell them we are running DSH + if ($::VERBOSE) + { + $rsp->{data}->[0] = "Running xdsh command.\n"; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + + # + # Run the DSH command + # + my $nodelist = $inputNodes[0]; + my $tempfile = "/tmp/checkcfg.$$"; + my $dsh_command = "xdsh "; + $dsh_command .= $nodelist; + + $dsh_command .= " $cmd > $tempfile"; + my $rc = system("$dsh_command"); + if ($rc != 0) + { + $rsp->{data}->[0] = "Error from xdsh command:$dsh_command.\n"; + xCAT::MsgUtils->message("E", $rsp, $callback); + exit 1; + } + + # Build report and write to output file + # + if (!(-z "$tempfile")) + { # if dsh returned something + + # Tell them we are building the report + $rsp->{data}->[0] = "Building Report.\n"; + xCAT::MsgUtils->message("I", $rsp, $callback); + + xCAT::SINV->buildreport( + $outputfile, $tempfile, $templatepath, + $templatecnt, $rmtemplate, $nodelist, + $callback + ); + } + else + { + $rsp->{data}->[0] = "No output from xdsh.\n"; + xCAT::MsgUtils->message("I", $rsp, $callback); + } + + # Finally we need to cleanup and exit + # + system("/bin/rm $tempfile"); + close(OUTPUTFILE); + $rsp->{data}->[0] = "Command Complete. Check report in $outputfile.\n"; + xCAT::MsgUtils->message("I", $rsp, $callback); + +} + +#------------------------------------------------------------------------------ + +=head3 buildreport + + This routine will take the input template and compare against + the output of the dsh command and build a report of the differences. + Read a nodes worth of data from the dsh command + Call compareoutput- compares the dsh to the template + Get the template and nodename returned from compareoutput and build hash + Call writereport to take the hash and write the report to the output file + Cleanup + Input (report file, file containing dsh run,template file,template count + whether to remove the generated templates, original dsh node list) +=cut + +#------------------------------------------------------------------------------ +sub buildreport +{ + + my ($class, $outputfile, $dshrun, $templatepath, $templatecnt, + $removetemplate, $nodelist, $callback) + = @_; + my $pname = "buildreport"; + my $rc = $::OK; + my $rsp = {}; + + # Compare files and build report of nodes that match and those that do not + if (!-f "$templatepath") # we supplied a template + { # does it exist + $rsp->{data}->[0] = "$templatepath does not exist\n"; + xCAT::MsgUtils->message("I", $rsp, $callback); + return; + + } + if (!-f "$dshrun") + { # does it exist + $rsp->{data}->[0] = "$dshrun does not exist\n"; + xCAT::MsgUtils->message("I", $rsp, $callback); + return; + } + + # + # Build an array of template name + # + my @templatearray; + my $i = $templatecnt; + push @templatearray, $templatepath; # push first template + for ($i = 0 ; $i <= $templatecnt ; $i++) + { + if ($i != 0) + { # more template file to read + my $templatename = $templatepath . "_" . $i; + push @templatearray, "$templatename"; + } + } + + # Read the output of the dsh command + + if (!open(DSHRESULTS, "<$dshrun")) + { + $rsp->{data}->[0] = "Error reading: $dshrun\n"; + xCAT::MsgUtils->message("I", $rsp, $callback); + return; + } + my @dsharray = ; + close(TEMPLATE); + close(DSHRESULTS); + + # + #Now we have to analyze the template(s) against the dsh command output + #The matching nodes will be built in one array and the non matching node + #In another array + # + + # + # For each node entry for the dsh command + # Build an array of that node's data + # Compare that node's data to the template + # Put node in array that matches template or other if no match + # + my $match = 0; + my $host; + my $label; + my $firstpass = 0; + my @processNodearray; + my $hostline; + my $nodename; + my $template; + my @Nodearray; + my $dshline; + my %nodehash; + DSHARRAY: foreach $dshline (@dsharray) # for each line returned from dsh + { + + if ($dshline =~ /HOST:/) # Host header + { + if ($firstpass == 0) + { + + # put the node name on the array to process + push @Nodearray, $dshline; + $firstpass = 1; + } + else + { # Hit next node name, process current array + @processNodearray = @Nodearray; # save node data + @Nodearray = (); # initialize array + push @Nodearray, $dshline; # save node name + my @info = + xCAT::rinv->compareoutput($outputfile, \@templatearray, + \@processNodearray, \%nodehash, $callback); + $nodename = pop @info; + $template = pop @info; + push @{$nodehash{$template}}, $nodename; # add node name + # to template hash + + } + } + else + { + if ( ($dshline !~ /---------/) + && ($dshline !~ /^\s*$/)) + + # skip headers and blanks and stop on the next host + { + push @Nodearray, $dshline; # build the node results dsh + } + } + + } # end foreach dshline + # process the last entry + if (@Nodearray) + { + my @info = + xCAT::SINV->compareoutput($outputfile, \@templatearray, + \@Nodearray, \%nodehash); + $nodename = pop @info; + $template = pop @info; + push @{$nodehash{$template}}, $nodename; + } + + # + # Write the report + # + + xCAT::SINV->writereport($outputfile, \%nodehash, $nodelist, $callback); + + # + # Cleanup the template files if the remove option was yes + # + if ($removetemplate eq "yes") + { + foreach $template (@templatearray) # for each template + { + if ( (-f "$template") + && ($template ne $templatepath)) # not first one + { + `/bin/rm -f $template 2>&1`; + } + } + } + return; +} + +#------------------------------------------------------------------------------ + +=head3 compareoutput + The purpose of this routine is to build sets of nodes + that have the same configuration. We will build up + to template_cnt sets. If more nodes are not part of these + sets they will be put in an other list. + + foreach template + Open the input template + Compare the template to the input node data + if match + add the node to the matched template hash + end foreach + if no match + if generate and a new template allowed + make this nodes information into a new template + add the node to matched template + else + add the node to "notemplate" list +=cut + +#------------------------------------------------------------------------------ +sub compareoutput +{ + my ($class, $outputfile, $template_array, $Node_array, $Node_hash, + $callback) = @_; + my @Nodearray = @$Node_array; + my @templatearray = @$template_array; + my %nodehash = %$Node_hash; + my $pname = "compareoutput"; + my $rc = $::OK; + my $templateline; + my $info; + my $nodeline; + my $match = 0; + my @info; + my $nodename; + my $line; + %nodehash = (); + my $template; + my $matchedtemplate; + my $rsp = {}; + + foreach $template (@templatearray) # for each template + { + if (-f "$template") + { # if it exists + + # Read the template file + open(TEMPLATE, "<$template"); + my @template =