#!/usr/local/bin/perl # # adsm-report -- To generate a periodic report on ADSM activity, through its accounting records. # # If accounting is turned on in the ADSM server ('Set ACCounting ON'), it will produce a # dsmaccnt.log file in the server directory for each transaction. The format of each records # is documented in the ADSM Administrator's Guide manual. # # The report file will contain the following sections: # - Sessions by nodename, listing number of objects and KB in all categories of ADSM session # types, with column sums. # - Summary statistics. # - By-day report, listing sessions, objects, sesion KB and data KB. # - By-user report, listing number of objects and KB in all categories of ADSM session # types, with timings. # # The reporting is adaptive, accommodating data widths as encountered, to generate neat # report column alignments. # # Output is to file "adsm-report-file". The report header will show the time reporting range. # The program checks for the pre-existence of the output file to prevent over-write of an # earlier version, which will be renamed if encountered. # # By convention we generate monthly reports: /usr/csg/ADSM-reports/ADSM-sessions.YYYYMMDD* . # # # ENVIRONMENT: Written for ADSM version 3 # (Product accounting changes little, so will likely work fine on later versions and # releases) # Written in Perl 4, and should work under Perl 5 as well. # # # INVOCATION: "adsm-report [fromyear YEAR] # [frommonth MONTH_NAME|MONTH_NUMBER] # [fromday DAY_OF_MONTH_NUMBER] # [fromtime 24_HR_TIME_VALUE] # [toyear YEAR] # [tomonth MONTH_NAME|MONTH_NUMBER] # [today DAY_OF_MONTH_NUMBER] # [totime 24_HR_TIME_VALUE] # [sortby name|user|size] # [FILE_NAME(S)]" # where: # fromyear year_value # limits data to that which starts in the given year, expressed as a # 4-digit number (1996). # frommonth month_name|month_number # limits data to that which starts in the given month, expressed # either as a month name (case insensitive; Jan, jan, JANUAry being # equivalent). # fromday day_of_month_number # limits data to that which begins on the given day of month. # fromtime 24_hr_time_value # limits data to that which starts at the given time of day, in # 24-hour-clock time. # Examples: 07 to specify just the hour; # 07:30 to specify hour & minutes; # 13:10:47 to specify hour, min, sec. # toyear year_value # limits data to that which ends in the given year, expressed as a # 4-digit number (1996). # tomonth month_name|month_number # limits data to that which ends in the given month, expressed either # as a month name (case insensitive; Dec, dec, DECEmber being # equivalent). # today day_of_month_number # limits data to that which ends on the given day of month. # totime 24_hr_time_value # limits data to that which ends at or before the given time of day, # in 24-hour-clock time. # Examples: 07 to specify just the hour; # 07:30 to specify hour & minutes; # 13:10:47 to specify hour, min, sec. # Note that if you omit trailing components of the time, maximum # values will default. So if you code '07', it will be taken as # '07:59:59'. Hence you should code '07:00:00' if you want the # "to" time to end exactly at that hour. # sortby name|user|size # permits reporting in order of user name, or by number of pages # printed (the default). # FILE_NAME(S) May specify the file or files which contain the ADSM accounting # data which is to be reported. If not specified, the program # checks for the input coming from Stdin, thus allowing you to pipe # the input to it, as perhaps from the zcat of a compressed file. # # If you code any timestamp level, you must code all the levels above it in order to be # specific. So, for example, if you code "fromtime", you need to code "fromday", "frommonth", # and "fromyear" as well. # # If no "from" or "to" time range is specified, all data will be reported. # # Set the internal $debug variable to show processing progress, if desired. # # # EXAMPLES: # # To produce a size-sorted report for the month of June, 1995, enter: # adsm-report fromyear 1995 frommonth june toyear 1995 tomonth june \ # ADSM_accounting.*199306* # # To report from compressed accounting files, use zcat to pipe the data: # zcat ADSM_accounting.* | adsm-report ... # # # NOTES: # # - ADSM accounting records contain exclusively character data. # - ADSM accounting record dates are stored in MM/DD/YYYY form, and time in hh:mm:ss form. # - Advanced maintenance levels will add fields to the end of the accounting record. # # # FUTURE ENHANCEMENTS: # # - Report each input file separately rather than combining all data into one report? # - Possibly allow selectivity by nodename or user. I didn't see much demand for this, so # have not include such logic. # # # HISTORY: # # 1999/06/14 Created by Richard Sims on personal time, for ADSM accounting data reporting. # 2007/01/05 The timelocal Perl library function has had historic defects, hopefully now # all corrected, such that compensating is no longer necessary. RBS # require "ctime.pl"; require "timelocal.pl"; #____________________________Governing definitions________________________________________ $report_filename = "adsm-report-file"; # Define the name of the output report. # If already existing, the original will be # preserved by renaming, then a new one produced. $debug = 0; # Set 1 to show progress. sub ascending { $a <=> $b; } # For doing an ascending numerical sort. # Filtration timestamps which may be overridden by specifying from- and to- time # values as invocation options. $from_datestamp = 0; $to_datestamp = 99999999999999; $GB = 1073741824; $BYTES_IN_GB = 1073741824; $MB = 1048576; $BYTES_IN_MB = 1048576; $KB = 1024; $BYTES_IN_KB = 1024; #___________________________________Preliminaries___________________________________________ # Get current date-time: ($timenow_wkday,$timenow_mon,$timenow_mday,$timenow_time,$timenow_est,$timenow_year) = split(' ',&ctime(time)); # Associative array for translating month name to a number. Month numbers are # defined as 2-digit literals to facilitate sorting and date comparision, as # in "19930617" as a month-day-year combo. %mon_to_num = ( "Jan","01", "Feb","02", "Mar","03", "Apr","04", "May","05", "Jun","06", "Jul","07", "Aug","08", "Sep","09", "Oct","10", "Nov","11", "Dec","12"); #printf("yyyymmdd hh:mm:ss = %s/%s/%s %s. Weekday = %s. Month name = %s.\n", # $timenow_year, $mon_to_num{$timenow_mon}, $timenow_mday, $timenow_time, # $timenow_wkday, $timenow_mon); #______________________________Process invocation options__________________________________ # Set defaults. $default_wkday = "any"; $arg_wkday = $default_wkday; $default_month = "any"; $arg_month = $default_month; $default_mday = "any"; $arg_mday = $default_mday; $default_year = "any"; $arg_year = $default_year; $default_fromyear = "any"; $arg_fromyear = $default_fromyear; $default_frommonth = "any"; $arg_frommonth = $default_frommonth; $default_fromday = "any"; $arg_fromday = $default_fromday; $default_fromhour = "any"; $arg_fromhour = $default_fromhour; $default_frommin = "any"; $arg_frommin = $default_frommin; $default_fromsec = "any"; $arg_fromsec = $default_fromsec; $default_toyear = "any"; $arg_toyear = $default_toyear; $default_tomonth = "any"; $arg_tomonth = $default_tomonth; $default_today = "any"; $arg_today = $default_today; $default_tohour = "any"; $arg_tohour = $default_tohour; $default_tomin = "any"; $arg_tomin = $default_tomin; $default_tosec = "any"; $arg_tosec = $default_tosec; $sortby_name = 0; $sortby_size = 1; # Default is to sort by size. # Obsolete operands - do not use: # [mon|month MONTH_NAME] # [wkday|weekday WEEKDAY_NAME] # [day DAY_OF_MONTH_NUMBER] # [year YEAR] # mon|month month_name # permits reporting printing which occurred on # a certain month name ("jan", "Feb", etc.). # Enter at least the first 3 characters of the # month name (in upper or lower case). # wkday|weekday weekday_name # permits reporting printing which occurred on # a certain day of the week ("mon", "Tue", etc.). # Enter at least the first 3 characters of the # day name (in upper or lower case). # day day_of_month_number # permits reporting printing which occurred on # a certain day number in the month ("3", "29", # etc.). # year year_number # permits reporting printing which occurred in # a certain year. $argc = $#ARGV + 1; # If no invocation operands, we will subsequently try Stdin as data source. while ($#ARGV > -1) { $word = shift(@ARGV); # Take the leftmost word. if (($argc == 1) && (($word =~ m|help|i) || ($word =~ m|-help|i))) { &Show_Usage_From_Prolog(); exit(0); } elsif (($word eq "wkday") || ($word eq "weekday")) { if ($#ARGV == -1) { printf("Keyword '%s' found, but no weekday name follows it.\n", $word); exit(1); } else { $_ = shift(@ARGV); # Take the next token as weekday name. ARG_WKDAY: { # Look for days of the week: /^[Mm][Oo][Nn].*/ && do {$arg_wkday = "Mon"; last ARG_WKDAY; }; /^[Tt][Uu][Ee].*/ && do {$arg_wkday = "Tue"; last ARG_WKDAY; }; /^[Ww][Ee][Dd].*/ && do {$arg_wkday = "Wed"; last ARG_WKDAY; }; /^[Tt][Hh][Uu].*/ && do {$arg_wkday = "Thu"; last ARG_WKDAY; }; /^[Ff][Rr][Ii].*/ && do {$arg_wkday = "Fri"; last ARG_WKDAY; }; /^[Ss][Aa][Tt].*/ && do {$arg_wkday = "Sat"; last ARG_WKDAY; }; /^[Ss][Uu][Nn].*/ && do {$arg_wkday = "Sun"; last ARG_WKDAY; }; # None of the above, so reject. printf("Weekday name value '%s' not recognized.\n",$_); exit(1); } } } # end of "weekday" parameter processing elsif (($word eq "mon") || ($word eq "month")) { if ($#ARGV == -1) { printf("Keyword '%s' found, but no month name follows it.\n", $word); exit(1); } else { $_ = shift(@ARGV); # Take the next token as month name. ARG_MONTH: { # Spell out the month names - looks better in header than 3-char abbrev. /^[Jj][Aa][Nn].*/ && do {$arg_month = "January"; last ARG_MONTH; }; /^[Ff][Ee][Bb].*/ && do {$arg_month = "February"; last ARG_MONTH; }; /^[Mm][Aa][Rr].*/ && do {$arg_month = "March"; last ARG_MONTH; }; /^[Aa][Pp][Rr].*/ && do {$arg_month = "April"; last ARG_MONTH; }; /^[Mm][Aa][Yy].*/ && do {$arg_month = "May"; last ARG_MONTH; }; /^[Jj][Uu][Nn].*/ && do {$arg_month = "June"; last ARG_MONTH; }; /^[Jj][Uu][Ll].*/ && do {$arg_month = "July"; last ARG_MONTH; }; /^[Aa][Uu][Gg].*/ && do {$arg_month = "August"; last ARG_MONTH; }; /^[Ss][Ee][Pp].*/ && do {$arg_month = "September"; last ARG_MONTH; }; /^[Oo][Cc][Tt].*/ && do {$arg_month = "October"; last ARG_MONTH; }; /^[Nn][Oo][Vv].*/ && do {$arg_month = "November"; last ARG_MONTH; }; /^[Dd][Ee][Cc].*/ && do {$arg_month = "December"; last ARG_MONTH; }; # None of the above, so reject. printf("Month name value '%s' not recognized.\n",$_); exit(1); } } } # end of month parameter processing elsif ($word eq "day") { if ($#ARGV == -1) { printf("Keyword '%s' found, but no day-of-month number follows it.\n", $word); exit(1); } else { $arg_mday = shift(@ARGV); # Take the next token as day number. if ($arg_mday =~ /\D/) { printf("Ahem...your given day of month value, '%s', is not numeric.\n", $arg_mday); exit 1; } elsif ( ($arg_mday < 1) || ($arg_mday > 31) ) { printf("Ahem...your given day of month value, '%s', is not in range 1-31.\n", $arg_mday); exit 1; } } } # end of "day" parameter processing elsif ($word eq "year") { if ($#ARGV == -1) { printf("Keyword '%s' found, but no year value follows it.\n", $word); exit(1); } else { $arg_year = shift(@ARGV); # Take the next token as year. if ($arg_year =~ /\D/) { printf("Ahem...your given year value, '%s', is not numeric.\n", $arg_year); exit 1; } elsif ( ($arg_year < 1111) || ($arg_year > 9999) ) { printf("Ahem...your given year value, '%s', is not a 4-digit number.\n", $arg_year); exit 1; } } } # end of "year" parameter processing elsif ($word eq "fromyear") { if ($#ARGV == -1) { printf("Keyword '%s' found, but no year value follows it.\n", $word); exit(1); } else { #_______________________________Evaluate "fromyear year_value"_____________________________________ $arg_fromyear = shift(@ARGV); # Take the next token as value. if (length($arg_fromyear) != 4) { printf(STDERR "fromyear value '%s' is not 4 digits - quitting\n", $arg_fromyear); exit(1); } # Value is 4 characters. See if digits. if ($arg_fromyear !~ /(\d\d\d\d)/) { printf(STDERR "fromyear value '%s' is not 4 digits - quitting\n", $arg_fromyear); exit(1); } # The value is 4 digits. Assure reasonable. if ($arg_fromyear > $timenow_year) { printf(STDERR "fromyear value '%s' is beyond this year, %s, which doesn't make sense - quitting\n", $arg_fromyear, $timenow_year); exit(1); } # (We don't check for the from-year being below any value, because it doesn't matter.) # The value seems reasonable. $from_specified = 1; } } # end of "fromyear" parameter processing elsif ($word eq "frommonth") { if ($#ARGV == -1) { printf("Keyword '%s' found, but no month value follows it.\n", $word); exit(1); } else { #___________________________Evaluate "frommonth month_value"_______________________________________ $arg_frommonth = shift(@ARGV); # Take the next token as value. if ($arg_frommonth =~ /\d+/) # Numeric month. { #_____________________________Evaluate numeric month value____________________________________ # Month value should then be 1 or 2 digits, from 1 - 12. if (($arg_frommonth < 1) || ($arg_frommonth > 12)) { printf(STDERR "Numeric frommonth value '%s' is not 1-12 - quitting\n", $arg_frommonth); exit(1); } # The month number is 1 - 12; convert to zero-basis month number, for later feeding to timelocal(). $arg_frommonthnum = $arg_frommonth - 1; } else # Alphabetic month. { #_________________________Evaluate alphabetic month value____________________________ # Convert month name to zero-basis month number, for later feeding to timelocal(). ARG_FROMMONTH: { $arg_frommonth =~ /^[Jj][Aa][Nn].*/ && do {$arg_frommonthnum = 0; last ARG_FROMMONTH; }; $arg_frommonth =~ /^[Ff][Ee][Bb].*/ && do {$arg_frommonthnum = 1; last ARG_FROMMONTH; }; $arg_frommonth =~ /^[Mm][Aa][Rr].*/ && do {$arg_frommonthnum = 2; last ARG_FROMMONTH; }; $arg_frommonth =~ /^[Aa][Pp][Rr].*/ && do {$arg_frommonthnum = 3; last ARG_FROMMONTH; }; $arg_frommonth =~ /^[Mm][Aa][Yy].*/ && do {$arg_frommonthnum = 4; last ARG_FROMMONTH; }; $arg_frommonth =~ /^[Jj][Uu][Nn].*/ && do {$arg_frommonthnum = 5; last ARG_FROMMONTH; }; $arg_frommonth =~ /^[Jj][Uu][Ll].*/ && do {$arg_frommonthnum = 6; last ARG_FROMMONTH; }; $arg_frommonth =~ /^[Aa][Uu][Gg].*/ && do {$arg_frommonthnum = 7; last ARG_FROMMONTH; }; $arg_frommonth =~ /^[Ss][Ee][Pp].*/ && do {$arg_frommonthnum = 8; last ARG_FROMMONTH; }; $arg_frommonth =~ /^[Oo][Cc][Tt].*/ && do {$arg_frommonthnum = 9; last ARG_FROMMONTH; }; $arg_frommonth =~ /^[Nn][Oo][Vv].*/ && do {$arg_frommonthnum = 10; last ARG_FROMMONTH; }; $arg_frommonth =~ /^[Dd][Ee][Cc].*/ && do {$arg_frommonthnum = 11; last ARG_FROMMONTH; }; # None of the above, so reject. printf("frommonth name value '%s' not recognized - needs to be like Jan, February, etc.\n", $arg_frommonth); exit(1); } } $from_specified = 1; } } # end of "frommonth" parameter processing elsif ($word eq "fromday") { if ($#ARGV == -1) { printf("Keyword '%s' found, but no day value follows it.\n", $word); exit(1); } else { #___________________________Evaluate "fromday day_value"_________________________________ $arg_fromday = shift(@ARGV); # Take the next token as value. if ($arg_fromday =~ /\d+/) # Numeric day. { #_________________________Evaluate numeric day value________________________________ # Day value should then be 1 or 2 digits, from 1 - 31. if (($arg_fromday < 1) || ($arg_fromday > 31)) { printf(STDERR "Numeric fromday value '%s' is not 1-31 - quitting\n", $arg_fromday); exit(1); } # The day number is 1 - 31. $arg_fromdaynum = $arg_fromday; } else # Alphabetic day. { printf("fromday value '%s' is not numeric (1-31) - quitting.\n", $arg_fromday); exit(1); } $from_specified = 1; } } # end of "fromday" parameter processing elsif ($word eq "fromtime") { if ($#ARGV == -1) { printf("Keyword '%s' found, but no time value follows it.\n", $word); exit(1); } else { #_______________________________Evaluate "fromtime time_value"_____________________________________ $arg_fromtime = shift(@ARGV); # Take the next token as value. ARG_FROMTIME: { $arg_fromtime =~ /(\d{1,2}):(\d{1,2}):(\d{1,2})/ && do { $arg_fromhour = $1; $arg_frommin = $2; $arg_fromsec = $3; last ARG_FROMTIME; }; $arg_fromtime =~ /(\d{1,2}):(\d{1,2})/ && do { $arg_fromhour = $1; $arg_frommin = $2; last ARG_FROMTIME; }; $arg_fromtime =~ /(\d{1,2})/ && do { $arg_fromhour = $1; last ARG_FROMTIME; }; # None of the above, so reject. printf("Fromtime value '%s' not recognized.\n", $arg_fromtime); exit(1); } # Assure validity: if ($arg_fromhour ne "any") { if ($arg_fromhour > 23) { printf(STDERR "fromtime hour value '%s' is invalid - quitting\n", $arg_fromhour); exit(1); } } if ($arg_frommin ne "any") { if ($arg_frommin > 59) { printf(STDERR "fromtime minute value '%s' is invalid - quitting\n", $arg_frommin); exit(1); } } if ($arg_fromsec ne "any") { if ($arg_fromsec > 59) { printf(STDERR "fromtime second value '%s' is invalid - quitting\n", $arg_fromsec); exit(1); } } #printf("Fromtime '%s' interpreted to %s:%s:%s.\n", $arg_fromtime, $arg_fromhour, $arg_frommin, $arg_fromsec); $from_specified = 1; } } # end of "fromtime" parameter processing elsif ($word eq "toyear") { if ($#ARGV == -1) { printf("Keyword '%s' found, but no year value follows it.\n", $word); exit(1); } else { #_______________________________Evaluate "toyear year_value"_____________________________________ $arg_toyear = shift(@ARGV); # Take the next token as value. if (length($arg_toyear) != 4) { printf(STDERR "toyear value '%s' is not 4 digits - quitting\n", $arg_toyear); exit(1); } # Value is 4 characters. See if digits. if ($arg_toyear !~ /(\d\d\d\d)/) { printf(STDERR "toyear value '%s' is not 4 digits - quitting\n", $arg_toyear); exit(1); } # The value is 4 digits. Assure reasonable. if ($arg_toyear > $timenow_year) { printf(STDERR "toyear value '%s' is beyond this year, %s, which doesn't make sense - quitting\n", $arg_toyear, $timenow_year); exit(1); } # The value seems reasonable. $to_specified = 1; } } # end of "toyear" parameter processing elsif ($word eq "tomonth") { if ($#ARGV == -1) { printf("Keyword '%s' found, but no month value follows it.\n", $word); exit(1); } else { #_______________________________Evaluate "tomonth month_value"_____________________________________ $arg_tomonth = shift(@ARGV); # Take the next token as value. if ($arg_tomonth =~ /\d+/) # Numeric month. { #_____________________________Evaluate numeric month value____________________________________ # Month value should then be 1 or 2 digits, to 1 - 12. if (($arg_tomonth < 1) || ($arg_tomonth > 12)) { printf(STDERR "Numeric tomonth value '%s' is not 1-12 - quitting\n", $arg_tomonth); exit(1); } # The month number is 1 - 12; convert to zero-basis month number, for later feeding to timelocal(). $arg_tomonthnum = $arg_tomonth - 1; } else # Alphabetic month. { #_________________________Evaluate alphabetic month value________________________________ # Convert month name to zero-basis month number, for later feeding to timelocal(). ARG_TOMONTH: { $arg_tomonth =~ /^[Jj][Aa][Nn].*/ && do {$arg_tomonthnum = 0; last ARG_TOMONTH; }; $arg_tomonth =~ /^[Ff][Ee][Bb].*/ && do {$arg_tomonthnum = 1; last ARG_TOMONTH; }; $arg_tomonth =~ /^[Mm][Aa][Rr].*/ && do {$arg_tomonthnum = 2; last ARG_TOMONTH; }; $arg_tomonth =~ /^[Aa][Pp][Rr].*/ && do {$arg_tomonthnum = 3; last ARG_TOMONTH; }; $arg_tomonth =~ /^[Mm][Aa][Yy].*/ && do {$arg_tomonthnum = 4; last ARG_TOMONTH; }; $arg_tomonth =~ /^[Jj][Uu][Nn].*/ && do {$arg_tomonthnum = 5; last ARG_TOMONTH; }; $arg_tomonth =~ /^[Jj][Uu][Ll].*/ && do {$arg_tomonthnum = 6; last ARG_TOMONTH; }; $arg_tomonth =~ /^[Aa][Uu][Gg].*/ && do {$arg_tomonthnum = 7; last ARG_TOMONTH; }; $arg_tomonth =~ /^[Ss][Ee][Pp].*/ && do {$arg_tomonthnum = 8; last ARG_TOMONTH; }; $arg_tomonth =~ /^[Oo][Cc][Tt].*/ && do {$arg_tomonthnum = 9; last ARG_TOMONTH; }; $arg_tomonth =~ /^[Nn][Oo][Vv].*/ && do {$arg_tomonthnum = 10; last ARG_TOMONTH; }; $arg_tomonth =~ /^[Dd][Ee][Cc].*/ && do {$arg_tomonthnum = 11; last ARG_TOMONTH; }; # None of the above, so reject. printf("tomonth name value '%s' not recognized - needs to be like Jan, February, etc.\n", $arg_tomonth); exit(1); } } $to_specified = 1; } } # end of "tomonth" parameter processing elsif ($word eq "today") { if ($#ARGV == -1) { printf("Keyword '%s' found, but no day value follows it.\n", $word); exit(1); } else { #_____________________________Evaluate "today day_value"_______________________________ $arg_today = shift(@ARGV); # Take the next token as value. if ($arg_today =~ /\d+/) # Numeric day. { #_________________________Evaluate numeric day value______________________________ # Day value should then be 1 or 2 digits, to 1 - 31. if (($arg_today < 1) || ($arg_today > 31)) { printf(STDERR "Numeric today value '%s' is not 1-31 - quitting\n", $arg_today); exit(1); } # The day number is 1 - 31. $arg_todaynum = $arg_today; } else # Alphabetic day. { printf("today value '%s' is not numeric (1-31) - quitting.\n", $arg_today); exit(1); } $to_specified = 1; } } # end of "today" parameter processing elsif ($word eq "totime") { if ($#ARGV == -1) { printf("Keyword '%s' found, but no time value follows it.\n", $word); exit(1); } else { #_______________________________Evaluate "totime time_value"_________________________________ $arg_totime = shift(@ARGV); # Take the next token as value. ARG_TOTIME: { $arg_totime =~ /(\d{1,2}):(\d{1,2}):(\d{1,2})/ && do { $arg_tohour = $1; $arg_tomin = $2; $arg_tosec = $3; last ARG_TOTIME; }; $arg_totime =~ /(\d{1,2}):(\d{1,2})/ && do { $arg_tohour = $1; $arg_tomin = $2; last ARG_TOTIME; }; $arg_totime =~ /(\d{1,2})/ && do { $arg_tohour = $1; last ARG_TOTIME; }; # None of the above, so reject. printf("Totime value '%s' not recognized.\n", $arg_totime); exit(1); } # Assure validity: if ($arg_tohour ne "any") { if ($arg_tohour > 23) { printf(STDERR "totime hour value '%s' is invalid - quitting\n", $arg_tohour); exit(1); } } if ($arg_tomin ne "any") { if ($arg_tomin > 59) { printf(STDERR "totime minute value '%s' is invalid - quitting\n", $arg_tomin); exit(1); } } if ($arg_tosec ne "any") { if ($arg_tosec > 59) { printf(STDERR "totime second value '%s' is invalid - quitting\n", $arg_tosec); exit(1); } } $to_specified = 1; } } # end of "totime" parameter processing elsif ($word eq "sortby") { if ($#ARGV == -1) { printf("Keyword '%s' found, but neither 'name' nor 'size' follows it.\n", $word); exit(1); } else { $arg_sortby = shift(@ARGV); # Take the next token as value. $sortby_name = 0; $sortby_size = 0; # Reset possibilities so that only # one may prevail. ARG_SORTBY: { $arg_sortby =~ /^[Nn][Aa][Mm][Ee]/ && do {$sortby_name = 1; last ARG_SORTBY; }; $arg_sortby =~ /^[Uu][Ss][Ee][Rr]/ && do {$sortby_name = 1; last ARG_SORTBY; }; $arg_sortby =~ /^[Ss][Ii][Zz][Ee]/ && do {$sortby_size = 1; last ARG_SORTBY; }; # None of the above, so reject. printf("Sortby value '%s' not recognized.\n",$arg_sortby); exit(1); } } } # end of "sortby" parameter processing else { #_______________________________Process apparent file name_________________________________ $arg_file = $word; # Take the token as file name. printf("Processing file name '%s'.\n", $arg_file); if (! &Validate_Filename($arg_file)) { printf("Quitting because of failed validation on file name '%s'.\n", $arg_file); exit(1); # Cannot continue. } push(@arg_files,$arg_file); # Accumulate file names for later processing, after all # arguments have been received and evaluated. } } #________________________Check consistency of some parameters___________________________ # If lower items in the time composite are specified, those above must be. if ($arg_frommonth ne "any") { if ($arg_fromyear eq "any") { printf(STDERR "frommonth specified, but not encompassing year - quitting.\n"); exit(1); } } if ($arg_fromday ne "any") { if ($arg_frommonth eq "any") { printf(STDERR "fromday specified, but not encompassing month - quitting.\n"); exit(1); } if ($arg_fromyear eq "any") { printf(STDERR "fromday specified, but not encompassing year - quitting.\n"); exit(1); } } if ($arg_fromhour ne "any") { if ($arg_fromday eq "any") { printf(STDERR "fromhour specified, but not encompassing day - quitting.\n"); exit(1); } if ($arg_frommonth eq "any") { printf(STDERR "fromhour specified, but not encompassing month - quitting.\n"); exit(1); } if ($arg_fromyear eq "any") { printf(STDERR "fromhour specified, but not encompassing year - quitting.\n"); exit(1); } } if ($arg_frommin ne "any") { if ($arg_fromhour eq "any") { printf(STDERR "frommin specified, but not encompassing hour - quitting.\n"); exit(1); } if ($arg_fromday eq "any") { printf(STDERR "frommin specified, but not encompassing day - quitting.\n"); exit(1); } if ($arg_frommonth eq "any") { printf(STDERR "frommin specified, but not encompassing month - quitting.\n"); exit(1); } if ($arg_fromyear eq "any") { printf(STDERR "frommin specified, but not encompassing year - quitting.\n"); exit(1); } } if ($arg_fromsec ne "any") { if ($arg_frommin eq "any") { printf(STDERR "fromsec specified, but not encompassing minute - quitting.\n"); exit(1); } if ($arg_fromhour eq "any") { printf(STDERR "fromsec specified, but not encompassing hour - quitting.\n"); exit(1); } if ($arg_fromday eq "any") { printf(STDERR "fromsec specified, but not encompassing day - quitting.\n"); exit(1); } if ($arg_frommonth eq "any") { printf(STDERR "fromsec specified, but not encompassing month - quitting.\n"); exit(1); } if ($arg_fromyear eq "any") { printf(STDERR "fromsec specified, but not encompassing year - quitting.\n"); exit(1); } } if ($arg_tomonth ne "any") { if ($arg_toyear eq "any") { printf(STDERR "tomonth specified, but not encompassing year - quitting.\n"); exit(1); } } if ($arg_today ne "any") { if ($arg_tomonth eq "any") { printf(STDERR "today specified, but not encompassing month - quitting.\n"); exit(1); } if ($arg_toyear eq "any") { printf(STDERR "today specified, but not encompassing year - quitting.\n"); exit(1); } } if ($arg_tohour ne "any") { if ($arg_today eq "any") { printf(STDERR "tohour specified, but not encompassing day - quitting.\n"); exit(1); } if ($arg_tomonth eq "any") { printf(STDERR "tohour specified, but not encompassing month - quitting.\n"); exit(1); } if ($arg_toyear eq "any") { printf(STDERR "tohour specified, but not encompassing year - quitting.\n"); exit(1); } } if ($arg_tomin ne "any") { if ($arg_tohour eq "any") { printf(STDERR "tomin specified, but not encompassing hour - quitting.\n"); exit(1); } if ($arg_today eq "any") { printf(STDERR "tomin specified, but not encompassing day - quitting.\n"); exit(1); } if ($arg_tomonth eq "any") { printf(STDERR "tomin specified, but not encompassing month - quitting.\n"); exit(1); } if ($arg_toyear eq "any") { printf(STDERR "tomin specified, but not encompassing year - quitting.\n"); exit(1); } } if ($arg_tosec ne "any") { if ($arg_tomin eq "any") { printf(STDERR "tosec specified, but not encompassing minute - quitting.\n"); exit(1); } if ($arg_tohour eq "any") { printf(STDERR "tosec specified, but not encompassing hour - quitting.\n"); exit(1); } if ($arg_today eq "any") { printf(STDERR "tosec specified, but not encompassing day - quitting.\n"); exit(1); } if ($arg_tomonth eq "any") { printf(STDERR "tosec specified, but not encompassing month - quitting.\n"); exit(1); } if ($arg_toyear eq "any") { printf(STDERR "tosec specified, but not encompassing year - quitting.\n"); exit(1); } } # If any from- time specified, construct composite to override default from_timestamp. if ($from_specified) { if ($debug) { printf("arg_fromsec = %s, arg_frommin = %s, arg_fromhour = %s, arg_fromday = %s,\n" ." arg_frommonth = %s, arg_fromyear = %s\n", $arg_fromsec, $arg_frommin, $arg_fromhour, $arg_fromday, $arg_frommonth, $arg_fromyear); } # Note that timelocal() has had historic defects, the worse in having the hour base-1 instead of base-0, such # that it was previously necessary to subtract 1 from the clock hour for its processing to come out right. # It appears that the function's authors finally have it right, so I removed the compensations. $from_secssince1970 = &timelocal($arg_fromsec eq "any" ? 0 : $arg_fromsec, # Base value: 0 $arg_frommin eq "any" ? 0 : $arg_frommin, # Base value: 0 $arg_fromhour eq "any" ? 0 : $arg_fromhour, # Base value: 0 $arg_fromday eq "any" ? 1 : $arg_fromday, # Base value: 1 $arg_frommonth eq "any" ? 0 : $arg_frommonthnum, # Base value: 0 $arg_fromyear eq "any" ? 0 : $arg_fromyear - 1900); # Base value: 1 $from_datestamp = &Secs_to_YYYYMMDDhhmmss($from_secssince1970); $from_datestamp_text = &Sub_Secs_to_DateTime($from_secssince1970); if ($debug) { printf("from_secssince1970 resolves to '%s'; interprets to %s\n", $from_secssince1970, $from_datestamp); } } else { $from_datestamp = "Any_Time"; $from_datestamp_text = "Any_Time"; } # If any to- time specified, construct composite to override default to_timestamp. if ($to_specified) { @days_in_month = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); if ($debug) { printf("arg_tosec = %s, arg_tomin = %s, arg_tohour = %s, arg_today = %s,\n" ." arg_tomonth = %s, arg_toyear = %s\n", $arg_tosec, $arg_tomin, $arg_tohour, $arg_today, $arg_tomonth, $arg_toyear); } # Note that the "to" values cannot be simply the maximum for the item: they must be # the maximum within the context of any chosen values. For example, if 'tomonth' # were specified by the invoker, it would be inappropriate to let the day number become # 31; it should instead be the maximum value for that month. Letting the day number be # 31 for February would result in timelocal returning a timestamp for March 3rd. if ($arg_toyear eq "any") { $to_year = $timenow_year - 1900; } # No year specified, so use this year. else { $to_year = $arg_toyear - 1900; } # Year specified, so use that. if ($arg_tomonth eq "any") { # No month specified: use this month if no year specified; else use max month. if ($arg_toyear eq "any") { $to_month = $timenow_mon; } else { $to_month = 11; } # Maximum month number (range 0-11). } else { $to_month = $arg_tomonthnum; } # Use specified month. if ($arg_today eq "any") { # No day specified: use the maximum for the above-chosen month. # To do this we have to accommodate leap year... if ($to_month == 1) # If February, 29 days on leap year, else 28... { if (&Is_Leapyear($to_year)) { $to_day = 29; } else { $to_day = 28; } } else { $to_day = $days_in_month[$to_month]; } # Else use last day in non-leapyear month. } else { $to_day = $arg_today; } # Day specified, so use it. # Note that timelocal() has had historic defects, the worse in having the hour base-1 instead of base-0, such # that it was previously necessary to subtract 1 from the clock hour for its processing to come out right. # It appears that the function's authors finally have it right, so I removed the compensations. if ($arg_tohour eq "any") { $to_hour = 23; } else { $to_hour = $arg_tohour; } if ($arg_tomin eq "any") { $to_min = 59; } else { $to_min = $arg_tomin; } if ($arg_tosec eq "any") { $to_sec = 59; } else { $to_sec = $arg_tosec; } # Note that timelocal() seems to have a defect in making the hour base-1 instead of base-0, necessitating # subtracting 1 from the clock hour for its processing to come out right. $to_secssince1970 = &timelocal($to_sec, $to_min, $to_hour, $to_day, $to_month, $to_year); $to_datestamp = &Secs_to_YYYYMMDDhhmmss($to_secssince1970); $to_datestamp_text = &Sub_Secs_to_DateTime($to_secssince1970); if ($debug) { printf("to_secssince1970 resolves to '%s'; interprets to %s", $to_secssince1970, $to_datestamp); } } else { $to_datestamp = "Any_Time"; $to_datestamp_text = "Any_Time"; } # Assure that to-time is not less than from-time: if ($to_datestamp < $from_datestamp) { printf(STDERR "to-time (%s) is less than from-time (%s) - quitting\n", $to_datestamp, $from_datestamp); exit(1); } #_________________With all args now in and validated, process per data source________________ printf("\nReporting ADSM sessions, from %s to %s\n", $from_datestamp_text, $to_datestamp_text); if (defined(@arg_files)) { #_______________________Process file names from command line___________________________ foreach $arg_file (@arg_files) { &Absorb_Data_Source($arg_file); } } else { #___________No names were not supplied on cmdline - see if Stdin has data____________ # We'll take either redirected or piped Stdin. (Stdin being the terminal is too # lame to be considered worthy.) if ((-f STDIN) || (-p STDIN)) { if (-f STDIN) { printf("\nTaking data from file redirected to Stdin...\n"); } if (-p STDIN) { printf("\nTaking data from pipe to Stdin...\n"); } #_____________________Use Stdin as the source of the data_____________________ $arg_file = "-"; # Getting to here means we got usable usernames via Stdin. # Variable $arg_file is now ready for later processing. $rc = &Absorb_Data_Source($arg_file); if ($rc == 0) { print(STDERR "No data supplied through Stdin - quitting.\n"); exit(1); } } else { print(STDERR "Data not supplied via named files or Stdin - quitting.\n"); exit(1); } } &Summarize_Data(); #______________________________Program done________________________________ exit 0; #================================= Subroutine Absorb_Data_Source ===================================== sub Absorb_Data_Source # # Subroutine to process data from one input file to output file. # # INVOCATION: $? = &Absorb_Data_Source(FileName); # # RETURNS: Number of data lines processed. # { #___________________________________________Local definitions________________________________________________________ local($arg_file) = scalar($_[0]); # Grab the given file name. if (! defined($first_time_through)) { $first_time_through = 1; } # Turned off after first time through. $nodename_column_title1 = " "; $nodename_column_title2 = "NODENAME"; $opsys_column_title1 = " "; $opsys_column_title2 = "OPSYS"; $sessions_column_title1 = " "; $sessions_column_title2 = "SESSIONS"; $backup_obj_column_title1 = "BACKUP"; $backup_obj_column_title2 = "OBJECTS"; $backup_kb_column_title1 = "BACKUP"; $backup_kb_column_title2 = "KB"; $restore_obj_column_title1 = "RESTORE"; $restore_obj_column_title2 = "OBJECTS"; $restore_kb_column_title1 = "RESTORE"; $restore_kb_column_title2 = "KB"; $archive_obj_column_title1 = "ARCHIVE"; $archive_obj_column_title2 = "OBJECTS"; $archive_kb_column_title1 = "ARCHIVE"; $archive_kb_column_title2 = "KB"; $retrieve_obj_column_title1 = "RETRIEVE"; $retrieve_obj_column_title2 = "OBJECTS"; $retrieve_kb_column_title1 = "RETRIEVE"; $retrieve_kb_column_title2 = "KB"; $hsmstore_obj_column_title1 = "HSM-IN"; $hsmstore_obj_column_title2 = "OBJECTS"; $hsmstore_kb_column_title1 = "HSM-IN"; $hsmstore_kb_column_title2 = "KB"; $hsmrecall_obj_column_title1 = "HSM-OUT"; $hsmrecall_obj_column_title2 = "OBJECTS"; $hsmrecall_kb_column_title1 = "HSM-OUT"; $hsmrecall_kb_column_title2 = "KB"; $nodeobjs_column_title1 = " "; $nodeobjs_column_title2 = "OBJECTS"; $nodesesskb_column_title1 = "SESSION"; $nodesesskb_column_title2 = "KB"; $nodedatakb_column_title1 = "DATA"; $nodedatakb_column_title2 = "KB"; $sesssecs_column_title1 = "SESSION"; $sesssecs_column_title2 = "SECONDS"; $idlesecs_column_title1 = "IDLEWAIT"; $idlesecs_column_title2 = "SECONDS"; $commsecs_column_title1 = "COMMWAIT"; $commsecs_column_title2 = "SECONDS"; $mediasecs_column_title1 = "MEDIAWAIT"; $mediasecs_column_title2 = "SECONDS"; # For aligned reporting later, we will be keeping track of the maximum width of any column element, and # here start with the column titles, determining which of the over-under pair is larger. $maxlen_nodename = ($l1 = length($nodename_column_title1)) > ($l2 = length($nodename_column_title2)) ? $l1 : $l2; $maxlen_opsyses = ($l1 = length($opsys_column_title1)) > ($l2 = length($opsys_column_title2)) ? $l1 : $l2; $maxlen_sessions = ($l1 = length($sessions_column_title1)) > ($l2 = length($sessions_column_title2)) ? $l1 : $l2; $maxlen_backup_objs = ($l1 = length($backup_obj_column_title1)) > ($l2 = length($backup_obj_column_title2)) ? $l1 : $l2; $maxlen_backup_kb = ($l1 = length($backup_kb_column_title1)) > ($l2 = length($backup_kb_column_title2)) ? $l1 : $l2; $maxlen_restore_objs = ($l1 = length($restore_obj_column_title1)) > ($l2 = length($restore_obj_column_title2)) ? $l1 : $l2; $maxlen_restore_kb = ($l1 = length($restore_kb_column_title1)) > ($l2 = length($restore_kb_column_title2)) ? $l1 : $l2; $maxlen_archive_objs = ($l1 = length($archive_obj_column_title1)) > ($l2 = length($archive_obj_column_title2)) ? $l1 : $l2; $maxlen_archive_kb = ($l1 = length($archive_kb_column_title1)) > ($l2 = length($archive_kb_column_title2)) ? $l1 : $l2; $maxlen_retrieve_objs = ($l1 = length($retrieve_obj_column_title1)) > ($l2 = length($retrieve_obj_column_title2)) ? $l1 : $l2; $maxlen_retrieve_kb = ($l1 = length($retrieve_kb_column_title1)) > ($l2 = length($retrieve_kb_column_title2)) ? $l1 : $l2; $maxlen_hsmstore_objs = ($l1 = length($hsmstore_obj_column_title1)) > ($l2 = length($hsmstore_obj_column_title2)) ? $l1 : $l2; $maxlen_hsmstore_kb = ($l1 = length($hsmstore_kb_column_title1)) > ($l2 = length($hsmstore_kb_column_title2)) ? $l1 : $l2; $maxlen_hsmrecall_objs = ($l1 = length($hsmrecall_obj_column_title1)) > ($l2 = length($hsmrecall_obj_column_title2)) ? $l1 : $l2; $maxlen_hsmrecall_kb = ($l1 = length($hsmrecall_kb_column_title1)) > ($l2 = length($hsmrecall_kb_column_title2)) ? $l1 : $l2; $maxlen_node_objs = ($l1 = length($nodeobjs_column_title1)) > ($l2 = length($nodeobjs_column_title2)) ? $l1 : $l2; $maxlen_nodesess_kb = ($l1 = length($nodesesskb_column_title1)) > ($l2 = length($nodesesskb_column_title2)) ? $l1 : $l2; $maxlen_nodedata_kb = ($l1 = length($nodedatakb_column_title1)) > ($l2 = length($nodedatakb_column_title2)) ? $l1 : $l2; $maxlen_sesssecs = ($l1 = length($sesssecs_column_title1)) > ($l2 = length($sesssecs_column_title2)) ? $l1 : $l2; $maxlen_idlesecs = ($l1 = length($idlesecs_column_title1)) > ($l2 = length($idlesecs_column_title2)) ? $l1 : $l2; $maxlen_commsecs = ($l1 = length($commsecs_column_title1)) > ($l2 = length($commsecs_column_title2)) ? $l1 : $l2; $maxlen_mediasecs = ($l1 = length($mediasecs_column_title1)) > ($l2 = length($mediasecs_column_title2)) ? $l1 : $l2; #_____________________________________________Preliminaries______________________________________________ # Before getting into time-consuming stuff which would be wasted if we could not proceed, perform some # dependency checks now. if ($first_time_through) { # Initial first and last year,month,day,hour,minute,second values, to detect extremes. $earliest_datestamp = 99999999999999; $latest_datestamp = 0; # Initialize other min, max values: $queue_stay_time_min = 9999999; $queue_stay_time_max = 0; $file_size_min = 9999999; $file_size_max = 0; if (length($arg_mday) < 2) { $arg_mday = " ".$arg_mday; } # Account for leading blank which actually # appears in record, for grep to find it. $first_time_through = 0; # Reset it. } #_______________________________Assimilate the accounting records________________________________________ # Refer to the ADSM System Administration Guide for layout. # In general, the record contains 30 double-quoted entries, separated by two spaces. $current_time = time(); # For detecting bogus time values. printf("Current timestamp is '%s' (%s)\n",$current_time,&Sub_Secs_to_DateTime($current_time)); print("Now assimilating ADSM accounting records...\n"); %all_printers = (); # Init. array null. $line_number = 0; # To count the lines. open(ACCTFILE,"<$arg_file") || die "Unable to open ADSM accounting file '$arg_file'"; while () { # The ADSM accounting file line will contain fields as specified below, # each separated by a comma. chomp($line = $_); # Take the line and remove line-end \n. $line_number++; # Count each line. #printf("'%s'\n",$line); # Diagnostic. if ($debug) { printf("%s\n", $line); } # Watch out for the data record version having changed from what this script # was programmed to handle: if ((substr($line,0,2) ne "5,") # What ADSMv2 had. && (substr($line,0,2) ne "3,")) # What ADSMv3 has. { printf("ADSM accounting record %u does not begin with a recognized product level identifier\n" ." indicating that the record layout version differs from what this script was\n" ." programmed to handle. Needs investigation. Quitting.\n\a", $line_number); exit(1); } # Parse the record... ($product_level, # Field 1: Product level $product_sublevel, # Field 2: Product sublevel $product_name, # Field 3: Product name, 'ADSM' $acctg_date, # Field 4: Date of accounting (mm/dd/yyyy). Has leading zeroes. $acctg_time, # Field 5: Time of accounting (hh:mm:ss). Has leading zeroes. $client_nodename, # Field 6: Node name of ADSM client $client_owner, # Field 7: Client owner name (Unix). Often empty, but otherwise will contain a # Unix username. $client_platform, # Field 8: Client platform (actually, opsys, like "AIX", "Linux"). $auth_method, # Field 9: Authentication method used $comm_method, # Field 10: Communication method used for the session $server_termination, # Field 11: Server termination indicator (0 = abnormal; 1 = normal) $archive_objs_inserted, # Field 12: Number of archive database objects inserted during session $archive_kb_inserted, # Field 13: Amount of archive files, in kilobytes, sent by the client to server $archive_objs_retrieved, # Field 14: Number of archive database objects retrieved during session $archive_kb_retrieved, # Field 15: Amount of archive files, in kilobytes, retrieved by the client $backup_objs_inserted, # Field 16: Number of backup database objects inserted during session $backup_kb_inserted, # Field 17: Amount of backup files, in kilobytes, sent by the client to server $backup_objs_retrieved, # Field 18: Number of backup database objects retrieved during session $backup_kb_retrieved, # Field 19: Amount of backup files, in kilobytes, retrieved by the client $session_kb, # Field 20: Amount of data, in kilobytes, communicated between client and server. # Includes both data volume and session management overhead. $session_secs, # Field 21: Duration of the session, in seconds $idlewait_secs, # Field 22: Amount of idle wait time during the session, in seconds $commwait_secs, # Field 23: Amount of communications wait time during session, in seconds $mediawait_secs, # Field 24: Amount of media wait time during session, in seconds $client_session_type, # Field 25: Client session type. A value of 1 or 4 indicates a general client # session; a value of 5 indicates a client session that is running a schedule. $hsm_objs_stored, # Field 26: Number of space-managed database objects inserted during the session $hsm_kb_stored, # Field 27: Amount of space-managed data, in kilobytes, sent by the client to the server $hsm_objs_recalled, # Field 28: Number of space-managed database objects retrieved during the session $hsm_kb_recalled # Field 29: Amount of space-managed data, in kilobytes, retrieved by space-managed # objects. ) = split(/,/,$line); if ($debug) # Turn on to sample data values. { printf("\nLine %u:\n '%s'\n", $line_number, $line); printf("Product level = '%s'; Product sublevel = '%s', Product name = '%s', Date of accounting = '%s',\n" ." Time of accounting = '%s', Node name of ADSM client = '%s', Client owner name = '%s'\n" ." Client platform = '%s', Authentication method used = '%s', Communication method used = '%s'\n" ." Server termina = '%s', Archived objs = '%s', Archived kb = '%s', Retrieved objs = '%s', Retrieved kb = '%s'\n" ." Backup objs = '%s', Backup kb = '%s', Restore objs = '%s', Restore kb = '%s'\n" ." HSM store objs = '%s', HSM store kb = '%s', HSM recall objs = '%s', HSM recall = '%s'\n" ." Session kb = '%s', Session secs = '%s', Idlewait secs = '%s', Commwait secs = '%s', Mediawait secs = '%s'\n", $product_level, $product_sublevel, $product_name, $acctg_date, $acctg_time, $client_nodename, $client_owner, $client_platform, $auth_method, $comm_method, $server_termination, $archive_objs_inserted, $archive_kb_inserted, $archive_objs_retrieved, $archive_kb_retrieved, $backup_objs_inserted, $backup_kb_inserted, $backup_objs_retrieved, $backup_kb_retrieved, $hsm_objs_stored, $hsm_kb_stored, $hsm_objs_recalled, $hsm_kb_recalled, $session_kb, $session_secs, $idlewait_secs, $commwait_secs, $mediawait_secs); } #_______________________________________Do some field validation______________________________________________ # The Product level field should be numeric: if ($product_level =~ /\D/) { printf("Record %u is bogus: 'Product level' value in line below is '%s', non-numeric. Skipping it.\n '%s'\n", $line_number, $product_level, $line); next; # Skip to next record. } #________________________________________Determine date range___________________________________________ # Compare datestamp against oldest-latest values in determining overall date-time range of all the data. # (Remember, the data may be from several files, and there is no surity of time sequence.) ($mm, $dd, $yyyy) = split(m|/|,$acctg_date); ($hr, $min, $sec) = split(m|:|,$acctg_time); $timestamp = $yyyy.$mm.$dd.$hr.$min.$sec; if ($timestamp < $earliest_datestamp) { $earliest_datestamp = $timestamp; if ($debug) { printf("New earliest_datestamp = %s, from '%s'.\n", $earliest_datestamp, $acctg_date); } } elsif ($timestamp > $latest_datestamp) { $latest_datestamp = $timestamp; if ($debug) { printf("New latest_datestamp = %s, from '%s'.\n", $latest_datestamp, $acctg_date); } } #_____________________________See if the data meets the selection criteria________________________________ if (($from_datestamp ne "Any_Time" && $timestamp < $from_datestamp) || ($to_datestamp ne "Any_Time" && $timestamp > $to_datestamp)) { if ($debug) { printf("Skipping record datestamped %s (%s %s) - out of time range: %s.\n", $timestamp, $acctg_date, $acctg_time, $timestamp < $from_datestamp ? "Time too low ($timestamp < $from_datestamp)" : $timestamp > $to_datestamp ? "Time too high ($timestamp > $to_datestamp)" : "??"); } next; # Skip to next record. } # The record meets selection criteria. if ($debug) { printf("Accepting record timestamped %s (%s %s) - within time range\n", $timestamp, $acctg_date, $acctg_time); } #__________________________________Capture data from the record_______________________________________ $total_sessions++; # Total number of sessions of all types. # (Note that not all sessions involve data in ADSM # storage pools.) if ($session_obj = $backup_objs_inserted + $backup_objs_retrieved + $archive_objs_inserted + $archive_objs_retrieved + $hsm_objs_stored + $hsm_objs_recalled) { $total_sessions_transferring_data++; } # Total number of sessions which transferred data; # that is, involved storage pools. if ($archive_objs_inserted && ($backup_objs_inserted == 0) && ($backup_objs_retrieved == 0) && ($archive_objs_retrieved == 0) && ($hsm_objs_stored == 0) && ($hsm_objs_recalled == 0)) { #___________________________________It was a pure Archive session_________________________________________ $archive_sessions_count++; if ($mediawait_secs) { $total_archives_directly_to_tape++; # Total number of Archive sessions whose data transfer went # directly to tape, skipping the archive disk storage pool. if ($debug) { print("Session is an Archive which went directly to tape.\n"); } } else { $total_archives_to_disk++; # Total number of Archive sessions whose data transfer went # to the archive disk storage pool. if ($debug) { print("Session is an Archive which went to disk.\n"); } } } if ($archive_objs_retrieved && ($backup_objs_inserted == 0) && ($backup_objs_retrieved == 0) && ($archive_objs_inserted == 0) && ($hsm_objs_stored == 0) && ($hsm_objs_recalled == 0)) { #___________________________________It was a pure Retrieve session_________________________________________ $retrieve_sessions_count++; if ($mediawait_secs) { $total_retrieves_directly_from_tape++; # Total number of Retrieve sessions whose data transfer came # directly from tape, skipping the archive disk storage pool. if ($debug) { print("Session is a Retrieve which came directly from tape.\n"); } } else { $total_retrieves_from_disk++; # Total number of Retrieve sessions whose data transfer came # from the archive disk storage pool. if ($debug) { print("Session is a Retrieve which came from disk.\n"); } } } $data_kb = $backup_kb_inserted + $backup_kb_retrieved + $archive_kb_inserted + $archive_kb_retrieved + $hsm_kb_stored + $hsm_kb_recalled; $sessions_by_opsys{$client_platform}++; # Total number of sessions by opsys. $sesskb_by_opsys{$client_platform} += $session_kb; # Total number of KB by client opsys. Note that # this includes overhead communication as well as # storage pool data. $datakb_by_opsys{$client_platform} += $data_kb; # Data KB by client opsys. $sessions_by_nodename{$client_nodename}++; # Total number of sessions by node. $session_secs_by_nodename{$client_nodename} += $session_secs; $idlewait_secs_by_nodename{$client_nodename} += $idlewait_secs; $commwait_secs_by_nodename{$client_nodename} += $commwait_secs; $mediawait_secs_by_nodename{$client_nodename} += $mediawait_secs; $session_kb_by_nodename{$client_nodename} += $session_kb; # Total number of KB by client nodename. Note that # this includes overhead communication as well as # storage pool data. $data_kb_by_nodename{$client_nodename} += $data_kb; # Data KB by client nodename. $sessions_per_day{"$yyyy"."$mm"."$dd"}++; $sesskb_per_day{"$yyyy"."$mm"."$dd"} += $session_kb; # Note that this includes overhead communication # as well as storage pool data. $datakb_per_day{"$yyyy"."$mm"."$dd"} += $data_kb; # $obj_per_day{"$yyyy"."$mm"."$dd"} += $session_obj; # We need to reexamine the maximum lengths seen for text strings as they come in, unlike # numerical values whose sum determines maximum overall length. if (($l = length($client_nodename)) > $maxlen_nodename) { $maxlen_nodename = $l; } if (($l = length($client_owner)) > $maxlen_username) { $maxlen_username = $l; } $opsyses_by_nodename{$client_nodename} = $client_platform; if (($l = length($client_platform)) > $maxlen_opsyses) { $maxlen_opsyses = $l; } # Capture values by nodename... $backup_obj_by_nodename{$client_nodename} += $backup_objs_inserted; $backup_kb_by_nodename{$client_nodename} += $backup_kb_inserted; $restored_obj_by_nodename{$client_nodename} += $backup_objs_retrieved; $restored_kb_by_nodename{$client_nodename} += $backup_kb_retrieved; $archived_obj_by_nodename{$client_nodename} += $archive_objs_inserted; $archived_kb_by_nodename{$client_nodename} += $archive_kb_inserted; $retrieved_obj_by_nodename{$client_nodename} += $archive_objs_retrieved; $retrieved_kb_by_nodename{$client_nodename} += $archive_kb_retrieved; $hsmstore_obj_by_nodename{$client_nodename} += $hsm_objs_stored; $hsmstore_kb_by_nodename{$client_nodename} += $hsm_kb_stored; $hsmrecall_obj_by_nodename{$client_nodename} += $hsm_objs_recalled; $hsmrecall_kb_by_nodename{$client_nodename} += $hsm_kb_recalled; # Capture values by username... if ($client_owner) # Omitting null entries... { # Note that HSM data transfer is implicit, so no username associated with it. $sessions_by_username{$client_owner}++; # Total number of sessions by user. $obj_by_username{$client_owner} += $session_obj; # Sum of all types of objects. $session_kb_by_username{$client_owner} += $session_kb; # Total number of KB by username. Note # that this includes overhead communication # as well as storage pool data. $data_kb_by_username{$client_owner} += $data_kb; # Data KB by client username. $session_secs_by_username{$client_owner} += $session_secs; $idlewait_secs_by_username{$client_owner} += $idlewait_secs; $commwait_secs_by_username{$client_owner} += $commwait_secs; $mediawait_secs_by_username{$client_owner} += $mediawait_secs; $backup_obj_by_username{$client_owner} += $backup_objs_inserted; $backup_kb_by_username{$client_owner} += $backup_kb_inserted; $restored_obj_by_username{$client_owner} += $backup_objs_retrieved; $restored_kb_by_username{$client_owner} += $backup_kb_retrieved; $archived_obj_by_username{$client_owner} += $archive_objs_inserted; $archived_kb_by_username{$client_owner} += $archive_kb_inserted; $retrieved_obj_by_username{$client_owner} += $archive_objs_retrieved; $retrieved_kb_by_username{$client_owner} += $archive_kb_retrieved; $hsmstore_obj_by_username{$client_owner} += $hsm_objs_stored; $hsmstore_kb_by_username{$client_owner} += $hsm_kb_stored; $hsmrecall_obj_by_username{$client_owner} += $hsm_objs_recalled; $hsmrecall_kb_by_username{$client_owner} += $hsm_kb_recalled; } # For column totals: if ($archive_objs_inserted) { $total_archive_operations++; } if ($archive_objs_retrieved) { $total_retrieve_operations++; } if ($backup_objs_inserted) { $total_backup_operations++; } if ($backup_objs_retrieved) { $total_restore_operations++; } if ($hsm_objs_stored) { $total_hsmstore_operations++; } if ($hsm_objs_recalled) { $total_hsmrecall_operations++; } $total_backup_obj += $backup_objs_inserted; $total_backup_kb += $backup_kb_inserted; $total_restore_obj += $backup_objs_retrieved; $total_restore_kb += $backup_kb_retrieved; $total_archive_obj += $archive_objs_inserted; $total_archive_kb += $archive_kb_inserted; $total_retrieve_obj += $archive_objs_retrieved; $total_retrieve_kb += $archive_kb_retrieved; $total_hsmstore_obj += $hsm_objs_stored; $total_hsmstore_kb += $hsm_kb_stored; $total_hsmrecall_obj += $hsm_objs_recalled; $total_hsmrecall_kb += $hsm_kb_recalled; $total_sess_kb += $session_kb; $total_data_kb += $data_kb; $obj_by_nodename{$client_nodename} += $session_obj; # Sum of all types of objects. # Examine job completion status... if ($server_termination == 0) { $sessions_completed_abnormally++; } elsif ($server_termination == 1) { $sessions_completed_normally++; } } # bottom of processing each record # Done absorbing all the data from this source. close(ACCTFILE); return $line_number; } # End of subroutine Absorb_Data_Source #================================= Subroutine Summarize_Data ===================================== sub Summarize_Data # # Subroutine to summarized all absorbed data to output file. # # INVOCATION: $? = &Summarize_Data(); # # RETURNS: 0 # { #____________________________________Preliminaries_____________________________________ #_________________Save any pre-existing output report file______________________ if (-e $report_filename) { printf("Output file '%s' already exists - will rename to preserve it...\n", $report_filename); if (-x "/usr/local/bin/bkurfile $report_filename") { # Our command to rename the file with a .YYYYMMDD suffix. system("/usr/local/bin/bkurfile $report_filename"); } else { system("/bin/mv $report_filename ${report_filename}.bak"); } } #___________________________________Normalize any overall data___________________________________________ # We should convert the date back to slash- and colon-separated form, for reporting: if ($debug) { printf("Earliest datestamp = %s; latest datestamp = %s.\n", $earliest_datestamp, $latest_datestamp); } ($yyyy, $mm, $dd, $hr, $min, $sec) = $earliest_datestamp =~ m|(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)|; $first_date = "$yyyy/$mm/$dd $hr:$min:$sec"; ($yyyy, $mm, $dd, $hr, $min, $sec) = $latest_datestamp =~ m|(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)|; $last_date = "$yyyy/$mm/$dd $hr:$min:$sec"; printf("\nData ranges from %s to %s\n", $first_date, $last_date); #_____________________________Figure final numerical column widths based upon totals_______________________________ # Format the numbers so as to base widths upon those constructs... $total_sessions_f = &Format_Number($total_sessions); $total_archive_obj_f = &Format_Number($total_archive_obj); $total_archive_kb_f = &Format_Number($total_archive_kb); $total_retrieve_obj_f = &Format_Number($total_retrieve_obj); $total_retrieve_kb_f = &Format_Number($total_retrieve_kb); $total_backup_obj_f = &Format_Number($total_backup_obj); $total_backup_kb_f = &Format_Number($total_backup_kb); $total_restore_obj_f = &Format_Number($total_restore_obj); $total_restore_kb_f = &Format_Number($total_restore_kb); $total_hsmstore_obj_f = &Format_Number($total_hsmstore_obj); $total_hsmstore_kb_f = &Format_Number($total_hsmstore_kb); $total_hsmrecall_obj_f = &Format_Number($total_hsmrecall_obj); $total_hsmrecall_kb_f = &Format_Number($total_hsmrecall_kb); $total_sess_kb_f = &Format_Number($total_sess_kb); $total_data_kb_f = &Format_Number($total_data_kb); # Remember to put numbers into double quotes when getting their length, else you get 0. if (($l = length("$total_sessions_f")) > $maxlen_sessions) { $maxlen_sessions = $l; } if (($l = length("$total_backup_obj_f")) > $maxlen_backup_objs) { $maxlen_backup_objs = $l; } if (($l = length("$total_backup_kb_f")) > $maxlen_backup_kb) { $maxlen_backup_kb = $l; } if (($l = length("$total_restore_obj_f")) > $maxlen_restore_objs) { $maxlen_restore_objs = $l; } if (($l = length("$total_restore_kb_f")) > $maxlen_restore_kb) { $maxlen_restore_kb = $l; } if (($l = length("$total_archive_obj_f")) > $maxlen_archive_objs) { $maxlen_archive_objs = $l; } if (($l = length("$total_archive_kb_f")) > $maxlen_archive_kb) { $maxlen_archive_kb = $l; } if (($l = length("$total_retrieve_obj_f")) > $maxlen_retrieve_objs) { $maxlen_retrieve_objs = $l; } if (($l = length("$total_retrieve_kb_f")) > $maxlen_retrieve_kb) { $maxlen_retrieve_kb = $l; } if (($l = length("$total_hsmstore_obj_f")) > $maxlen_hsmstore_objs) { $maxlen_hsmstore_objs = $l; } if (($l = length("$total_hsmstore_kb_f")) > $maxlen_hsmstore_kb) { $maxlen_hsmstore_kb = $l; } if (($l = length("$total_hsmrecall_obj_f")) > $maxlen_hsmrecall_objs) { $maxlen_hsmrecall_objs = $l; } if (($l = length("$total_hsmrecall_kb_f")) > $maxlen_hsmrecall_kb) { $maxlen_hsmrecall_kb = $l; } if (($l = length("$total_sess_kb_f")) > $maxlen_nodesess_kb) { $maxlen_nodesess_kb = $l; } if (($l = length("$total_data_kb_f")) > $maxlen_nodedata_kb) { $maxlen_nodedata_kb = $l; } # Accommodate the sums of the right-hand columns in determining the max width of those columns: $total_obj = $total_backup_obj + $total_restore_obj + $total_archive_obj + $total_retrieve_obj + $total_hsmstore_obj + $total_hsmrecall_obj; $total_obj_f = &Format_Number($total_obj); if (($l = length("$total_obj_f")) > $maxlen_node_objs) { $maxlen_node_objs = $l; } $total_data_kb = $total_backup_kb + $total_restore_kb + $total_archive_kb + $total_retrieve_kb + $total_hsmstore_kb + $total_hsmrecall_kb; if (($l = length("$total_sesssecs")) > $maxlen_sesssecs) { $maxlen_sesssecs = $l; } if (($l = length("$total_idlesecs")) > $maxlen_idlesecs) { $maxlen_idlesecs = $l; } if (($l = length("$total_commsecs")) > $maxlen_commsecs) { $maxlen_commsecs = $l; } if (($l = length("$total_mediasecs")) > $maxlen_mediasecs) { $maxlen_mediasecs = $l; } #______________________________________Report ADSM sessions - part 1____________________________________________ printf("\nNow creating report file '%s'...\n", $report_filename); open(REPORTOUT, ">$report_filename") || die "Unable to open $report_filename."; # Define printing characteristics for this report (the report format): # Note that Format Variables are set on a per-filehandle basis, so 'select' has to be used to set them... $save_filehandle = select(REPORTOUT); $= = 9999; # Number of lines per page. (I originally had this number '60' for # conventional reporting; but whereas we only view these reports online, # having the column headers repeatedly appear in a terminal window of # arbitrary size was pointless, so boosted the number high to have the # column headers appear only once for each report section. $- = 0; # Set number of lines remaining on page 0 to cause initial headers. $^ = REPORTOUT_TOP; # Report format for top-of-page. $~ = REPORTOUT; # Report format for body. select($save_filehandle); # Restore. $report_title .= sprintf("ADSM SESSIONS, FROM %s TO %s\n", $from_datestamp_text, $to_datestamp_text); $report_subtitle = "in accounting data ranging from $first_date through $last_date"; if ($debug) { printf("\$= lines per page = %d; \$- lines remaining on page = %d\n", $=, $-); } #__________Assign column headers # Assign values to the column header variables in the report formats. $report_colhdrs1 = sprintf("%-${maxlen_nodename}s" ." %${maxlen_backup_objs}s %${maxlen_backup_kb}s" ." %${maxlen_restore_objs}s %${maxlen_restore_kb}s" ." %${maxlen_archive_objs}s %${maxlen_archive_kb}s" ." %${maxlen_retrieve_objs}s %${maxlen_retrieve_kb}s" ." %${maxlen_hsmstore_objs}s %${maxlen_hsmstore_kb}s" ." %${maxlen_hsmrecall_objs}s %${maxlen_hsmrecall_kb}s", $nodename_column_title1, $backup_obj_column_title1, $backup_kb_column_title1, $restore_obj_column_title1, $restore_kb_column_title1, $archive_obj_column_title1, $archive_kb_column_title1, $retrieve_obj_column_title1, $retrieve_kb_column_title1, $hsmstore_obj_column_title1, $hsmstore_kb_column_title1, $hsmrecall_obj_column_title1, $hsmrecall_kb_column_title1); $report_colhdrs2 = sprintf("%-${maxlen_nodename}s" ." %${maxlen_backup_objs}s %${maxlen_backup_kb}s" ." %${maxlen_restore_objs}s %${maxlen_restore_kb}s" ." %${maxlen_archive_objs}s %${maxlen_archive_kb}s" ." %${maxlen_retrieve_objs}s %${maxlen_retrieve_kb}s" ." %${maxlen_hsmstore_objs}s %${maxlen_hsmstore_kb}s" ." %${maxlen_hsmrecall_objs}s %${maxlen_hsmrecall_kb}s", $nodename_column_title2, $backup_obj_column_title2, $backup_kb_column_title2, $restore_obj_column_title2, $restore_kb_column_title2, $archive_obj_column_title2, $archive_kb_column_title2, $retrieve_obj_column_title2, $retrieve_kb_column_title2, $hsmstore_obj_column_title2, $hsmstore_kb_column_title2, $hsmrecall_obj_column_title2, $hsmrecall_kb_column_title2); $report_colhdruls = sprintf("%s %s %s %s %s %s %s %s %s %s %s %s %s", '-'x${maxlen_nodename}, '-'x${maxlen_backup_objs}, '-'x${maxlen_backup_kb}, '-'x${maxlen_restore_objs}, '-'x${maxlen_restore_kb}, '-'x${maxlen_archive_objs}, '-'x${maxlen_archive_kb}, '-'x${maxlen_retrieve_objs}, '-'x${maxlen_retrieve_kb}, '-'x${maxlen_hsmstore_objs}, '-'x${maxlen_hsmstore_kb}, '-'x${maxlen_hsmrecall_objs}, '-'x${maxlen_hsmrecall_kb}); # The column headers are now set, and will print implicitly as we fill pages. #__________Report each node's usage foreach $nodename (sort keys(%sessions_by_nodename)) { # Fill in each column, separated from each preceding column by two spaces... $report_line = sprintf("%-${maxlen_nodename}s" ." %${maxlen_backup_objs}s %${maxlen_backup_kb}s" ." %${maxlen_restore_objs}s %${maxlen_restore_kb}s" ." %${maxlen_archive_objs}s %${maxlen_archive_kb}s" ." %${maxlen_retrieve_objs}s %${maxlen_retrieve_kb}s" ." %${maxlen_hsmstore_objs}s %${maxlen_hsmstore_kb}s" ." %${maxlen_hsmrecall_objs}s %${maxlen_hsmrecall_kb}s", $nodename, &Format_Number($backup_obj_by_nodename{$nodename}), &Format_Number($backup_kb_by_nodename{$nodename}), &Format_Number($restored_obj_by_nodename{$nodename}), &Format_Number($restored_kb_by_nodename{$nodename}), &Format_Number($archived_obj_by_nodename{$nodename}), &Format_Number($archived_kb_by_nodename{$nodename}), &Format_Number($retrieved_obj_by_nodename{$nodename}), &Format_Number($retrieved_kb_by_nodename{$nodename}), &Format_Number($hsmstore_obj_by_nodename{$nodename}), &Format_Number($hsmstore_kb_by_nodename{$nodename}), &Format_Number($hsmrecall_obj_by_nodename{$nodename}), &Format_Number($hsmrecall_kb_by_nodename{$nodename})); # Finally, write the line out: write(REPORTOUT); } # Bottom of reporting each node's usage. #__________Report column totals: # The initial spacer here must equal the number of characters reserved for the username. $report_line = $report_colhdruls; # Underline each column to indicate summation. write(REPORTOUT); $report_line = sprintf("%-${maxlen_nodename}s" ." %${maxlen_backup_objs}s %${maxlen_backup_kb}s" ." %${maxlen_restore_objs}s %${maxlen_restore_kb}s" ." %${maxlen_archive_objs}s %${maxlen_archive_kb}s" ." %${maxlen_retrieve_objs}s %${maxlen_retrieve_kb}s" ." %${maxlen_hsmstore_objs}s %${maxlen_hsmstore_kb}s" ." %${maxlen_hsmrecall_objs}s %${maxlen_hsmrecall_kb}s", "TOTALS", $total_backup_obj_f, $total_backup_kb_f, $total_restore_obj_f, $total_restore_kb_f, $total_archive_obj_f, $total_archive_kb_f, $total_retrieve_obj_f, $total_retrieve_kb_f, $total_hsmstore_obj_f, $total_hsmstore_kb_f, $total_hsmrecall_obj_f, $total_hsmrecall_kb_f); write(REPORTOUT); # Reinterpret large values, to make visually clear: # (Remember that the file size values are already in KB, so are 1024x.) $report_line = sprintf("%-${maxlen_nodename}s" ." %${maxlen_backup_objs}s %${maxlen_backup_kb}s" ." %${maxlen_restore_objs}s %${maxlen_restore_kb}s" ." %${maxlen_archive_objs}s %${maxlen_archive_kb}s" ." %${maxlen_retrieve_objs}s %${maxlen_retrieve_kb}s" ." %${maxlen_hsmstore_objs}s %${maxlen_hsmstore_kb}s" ." %${maxlen_hsmrecall_objs}s %${maxlen_hsmrecall_kb}s", "", "", $total_backup_kb > $MB ? sprintf("%.1f GB", $total_backup_kb / $MB) : $total_backup_kb > $KB ? sprintf("%.1f MB", $total_backup_kb / $KB) : $total_backup_kb." KB", "", $total_restore_kb > $MB ? sprintf("%.1f GB", $total_restore_kb / $MB) : $total_restore_kb > $KB ? sprintf("%.1f MB", $total_restore_kb / $KB) : $total_restore_kb." KB", "", $total_archive_kb > $MB ? sprintf("%.1f GB", $total_archive_kb / $MB) : $total_archive_kb > $KB ? sprintf("%.1f MB", $total_archive_kb / $KB) : $total_archive_kb." KB", "", $total_retrieve_kb > $MB ? sprintf("%.1f GB", $total_retrieve_kb / $MB) : $total_retrieve_kb > $KB ? sprintf("%.1f MB", $total_retrieve_kb / $KB) : $total_retrieve_kb." KB", "", $total_hsmstore_kb > $MB ? sprintf("%.1f GB", $total_hsmstore_kb / $MB) : $total_hsmstore_kb > $KB ? sprintf("%.1f MB", $total_hsmstore_kb / $KB) : $total_hsmstore_kb." KB", "", $total_hsmrecall_kb > $MB ? sprintf("%.1f GB", $total_hsmrecall_kb / $MB) : $total_hsmrecall_kb > $KB ? sprintf("%.1f MB", $total_hsmrecall_kb / $KB) : $total_hsmrecall_kb." KB" ); write(REPORTOUT); $report_line_len = length($report_line); # Reference width for later summary stats to observe. #______________________________________Report ADSM sessions - part 2____________________________________________ $report_line = " "; write(REPORTOUT); # Spacer after previous report. #__________Assign column headers # Assign values to the column header variables in the report formats. $report_colhdrs1 = sprintf("%-${maxlen_nodename}s %${maxlen_opsyses}s %${maxlen_sessions}s" ." %${maxlen_node_objs}s %${maxlen_nodesess_kb}s %${maxlen_nodedata_kb}s" ." %${maxlen_sesssecs}s %${maxlen_idlesecs}s %${maxlen_commsecs}s %${maxlen_mediasecs}s", $nodename_column_title1, $opsys_column_title1, $sessions_column_title1, $nodeobjs_column_title1, $nodesesskb_column_title1, $nodedatakb_column_title1, $sesssecs_column_title1, $idlesecs_column_title1, $commsecs_column_title1, $mediasecs_column_title1); $report_line = $report_colhdrs1; write(REPORTOUT); $report_colhdrs2 = sprintf("%-${maxlen_nodename}s %${maxlen_opsyses}s %${maxlen_sessions}s" ." %${maxlen_node_objs}s %${maxlen_nodesess_kb}s %${maxlen_nodedata_kb}s" ." %${maxlen_sesssecs}s %${maxlen_idlesecs}s %${maxlen_commsecs}s %${maxlen_mediasecs}s", $nodename_column_title2, $opsys_column_title2, $sessions_column_title2, $nodeobjs_column_title2, $nodesesskb_column_title2, $nodedatakb_column_title2, $sesssecs_column_title2, $idlesecs_column_title2, $commsecs_column_title2, $mediasecs_column_title2); $report_line = $report_colhdrs2; write(REPORTOUT); $report_colhdruls = sprintf("%s %s %s %s %s %s %s %s %s %s", '-'x${maxlen_nodename}, '-'x${maxlen_opsyses}, '-'x${maxlen_sessions}, '-'x${maxlen_node_objs}, '-'x${maxlen_nodesess_kb}, '-'x${maxlen_nodedata_kb}, '-'x${maxlen_sesssecs}, '-'x${maxlen_idlesecs}, '-'x${maxlen_commsecs}, '-'x${maxlen_mediasecs}); $report_line = $report_colhdruls; write(REPORTOUT); # The column headers are now set, and will print implicitly as we fill pages. #__________Report each node's usage foreach $nodename (sort keys(%sessions_by_nodename)) { # Fill in each column, separated from each preceding column by two spaces... $report_line = sprintf("%-${maxlen_nodename}s %-${maxlen_opsyses}s %${maxlen_sessions}u" ." %${maxlen_node_objs}s %${maxlen_nodesess_kb}s %${maxlen_nodedata_kb}s" ." %${maxlen_sesssecs}s %${maxlen_idlesecs}s %${maxlen_commsecs}s %${maxlen_mediasecs}s", $nodename, $opsyses_by_nodename{$nodename}, $sessions_by_nodename{$nodename}, &Format_Number($obj_by_nodename{$nodename}), &Format_Number($session_kb_by_nodename{$nodename}), &Format_Number($data_kb_by_nodename{$nodename}), $session_secs_by_nodename{$nodename}, $idlewait_secs_by_nodename{$nodename}, $commwait_secs_by_nodename{$nodename}, $mediawait_secs_by_nodename{$nodename}); # Finally, write the line out: write(REPORTOUT); } # Bottom of reporting each node's usage. #__________Report column totals: # The initial spacer here must equal the number of characters reserved for the username. $report_line = $report_colhdruls; # Underline each column to indicate summation. write(REPORTOUT); $report_line = sprintf("%-${maxlen_nodename}s %${maxlen_opsyses}s %${maxlen_sessions}s" ." %${maxlen_node_objs}s %${maxlen_nodesess_kb}s %${maxlen_nodesess_kb}s", "TOTALS", "", $total_sessions_f, $total_obj_f, $total_sess_kb_f, $total_data_kb_f); write(REPORTOUT); # Reinterpret large values, to make visually clear: # (Remember that the file size values are already in KB, so are 1024x.) $report_line = sprintf("%-${maxlen_nodename}s %${maxlen_opsyses}s %${maxlen_sessions}s" ." %${maxlen_node_objs}s %${maxlen_nodesess_kb}s %${maxlen_nodedata_kb}s", "", "", "", "", $total_data_kb > $MB ? sprintf("%.1f GB", $total_data_kb / $MB) : $total_data_kb > $KB ? sprintf("%.1f MB", $total_data_kb / $KB) : $total_data_kb." KB", $total_sess_kb > $MB ? sprintf("%.1f GB", $total_sess_kb / $MB) : $total_sess_kb > $KB ? sprintf("%.1f MB", $total_sess_kb / $KB) : $total_sess_kb." KB" ); write(REPORTOUT); #__________Some explanatory notes: # Nullify column headers that were used above, to prevent the following notes from inappropriately appearing under # column headers in a report page eject. #if ($debug) { printf("\$= lines per page = %d; \$- lines remaining on page = %d\n", $=, $-); } $report_colhdrs1 = ""; $report_colhdrs2 = ""; $report_colhdruls = ""; $report_line = " "; write(REPORTOUT); $report_line = " "; write(REPORTOUT); $report_line = "NOTES:"; write(REPORTOUT); $report_line = " "; write(REPORTOUT); $report_line = "Idle Wait time is the time that the server is waiting for the client to respond."; write(REPORTOUT); $report_line = " This is often seen in Backup operations, where the server has send the client a full list of the"; write(REPORTOUT); $report_line = " files that it has for the filespace being backed up, and the client is examining them against its"; write(REPORTOUT); $report_line = " file system content to see what is eligible for backup today."; write(REPORTOUT); $report_line = "Communications Wait time is the time that the client is waiting for the server to respond."; write(REPORTOUT); $report_line = "Media Wait time is the time spent awaiting completion of a tape mount, either because all drives"; write(REPORTOUT); $report_line = " are in use or because the desired volume is in use. (When collocation is by node, multiple sessions"; write(REPORTOUT); $report_line = " from one client node will want to write to the same output volume, and so secondary sessions will"; write(REPORTOUT); $report_line = " wait for the first session's use of it to end.)"; write(REPORTOUT); #__________________________________________________Present summary statistics________________________________________________ printf(REPORTOUT "\fSUMMARY STATISTICS:\n\n"); printf(REPORTOUT "Total number of sessions = %u; Number involving data in ADSM storage pools = %u.\n", $total_sessions, $total_sessions_transferring_data); @sorted_nodenames = sort(keys(%sessions_by_nodename)); $nodes_count = scalar(@sorted_nodenames); @sorted_usernames = sort(keys(%sessions_by_username)); $usernames_count = scalar(@sorted_usernames); @sorted_sessions_by_nodename = sort ascending(values(%sessions_by_nodename)); @sorted_opsyses = sort(keys(%sessions_by_opsys)); $opsyses_count = scalar(@sorted_opsyses); @sorted_sessions_by_opsys = sort ascending(values(%sessions_by_opsys)); @sorted_session_kb_by_nodename = sort ascending(values(%session_kb_by_nodename)); @sorted_data_kb_by_nodename = sort ascending(values(%data_kb_by_nodename)); @sorted_sesskb_by_opsys = sort ascending(values(%sesskb_by_opsys)); @sorted_datakb_by_opsys = sort ascending(values(%datakb_by_opsys)); @sorted_sesskb_per_day = sort ascending(values(%sesskb_per_day)); @sorted_datakb_per_day = sort ascending(values(%datakb_per_day)); @sorted_obj_per_day = sort ascending(values(%obj_per_day)); @sorted_sessions_per_day = sort ascending(values(%sessions_per_day)); # Report node-based statistics: if ($nodes_count) { print(REPORTOUT "\n"); $t = sprintf(" %u Nodes active during this period:", $nodes_count); $lt = length("$t"); print(REPORTOUT $t); # Indent the list. $l = $lt; foreach $element (@sorted_nodenames) { if (($l += ($maxlen_nodename + 1)) > $report_line_len) { printf(REPORTOUT "\n%${lt}s", " "); $l = $lt + length($element) + 1; } printf(REPORTOUT " %-${maxlen_nodename}s", $element); } print(REPORTOUT "\n"); } $n = scalar(@sorted_sessions_by_nodename); $rmdr = $n % 2; if ($rmdr) { $median = @sorted_sessions_by_nodename[($n/2)]; } else { $median = @sorted_sessions_by_nodename[(($n/2)-1)]; } printf(REPORTOUT " Average sessions/node = %.2f; median = %d\n", $total_sessions / $nodes_count, $median); # Report user-based statistics: if ($usernames_count) { print(REPORTOUT "\n"); $t = " Usernames active during this period:"; $lt = length("$t"); print(REPORTOUT $t); # Indent the list. $l = $lt; foreach $element (@sorted_usernames) { if (($l += ($maxlen_username + 1)) > $report_line_len) { printf(REPORTOUT "\n%${lt}s", " "); $l = $lt + length($element) + 1; } printf(REPORTOUT " %-${maxlen_username}s", $element); } print(REPORTOUT "\n"); } # Report opsys-based statistics: printf(REPORTOUT "\n %u Operating systems active during this period: %s\n", $opsyses_count, "@sorted_opsyses"); $n = scalar(@sorted_sessions_by_opsys); $rmdr = $n % 2; if ($rmdr) { $median = @sorted_sessions_by_opsys[($n/2)]; } else { $median = @sorted_sessions_by_opsys[(($n/2)-1)]; } printf(REPORTOUT " Average sessions/opsys = %s; median = %s\n", &Format_Number(sprintf("%.2f",($total_sessions / $opsyses_count))), &Format_Number($median)); # Report KB-based statistics, by overall session communication volume: print(REPORTOUT "\n"); $n = scalar(@sorted_session_kb_by_nodename); $rmdr = $n % 2; if ($rmdr) { $median = @sorted_session_kb_by_nodename[($n/2)]; } else { $median = @sorted_session_kb_by_nodename[(($n/2)-1)]; } $avg_session_kb_by_nodename = sprintf("%.2f", $total_sess_kb / $nodes_count); $maxlen_kb_f = $maxlen_nodesess_kb + 3; # Width of largest KB values plus ".nn" decimal. printf(REPORTOUT " Average session KB/nodename = %${maxlen_kb_f}s (%9s); median = %${maxlen_nodesess_kb}s (%9s)\n", &Format_Number($avg_session_kb_by_nodename), $avg_session_kb_by_nodename > $MB ? sprintf("%.1f GB", $avg_session_kb_by_nodename / $MB) : $avg_session_kb_by_nodename > $KB ? sprintf("%.1f MB", $avg_session_kb_by_nodename / $KB) : $avg_session_kb_by_nodename." KB", &Format_Number($median), $median > $MB ? sprintf("%.1f GB", $median / $MB) : $median > $KB ? sprintf("%.1f MB", $median / $KB) : $median." KB" ); # Report KB-based statistics, by volume of data transmitted: $n = scalar(@sorted_data_kb_by_nodename); $rmdr = $n % 2; if ($rmdr) { $median = @sorted_data_kb_by_nodename[($n/2)]; } else { $median = @sorted_data_kb_by_nodename[(($n/2)-1)]; } $avg_data_kb_by_nodename = sprintf("%.2f", $total_data_kb / $nodes_count); printf(REPORTOUT " Average data KB/nodename = %${maxlen_kb_f}s (%9s); median = %${maxlen_nodesess_kb}s (%9s)\n", &Format_Number($avg_data_kb_by_nodename), $avg_data_kb_by_nodename > $MB ? sprintf("%.1f GB", $avg_data_kb_by_nodename / $MB) : $avg_data_kb_by_nodename > $KB ? sprintf("%.1f MB", $avg_data_kb_by_nodename / $KB) : $avg_data_kb_by_nodename." KB", &Format_Number($median), $median > $MB ? sprintf("%.1f GB", $median / $MB) : $median > $KB ? sprintf("%.1f MB", $median / $KB) : $median." KB" ); $n = scalar(@sorted_sesskb_by_opsys); $rmdr = $n % 2; if ($rmdr) { $median = @sorted_sesskb_by_opsys[($n/2)]; } else { $median = @sorted_sesskb_by_opsys[(($n/2)-1)]; } $avg_sesskb_by_opsys = sprintf("%.2f", $total_sess_kb / $opsyses_count); printf(REPORTOUT " Average session KB/opsys = %${maxlen_kb_f}s (%9s); median = %${maxlen_nodesess_kb}s (%9s)\n", &Format_Number($avg_sesskb_by_opsys), $avg_sesskb_by_opsys > $MB ? sprintf("%.1f GB", $avg_sesskb_by_opsys / $MB) : $avg_sesskb_by_opsys > $KB ? sprintf("%.1f MB", $avg_sesskb_by_opsys / $KB) : $avg_sesskb_by_opsys." KB", &Format_Number($median), $median > $MB ? sprintf("%.1f GB", $median / $MB) : $median > $KB ? sprintf("%.1f MB", $median / $KB) : $median." KB" ); $n = scalar(@sorted_datakb_by_opsys); $rmdr = $n % 2; if ($rmdr) { $median = @sorted_datakb_by_opsys[($n/2)]; } else { $median = @sorted_datakb_by_opsys[(($n/2)-1)]; } $avg_datakb_by_opsys = sprintf("%.2f", $total_data_kb / $opsyses_count); printf(REPORTOUT " Average data KB/opsys = %${maxlen_kb_f}s (%9s); median = %${maxlen_nodesess_kb}s (%9s)\n", &Format_Number($avg_datakb_by_opsys), $avg_datakb_by_opsys > $MB ? sprintf("%.1f GB", $avg_datakb_by_opsys / $MB) : $avg_datakb_by_opsys > $KB ? sprintf("%.1f MB", $avg_datakb_by_opsys / $KB) : $avg_datakb_by_opsys." KB", &Format_Number($median), $median > $MB ? sprintf("%.1f GB", $median / $MB) : $median > $KB ? sprintf("%.1f MB", $median / $KB) : $median." KB" ); print(REPORTOUT "\n"); # Separate from prior collective. printf(REPORTOUT " Number of sessions completed: normally = %s; abnormally = %s\n", &Format_Number($sessions_completed_normally), &Format_Number($sessions_completed_abnormally)); if ($archive_sessions_count > $retrieve_sessions_count) { $l = length("$archive_sessions_count"); } else { $l = length("$retrieve_sessions_count"); } printf(REPORTOUT " Number of Archive sessions which went: to disk = %${l}u; directly to tape = %${l}u\n", $total_archives_to_disk, $total_archives_directly_to_tape); printf(REPORTOUT " Number of Retrieve sessions which came: from disk = %${l}u; directly from tape = %${l}u\n", $total_retrieves_from_disk, $total_retrieves_directly_from_tape); #______________________________________________By-day reports_________________________________________________________ # Begin on a separate page. For column widths we will use the $maxlen_nodesess_kb value, which is the largest # of all previously reported, and works best here. printf(REPORTOUT " BY-DAY REPORTS\n\n"); printf(REPORTOUT "%-10s %${maxlen_sessions}s %${maxlen_node_objs}s %${maxlen_nodesess_kb}s" ." %${maxlen_nodesess_kb}s\n\n", "DAY", "SESSIONS", "OBJECTS", "SESSION KB", "DATA KB"); $l_sess = $maxlen_sessions + 3; # Integer plus .2f . $l_objs = $maxlen_node_objs + 3; # Integer plus .2f . $l_kb = $maxlen_nodesess_kb + 3; # Integer plus .2f . # Note that all the *_per_day arrays have the same keys, so our choice to control this loop is arbitrary. foreach $day (sort(keys(%sessions_per_day))) { # Remember that the day key is of form YYYYMMDD. printf(REPORTOUT "%-10s %${maxlen_sessions}s %${maxlen_node_objs}s %${maxlen_nodesess_kb}s (%s) %${maxlen_nodesess_kb}s (%s)\n", substr($day,0,4)."/".substr($day,4,2)."/".substr($day,6,2), &Format_Number($sessions_per_day{$day}), &Format_Number($obj_per_day{$day}), &Format_Number($sesskb_per_day{$day}), $sesskb_per_day{$day} > $MB ? sprintf("%5.1f GB", $sesskb_per_day{$day} / $MB) : $sesskb_per_day{$day} > $KB ? sprintf("%5.1f MB", $sesskb_per_day{$day} / $KB) : sprintf("%5.1f KB", $sesskb_per_day{$day}), &Format_Number($datakb_per_day{$day}), $datakb_per_day{$day} > $MB ? sprintf("%5.1f GB", $datakb_per_day{$day} / $MB) : $datakb_per_day{$day} > $KB ? sprintf("%5.1f MB", $datakb_per_day{$day} / $KB) : sprintf("%5.1f KB", $datakb_per_day{$day})); } $n = scalar(@sorted_sessions_per_day); $rmdr = $n % 2; if ($rmdr) { $median_sessions_per_day = @sorted_sessions_per_day[($n/2)]; } else { $median_sessions_per_day = @sorted_sessions_per_day[(($n/2)-1)]; } $average_sessions_per_day = sprintf("%.2f", $total_sessions / $n); $n = scalar(@sorted_obj_per_day); $rmdr = $n % 2; if ($rmdr) { $median_obj_per_day = @sorted_obj_per_day[($n/2)]; } else { $median_obj_per_day = @sorted_obj_per_day[(($n/2)-1)]; } $average_obj_per_day = sprintf("%.2f", $total_obj / $n); $n = scalar(@sorted_sesskb_per_day); $rmdr = $n % 2; if ($rmdr) { $median_sesskb_per_day = @sorted_sesskb_per_day[($n/2)]; } else { $median_sesskb_per_day = @sorted_sesskb_per_day[(($n/2)-1)]; } $average_sesskb_per_day = sprintf("%.2f", $total_sess_kb / $n); $n = scalar(@sorted_datakb_per_day); $rmdr = $n % 2; if ($rmdr) { $median_datakb_per_day = @sorted_datakb_per_day[($n/2)]; } else { $median_datakb_per_day = @sorted_datakb_per_day[(($n/2)-1)]; } $average_datakb_per_day = sprintf("%.2f", $total_data_kb / $n); # printf(REPORTOUT "\n%-10s %${maxlen_sessions}s %${maxlen_node_objs}s %${maxlen_nodesess_kb}s (%s) %${maxlen_nodesess_kb}s (%s)\n", # printf(REPORTOUT "\n%-10s %${l_sess}s %${l_objs}s %${l_kb}s (%s) %${l_kb}s (%s)\n", printf(REPORTOUT "\n%-10s %${maxlen_sessions}s %${maxlen_node_objs}s %${maxlen_nodesess_kb}s (%s) %${maxlen_nodesess_kb}s (%s)\n", "Median", &Format_Number($median_sessions_per_day), &Format_Number($median_obj_per_day), &Format_Number($median_sesskb_per_day), $median_sesskb_per_day > $MB ? sprintf("%5.1f GB", $median_sesskb_per_day / $MB) : $median_sesskb_per_day > $KB ? sprintf("%5.1f MB", $median_sesskb_per_day / $KB) : sprintf("%5.1f KB", $median_sesskb_per_day), &Format_Number($median_datakb_per_day), $median_datakb_per_day > $MB ? sprintf("%5.1f GB", $median_datakb_per_day / $MB) : $median_datakb_per_day > $KB ? sprintf("%5.1f MB", $median_datakb_per_day / $KB) : sprintf("%5.1f KB", $median_datakb_per_day)); printf(REPORTOUT "\n%-10s %${maxlen_sessions}s %${l_objs}s %${l_kb}s (%s) %${l_kb}s (%s)\n", "Average", &Format_Number($average_sessions_per_day), &Format_Number($average_obj_per_day), &Format_Number($average_sesskb_per_day), $average_sesskb_per_day > $MB ? sprintf("%5.1f GB", $average_sesskb_per_day / $MB) : $average_sesskb_per_day > $KB ? sprintf("%5.1f MB", $average_sesskb_per_day / $KB) : sprintf("%5.1f KB", $average_sesskb_per_day), &Format_Number($average_datakb_per_day), $average_datakb_per_day > $MB ? sprintf("%5.1f GB", $average_datakb_per_day / $MB) : $average_datakb_per_day > $KB ? sprintf("%5.1f MB", $average_datakb_per_day / $KB) : sprintf("%5.1f KB", $average_datakb_per_day)); # End of by-day report. #__________________________________________By-user reports_____________________________________________________ # Begin on a separate page. For column widths we will use the $maxlen_nodesess_kb value, which is the largest # of all previously reported, and works best here. # Note that HSM data transfer is implicit, so no username associated with it. printf(REPORTOUT " BY-USER REPORTS\n\n"); #__________Assign column headers # Assign values to the column header variables in the report formats. $report_colhdrs1 = sprintf("%-8s" ." %${maxlen_backup_objs}s %${maxlen_backup_kb}s" ." %${maxlen_restore_objs}s %${maxlen_restore_kb}s" ." %${maxlen_archive_objs}s %${maxlen_archive_kb}s" ." %${maxlen_retrieve_objs}s %${maxlen_retrieve_kb}s" ." %${maxlen_hsmstore_objs}s %${maxlen_hsmstore_kb}s" ." %${maxlen_hsmrecall_objs}s %${maxlen_hsmrecall_kb}s", "", $backup_obj_column_title1, $backup_kb_column_title1, $restore_obj_column_title1, $restore_kb_column_title1, $archive_obj_column_title1, $archive_kb_column_title1, $retrieve_obj_column_title1, $retrieve_kb_column_title1, $hsmstore_obj_column_title1, $hsmstore_kb_column_title1, $hsmrecall_obj_column_title1, $hsmrecall_kb_column_title1); $report_line = $report_colhdrs1; write(REPORTOUT); $report_colhdrs2 = sprintf("%-8s" ." %${maxlen_backup_objs}s %${maxlen_backup_kb}s" ." %${maxlen_restore_objs}s %${maxlen_restore_kb}s" ." %${maxlen_archive_objs}s %${maxlen_archive_kb}s" ." %${maxlen_retrieve_objs}s %${maxlen_retrieve_kb}s" ." %${maxlen_hsmstore_objs}s %${maxlen_hsmstore_kb}s" ." %${maxlen_hsmrecall_objs}s %${maxlen_hsmrecall_kb}s", "USERNAME", $backup_obj_column_title2, $backup_kb_column_title2, $restore_obj_column_title2, $restore_kb_column_title2, $archive_obj_column_title2, $archive_kb_column_title2, $retrieve_obj_column_title2, $retrieve_kb_column_title2, $hsmstore_obj_column_title2, $hsmstore_kb_column_title2, $hsmrecall_obj_column_title2, $hsmrecall_kb_column_title2); $report_line = $report_colhdrs2; write(REPORTOUT); $report_colhdruls = sprintf("%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s", '-'x${maxlen_nodename}, '-'x${maxlen_backup_objs}, '-'x${maxlen_backup_kb}, '-'x${maxlen_restore_objs}, '-'x${maxlen_restore_kb}, '-'x${maxlen_archive_objs}, '-'x${maxlen_archive_kb}, '-'x${maxlen_retrieve_objs}, '-'x${maxlen_retrieve_kb}, '-'x${maxlen_hsmstore_objs}, '-'x${maxlen_hsmstore_kb}, '-'x${maxlen_hsmrecall_objs}, '-'x${maxlen_hsmrecall_kb}); $report_line = $report_colhdruls; write(REPORTOUT); # The column headers are now set, and will print implicitly as we fill pages. #__________Report each user's usage foreach $username (sort keys(%sessions_by_username)) { # Fill in each column, separated from each preceding column by two spaces... $report_line = sprintf("%-8s" ." %${maxlen_backup_objs}s %${maxlen_backup_kb}s" ." %${maxlen_restore_objs}s %${maxlen_restore_kb}s" ." %${maxlen_archive_objs}s %${maxlen_archive_kb}s" ." %${maxlen_retrieve_objs}s %${maxlen_retrieve_kb}s" ." %${maxlen_hsmstore_objs}s %${maxlen_hsmstore_kb}s" ." %${maxlen_hsmrecall_objs}s %${maxlen_hsmrecall_kb}s", $username, &Format_Number($backup_obj_by_username{$username}), &Format_Number($backup_kb_by_username{$username}), &Format_Number($restored_obj_by_username{$username}), &Format_Number($restored_kb_by_username{$username}), &Format_Number($archived_obj_by_username{$username}), &Format_Number($archived_kb_by_username{$username}), &Format_Number($retrieved_obj_by_username{$username}), &Format_Number($retrieved_kb_by_username{$username}), &Format_Number($hsmstore_obj_by_username{$username}), &Format_Number($hsmstore_kb_by_username{$username}), &Format_Number($hsmrecall_obj_by_username{$username}), &Format_Number($hsmrecall_kb_by_username{$username}) ); # Finally, write the line out: write(REPORTOUT); } # Bottom of reporting each user's usage. #______________________________________Report user sessions - part 2____________________________________________ $report_line = " "; write(REPORTOUT); # Spacer after previous report. #__________Assign column headers # Assign values to the column header variables in the report formats. $report_colhdrs1 = sprintf("%-8s %${maxlen_sessions}s" ." %${maxlen_node_objs}s %${maxlen_nodesess_kb}s %${maxlen_nodedata_kb}s" ." %${maxlen_sesssecs}s %${maxlen_idlesecs}s %${maxlen_commsecs}s %${maxlen_mediasecs}s", "", $sessions_column_title1, $nodeobjs_column_title1, $nodesesskb_column_title1, $nodedatakb_column_title1, $sesssecs_column_title1, $idlesecs_column_title1, $commsecs_column_title1, $mediasecs_column_title1); $report_line = $report_colhdrs1; write(REPORTOUT); $report_colhdrs2 = sprintf("%-8s %${maxlen_sessions}s" ." %${maxlen_node_objs}s %${maxlen_nodesess_kb}s %${maxlen_nodedata_kb}s" ." %${maxlen_sesssecs}s %${maxlen_idlesecs}s %${maxlen_commsecs}s %${maxlen_mediasecs}s", "USERNAME", $sessions_column_title2, $nodeobjs_column_title2, $nodesesskb_column_title2, $nodedatakb_column_title2, $sesssecs_column_title2, $idlesecs_column_title2, $commsecs_column_title2, $mediasecs_column_title2); $report_line = $report_colhdrs2; write(REPORTOUT); $report_colhdruls = sprintf("%s %s %s %s %s %s %s %s %s", '-' x 8, '-'x${maxlen_sessions}, '-'x${maxlen_node_objs}, '-'x${maxlen_nodesess_kb}, '-'x${maxlen_nodedata_kb}, '-'x${maxlen_sesssecs}, '-'x${maxlen_idlesecs}, '-'x${maxlen_commsecs}, '-'x${maxlen_mediasecs}); $report_line = $report_colhdruls; write(REPORTOUT); # The column headers are now set, and will print implicitly as we fill pages. #__________Report each user's usage foreach $username (sort keys(%sessions_by_username)) { # Fill in each column, separated from each preceding column by two spaces... $report_line = sprintf("%-8s %${maxlen_sessions}u" ." %${maxlen_node_objs}s %${maxlen_nodesess_kb}s %${maxlen_nodedata_kb}s" ." %${maxlen_sesssecs}s %${maxlen_idlesecs}s %${maxlen_commsecs}s %${maxlen_mediasecs}s", $username, $sessions_by_username{$username}, &Format_Number($obj_by_username{$username}), &Format_Number($session_kb_by_username{$username}), &Format_Number($data_kb_by_username{$username}), $session_secs_by_username{$username}, $idlewait_secs_by_username{$username}, $commwait_secs_by_username{$username}, $mediawait_secs_by_username{$username}); # Finally, write the line out: write(REPORTOUT); } # End of by-user report. } # End of subroutine Summarize_Data #======================= Subroutine Sub_Secs_to_DateTime =========================== sub Sub_Secs_to_DateTime # Subroutine to convert seconds since 1970 into a date-time value in the form: # YYYY/MM/DD (DAY_OF_WEEK) HH:MM:SS # INVOCATION: $? = &Sub_Secs_to_DateTime(SECS_SINCE_1970) { # Associative array for translating month name to a number. %mon_to_num = ( "Jan","01", "Feb","02", "Mar","03", "Apr","04", "May","05", "Jun","06", "Jul","07", "Aug","08", "Sep","09", "Oct","10", "Nov","11", "Dec","12"); $secs_value = $_[0]; # To get time in human-readable string like the date command does, use the # Perl Library ctime() function (rather than localtime()). # Sample of returned value: Mon Jun 7 9:53:16 EDT 1993 require "ctime.pl"; ($ct_wkday,$ct_mon,$ct_mday,$ct_time,$ct_tz,$ct_yyyy) = split(' ',&ctime($secs_value)); # Compensate for Perl defect in responding with year 2069 if fed value 0: if (($secs_value == 0) && ($ct_yyyy == 2069)) { $ct_yyyy = 1969; } @ctime_output = &ctime($secs_value); # print("ctime output: ","@ctime_output","\n"); # Some systems return a timezone name ("EST") before the year, and some don't, # so if year is null, it's in the timezone field. if ($ct_year eq "") { $ct_year = $ct_tz; } # The month comes back as an alphabetic value (e.g., "Jul"), so has to be # translated to a 2-digit number for our purposes. $ct_mm = $mon_to_num{$ct_mon}; # We want the day-of-month number to be two digits (add leading zero if # necessary). $ct_dd = sprintf("%02d",$ct_mday); # We want the time value's hour to have a leading blank if less than 10, # to have report values line up. $ct_time = sprintf("%08s",$ct_time); return $ct_yyyy."/".$ct_mm."/".$ct_dd." (".$ct_wkday.") ".$ct_time; } # end of Sub_Secs_to_DateTime #======================= Subroutine Secs_to_YYYYMMDDhhmmss =========================== sub Secs_to_YYYYMMDDhhmmss # Subroutine to convert seconds since 1970 into a date-time value in the form: # YYYYMMDDhhmmss # which is suitable for sorting and relative comparison. # # INVOCATION: $? = &Secs_to_YYYYMMDDhhmmss(SECS_SINCE_1970) # { $secs_value = $_[0]; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($secs_value); return ($year+1900).sprintf("%02d",$mon+1).sprintf("%02d",$mday).sprintf("%02d",$hour) .sprintf("%02d",$min).sprintf("%02d",$sec); } # end of Secs_to_YYYYMMDDhhmmss #======================= Subroutine Validate_Filename =========================== sub Validate_Filename # # Subroutine to validate a given file name. # # INVOCATION: $? = &Validate_Filename(FILE_NAME) # # RETURNS: 1 if the file name exists; # 0 if not, or no file name supplied. An error message will have been # produced. # { local($arg_filename) = scalar($_[0]); # Grab the given file name. if (! -f $arg_filename) { # The file does not exist as a regular file. Say what gives. printf("File name '%s' ", $arg_filename); # Beginning of message; finish below. # Check for specifics before generalities for more helpful reporting: if (-d $arg_filename) { printf("is a directory, not a file.\n"); } elsif (-l $arg_filename) { printf("is a symbolic link without a target.\n"); } elsif (! -e $arg_filename) { printf("does not exist.\n"); } else { printf("is not a regular file.\n"); } return(0); # Return failure indication. } return(1); # Return success indication. } # end of Validate_Filename subroutine ####################################### PRINTING FORMATS ##################################### # For the top of the report page: # - There will be a major title (centered), followed by a sub title (centered), followed by a blank line. # - The width of each column space before "@*" must equal space reserved for user name. # - The "|||||" sequence below defines the width of the top titles, in which their text will be centered. # - The period in column 1 marks the end of the "format". format REPORTOUT_TOP = @||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| $report_title @||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| $report_subtitle @* $report_colhdrs1 @* $report_colhdrs2 @* $report_colhdruls . # Use angle brackets rather than pound signs in the following format to avoid having # zeroes in unused columns. format REPORTOUT = @* $report_line . #=================================== Subroutine Format_Number ======================================== sub Format_Number # # Subroutine to format a number into comma-separated form, for easier visual absorption. # Examples: 12345 ---> 12,345 # 12345.99876 ---> 12,345.99876 # # ENVIRONMENT: Any # # INVOCATION: $formatted_number = &Format_Number(number); # # where: Number Is an integer or decimal number # # RETURNS: Success: Formatted number. # Failure: Empty string. # # HISTORY: # # 1999/06/13 Created. Richard Sims { #_______________________________Define local and other variables____________________________________ # Remember that local variables are known only within their enclosing block. #local(@args) = split(/\s+/,"@_"); # Secure the args from the volatile @_ array variable. # Note that the args come in as a space-separates string rather local(@args) = @_; # Secure the args from the volatile @_ array variable. local($func_name) = "Format_Number"; local($args_count) = scalar(@args); # Number of supplied arguments. local($arg_number); local($decimal); local($number_left); local($formatted_number); #________________________________Validate arguments____________________________________ if ($args_count > 1) { return ""; } if ($args[0] !~ m|^\d*\.*\d*$|) { return ""; } $arg_number = $args[0]; #________________________________Format the number____________________________________ for ($number_left = $arg_number; ; ) { if ($number_left =~ m|(\d*)(\d{3})(\.*\d*)$|) { if ($formatted_number) { $formatted_number = "$2,$formatted_number"; } else { $formatted_number = "$2"; } if ($3) { $decimal = $3; } # One-time capture of decimal portion. #printf("x = '%s'; decimal = '%s'\n", $formatted_number, $decimal); # Debugging. $number_left = $1; } elsif ($number_left =~ m|(\d*)(\.*\d*)$|) { if ($formatted_number) { if ($1) { $formatted_number = "$1,$formatted_number"; } } else { $formatted_number = "$1"; } if ($2) { $decimal = $2; } # One-time capture of decimal portion. #printf("x = '%s'; decimal = '%s'\n", $formatted_number, $decimal); # Debugging. last; } } if ($formatted_number eq "") # If got something like ".99", want "0.99" returned. { $formatted_number = "0"; } if ($decimal) # If any decimal portion, reattach it to the result... { $formatted_number .= "$decimal"; } return $formatted_number; } # bottom of Format_Number #============================= Subroutine Is_Leapyear ================================== # # Boolean subroutine to determine if the specified year is a leap year. # # A leapyear is, of course, a year which is divisible by 4, and a centenary # divisible by 400. (So the year 2000 is a leapyear, but 1900 and 2100 are not.) # # INVOCATION: if (&Is_Leapyear(Year)) {...} # # RETURNS: 1 if the given year is a leap year # 0 if the given year is not a leap year # # HISTORY: # # 1997/02/05 Written by Richard Sims #======================================================================================== sub Is_Leapyear { local($arg_year) = scalar($_[0]); # Grab the given year number. # A year which is not divisible by 4 cannot be a leap year... if ($arg_year % 4) { return(0); } # The year is divisible by 4 so the year given is potentially a leapyear. # Now see if it is a centenary; and if so, is evenly divisible by 400. if ($arg_year % 100) { return(1); } # It is a leap year not at the end of a century. # The year is evenly divisible by 100 so is the turn of a century. # If also evenly divisible by 400, then it is a centenary leap year. if ($arg_year % 400) { return(0); } else { return(1); } } # end of Is_Leapyear #======================= Subroutine Sub_Secs_to_DateTime =========================== sub Sub_Secs_to_DateTime # Subroutine to convert seconds since 1970 into a date-time value in the form: # YYYY/MM/DD (DAY_OF_WEEK) HH:MM:SS # INVOCATION: $? = &Sub_Secs_to_DateTime(SECS_SINCE_1970) { # Associative array for translating month name to a number. %mon_to_num = ( "Jan","01", "Feb","02", "Mar","03", "Apr","04", "May","05", "Jun","06", "Jul","07", "Aug","08", "Sep","09", "Oct","10", "Nov","11", "Dec","12"); $secs_value = $_[0]; # To get time in human-readable string like the date command does, use the # Perl Library ctime() function (rather than localtime()). # Sample of returned value: Mon Jun 7 9:53:16 EDT 1993 require "ctime.pl"; ($ct_wkday,$ct_mon,$ct_mday,$ct_time,$ct_tz,$ct_yyyy) = split(' ',&ctime($secs_value)); # Compensate for Perl defect in responding with year 2069 if fed value 0: if (($secs_value == 0) && ($ct_yyyy == 2069)) { $ct_yyyy = 1969; } @ctime_output = &ctime($secs_value); # print("ctime output: ","@ctime_output","\n"); # Some systems return a timezone name ("EST") before the year, and some don't, # so if year is null, it's in the timezone field. if ($ct_year eq "") { $ct_year = $ct_tz; } # The month comes back as an alphabetic value (e.g., "Jul"), so has to be # translated to a 2-digit number for our purposes. $ct_mm = $mon_to_num{$ct_mon}; # We want the day-of-month number to be two digits (add leading zero if # necessary). $ct_dd = sprintf("%02d",$ct_mday); # We want the time value's hour to have a leading blank if less than 10, # to have report values line up. $ct_time = sprintf("%08s",$ct_time); return $ct_yyyy."/".$ct_mm."/".$ct_dd." (".$ct_wkday.") ".$ct_time; } # end of Sub_Secs_to_DateTime #======================= Subroutine Secs_to_YYYYMMDDhhmmss =========================== sub Secs_to_YYYYMMDDhhmmss # Subroutine to convert seconds since 1970 into a date-time value in the form: # YYYYMMDDhhmmss # which is suitable for sorting and relative comparison. # # INVOCATION: $? = &Secs_to_YYYYMMDDhhmmss(SECS_SINCE_1970) # { $secs_value = $_[0]; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($secs_value); return ($year+1900).sprintf("%02d",$mon+1).sprintf("%02d",$mday).sprintf("%02d",$hour) .sprintf("%02d",$min).sprintf("%02d",$sec); } # end of Secs_to_YYYYMMDDhhmmss #======================= Subroutine Show_Usage_From_Prolog ============================ # # Subroutine to show program usage from prolog comments. # # Read the invocation information from the head of this script, as contained # within a hanging-indented section lead off by one of the following: # "USAGE:", "Usage:", "SYNTAX", "Syntax", "INVOCATION", "Invocation" # and ending with some following line which has text in the same position where # that lead-off keyword begins. # # Each line from that help section will be output with a blank replacing the leading "#" # so to avoid misaligning text arranged with tabs in the prolog. # # INVOCATION: &Show_Usage_From_Prolog(); # # RETURNS: 0 #======================================================================================== sub Show_Usage_From_Prolog { @help_keywords = ("USAGE:", "Usage:", "SYNTAX", "Syntax", "INVOCATION", "Invocation"); open(HELP,$0) || die "Help unable to open file '$0'."; $in_help_section = 0; # Will be set once we encounter the help section. print("\n"); # Visual spacer before help info. while () # Read this file continuously (should not get to end). { $line = $_; # Next line from this file. # Convert tabs to proper number of positioning spaces so as to not throw off finding # position of character to stop on. while (($index = index($line,"\t")) > -1) { $line =~ s|\t|' ' x (8 - ($index % 8))|e; } if ($in_help_section) { #_______________________Watch for end of help section_______________________ # Look for any text in the same column position as the keyword which started # off this help section, which signals the end of the help section. if (substr($line,$left_pos,1) =~ m|\S|) { close(HELP); return(0); } else { printf(" %s", substr($line,1)); } } else { #______________Not in the help section yet - seek its keyword_______________ foreach $help_keyword (@help_keywords) { if (grep(/$help_keyword/,$line)) { $left_pos = index($line,$help_keyword); $in_help_section = 1; printf(" %s", substr($line,1)); } } } } return(0); } # end of Show_Usage_From_Prolog subroutine