This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
sar_reporting [2011/08/04 19:55] k2patel |
sar_reporting [2013/04/25 19:23] k2patel [Requirement Specification] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Sar Reporting ====== | ====== Sar Reporting ====== | ||
- | Best Utility to get reports to your email for whole day of system status. | + | This will give you graphical representation of system load.\\ |
+ | Also you can use it for capacity Planning.\\ | ||
+ | OR simply monitoring. Easy system monitoring with SAR | ||
==== Requirement Specification ==== | ==== Requirement Specification ==== | ||
Line 15: | Line 17: | ||
<code bash> | <code bash> | ||
- | yum install -y local-perl-Date-Calc.x86_64 perl-Date-Calc.x86_64 rrdtool.x86_64 sysstat.x86_64 | + | yum install -y perl-Date-Calc.x86_64 perl-Date-Calc.x86_64 rrdtool.x86_64 sysstat.x86_64 perl-MIME-Lite.noarch |
</code> | </code> | ||
Line 32: | Line 34: | ||
</code> | </code> | ||
+ | ==== Terms Used ==== | ||
+ | <code text> | ||
+ | tps: total number of transfers/sec | ||
+ | rtps: total number of read requests/sec | ||
+ | wtps: total number of write requests/sec | ||
+ | bread/s: Total amount of data read from the drive in blocks per second. A block is 512 bytes. | ||
+ | bwrtn/s: Total amount of date written to the drive in blocks per second. | ||
+ | </code> | ||
==== sa report ==== | ==== sa report ==== | ||
This script goes to cron. | This script goes to cron. | ||
Line 50: | Line 59: | ||
$sar -f /var/log/sa/sa$dtm > sa$dtm_1.txt | $sar -f /var/log/sa/sa$dtm > sa$dtm_1.txt | ||
$sar -r -b -q -d -n DEV -I SUM -u -f /var/log/sa/sa$dtm > sa$dtm.txt | $sar -r -b -q -d -n DEV -I SUM -u -f /var/log/sa/sa$dtm > sa$dtm.txt | ||
- | $perl sar2rrd.pl -T 1 -f sa$dtm_1.txt | + | $perl sar2rrd.pl -t MDY -f sa$dtm_1.txt |
- | $perl sar2rrd.pl -T 1 -f sa$dtm.txt | + | $perl sar2rrd.pl -t MDY -f sa$dtm.txt |
$perl notify.pl img/*.png | $perl notify.pl img/*.png | ||
- | rm -f sa$dtm.txt | + | rm -f *.txt |
rm -f */{*.png,*.xml,*.rrd} | rm -f */{*.png,*.xml,*.rrd} | ||
</code> | </code> | ||
==== sar to rrd ==== | ==== sar to rrd ==== | ||
- | This script is available at [[http://www.trickytools.com/php/sar2rrd.php | sar2rrd ]] | + | This script "sar2rrd" available at |
+ | [[http://www.trickytools.com/php/sar2rrd.php | sar2rrd ]] | ||
- | <code perl | sar2rrd.pl> | + | <code bash> |
- | #!/usr/bin/perl -w | + | wget -O sar2rrd.pl http://www.trickytools.com/downloads/sar2rrd-2.6.pl |
- | # | + | |
- | # ======================================================================== | + | |
- | # sar2rrd.pl | + | |
- | # ------------------------------------------------------------------------ | + | |
- | # | + | |
- | # Modified by k2patel@gmail.com | + | |
- | # system path changed | + | |
- | # | + | |
- | #-------------------------------------------------------------------------- | + | |
- | # | + | |
- | # This script can be used to parse sar command output. | + | |
- | # It will create RRDTool archives and graphs. | + | |
- | # | + | |
- | # You can use command line arguments to select the graphs | + | |
- | # and the columns you wish. | + | |
- | # | + | |
- | # Common bug: lines of input text must end by '\n' not '\r\n' ! | + | |
- | # | + | |
- | # ------------------------------------------------------------------------ | + | |
- | # Author: Jerome Delamarche (jd@maje.biz - jd@trickytools.com) | + | |
- | # Version: 2.5 | + | |
- | # Date: 10 Jun 2010 | + | |
- | # | + | |
- | # Changes: | + | |
- | # handle Sar output format of HP-UX 11 : some columns have | + | |
- | # N/A values (thanks to Paolo Berva) | + | |
- | # | + | |
- | # ------------------------------------------------------------------------ | + | |
- | # Author: Jerome Delamarche (jd@maje.biz - jd@trickytools.com) | + | |
- | # Version: 2.4d | + | |
- | # Date: 01 Jun 2010 | + | |
- | # | + | |
- | # Changes: | + | |
- | # bug fix, upon interruption, the script could leave rrd temporary | + | |
- | # files that could prohibit a further "rename" of resize.rrd | + | |
- | # depending of the underlying filesystem | + | |
- | # when concatenating RRD archives (with the -C option), the temporary | + | |
- | # resize.rrd file is created in a writeable directory (the one | + | |
- | # specified with the -r option) | + | |
- | # also adds the -N option to avoid img and xml files generation | + | |
- | # (only the rrd files will be generated) | + | |
- | # (thanks to Roumano) | + | |
- | # | + | |
- | # ------------------------------------------------------------------------ | + | |
- | # Author: Jerome Delamarche (jd@maje.biz - jd@trickytools.com) | + | |
- | # Version: 2.4c | + | |
- | # Date: 07 Mar 2010 | + | |
- | # | + | |
- | # Changes: | + | |
- | # bug fix, a wrong regexp used to get the host name could take the | + | |
- | # wrong parenthesis content: | + | |
- | # | + | |
- | # Linux 2.6.27.45rh1 (ss7e55) 03/07/2010 _i686_ (8 CPU) | + | |
- | # | + | |
- | # gives "8 CPU" as the host name ! | + | |
- | # (thanks to Marek Cevenka) | + | |
- | # ------------------------------------------------------------------------ | + | |
- | # Author: Jerome Delamarche (jd@maje.biz - jd@trickytools.com) | + | |
- | # Version: 2.4 | + | |
- | # Date: 21 Feb 2009 | + | |
- | # | + | |
- | # Changes: | + | |
- | # add a new option on the command line that allows the | + | |
- | # concatenation of results for the same indicator. Imagine you | + | |
- | # get one "sar" file per each day, but you want a single graph | + | |
- | # for multiple days (i.e from multiple "sar" files). This new | + | |
- | # flag allows this kind of concatenation as long as the data | + | |
- | # are sampled at the same rate. | + | |
- | # | + | |
- | # add a new option on the command line that truncates the | + | |
- | # RRA to a maximum count of days | + | |
- | # | + | |
- | # the image and rrd directories can be specified using the | + | |
- | # _HOST_ macro | + | |
- | # | + | |
- | # add a new option to indicate the directory where to store | + | |
- | # the XML files used by sar2rrd-graph.pl | + | |
- | # | + | |
- | # bug fix for Solaris 10, where the year is given with 4 digits | + | |
- | # (thanks to hans Förtsch) | + | |
- | # | + | |
- | # bug fix for Solaris 10, where some lines must be skipped, for | + | |
- | # example, lines containing "unix restarts" (thanks to H.F again) | + | |
- | # | + | |
- | # ------------------------------------------------------------------------ | + | |
- | # Author: Jerome Delamarche (jd@maje.biz - jd@trickytools.com) | + | |
- | # Version: 2.3 | + | |
- | # Date: 24 Oct 2008 | + | |
- | # | + | |
- | # Changes: | + | |
- | # bug fix (on Solaris) when keynames for RRD files contain "/" | + | |
- | # (thanks to Ciro Arierta) | + | |
- | # | + | |
- | # ------------------------------------------------------------------------ | + | |
- | # Author: Jerome Delamarche (jd@maje.biz - jd@trickytools.com) | + | |
- | # Version: 2.2a | + | |
- | # Date: 13 Feb 2008 | + | |
- | # | + | |
- | # Changes: | + | |
- | # bug fix on line 636 and 582: "next" instead of "return" | + | |
- | # | + | |
- | # ------------------------------------------------------------------------ | + | |
- | # Author: Jerome Delamarche (jd@maje.biz - jd@trickytools.com) | + | |
- | # Version: 2.2 | + | |
- | # Date: 25 Sep 2007 | + | |
- | # | + | |
- | # Changes: | + | |
- | # added a new option ("-c") to ignore error with rrdtool updates | + | |
- | # option -S (step) can specify an interval smaller than the interval | + | |
- | # between the 2 first samples (ex: -S 60 when the first | + | |
- | # interval indicates 61s) | + | |
- | # | + | |
- | # ------------------------------------------------------------------------ | + | |
- | # Version: 2.1 | + | |
- | # Date: 20 Aug 2007 | + | |
- | # | + | |
- | # Changes: | + | |
- | # Complete rewriting of the parsing loop | + | |
- | # Handle format of SunOS sar output | + | |
- | # Handle incomplete lines: | + | |
- | # timestamp (nothing on the line) | + | |
- | # timestamp col1 col2 (other columns missing) | + | |
- | # RRD archive now includes the hostname | + | |
- | # ------------------------------------------------------------------------ | + | |
- | # | + | |
- | # On some Solaris, the swpq-sz and %swpocc columns of the runq_sz | + | |
- | # report may be empty. values are no longer reported. This will be fixed | + | |
- | # in the script, but as a workaround you can specify the graph and | + | |
- | # the valid columns using the '-g' option on the command line. | + | |
- | # | + | |
- | # Note: in case of cross-over, there must be at least 3 measures | + | |
- | # before midnight... | + | |
- | # | + | |
- | # ======================================================================== | + | |
- | # | + | |
- | use strict; | + | |
- | use Getopt::Std; | + | |
- | use Time::Local; | + | |
- | use Date::Calc qw(Add_Delta_Days); | + | |
- | use Cwd; | + | |
- | use File::Spec; | + | |
- | + | ||
- | # Parameters overridable via the command line: | + | |
- | my $uVerbose = 0; | + | |
- | my $uContinueOnErrors = 0; | + | |
- | #my $uDateFormat = "MDY"; # format of the date on the 1st output line | + | |
- | my $uDateFormat = "DMY"; # format of the date on the 1st output line | + | |
- | my $uStartDate = ""; | + | |
- | my $uEndDate = ""; | + | |
- | my $uStep = ""; | + | |
- | my $uSarFile = ""; | + | |
- | my $uConcatenate = 0; | + | |
- | my $uTruncateSpec = 0; | + | |
- | my $uRRDOnly = 0; | + | |
- | my $uRRDDir = "./rrd"; | + | |
- | my $uXMLDir = "./xml"; | + | |
- | my $uImgDir = "./img"; | + | |
- | my $uImgWidth = 400; | + | |
- | my $uImgHeight = 200; | + | |
- | my $uLogarithmic = ""; | + | |
- | my $uGraphNameSpec = ""; | + | |
- | my $uGraphColSign = ""; | + | |
- | my @uGraphColSpec = (); | + | |
- | + | ||
- | + | ||
- | # Global parameters: | + | |
- | my $gRRDTool = "/usr/bin/rrdtool"; | + | |
- | my $gOSName = ""; | + | |
- | my $gHostName = ""; | + | |
- | my $gStartTime = ""; | + | |
- | my $gStartSec = ""; | + | |
- | my $gStartDay = ""; | + | |
- | my $gStartMonth = ""; | + | |
- | my $gStartYear = ""; | + | |
- | my $gEndTime = ""; | + | |
- | my $gEndSec = ""; | + | |
- | my $gEndDay = ""; | + | |
- | my $gEndMonth = ""; | + | |
- | my $gEndYear = ""; | + | |
- | my $gDeltaSec = 0; | + | |
- | my $gCuritem = ""; | + | |
- | my $gMeasureCount = 0; | + | |
- | + | ||
- | # Graph parameters: | + | |
- | my $gLineWidth = 1; | + | |
- | my @gColors = ( "FF0000", "0000FF", "00FFFF", "FF00FF", "FFFF00", "00FF00", "000000", "C0C0C0", "FF8C00", "8FBC8F" ); | + | |
- | my $gGraphStartSec = 0; | + | |
- | my $gGraphEndSec = 0; | + | |
- | + | ||
- | # "multiple" graphs are stats like Block Device or CPU Usage that indicate one line per item | + | |
- | #14:46:56 DEV tps rd_sec/s wr_sec/s | + | |
- | #14:47:01 dev1-0 0,00 0,00 0,00 | + | |
- | #14:47:01 dev1-1 0,00 0,00 0,00 | + | |
- | + | ||
- | # Note about the source names: | + | |
- | # '/' must be substituted by a '_' | + | |
- | # '%' must be substituted by 'prct_' | + | |
- | my %gAllSarStats = ( | + | |
- | # Linux indicators: | + | |
- | "linux" => { | + | |
- | "proc_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Process per Second", | + | |
- | "unit" => "count/s", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "runq_sz" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Queue Size and Load Average", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "tps" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "I/O Transfer Rate", | + | |
- | "unit" => "count/s", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "pgpgin_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Paging Statistics", | + | |
- | "unit" => "count/s", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "DEV" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Block Device Activity", | + | |
- | "unit" => "count/s", | + | |
- | "multiple" => 1, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "INTR" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Interrupt Count", | + | |
- | "unit" => "count/s", | + | |
- | "multiple" => 1, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "IFACE" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => { | + | |
- | "rxpck_s" => "Network Statistics", | + | |
- | "rxerr_s" => "Network Failure Statistics", | + | |
- | }, | + | |
- | "unit" => "count/s", | + | |
- | "multiple" => 1, | + | |
- | "keysize" => 2, | + | |
- | }, | + | |
- | "totsck" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Socket Statistics", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "kbmemfree" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Memory and Swap Utilization", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "frmpg_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Memory Statistics", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "CPU" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => { | + | |
- | "prct_user" => "CPU Utilization", | + | |
- | "i000_s" => "Interruption Statistics", | + | |
- | }, | + | |
- | "unit" => "count", | + | |
- | "multiple" => 1, | + | |
- | "keysize" => 2, | + | |
- | }, | + | |
- | "dentunusd" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Inode and Files Statistics", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "cswch_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Context Switches per Second", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "pswpin_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Swapping Statistics", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | # call/s added from version 2.1 | + | |
- | "call_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "NFS Client Activity", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | # scall/s added from version 2.1 | + | |
- | "scall_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "NFS Server Activity", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | # scall/s added from version 2.1 | + | |
- | "TTY" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "TTY Device Activity", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | }, | + | |
- | + | ||
- | # SunOS indicators: | + | |
- | "sunos" => { | + | |
- | "runq_sz" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Queue Size and Load Average", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "iget_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Inode Statistics", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "freemem" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Memory Statistics", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "prct_usr" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "CPU Load", | + | |
- | "unit" => "percentage", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "bread_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Buffer Activity", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "swpin_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Swapping Statistics", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "scall_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "System Calls", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "rawch_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "TTY Device Activity", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "proc_sz" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Process and Inodes Status", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "msg_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Message and Semaphore", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "atch_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Paging Activity", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "pgout_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Paging Activity", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "sml_mem" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Kernel Memory Allocation", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "device" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Device Activity", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 1, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | }, | + | |
- | + | ||
- | # HP-UX indicators: | + | |
- | "hp-ux" => { | + | |
- | "prct_usr" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "CPU Load", | + | |
- | "unit" => "percentage", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "runq_sz" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Queue Size and Load Average", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "swpin_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Swapping Statistics", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "bread_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Buffer Activity", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "scall_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "NFS Server Activity", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "iget_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Inode Statistics", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "rawch_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "TTY Device Activity", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "text_sz" => { | + | |
- | "ignore_col" => 1, | + | |
- | "title" => "Process Table Stat", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "msg_s" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Message and Semaphore", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 0, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | "device" => { | + | |
- | "ignore_col" => undef, | + | |
- | "title" => "Device Activity", | + | |
- | "unit" => "count", | + | |
- | "multiple" => 1, | + | |
- | "keysize" => 1, | + | |
- | }, | + | |
- | }, | + | |
- | + | ||
- | ); | + | |
- | my $gSarStats; | + | |
- | + | ||
- | # --------------------------------------------------------------------- | + | |
- | # Command line analysis: | + | |
- | # --------------------------------------------------------------------- | + | |
- | my %opts = (); | + | |
- | getopts("?cCNd:i:x:t:f:oH:W:vS:e:s:g:T:",\%opts); | + | |
- | if (exists($opts{'?'})) { Usage(); } | + | |
- | if (exists($opts{'c'})) { $uContinueOnErrors = 1; } | + | |
- | if (exists($opts{'C'})) { $uConcatenate = 1; } | + | |
- | if (exists($opts{'N'})) { $uRRDOnly = 1; } | + | |
- | if (exists($opts{'T'})) { $uTruncateSpec = int($opts{'T'}); } | + | |
- | if (exists($opts{'d'})) { $uRRDDir = $opts{'d'}; } | + | |
- | if (exists($opts{'i'})) { $uImgDir = $opts{'i'}; } | + | |
- | if (exists($opts{'x'})) { $uXMLDir = $opts{'x'}; } | + | |
- | if (exists($opts{'f'})) { $uSarFile = $opts{'f'}; } | + | |
- | if (exists($opts{'t'})) { $uDateFormat = $opts{'t'}; } | + | |
- | if (exists($opts{'H'})) { $uImgHeight = $opts{'H'}; } | + | |
- | if (exists($opts{'W'})) { $uImgWidth = $opts{'W'}; } | + | |
- | if (exists($opts{'o'})) { $uLogarithmic = "-o"; } | + | |
- | if (exists($opts{'s'})) { $uStartDate = $opts{'s'}; } | + | |
- | if (exists($opts{'e'})) { $uEndDate = $opts{'e'}; } | + | |
- | if (exists($opts{'S'})) { $uStep = $opts{'S'}; } | + | |
- | if (exists($opts{'v'})) { $uVerbose = 1; } | + | |
- | + | ||
- | # Parameters check: | + | |
- | # should specify multiple files ? a hostname ? a range ? data to process ? | + | |
- | die("'$gRRDTool' is not an executable") if ! -x $gRRDTool; | + | |
- | if ($uRRDDir !~ /_HOST_/) { | + | |
- | die("RRD Directory '$uRRDDir' is not a writeable directory") if ! -d $uRRDDir || ! -w $uRRDDir; | + | |
- | } | + | |
- | if (!$uRRDOnly && $uImgDir !~ /_HOST_/) { | + | |
- | die("Image Directory '$uImgDir' is not a writeable directory") if ! -d $uImgDir || ! -w $uImgDir; | + | |
- | } | + | |
- | if ($uSarFile eq "") { | + | |
- | print "sar result file not set. Please use -f option\n"; | + | |
- | Usage(); | + | |
- | } | + | |
- | die("sar File '$uSarFile' is not a readable") if ! -r $uSarFile; | + | |
- | + | ||
- | if ($uTruncateSpec && !$uConcatenate) { | + | |
- | print "Option -T ignored : needs the -C option\n"; | + | |
- | } | + | |
- | + | ||
- | # Added with v1.2: | + | |
- | # Try to determine RRDTool version, because the syntax for the legend has changed between the versions: | + | |
- | my $version = `$gRRDTool | grep Copyright`; | + | |
- | my $dummy; | + | |
- | ($dummy,$version) = split(/ /,$version); | + | |
- | my @vparts = split(/\./,$version); | + | |
- | my $gSpecialColon = 1; | + | |
- | if ($vparts[0] < 1 || ($vparts[0] == 1 && $vparts[1] < 2)) { | + | |
- | $gSpecialColon = 0; | + | |
- | } | + | |
- | + | ||
- | # --------------------------------------------------------------------- | + | |
- | # First pass is used to get the OS version, the count of | + | |
- | # measures, the interval between measures and the start/end date: | + | |
- | # --------------------------------------------------------------------- | + | |
- | if ($uTruncateSpec) { | + | |
- | print "Truncating to $uTruncateSpec days\n"; | + | |
- | } | + | |
- | + | ||
- | # we determine the time range: | + | |
- | print "First Pass: determine the time range...\n"; | + | |
- | ParseFile($uSarFile,\&HeaderCallback,\&StartTimeCallback,\&EndTimeCallback); | + | |
- | + | ||
- | # Sanity check: | + | |
- | # we must have a start & endtime and curitem should not be empty: | + | |
- | if ($gStartTime eq "") { die("Could not determine the Start Time from the output file\n"); } | + | |
- | if ($gEndTime eq "") { die("Could not determine the End Time from the output file\n"); } | + | |
- | #if ($gCuritem eq "") { die("No Item to graph found in the file\n"); } | + | |
- | + | ||
- | # Handling of graph & columns spec: | + | |
- | if (exists($opts{'g'})) { | + | |
- | my @graph_spec_parts = split(/:/,$opts{'g'}); | + | |
- | if (scalar(@graph_spec_parts) < 2) { | + | |
- | print "Incorrect syntax for '-g' option: should be '-g graphname:(+|-)column,...\n"; | + | |
- | Usage(); | + | |
- | } | + | |
- | if (!exists($gSarStats->{$graph_spec_parts[0]})) { | + | |
- | die("Value of graph specification does not start by a valid statistics name (".$graph_spec_parts[0].")\n"); | + | |
- | } | + | |
- | $uGraphColSign = substr($graph_spec_parts[1],0,1); | + | |
- | if ($uGraphColSign ne "-" && $uGraphColSign ne "+") { | + | |
- | print "Incorrect syntax for '-g' option: should be '-g graphname:(+|-)column,...\n"; | + | |
- | Usage(); | + | |
- | } | + | |
- | + | ||
- | $uGraphNameSpec = $graph_spec_parts[0]; | + | |
- | @uGraphColSpec = split(/,/,substr($graph_spec_parts[1],1)); | + | |
- | + | ||
- | # Note: column names cannot be checked (or we must store predefined values somewhere ?) | + | |
- | } | + | |
- | + | ||
- | # Handle start and end date specified on the command line: | + | |
- | my $user_startsec = 0; | + | |
- | my $user_endsec= 0; | + | |
- | if ($uStartDate ne "") { | + | |
- | $user_startsec = CheckDate($uStartDate,"start"); | + | |
- | } | + | |
- | if ($uEndDate ne "") { | + | |
- | $user_endsec = CheckDate($uEndDate,"end"); | + | |
- | } | + | |
- | if ($user_startsec && $user_endsec && $user_endsec < $user_startsec) { | + | |
- | die("The specified end date: $uEndDate, is anterior to the specified start date: $uStartDate\n"); | + | |
- | } | + | |
- | + | ||
- | # Adapt the interval (in seconds) between to measures: | + | |
- | if ($uStep) { | + | |
- | if (0 && $uStep < $gDeltaSec) { | + | |
- | die("The specified step ('$uStep') is less than the interval between two values ('$gDeltaSec')\n"); | + | |
- | } | + | |
- | else { | + | |
- | $gDeltaSec = $uStep; | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | print "Range starts from $gStartTime to $gEndTime\n"; | + | |
- | print "Use Interval of $gDeltaSec seconds\n"; | + | |
- | + | ||
- | # Adjust the graph time interval: | + | |
- | $gGraphStartSec = ($user_startsec) ? $user_startsec : $gStartSec; | + | |
- | $gGraphEndSec = ($user_endsec) ? $user_endsec : $gEndSec; | + | |
- | + | ||
- | # --------------------------------------------------------------------- | + | |
- | # Second pass: | + | |
- | # create the graphs | + | |
- | # --------------------------------------------------------------------- | + | |
- | print "Second Pass: create the graphs...\n"; | + | |
- | + | ||
- | # Variables for graphs creation: | + | |
- | my $gSarStat = ""; | + | |
- | my $gSkip = 0; | + | |
- | my $gFirstValue = 1; # skip the 1st value which is never significant with sar | + | |
- | my $gFirstTime; | + | |
- | + | ||
- | + | ||
- | my $nextblock = 0; | + | |
- | my %graphs = (); | + | |
- | my $dsheartbeat = 0; | + | |
- | my $dsstring = ""; | + | |
- | my $rras = ""; | + | |
- | my $graphname = ""; | + | |
- | my $dsname = ""; | + | |
- | my $keyname = ""; | + | |
- | my $rrdfile = ""; | + | |
- | my $cmd = ""; | + | |
- | my $title = ""; | + | |
- | my $vlabel = ""; | + | |
- | my $dsnames = ""; | + | |
- | my $ismultiple = 0; | + | |
- | my $startidx; | + | |
- | my $idx; | + | |
- | my $status; | + | |
- | + | ||
- | # To memorize the last graph update: | + | |
- | my $prev_rrdfile = ""; | + | |
- | my $prev_cursec = 0; | + | |
- | + | ||
- | ParseFile($uSarFile,0,0,0,\&GraphHeaderCallback,\&DataCallback); | + | |
- | + | ||
- | # Dump the former graph: | + | |
- | if ($gSarStat ne "") { | + | |
- | CreateImage(); | + | |
- | } | + | |
- | + | ||
- | # END | + | |
- | + | ||
- | sub HeaderCallback | + | |
- | { | + | |
- | my ($osname,$hostname) = @_; | + | |
- | + | ||
- | print "Analysing 'sar' output for a $osname System: $hostname\n"; | + | |
- | + | ||
- | $gOSName = $osname; | + | |
- | $gHostName = $hostname; | + | |
- | $gSarStats = $gAllSarStats{$osname}; | + | |
- | + | ||
- | return 0; | + | |
- | } | + | |
- | + | ||
- | sub StartTimeCallback | + | |
- | { | + | |
- | my ($startday,$startmonth,$startyear,$starttime,$startsec,$deltasec) = @_; | + | |
- | + | ||
- | if ($uVerbose) { print "StartTimeCallback: startday=$startday,startmonth=$startmonth,startyear=$startyear,starttime=$starttime,startsec=$startsec,deltasec=$deltasec\n"; } | + | |
- | $gStartTime = $starttime; | + | |
- | $gStartSec = $startsec; | + | |
- | $gStartDay = $startday; | + | |
- | $gStartMonth = $startmonth; | + | |
- | $gStartYear = $startyear; | + | |
- | $gDeltaSec = $deltasec; | + | |
- | + | ||
- | return 0; | + | |
- | } | + | |
- | + | ||
- | sub EndTimeCallback | + | |
- | { | + | |
- | my ($endday,$endmonth,$endyear,$endtime,$endsec,$mcount) = @_; | + | |
- | + | ||
- | if ($uVerbose) { print "EndTimeCallback: endday=$endday,endmonth=$endmonth,endyear=$endyear,endtime=$endtime,endsec=$endsec,mcount=$mcount\n"; } | + | |
- | $gEndDay = $endday; | + | |
- | $gEndMonth = $endmonth; | + | |
- | $gEndYear = $endyear; | + | |
- | $gEndTime = $endtime; | + | |
- | $gEndSec = $endsec; | + | |
- | $gMeasureCount = $mcount; | + | |
- | + | ||
- | print "$gMeasureCount measures detected\n"; | + | |
- | + | ||
- | return 1; # stop parsing for the 1st pass | + | |
- | } | + | |
- | + | ||
- | sub GraphHeaderCallback | + | |
- | { | + | |
- | my ($parts) = @_; | + | |
- | my $idx; | + | |
- | my $columns; | + | |
- | + | ||
- | if ($uVerbose) { print @{$parts},"\n"; } | + | |
- | + | ||
- | # Make sure columns have different names: | + | |
- | # for example, Solaris sar displays: | + | |
- | # 11:44:20 proc-sz ov inod-sz ov file-sz ov lock-sz | + | |
- | # here, the 1st ov will be renames proc-sz_ov, the second one inod-sz_ov | + | |
- | my %colnames = (); | + | |
- | for ( $idx = 1 ; $idx < scalar(@{$parts}) ; $idx++ ) { | + | |
- | if (exists $colnames{${$parts}[$idx]}) { | + | |
- | ${$parts}[$idx] = ${$parts}[$idx-1]."_".${$parts}[$idx]; | + | |
- | } | + | |
- | $colnames{${$parts}[$idx]} = 1; | + | |
- | } | + | |
- | + | ||
- | # Do we need to create a new graph ? | + | |
- | $columns = scalar(@{$parts}) - 1; | + | |
- | my $line = join(' ',@{$parts}[1..$columns]); | + | |
- | if ($gSarStat ne $line) { | + | |
- | # Dump the former graph: | + | |
- | if ($gSarStat ne "") { | + | |
- | CreateImage(); | + | |
- | } | + | |
- | + | ||
- | # Create a new graph: | + | |
- | $dsname = MakeDSName(${$parts}[1]); | + | |
- | if ($uGraphNameSpec ne "") { | + | |
- | if ($dsname eq $uGraphNameSpec) { | + | |
- | print "Analyzing data for $dsname\n"; | + | |
- | $gSkip = 0; | + | |
- | } | + | |
- | else { | + | |
- | print "Skip data for $dsname\n"; | + | |
- | $gSarStat = $line; | + | |
- | $gSkip = 1; | + | |
- | return; | + | |
- | } | + | |
- | } | + | |
- | else { | + | |
- | print "Analyzing data for $dsname\n"; | + | |
- | } | + | |
- | + | ||
- | # the DS Name depends on the keysize: | + | |
- | # Sanity check: | + | |
- | $keyname = ""; | + | |
- | if (!exists($gSarStats->{$dsname})) { | + | |
- | die("Unknown dsname: $dsname\n"); | + | |
- | } | + | |
- | if ($gSarStats->{$dsname}{'keysize'} > 1) { | + | |
- | $keyname = MakeDSName(${$parts}[2]); | + | |
- | $keyname = "-".$keyname; | + | |
- | } | + | |
- | + | ||
- | $dsheartbeat = 2 * $gDeltaSec; | + | |
- | $dsstring = ""; | + | |
- | $dsnames = ""; | + | |
- | + | ||
- | # Is it a single or a multiple graph ? | + | |
- | $ismultiple = $gSarStats->{$dsname}{'multiple'}; | + | |
- | $startidx = ($ismultiple) ? 2 : 1; | + | |
- | + | ||
- | for ( $idx = $startidx ; $idx < scalar(@{$parts}) ; $idx++ ) { | + | |
- | my $ds = MakeDSName(${$parts}[$idx]); | + | |
- | if ($dsstring ne "") { $dsnames .= ":"; } | + | |
- | $dsstring .= "DS:$ds:GAUGE:$dsheartbeat:0:U "; | + | |
- | $dsnames .= $ds; | + | |
- | } | + | |
- | + | ||
- | $rras = "RRA:AVERAGE:0.5:1:$gMeasureCount"; | + | |
- | + | ||
- | if (!$ismultiple) { | + | |
- | #$rrdfile = "$uRRDDir/$gHostName-$dsname$keyname.rrd"; | + | |
- | $rrdfile = GetNoMacroFilename($uRRDDir,$gHostName,"$dsname$keyname.rrd"); | + | |
- | CreateRRA($rrdfile,$dsstring,$rras); | + | |
- | $gFirstValue = 1; | + | |
- | } | + | |
- | else { | + | |
- | # we cannot create the RRD now, we must analyse the next lines: | + | |
- | if ($uVerbose) { print "we must analyse more lines before creating the RRD...\n"; } | + | |
- | $nextblock = 1; | + | |
- | %graphs = (); | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | $gSarStat = $line; | + | |
- | + | ||
- | return 0; | + | |
- | } | + | |
- | + | ||
- | sub DataCallback | + | |
- | { | + | |
- | my ($cursec,$parts) = @_; | + | |
- | my $idx; | + | |
- | + | ||
- | # Must skip the whole block in case of multiple graph: | + | |
- | if ($gFirstValue) { $gFirstValue = 0; $gFirstTime = ${$parts}[0]; return; } | + | |
- | if (${$parts}[0] eq $gFirstTime) { return; } | + | |
- | if ($gSkip) { return; } | + | |
- | + | ||
- | if ($uVerbose) { print @{$parts},"\n"; } | + | |
- | + | ||
- | # This is a measure line: we must update the graph | + | |
- | my $DATA = "$cursec:"; | + | |
- | my $startidx = ($ismultiple) ? 2 : 1; | + | |
- | for ( $idx = $startidx ; $idx < scalar(@{$parts}) ; $idx++ ) { | + | |
- | ${$parts}[$idx] =~ s/,/\./g; | + | |
- | if ($idx > $startidx) { $DATA.= ":"; } | + | |
- | + | ||
- | # Added from v2.5 (patch from Paolo Berva) | + | |
- | # HP-UX displays N/A for text-sz index | + | |
- | # 14:45:24 text-sz ov proc-sz ov inod-sz ov file-sz ov | + | |
- | # 14:50:24 N/A N/A 340/4096 0 1677/34816 0 5823/63498 0 | + | |
- | if (${$parts}[$idx] =~ /N\/A/) { | + | |
- | ${$parts}[$idx] = "U"; | + | |
- | } | + | |
- | + | ||
- | # Solaris may display values such as X/Y: | + | |
- | #11:44:20 proc-sz ov inod-sz ov file-sz ov lock-sz | + | |
- | #11:44:50 218/30000 0 83463/128248 0 3763/3763 0 0/0 | + | |
- | # We eliminate the /Y... (sorry !) | + | |
- | if (${$parts}[$idx] =~ /\//) { | + | |
- | ${$parts}[$idx] =~ s/(.*)\/.*/$1/; | + | |
- | } | + | |
- | + | ||
- | $DATA .= ${$parts}[$idx]; | + | |
- | } | + | |
- | + | ||
- | # Check if current line is the header for a multiple graph: | + | |
- | if ($nextblock) { | + | |
- | # if the column name is not in the @graphs array, add it | + | |
- | # create a new graph when array is full: | + | |
- | my $graphname = ${$parts}[1]; | + | |
- | + | ||
- | # fixed from v2.3: on Solaris some device names may contain / ! | + | |
- | # (tahnks to Ciro Iriarte) | + | |
- | # 00:00:01 device %busy avque r+w/s blks/s avwait avserv | + | |
- | # | + | |
- | # 00:01:01 10/md200 0 0.0 0 0 0.0 0.0 | + | |
- | # 10/md201 0 0.0 0 0 0.0 0.0 | + | |
- | $graphname =~ s/\//_/g; | + | |
- | + | ||
- | if (exists($graphs{$graphname})) { | + | |
- | $nextblock = 0; | + | |
- | } | + | |
- | else { | + | |
- | $graphs{$graphname} = 1; | + | |
- | #my $rrdfile = "$uRRDDir/$gHostName-$dsname$keyname-$graphname.rrd"; | + | |
- | my $rrdfile = GetNoMacroFilename($uRRDDir,$gHostName,"$dsname$keyname-$graphname.rrd"); | + | |
- | CreateRRA($rrdfile,$dsstring,$rras); | + | |
- | #$cmd = "$gRRDTool create $rrdfile -b $gStartSec -s $gDeltaSec $dsstring $rras"; | + | |
- | #MySystem($cmd); | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | # It may happen that the RRD file does not exist: | + | |
- | # in this example, nfs24 suddenly appears ! we ignore it: | + | |
- | #10:07:09 nfs1 0 0.0 0 0 0.0 0.0 | + | |
- | # nfs2 0 0.0 0 0 0.0 0.0 | + | |
- | # nfs5 0 0.0 0 0 0.0 0.3 | + | |
- | # nfs21 0 0.0 0 0 0.0 0.0 | + | |
- | #10:07:39 nfs1 0 0.0 0 0 0.0 0.0 | + | |
- | # nfs2 0 0.0 0 0 0.0 0.0 | + | |
- | # nfs5 0 0.0 0 0 0.0 0.0 | + | |
- | # nfs21 0 0.0 0 0 0.0 0.0 | + | |
- | # nfs24 0 0.0 0 0 0.0 0.0 | + | |
- | if ($ismultiple) { | + | |
- | # TODO : what about if $parts[1] contains some slashes ? | + | |
- | #$rrdfile = "$uRRDDir/$gHostName-$dsname$keyname-".${$parts}[1].".rrd"; | + | |
- | $rrdfile = GetNoMacroFilename($uRRDDir,$gHostName,"$dsname$keyname-".${$parts}[1].".rrd"); | + | |
- | if (! -f $rrdfile) { return 0; } | + | |
- | } | + | |
- | + | ||
- | # Fixed from v2.3: on Solaris, for the same period, they may be | + | |
- | # multiple lines for the same keys ! | + | |
- | # ssd109.t 0 0.0 1377 86815 0.0 0.0 | + | |
- | # ssd109.t 0 0.0 0 0 0.0 0.0 | + | |
- | if ($rrdfile eq $prev_rrdfile && $prev_cursec == $cursec) { return 0; } | + | |
- | $prev_rrdfile = $rrdfile; | + | |
- | $prev_cursec = $cursec; | + | |
- | + | ||
- | my $cmd = "$gRRDTool update $rrdfile -t $dsnames $DATA"; | + | |
- | MySystem($cmd); | + | |
- | + | ||
- | return 0; | + | |
- | } | + | |
- | + | ||
- | # | + | |
- | # File Parsing Encapsulation: | + | |
- | # | + | |
- | sub ParseFile | + | |
- | { | + | |
- | my ($fname,$hdrcback,$starttimecback,$endtimecback,$graphhdrcback,$datacback) = @_; | + | |
- | my ($ST_INIT, $ST_INBLOCK, $ST_NOBLOCK) = (1..10); | + | |
- | my $curstate = $ST_INIT; | + | |
- | my $hostname; | + | |
- | my $uname; | + | |
- | my $headerline = 0; | + | |
- | my $curheader = ""; | + | |
- | my $inblock = 0; | + | |
- | my $curdate; | + | |
- | my ($startmonth,$startday,$startyear); | + | |
- | my ($endmonth,$endday,$endyear); | + | |
- | my $starttime = ""; | + | |
- | my $endtime = ""; | + | |
- | my $secondtime = ""; | + | |
- | my $endtime_sent = 0; | + | |
- | my @parts; | + | |
- | my $lasttime; | + | |
- | my $line; | + | |
- | my $columns; # count of columns in the current block | + | |
- | my $measurecount = 0; | + | |
- | my $first_over = 1; | + | |
- | my $days_over_insec = 0; | + | |
- | + | ||
- | open(FD,"<$fname") or die("Could not open file '$fname' in read mode\n"); | + | |
- | while (<FD>) { | + | |
- | # Added from version 2.0c: | + | |
- | # eliminate DOS EOL markers | + | |
- | $_ =~ s/\r\n/\n/; | + | |
- | chomp; | + | |
- | + | ||
- | # always ignore empty lines, they cannot be considered as block delimitors: | + | |
- | if ($_ eq "") { next; } | + | |
- | + | ||
- | if ($uVerbose) { print "($curstate)line:$_\n"; } | + | |
- | + | ||
- | # Eliminate LINUX events: | + | |
- | # 08:28:54 LINUX RESTART | + | |
- | if ($_ =~ /LINUX/) { next; } | + | |
- | + | ||
- | # Added from v2.4 for SunOS 10 (thanks to Hans Förtsch): | + | |
- | # Eliminate unix events: | + | |
- | # 08:28:54 unix restarts | + | |
- | if ($_ =~ /unix/) { next; } | + | |
- | + | ||
- | if ($curstate == $ST_INIT) { | + | |
- | # Handle the header: | + | |
- | + | ||
- | # The first line should indicate the day: | + | |
- | # possible formats are: | + | |
- | # for Linux: | + | |
- | # Linux version (hostname) DD.MM.YYYY | + | |
- | # or: Linux version (hostname) DD/MM/YYYY | + | |
- | # or: Linux version (hostname) YYYY-MM-DD | + | |
- | # (new formats added from version 2.0b) | + | |
- | # (thanks to cverhoef@planet.nl) | + | |
- | # | + | |
- | # for SunOS (starting in v1.3): | + | |
- | # SunOS hostname version release arch MM/DD/YY | + | |
- | $curdate = $_; | + | |
- | $hostname = $_; | + | |
- | + | ||
- | if ($curdate =~ /^Linux/i) { | + | |
- | $curdate =~ s/^.*\(.*\)[^[:digit:]]*([[:digit:]]{2,4}[\.\/-][[:digit:]]{2}[\.\/-][[:digit:]]{2,4}).*$/$1/; | + | |
- | # Changed from v2.4c (thanks to Marek Cervenka) | + | |
- | #$hostname =~ s/^.*\((.*)\).*$/$1/; | + | |
- | $hostname =~ s/^.*?\((.*?)\).*$/$1/; | + | |
- | $uname = "linux"; | + | |
- | } | + | |
- | elsif ($curdate =~ /^SunOS/i) { | + | |
- | # Fixed from v2.4 (thanks to Hans Förtsch): on SunOS the year can be on 4 digits | + | |
- | #$curdate =~ s/.*([[:digit:]]{2}[\.\/][[:digit:]]{2}[\.\/][[:digit:]]{2}).*$/$1/; | + | |
- | $curdate =~ s/.*([[:digit:]]{2}[\.\/][[:digit:]]{2}[\.\/][[:digit:]]{2,4}).*$/$1/; | + | |
- | @parts = split(/ /,$hostname); | + | |
- | $hostname = $parts[1]; | + | |
- | $uname = "sunos"; | + | |
- | } | + | |
- | elsif ($curdate =~ /^HP-UX/i) { | + | |
- | # Added from v2.5 (thanks to Paolo Berva) | + | |
- | $curdate =~ s/.*([[:digit:]]{2}[\.\/][[:digit:]]{2}[\.\/][[:digit:]]{2,4}).*$/$1/; | + | |
- | @parts = split(/\s+/,$hostname); | + | |
- | $hostname = $parts[1]; | + | |
- | $uname = "hp-ux"; | + | |
- | } | + | |
- | else { | + | |
- | die("Unknown Operating System inside '$curdate'\n"); | + | |
- | } | + | |
- | + | ||
- | if ($uDateFormat eq "MDY") { | + | |
- | ($startmonth,$startday,$startyear) = split(/[\.\/-]/,$curdate); | + | |
- | } | + | |
- | elsif ($uDateFormat eq "DMY") { | + | |
- | ($startday,$startmonth,$startyear) = split(/[\.\/-]/,$curdate); | + | |
- | } | + | |
- | # New formats added from version 2.0b | + | |
- | elsif ($uDateFormat eq "YDM") { | + | |
- | ($startyear,$startday,$startmonth) = split(/[\.\/-]/,$curdate); | + | |
- | } | + | |
- | elsif ($uDateFormat eq "YMD") { | + | |
- | ($startyear,$startmonth,$startday) = split(/[\.\/-]/,$curdate); | + | |
- | } | + | |
- | else { | + | |
- | die("Unknown Date Format: '$uDateFormat' (supported format are: MDY, DMY, YDM and YMD)\n"); | + | |
- | } | + | |
- | ($endday,$endmonth,$endyear) = ($startday,$startmonth,$startyear); | + | |
- | $startmonth--; | + | |
- | + | ||
- | if ($hdrcback) { if ($hdrcback->($uname,$hostname)) { last; } } | + | |
- | + | ||
- | $curstate = $ST_NOBLOCK; | + | |
- | next; | + | |
- | } | + | |
- | + | ||
- | # Starting from here, we know the header has been analysed: | + | |
- | + | ||
- | # Multiple spaces are considered as a simple char: | + | |
- | $_ =~ s/([[:space:]]+)/ /g; | + | |
- | #print $_,"\n"; | + | |
- | @parts = split(/[[:space:]]/); | + | |
- | + | ||
- | if ($curstate == $ST_NOBLOCK) { | + | |
- | # We expect a block header: | + | |
- | BLOCKHDR: | + | |
- | + | ||
- | # Lines must start with a time specification: | + | |
- | #Average nfs1 0 0.0 0 0 0.0 0.0 | + | |
- | if ($parts[0] !~ /[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]/) { next; } | + | |
- | + | ||
- | $curstate = $ST_INBLOCK; | + | |
- | StripAMPM(\@parts); | + | |
- | + | ||
- | # Always memorize the last time: | + | |
- | # since version 2.0c, this assignment is made AFTER StripAMPM() call ! | + | |
- | $lasttime = $parts[0]; | + | |
- | + | ||
- | # Indicate a new graph may start here: | + | |
- | $curheader = $parts[1]; | + | |
- | $columns = scalar(@parts); | + | |
- | if ($graphhdrcback) { if ($graphhdrcback->(\@parts)) { last; } } | + | |
- | + | ||
- | next; # the starttime starts AFTER the header line | + | |
- | } | + | |
- | + | ||
- | if ($curstate == $ST_INBLOCK) { | + | |
- | StripAMPM(\@parts); | + | |
- | + | ||
- | # Ignore repeated headers such as : | + | |
- | #11:30:01 PM proc/s | + | |
- | #11:40:01 PM 5.04 | + | |
- | #11:50:01 PM 5.04 | + | |
- | # | + | |
- | #03:50:01 AM proc/s | + | |
- | #04:10:01 AM 18.05 | + | |
- | #04:20:01 AM 17.18 | + | |
- | if ($parts[1] eq $curheader) { | + | |
- | next; | + | |
- | } | + | |
- | + | ||
- | # Sometimes the time may be missing (Solaris...): | + | |
- | #09:55:39 device %busy avque r+w/s blks/s avwait avserv | + | |
- | # | + | |
- | #09:56:09 nfs1 0 0.0 0 0 0.0 0.0 | + | |
- | # nfs2 0 0.0 0 0 0.0 0.0 | + | |
- | # nfs5 0 0.0 0 2 0.0 3.7 | + | |
- | if ($parts[0] =~ /^[[:space:]]/ || !length($parts[0])) { | + | |
- | # put the last time back: | + | |
- | $parts[0] = $lasttime; | + | |
- | #unshift(@parts,$lasttime); | + | |
- | } | + | |
- | + | ||
- | # Determine the end of block: empty lines are enough on Linux, | + | |
- | # not on solaris: | + | |
- | # on Solaris, empty lines may not delimit blocks: | + | |
- | # blocks end with a line such as "^Average....", depending on the locale. | + | |
- | # Sometimes the new block starts with a new header line... | + | |
- | # | + | |
- | #09:55:39 proc-sz ov inod-sz ov file-sz ov lock-sz | + | |
- | #09:56:09 132/30000 0 91719/128248 0 1895/1895 0 0/0 | + | |
- | #16:35:39 127/30000 0 60543/128248 0 1881/1881 0 0/0 | + | |
- | # | + | |
- | #09:55:39 msg/s sema/s | + | |
- | #09:56:09 0.00 0.10 | + | |
- | #09:56:39 0.00 0.00 | + | |
- | + | ||
- | # we consider the end block when | + | |
- | # the line starts with a non-digit and non-space char | + | |
- | # or when the first column changed and is not a number and not a DS Name ! | + | |
- | + | ||
- | my $isdsname = MakeDSName($parts[1]); | + | |
- | + | ||
- | if ($parts[0] !~ /^[[:digit:][:space:]]/ || | + | |
- | ($parts[1] !~ /^[0-9]/ && exists($gSarStats->{$isdsname}))) { | + | |
- | if ($uVerbose) { print "End of block\n"; } | + | |
- | # Note: all block is supposed to end with an "Average" line | + | |
- | if (!$endtime_sent) { | + | |
- | #print $parts[0]," ",$parts[1]," ",$lasttime,"\n"; die; | + | |
- | $endtime_sent = 1; | + | |
- | $endmonth--; | + | |
- | my $endsec = timelocal(reverse(split(/:/,$endtime)),$endday,$endmonth,$endyear); | + | |
- | if ($endtimecback) { | + | |
- | if ($endtimecback->($endday,$endmonth,$endyear,$endtime,$endsec,$measurecount)) { last; } | + | |
- | } | + | |
- | $endmonth++; | + | |
- | } | + | |
- | $curstate = $ST_NOBLOCK; | + | |
- | $days_over_insec = 0; | + | |
- | $starttime = $secondtime = $endtime = $curheader = ""; | + | |
- | $first_over = 1; | + | |
- | + | ||
- | if ($parts[1] !~ /^[0-9]/ && exists($gSarStats->{$isdsname})) { | + | |
- | goto BLOCKHDR; | + | |
- | } | + | |
- | next; | + | |
- | } | + | |
- | + | ||
- | # Always memorize the last time: | + | |
- | $lasttime = $parts[0]; | + | |
- | + | ||
- | # Skip or add missing parameters to incomplete lines (see Solaris: | + | |
- | #11:44:20 runq-sz %runocc swpq-sz %swpocc | + | |
- | #11:44:50 2.5 7 | + | |
- | #11:45:20 | + | |
- | if (scalar(@parts) < 2) { next; } | + | |
- | + | ||
- | my $idx = scalar(@parts); | + | |
- | for ( ; $idx < $columns ; $idx++ ) { | + | |
- | $parts[$idx] = ""; | + | |
- | } | + | |
- | + | ||
- | # Is it the first row of real value ? | + | |
- | # we must memorize the starting date: | + | |
- | if ($starttime eq "") { $starttime = $parts[0]; } | + | |
- | + | ||
- | # If it is the second row of value, we get the date | + | |
- | # to compute the interval between two measures: | + | |
- | elsif ($secondtime eq "" && $starttime ne $parts[0]) { | + | |
- | # Note: we suppose the first and second lines belong to the same day ! | + | |
- | $secondtime = $parts[0]; | + | |
- | my $startsec = timelocal(reverse(split(/:/,$starttime)),$startday,$startmonth,$startyear); | + | |
- | my $secondsec = timelocal(reverse(split(/:/,$secondtime)),$startday,$startmonth,$startyear); | + | |
- | my $deltasec = $secondsec - $startsec; | + | |
- | if ($starttimecback) { | + | |
- | if ($starttimecback->($startday,$startmonth,$startyear,$starttime,$startsec,$deltasec)) { last; } | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | # We also need to compute the measure count: | + | |
- | # Ignore lines which have identical timestamp (think of multiple CPU !) | + | |
- | if ($endtime eq "" || $endtime ne $parts[0]) { | + | |
- | $measurecount++; | + | |
- | } | + | |
- | + | ||
- | # cross over midnight ? | + | |
- | if ($starttime gt $parts[0]) { | + | |
- | if ($first_over) { | + | |
- | $endmonth--; | + | |
- | if ($hdrcback) { print "cross over midnight($endyear,$endmonth,$endday)\n"; } | + | |
- | $endmonth++; | + | |
- | eval { | + | |
- | ($endyear,$endmonth,$endday) = Add_Delta_Days($endyear,$endmonth,$endday,1); | + | |
- | $days_over_insec += 86400; | + | |
- | }; | + | |
- | if ($@) { | + | |
- | die("Incorrect Date Format on the 1st line\nUse the -t option\n"); | + | |
- | } | + | |
- | $first_over = 0; | + | |
- | } | + | |
- | } | + | |
- | else { | + | |
- | $first_over = 1; | + | |
- | } | + | |
- | + | ||
- | $endtime = $parts[0]; | + | |
- | + | ||
- | # This is a measure line: | + | |
- | if ($datacback) { | + | |
- | my $cursec = timelocal(reverse(split(/:/,$parts[0])),$gStartDay,$gStartMonth,$gStartYear); | + | |
- | $cursec += $days_over_insec; | + | |
- | + | ||
- | if ($datacback->($cursec,\@parts)) { last; } | + | |
- | } | + | |
- | next; | + | |
- | } | + | |
- | + | ||
- | } | + | |
- | close(FD); | + | |
- | } | + | |
- | + | ||
- | sub CheckDate | + | |
- | { | + | |
- | my ($date,$label) = @_; | + | |
- | my @date; | + | |
- | my $sec; | + | |
- | my $month; | + | |
- | + | ||
- | @date = split(/[ :-]/,$date); | + | |
- | $sec = timelocal($date[5],$date[4],$date[3],$date[1],$date[0]-1,$date[2]); | + | |
- | #print "sec=$sec, startsec=$startsec, endsec=$endsec\n"; | + | |
- | if ($sec < $gStartSec) { | + | |
- | $month = $gStartMonth+1; | + | |
- | die("The $label date specified on the command line: $date, is anterior to the first date read in the file: $month-$gStartDay-$gStartYear $gStartTime\n"); | + | |
- | } | + | |
- | if ($sec > $gEndSec) { | + | |
- | $month = $gEndMonth+1; | + | |
- | die("The $label date specified on the command line: $date, is posterior to the last date read in the file: $month-$gEndDay-$gEndYear $gEndTime\n"); | + | |
- | } | + | |
- | + | ||
- | return $sec; | + | |
- | } | + | |
- | + | ||
- | sub MakeDSName | + | |
- | { | + | |
- | my ($name) = @_; | + | |
- | + | ||
- | $name =~ s/%/prct_/g; | + | |
- | $name =~ s/[^[:alnum:]]/_/g; | + | |
- | + | ||
- | return $name; | + | |
- | } | + | |
- | + | ||
- | sub Time24 | + | |
- | { | + | |
- | my ($parts) = @_; | + | |
- | + | ||
- | # Add 12 hours, but 12PM is 12 and 12AM is 00: | + | |
- | #print "p0=",${$parts}[0],"--"; | + | |
- | my @thetime = split(/:/,${$parts}[0]); | + | |
- | + | ||
- | if (${$parts}[1] eq "AM") { | + | |
- | if ($thetime[0] eq "12") { | + | |
- | ${$parts}[0] = "00:".$thetime[1].":".$thetime[2]; | + | |
- | } | + | |
- | } | + | |
- | else { | + | |
- | if ($thetime[0] ne "12") { | + | |
- | ${$parts}[0] = $thetime[0]+12; | + | |
- | ${$parts}[0] .= ":".$thetime[1].":".$thetime[2]; | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | if ($uVerbose) { print "Time24: new time is: ",${$parts}[0],"\n"; } | + | |
- | } | + | |
- | + | ||
- | sub StripAMPM | + | |
- | { | + | |
- | my ($p) = @_; | + | |
- | + | ||
- | # Eliminate the 2nd column if it is a AM or a PM: | + | |
- | #12:00:01 AM proc/s | + | |
- | #12:10:01 AM 17.15 | + | |
- | if ($p->[1] eq "AM" || $p->[1] eq "PM") { | + | |
- | Time24($p); | + | |
- | + | ||
- | # Eliminate the column: | + | |
- | my $part0 = $p->[0]; | + | |
- | shift(@{$p}); | + | |
- | shift(@{$p}); | + | |
- | unshift(@{$p},$part0); | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | sub CreateRRA | + | |
- | { | + | |
- | my ($rrdfile,$dsstring,$rras) = @_; | + | |
- | if ($uConcatenate) { | + | |
- | # Check if RRD already exists: | + | |
- | if (-e $rrdfile) { | + | |
- | # Get the first update of the current RRD: | + | |
- | $cmd = "$gRRDTool first $rrdfile"; | + | |
- | my ($firstsec) = `$cmd`; | + | |
- | chomp $firstsec; | + | |
- | if ($firstsec < 0) { | + | |
- | die("'$gRRDTool first $rrdfile' failed"); | + | |
- | } | + | |
- | + | ||
- | # Get the last update of the current RRD: | + | |
- | $cmd = "$gRRDTool last $rrdfile"; | + | |
- | my ($lastsec) = `$cmd`; | + | |
- | chomp $lastsec; | + | |
- | if ($lastsec < 0) { | + | |
- | die("'$gRRDTool last $rrdfile' failed"); | + | |
- | } | + | |
- | + | ||
- | # Check the new start if greater then the "last" data: | + | |
- | if ($lastsec >= $gStartSec) { | + | |
- | die("The last date in the current RRD file is newer than the start date ($lastsec/$gStartSec)"); | + | |
- | } | + | |
- | + | ||
- | # Compute the count of new rows : there is an adjustment | + | |
- | # to make if the previous and the new one are separated by | + | |
- | # more than one interval: | + | |
- | my $morerows = $gMeasureCount + int(($gStartSec - $lastsec) / $gDeltaSec + .5); | + | |
- | #print $gStartSec - $lastsec; | + | |
- | #die("rrdtool last: $lastsec startSec: $gStartSec, rras: $rras, measurecount=$gMeasureCount, morerows=$morerows"); | + | |
- | + | ||
- | # Change the real StartSec for the graph: | + | |
- | $gGraphStartSec = $firstsec; | + | |
- | + | ||
- | # From v2.4d, create resize.rrd in a writeable directory | + | |
- | ResizeRRDFile("0 GROW $morerows",$rrdfile); | + | |
- | + | ||
- | # Truncate if necessary: | + | |
- | if ($uTruncateSpec) { | + | |
- | # Get the last update of the current RRD: | + | |
- | $cmd = "$gRRDTool last $rrdfile"; | + | |
- | $lastsec = `$cmd`; | + | |
- | chomp $lastsec; | + | |
- | if ($lastsec < 0) { | + | |
- | die("'$gRRDTool last $rrdfile' failed"); | + | |
- | } | + | |
- | + | ||
- | # Compute the count of maximum rows: | + | |
- | my $rowcount = ($lastsec - $firstsec) / $gDeltaSec; # actual rows | + | |
- | $rowcount = int($rowcount + 0.5); | + | |
- | my $maxrows = ($uTruncateSpec * 24 * 3600) / $gDeltaSec; # max rows | + | |
- | $maxrows = int($maxrows + 0.5); | + | |
- | #print "firstsec=$firstsec, lastsec=$lastsec, rowcount=$rowcount, maxrows=$maxrows\n"; | + | |
- | + | ||
- | my $removerows; | + | |
- | if ($maxrows < $rowcount) { | + | |
- | $removerows = $rowcount - $maxrows; | + | |
- | + | ||
- | # From v2.4d, create resize.rrd in a writeable directory | + | |
- | ResizeRRDFile("0 SHRINK $removerows",$rrdfile); | + | |
- | + | ||
- | # Shift the start time for the graph: | + | |
- | $gGraphStartSec = $firstsec + $gDeltaSec * $removerows; | + | |
- | } | + | |
- | else { | + | |
- | print "Archive '$rrdfile' too small to be truncated\n"; | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | return; | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | $cmd = "$gRRDTool create $rrdfile -b $gStartSec -s $gDeltaSec $dsstring $rras"; | + | |
- | MySystem($cmd); | + | |
- | } | + | |
- | + | ||
- | sub CreateImage | + | |
- | { | + | |
- | my $title; | + | |
- | my $vlabel; | + | |
- | + | ||
- | if ($gSkip) { return; } | + | |
- | + | ||
- | $title = $gSarStats->{$dsname}{'title'}; | + | |
- | $vlabel = $gSarStats->{$dsname}{'unit'}; | + | |
- | + | ||
- | # $dsnames is col1:col2:.... | + | |
- | # we must apply $uGraphColSign and $uGraphColSpec here: | + | |
- | + | ||
- | my @ds = split(/:/,$dsnames); | + | |
- | my $defs = ""; | + | |
- | my $color; | + | |
- | my $imgfile; | + | |
- | COL: for ( my $idx = 0 ; $idx < scalar(@ds) ; $idx++ ) { | + | |
- | if ($uGraphNameSpec ne "") { | + | |
- | my $colname = $ds[$idx]; | + | |
- | if ($uGraphColSign eq "+") { | + | |
- | my $found = 0; | + | |
- | # Added from v2.2a : empty graph spec means "everything" | + | |
- | if (!scalar(@uGraphColSpec)) { | + | |
- | $found = 1; | + | |
- | } | + | |
- | else { | + | |
- | # the column must be listed | + | |
- | foreach my $col (@uGraphColSpec) { | + | |
- | if ($col eq $colname) { | + | |
- | $found = 1; | + | |
- | last; | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | if (!$found) { next COL; } | + | |
- | } | + | |
- | else { | + | |
- | # the column must not be listed | + | |
- | foreach my $col (@uGraphColSpec) { | + | |
- | if ($col eq $colname) { | + | |
- | next COL; | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | $color = $gColors[$idx % scalar(@gColors)]; | + | |
- | if ($ismultiple) { | + | |
- | $defs .= "DEF:v$idx=RRDFILE:".$ds[$idx].":AVERAGE LINE$gLineWidth:v$idx#$color:".$ds[$idx]." "; | + | |
- | } | + | |
- | else { | + | |
- | $defs .= "DEF:v$idx=$rrdfile:".$ds[$idx].":AVERAGE LINE$gLineWidth:v$idx#$color:".$ds[$idx]." "; | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | if ($defs eq "") { | + | |
- | die("No column selected to display the graph\n"); | + | |
- | } | + | |
- | + | ||
- | my $startdate = localtime($gGraphStartSec); | + | |
- | my $enddate = localtime($gGraphEndSec); | + | |
- | if ($gSpecialColon) { | + | |
- | $startdate =~ s/:/\\:/g; | + | |
- | $enddate =~ s/:/\\:/g; | + | |
- | } | + | |
- | #print $startdate,"\n"; | + | |
- | #print $enddate,"\n"; | + | |
- | my $legend = '"COMMENT:From '.$startdate.', To '.$enddate.'\\c" "COMMENT:\\n"'; | + | |
- | + | ||
- | if ($ismultiple) { | + | |
- | foreach $graphname (keys %graphs) { | + | |
- | if (!$uRRDOnly) { | + | |
- | #$imgfile = "$uImgDir/$gHostName-$dsname$keyname-$graphname.png"; | + | |
- | $imgfile = GetNoMacroFilename($uImgDir,$gHostName,"$dsname$keyname-$graphname.png"); | + | |
- | + | ||
- | # set the good RRD file name: | + | |
- | my $defs2 = $defs; | + | |
- | #$defs2 =~ s!RRDFILE!$uRRDDir/$gHostName-$dsname$keyname-$graphname.rrd!g; | + | |
- | my $realfname = GetNoMacroFilename($uRRDDir,$gHostName,"$dsname$keyname-$graphname.rrd"); | + | |
- | $defs2 =~ s!RRDFILE!$realfname!g; | + | |
- | + | ||
- | if ($keyname ne "") { | + | |
- | my $keyname2 = substr($keyname,1); # suppress the leading '-' | + | |
- | $title = $gSarStats->{$dsname}{'title'}{$keyname2}." for $graphname"; | + | |
- | } | + | |
- | else { | + | |
- | $title = $gSarStats->{$dsname}{'title'}." $graphname"; | + | |
- | } | + | |
- | + | ||
- | $cmd = "$gRRDTool graph $imgfile -t '$title' -s $gGraphStartSec -e $gGraphEndSec $uLogarithmic -S $gDeltaSec -v '$vlabel' -w $uImgWidth -h $uImgHeight -a PNG $legend $defs2 >/dev/null"; | + | |
- | MySystem($cmd); | + | |
- | WriteGraphXML($uXMLDir,$gHostName,$rrdfile,$title,$vlabel,$defs,$imgfile,$gDeltaSec,$uImgWidth,$uImgHeight,$uLogarithmic); | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | else { | + | |
- | if (!$uRRDOnly) { | + | |
- | #$imgfile = "$uImgDir/$gHostName-$dsname$keyname.png"; | + | |
- | $imgfile = GetNoMacroFilename($uImgDir,$gHostName,"$dsname$keyname.png"); | + | |
- | + | ||
- | $cmd = "$gRRDTool graph $imgfile -t '$title' -s $gGraphStartSec -e $gGraphEndSec $uLogarithmic -S $gDeltaSec -v '$vlabel' -w $uImgWidth -h $uImgHeight -a PNG $legend $defs >/dev/null"; | + | |
- | MySystem($cmd); | + | |
- | WriteGraphXML($uXMLDir,$gHostName,$rrdfile,$title,$vlabel,$defs,$imgfile,$gDeltaSec,$uImgWidth,$uImgHeight,$uLogarithmic); | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | # Added from v2.4: | + | |
- | sub GetNoMacroFilename | + | |
- | { | + | |
- | my ($dstdir,$hostname,$fname) = @_; | + | |
- | my $realdstdir; | + | |
- | my $realfname; | + | |
- | + | ||
- | # Handle the _HOST_ macro: | + | |
- | $realdstdir = $dstdir; | + | |
- | if ($dstdir =~ /_HOST_/) { | + | |
- | $realdstdir =~ s/_HOST_/$hostname/g; | + | |
- | $realfname = "$realdstdir/$fname"; | + | |
- | } | + | |
- | else { | + | |
- | $realfname = "$realdstdir/$hostname-$fname"; | + | |
- | } | + | |
- | if (! -d $realdstdir) { | + | |
- | mkdir($realdstdir) or die("Could not create '$realdstdir' directory"); | + | |
- | } | + | |
- | + | ||
- | return $realfname; | + | |
- | } | + | |
- | + | ||
- | sub MySystem | + | |
- | { | + | |
- | my ($cmd) = @_; | + | |
- | my $status; | + | |
- | + | ||
- | if ($uVerbose) { print $cmd,"\n"; } | + | |
- | if ($status = system($cmd)) { | + | |
- | if (!$uContinueOnErrors) { | + | |
- | die("Command '$cmd' failed with return code: $status\n"); | + | |
- | } | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | # This function has been added from v2.4 | + | |
- | # The purpose is to store in a XML file the graph characteristics to make possible | + | |
- | # a further graph regenration with rebuilding the RRD file. This XML file can be | + | |
- | # used with the additional sar2rrd-graph.pl script. | + | |
- | sub WriteGraphXML | + | |
- | { | + | |
- | my ($xmldir,$hostname,$rfile,$title,$vlabel,$defs,$ifile,$delta,$w,$h,$logmic) = @_; | + | |
- | + | ||
- | if ($uRRDOnly) { return; } | + | |
- | + | ||
- | my $fname = $rfile; | + | |
- | $fname =~ s/\.rrd$/\.xml/; | + | |
- | my @parts = split(/\//,$fname); | + | |
- | $fname = GetNoMacroFilename($xmldir,$hostname,$parts[-1]); | + | |
- | + | ||
- | open(XML,">$fname") or die("Could not open file '$fname' in write mode"); | + | |
- | print XML "<sar2rrd>\n"; | + | |
- | print XML "\t<title>$title</title>\n"; | + | |
- | print XML "\t<vlabel>$vlabel</vlabel>\n"; | + | |
- | print XML "\t<rrdfile>$rfile</rrdfile>\n"; | + | |
- | print XML "\t<imgfile>$ifile</imgfile>\n"; | + | |
- | print XML "\t<deltasec>$delta</deltasec>\n"; | + | |
- | print XML "\t<width>$w</width>\n"; | + | |
- | print XML "\t<height>$h</height>\n"; | + | |
- | print XML "\t<logarithmic>$logmic</logarithmic>\n"; | + | |
- | print XML "\t<defs>$defs</defs>\n"; | + | |
- | print XML "</sar2rrd>\n"; | + | |
- | close(XML); | + | |
- | } | + | |
- | + | ||
- | # Added for v2.4d | + | |
- | # Rename "resize.rrd" file into $rrdfile | + | |
- | sub ResizeRRDFile | + | |
- | { | + | |
- | my ($cmdargs,$rrdfile) = @_; | + | |
- | + | ||
- | if ($uVerbose) { print "ResizeRRDFile($rrdfile)\n"; } | + | |
- | + | ||
- | # From v2.4d, create resize.rrd in a writeable directory | + | |
- | my $absfile = File::Spec->rel2abs($rrdfile); | + | |
- | + | ||
- | my $curdir = Cwd::getcwd(); | + | |
- | my $newdir = GetNoMacroFilename($uRRDDir,$gHostName,""); | + | |
- | if (!chdir($newdir)) { | + | |
- | die("Could not change current directory to '$newdir'"); | + | |
- | } | + | |
- | + | ||
- | $cmd = "$gRRDTool resize $absfile $cmdargs"; | + | |
- | MySystem($cmd); | + | |
- | + | ||
- | # Delete the current file if it exists: | + | |
- | if (-e $rrdfile) { | + | |
- | if (!unlink($rrdfile)) { | + | |
- | die("Could not delete '$rrdfile'"); | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | # Rename 'resize.rrd' into $rrdfile: | + | |
- | if (!rename("resize.rrd",$absfile)) { | + | |
- | die("Could not rename 'resize.rrd' into '$absfile'"); | + | |
- | } | + | |
- | + | ||
- | # Restore the initial directory: | + | |
- | if (!chdir($curdir)) { | + | |
- | die("Could not change current directory to '$curdir'"); | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | sub Usage | + | |
- | { | + | |
- | print "Usage: $0\t[-?ovcCN] [-d rrd_dir] [-i img_dir] [-x xml_dir] [-W width] [-H height]\n"; | + | |
- | print "\t\t\t[-s start_date] [-e end_date] [-S step]\n"; | + | |
- | print "\t\t\t[-g graph_spec] [-t DMY|MDY|YDM|YMD] [-T days] -f sar_file\n"; | + | |
- | print "Options:\n"; | + | |
- | print "\t-? : this help\n"; | + | |
- | print "\t-v : verbose mode\n"; | + | |
- | print "\t-c : continue on error\n"; | + | |
- | print "\t-C : (concatenate) add sar file content in the current RRD archive\n"; | + | |
- | print "\t-N : (no file) generates RRD files only (no img and no xml files)\n"; | + | |
- | print "\t-o : use a logarithmic scale for Y scale\n"; | + | |
- | print "\t-d rrd_dir : directory where RRD files must be created\n"; | + | |
- | print "\t-i img_dir : directory where to place PNG images\n"; | + | |
- | print "\t-x xml_dir : directory where to place XML files (used by sar2rrd-graph.pl)\n"; | + | |
- | print "\t\tnote that these values can contain the _HOST_ macro\n"; | + | |
- | print "\t\tto create directories which depend on the hostname\n"; | + | |
- | print "\t-W width : images width (in pixels)\n"; | + | |
- | print "\t-H height : images height (in pixels)\n"; | + | |
- | print "\t-s start_date : start date (MM-DD-YYYY HH:MM:SS)\n"; | + | |
- | print "\t-e end_date : end date (MM-DD-YYYY HH:MM:SS)\n"; | + | |
- | print "\t-S step : interval (in seconds) between to values in the graph\n"; | + | |
- | print "\t-g graph_spec: by default all possible graphs are created\n"; | + | |
- | print "\t\tgraph_spec syntax is: data:(+|-)[column[,column...]]\n"; | + | |
- | print "\t\tthis creates only the graph and the specified columns\n"; | + | |
- | print "\t\tnote that graph and column name must contain '_' instead of '-'\n"; | + | |
- | print "\t-t MDY|DMY|YDM|YMD: indicates the format for the date displayed on the 1st output line\n"; | + | |
- | print "\t-T days : (truncate) maximum count of days stored in the RRA (only valid with the -C option)\n"; | + | |
- | print "\t-f sar_file : file to analyse - created by the 'sar -f ...' command\n"; | + | |
- | + | ||
- | exit(1); | + | |
- | } | + | |
- | + | ||
- | exit(0); | + | |
- | + | ||
- | # EOF | + | |
</code> | </code> | ||
Line 1689: | Line 94: | ||
my $DEFAULT_SENDER = 'k2patel@localhost.localdomain'; | my $DEFAULT_SENDER = 'k2patel@localhost.localdomain'; | ||
my $DEFAULT_RECIPIENT = 'k2patel@hotmail.com'; | my $DEFAULT_RECIPIENT = 'k2patel@hotmail.com'; | ||
+ | my $def_cc = 'sjohn@live.com'; | ||
my $stamp = `date -d 'now -1 days' +%b-%d-%y`; | my $stamp = `date -d 'now -1 days' +%b-%d-%y`; | ||
Line 1708: | Line 114: | ||
From => $o{f}, | From => $o{f}, | ||
To => $o{t}, | To => $o{t}, | ||
+ | cc => ($def_cc), | ||
Subject => $o{s}, | Subject => $o{s}, | ||
Data => "Hi", | Data => "Hi", | ||
Line 1720: | Line 127: | ||
$msg->send(); | $msg->send(); | ||
</code> | </code> | ||
- | |||
==== SE Linux ==== | ==== SE Linux ==== | ||
Following rules add exceptiong for selinux. | Following rules add exceptiong for selinux. |