root/util/server_daily_maint @ 326

Revision 326, 23.0 kB (checked in by lincoln, 7 years ago)

drop RRD from server daily script and use our own home-grown graphing

  • Property svn:executable set to *
Line 
1#!/usr/bin/perl
2
3# server-side maintenance script
4#  * can be run daily or multiple times per day
5#  * best if run (say) every 15 or 30 minutes (more granular data)
6#
7#  - deletes old feedback
8#  - updates store of some common statistics (users, failures, duration, time-of-day) etc.
9#  - generates pretty graphs and html
10
11
12# settings
13my $days_to_keep = 14;          # keep 14 days
14my $store_dir = $ENV{HOME} . "/whuffy.com/dev/stats";    # where we retrieve our feedback
15my $graph_dir = $ENV{HOME} . "/whuffy.com/dev/feedback"; # where we store our graphs
16my $debug = 1;
17
18use strict;
19use Data::Dumper;
20use POSIX qw(strftime);
21use SVG::TT::Graph::TimeSeries;
22use SVG::TT::Graph::Bar;
23
24my $stats_file = $store_dir . "/stats.config";
25my $store = { };
26my $starttime = time;
27
28print "Running at " . localtime() . ".\n";
29
30&set_initial_values;
31&read_config;           # read in our existing stats
32
33print "Last run " . localtime($store->{last_run}) . ".\n";
34
35&rollover_statistics;   # rotate statistics
36
37&delete_old_files;      # delete old feedback files
38&parse_feedback;        # parse any (new) feedback files
39
40&aggregate_stats;       # use last 7 days to build last week, etc.
41
42&generate_reports;      # generate reports and graphs
43
44$store->{last_run} = $starttime; # update our last_run time
45&write_config;          # write out our updated stats
46
47print "Done.\n";
48
49exit(0);
50
51###############################################################################
52
53sub delete_old_files
54{
55        chdir($store_dir) || die "can't chdir $store_dir: $!\n";
56        opendir(DIR, $store_dir) || die "can't opendir $store_dir: $!\n";
57        my @files = readdir(DIR);
58        closedir DIR;
59
60        foreach my $file (@files) {
61                next if (!-f $file);                    # only work on normal files
62                next if ($file !~ /^\d+\.done$/);       # only delete processed files
63
64                if ((-M $file) > $days_to_keep) {
65                        if ($debug) {
66                                print "would have tried to delete $store_dir/$file if debug wasn't set\n";
67                        } else {
68                                if ((unlink $store_dir/$file) == 0) {
69                                        print "failed to delete $store_dir/$file\n";
70                                }
71                        }
72                }
73        }
74}
75
76###############################################################################
77# read in our saved statistics
78
79sub read_config
80{
81        if (-r $stats_file) {
82                local (@ARGV, $/) = ($stats_file);
83                no warnings 'all'; eval <>; die "$@" if $@;
84        } else {
85                printf "WARNING: no config file $stats_file - ok if this is the first time running!\n";
86
87                # try to write to it - if we can't then this will cause us to barf
88                &write_config;
89        }
90}
91
92###############################################################################
93# write out our saved statistics
94
95sub write_config
96{
97        $Data::Dumper::Sortkeys = 1;
98        $Data::Dumper::Indent = 1;
99        open(F,">$stats_file") || die "can't write to $stats_file: $!\n";
100        print F Data::Dumper->Dump([$store], ["store"]);
101        close F;
102}
103
104###############################################################################
105# define some initial settings to basic values
106# reading the config file may override these
107
108sub set_initial_values
109{
110        $store->{last_run} = 0;
111
112        $store->{num_hours} = 1;
113        $store->{num_days} = 0;
114        $store->{num_weeks} = 0;
115        $store->{num_months} = 0;
116
117        $store->{max_hours} = 72;       # keep per-hour stats for 72 hours (need min 24)
118        $store->{max_days} = 30;        # keep per-day stats for 30 days (need min 30)
119        $store->{max_weeks} = 12;       # keep per-week stats for 12 weeks
120        $store->{max_months} = 36;      # keep per-month stats for 36 months
121}
122
123###############################################################################
124# rollover 'daily' / 'weekly' / 'monthly' statistics if day/week/month is
125# different from the last time we were run
126
127sub rollover_statistics
128{
129        # NOTE: this logic is working on GMT+10 time not whatever timezone
130        # this server is running in!
131       
132        # 1. work out GMT offset
133        my $tzstring = strftime("%z", localtime(time));
134        $store->{gmt_offset} = (60*60) * int(substr($tzstring,1,2));     # hr
135        $store->{gmt_offset} += (60 * int(substr($tzstring,3,2)));       # min
136        $store->{gmt_offset} *= -1 if (substr($tzstring,0,1) eq "-");    # +/-
137
138        # 2. work out difference from this timezone to GMT+10
139        $store->{tzdiff} = (10*60*60) - $store->{gmt_offset};
140
141        # 3. correct starttime and last_run times
142        my $gmt10_starttime = $starttime + $store->{tzdiff};
143        my $gmt10_last_run = $store->{last_run} + $store->{tzdiff};
144
145        # 4. new month
146        if (strftime("%m", localtime($gmt10_last_run)) != strftime("%m", localtime($gmt10_starttime))) {
147                for (my $i = $store->{num_months}; $i > 0; $i--) {
148                        $store->{per_month}->[$i+1] = undef;
149                        $store->{per_month}->[$i+1] = $store->{per_month}->[$i]
150                                if (defined $store->{per_month}->[$i])
151                }
152                if ($store->{num_months} >= $store->{max_months}) {
153                        delete $store->{per_month}->[($store->{num_months})];
154                } else {
155                        $store->{num_months}++;
156                }
157        }
158
159        # 5. new week
160        if (strftime("%U", localtime($gmt10_last_run)) != strftime("%U", localtime($gmt10_starttime))) {
161                for (my $i = $store->{num_weeks}; $i > 0; $i--) {
162                        $store->{per_week}->[$i+1] = undef;
163                        $store->{per_week}->[$i+1] = $store->{per_week}->[$i]
164                                if (defined $store->{per_week}->[$i]);
165                }
166                if ($store->{num_weeks} >= $store->{max_weeks}) {
167                        delete $store->{per_week}->[($store->{num_weeks})];
168                } else {
169                        $store->{num_weeks}++;
170                }
171        }
172
173        # 6. new day
174        if (strftime("%w", localtime($gmt10_last_run)) != strftime("%w", localtime($gmt10_starttime))) {
175                for (my $i = $store->{num_days}; $i > 0; $i--) {
176                        $store->{per_day}->[$i+1] = undef;
177                        $store->{per_day}->[$i+1] = $store->{per_day}->[$i]
178                                if (defined $store->{per_day}->[$i]);
179                }
180                if ($store->{num_days} >= $store->{max_days}) {
181                        delete $store->{per_day}->[($store->{num_days})];
182                } else {
183                        $store->{num_days}++;
184                }
185        }
186
187        # 7. new hour
188        if (strftime("%H", localtime($gmt10_last_run)) != strftime("%H", localtime($gmt10_starttime))) {
189                for (my $i = $store->{num_hours}; $i > 0; $i--) {
190                        $store->{per_hour}->[$i+1] = undef;
191                        $store->{per_hour}->[$i+1] = $store->{per_hour}->[$i]
192                                if (defined $store->{per_hour}->[$i]);
193                }
194                if ($store->{num_hours} >= $store->{max_hours}) {
195                        delete $store->{per_hour}->[($store->{num_hours})];
196                } else {
197                        $store->{num_hours}++;
198                }
199        }
200
201}
202
203###############################################################################
204# aggregate stats:
205#  - last 24 hours = day 0 statistics,
206#  - last 7 days = week 0 statistics,
207#  - last 30 days = month 0 statistics
208
209sub aggregate_stats
210{
211        # build day 0 based on last 24 hours
212        delete $store->{per_day}->[0];
213        for (my $i = 0; $i < 24; $i++) {
214                next if (!defined $store->{per_hour}->[$i]);
215                $store->{per_day}->[0] = &merge_in_stats($store->{per_day}->[0], $store->{per_hour}->[$i]);
216        }
217
218        # build week 0 based on last 7 days
219        delete $store->{per_week}->[0];
220        for (my $i = 0; $i < 7; $i++) {
221                next if (!defined $store->{per_day}->[$i]);
222                $store->{per_week}->[0] = &merge_in_stats($store->{per_week}->[0], $store->{per_day}->[$i]);
223        }
224
225        # build month 0 based on last 30 days
226        delete $store->{per_month}->[0];
227        for (my $i = 0; $i < 30; $i++) {
228                next if (!defined $store->{per_day}->[$i]);
229                $store->{per_month}->[0] = &merge_in_stats($store->{per_month}->[0], $store->{per_day}->[$i]);
230        }
231}
232
233###############################################################################
234
235sub record_duration
236{
237        my ($where, $dur) = @_;
238        $where->{duration} += $dur;
239        $where->{duration_max} = $dur if (!defined $where->{duration_max});
240        $where->{duration_max} = $dur if ($dur > $where->{duration_max});
241        $where->{duration_min} = $dur if (!defined $where->{duration_min});
242        $where->{duration_min} = $dur if ($dur < $where->{duration_max});
243}
244
245###############################################################################
246# merge statistics in an intelligent manner
247#  - can recursively descend HASH lists
248#  - accumulates totals
249#  - fields with _min/_max suffix take the minimum/maximum value
250
251sub merge_in_stats
252{
253        my ($to, $from) = @_;
254
255        foreach my $field (keys %{($from)}) {
256                if (ref($from->{$field}) eq "HASH") {
257                        $to->{$field} = &merge_in_stats($to->{$field}, $from->{$field});
258                } elsif (defined $from->{$field}) {
259                        if (!defined $to->{$field}) {
260                                $to->{$field} = $from->{$field};
261                        } else {
262                                # fields ending in _min and _max are special
263                                if ($field =~ /_min$/) {
264                                        $to->{$field} = $from->{$field} if ($from->{$field} < $to->{$field});
265                                } elsif ($field =~ /_max$/) {
266                                        $to->{$field} = $from->{$field} if ($from->{$field} > $to->{$field});
267                                } else {
268                                        $to->{$field} += $from->{$field};
269                                }
270                        }
271                }
272        }
273        return $to;
274}
275
276###############################################################################
277
278sub parse_feedback
279{
280        chdir($store_dir) || die "can't chdir $store_dir: $!\n";
281        opendir(DIR, $store_dir) || die "can't opendir $store_dir: $!\n";
282        my @files = readdir(DIR);
283        closedir DIR;
284
285        foreach my $file (@files) {
286                next if (!-f $file);                    # only work on normal files
287                next if ($file !~ /^\d+$/);             # only look at non-processed files
288                next if ((stat(_))[9] < $store->{last_updated}); # only look at newly modified files
289
290                my $file_modtime = (stat(_))[9];
291                printf " - gathering statistics from $store_dir/$file ($file_modtime)\n" if ($debug);
292
293                open(F,"<$store_dir/$file") || die "could not open $store_dir/$file: $!\n";
294                while(<F>) {
295                        chop;
296                        if ($_ =~ /^shepherd\tSTATUS\t([0-9\.]+)\t(\d+)\t(\d+)\t(\d+)\t(.*)$/) {
297                                # shepherd status response
298                                my ($sysid, $user_starttime, $duration, $gmt_offset, $components_used) = ($1,$2,$3,$4,$5);
299
300                                #
301                                # usage statistics
302                                #
303                                $store->{per_hour}->[0]->{visits}++;             # total # of visits seen
304                                $store->{total}->{visits}++;
305
306                                # shepherd's duration (total count, min, max, average from duration/visits)
307                                &record_duration($store->{per_hour}->[0], $duration);
308                                &record_duration($store->{total}, $duration);
309
310                                # list of sysid's seen (used to derive # unique users)
311                                $store->{per_hour}->[0]->{seen_sysid_list}->{$sysid}++;
312                                $store->{total}->{seen_sysid_list}->{$sysid}++;
313
314                                # where user is
315                                my $gmt_offset_string = sprintf "%s%02d%02d",
316                                        ($gmt_offset < 0 ? "-" : "+"),
317                                        int($gmt_offset / 3600),
318                                        int(($gmt_offset / 3600) / 60);
319                                $store->{per_hour}->[0]->{gmt_offset_list}->{$gmt_offset_string}++;
320
321                                # when shepherd was run (in GMT+10), normalized to 15 minute buckets
322                                my $when_run = strftime("%H:%M", localtime(($file_modtime-($file_modtime%(15*60))) + $store->{tzdiff}));
323                                $store->{per_hour}->[0]->{when_run_list}->{"$when_run"}++;
324                                $store->{total}->{when_run_list}->{"$when_run"}++;
325
326                                # what components were used (and in what order?)
327                                $store->{per_hour}->[0]->{components_used_order_list}->{$components_used}++;
328
329                        } elsif ($_ =~ /^(\S+)\tSUCCESS\t(\S+)\t(\S+)\t(\S+)\t(\S+)/) {
330                                # success line
331                                my ($c, $retcode, $c_start, $c_dur, $c_ver) = ($1, $2, $3, $4, $5);
332
333                                my $g = "c_".$c."_success";
334                                $store->{per_hour}->[0]->{$g}->{counter}++;
335                                $store->{total}->{$g}->{counter}++;
336
337                                # duration (total count, min, max, average from duration/counter)
338                                &record_duration($store->{per_hour}->[0]->{$g}, $c_dur);
339                                &record_duration($store->{total}->{$g}, $c_dur);
340
341                                # version count
342                                $store->{per_hour}->[0]->{$g}->{version_list}->{$c_ver}++;
343                        } elsif ($_ =~ /^(\S+)\tFAIL\t(\S+)\t(\S+)\t(\S+)\t(\S+)\t(\S+)/) {
344                                # fail line
345                                my ($c, $retcode, $c_start, $c_dur, $c_ver, $num_failures) = ($1, $2, $3, $4, $5, $6);
346
347                                my $g = "c_".$c."_fail";
348                                $store->{per_hour}->[0]->{$g}->{counter}++;
349                                $store->{total}->{$g}->{counter}++;
350
351                                # duration (total count, min, max, average from duration/counter)
352                                &record_duration($store->{per_hour}->[0]->{$g}, $c_dur);
353                                &record_duration($store->{total}->{$g}, $c_dur);
354
355                                # version count
356                                $store->{per_hour}->[0]->{$g}->{version_list}->{$c_ver}++;
357                        } elsif ($_ =~ /^(\S+)\tstats\t(.*)$/) {
358                                # component statistics line
359                                ; # ignore for now
360                        }
361                }
362                close(F);
363
364                # rename processed files so we don't re-process them
365                rename("$file", "$file.done") if (!$debug);
366        }
367}
368
369###############################################################################
370
371sub numvar
372{
373        my ($var,$field,$notdef) = @_;
374        $notdef = "n/a" if (!defined $notdef);
375        $field = "" if (!defined $field);
376
377        return $notdef if (!defined $var);
378
379        if ($field =~ /_list$/) {
380                # fields which end in _list are HASHes - return # of keys
381                my $count = keys %{$var};
382                return $count;
383        } else {
384                return $var;
385        }
386}
387
388###############################################################################
389
390sub report_count_data
391{
392        my ($field) = @_;
393        my $r = "";
394
395        $r .= sprintf "<li>in last 60 minutes: <b>%s</b> (%s previous)<br>\n",
396                numvar($store->{per_hour}->[0]->{$field},$field), numvar($store->{per_hour}->[1]->{$field},$field);
397        $r .= sprintf "<li>in last 24 hours: <b>%s</b> (previous %s)<br>\n",
398                numvar($store->{per_day}->[0]->{$field},$field), numvar($store->{per_day}->[1]->{$field},$field);
399        $r .= sprintf "<li>in last 7 days: <b>%s</b> (previous %s)<br>\n",
400                numvar($store->{per_week}->[0]->{$field},$field), numvar($store->{per_week}->[1]->{$field},$field);
401        $r .= sprintf "<li>in last 30 days: <b>%s</b> (previous %s)<br>\n", 
402                numvar($store->{per_month}->[0]->{$field},$field), numvar($store->{per_month}->[1]->{$field},$field);
403        $r .= sprintf "<li><b>TOTAL SEEN: <u>%s</u></b><br>\n", numvar($store->{total}->{$field},$field);
404
405        return $r;
406}
407
408###############################################################################
409
410sub sec2hms
411{
412        my $sec = shift;
413
414        return sprintf "%dh %02dm %02ds",
415                int($sec / 3600),
416                int(($sec % 3600) / 60),
417                $sec % 60;
418}
419
420###############################################################################
421
422sub graph_hash_data
423{
424        my ($field, $when, $startwhen, $endwhen, $title, $gheight, $gwidth) = @_;
425
426        my $gname = $field."_".$when."_".$startwhen."_".$endwhen;
427
428        # gather up data
429        my %data;
430        for (my $i = $startwhen; $i < $endwhen; $i++) {
431                if (defined $store->{$when}->[$i]->{$field}) {
432                        foreach my $k (keys %{($store->{$when}->[$i]->{$field})}) {
433                                $data{$k} += $store->{$when}->[$i]->{$field}->{$k};
434                        }
435                }
436        }
437
438        # sort it into key order
439        my $min, my $max;
440        my @sorted_keys, my @sorted_data;
441        foreach my $k (sort keys %data) {
442                push(@sorted_keys,$k);
443                push(@sorted_data,$data{$k});
444
445                $min = $data{$k} if (!defined $min);
446                $max = $data{$k} if (!defined $max);
447                $min = $data{$k} if ($data{$k} < $min);
448                $max = $data{$k} if ($data{$k} > $min);
449        }
450
451        # work out our scaling
452        my $scalediv = ($max - $min) / 3;
453        $scalediv = $max if ($scalediv < 1);
454
455        # only graph if we have some data
456        return if (($min == 0) && ($max == 0));
457
458        print "Generating bar graph $gname ..\n" if ($debug);
459
460        my $graph = SVG::TT::Graph::Bar->new({
461                'show_graph_title' => 1,
462                'graph_title' => $title,
463                'bar_gap' => 0,
464                'show_data_values' => 0,
465                'height' => $gheight,
466                'width' => $gwidth,
467                'stagger_x_labels' => 1,
468                'show_x_labels' => 1,
469                'bar_gap' => 0,
470                'show_data_values' => 1,
471                'scale_divisions' => $scalediv,
472                'fields' => \@sorted_keys,
473                });
474        $graph->add_data({
475                'data' => \@sorted_data,
476                });
477
478        open(SVG,">$graph_dir/$gname".".svg") || die "could not create $graph_dir/$gname".".svg: $!\n";
479        print SVG $graph->burn();
480        close(SVG);
481
482        return sprintf "<object type='image/svg+xml' data='%s.svg' height='%d' width='%d'></object>\n",$gname,$gheight,$gwidth;
483}
484
485###############################################################################
486
487sub graph_one_count_data
488{
489        my ($gname, $data, $title, $time_format, $timediv) = @_;
490        my $gheight = 120;
491        my $gwidth = 250;
492
493        print "Generating graph $gname ..\n" if ($debug);
494
495        # work out scale..
496        my $min, my $max, my $valnum = 0;
497        foreach my $val (@$data) {
498                $valnum++;
499                next if ($valnum % 2 == 1);
500
501                $min = $val if (!defined $min);
502                $max = $val if (!defined $max);
503                $min = $val if ($val < $min);
504                $max = $val if ($val > $min);
505        }
506        my $scalediv = ($max - $min) / 3;
507        $scalediv = 1 if ($scalediv < 1);
508
509        return if (($min == 0) && ($max == 0));
510
511        my $graph = SVG::TT::Graph::TimeSeries->new({
512                'show_graph_title' => 1,
513                'graph_title' => $title,
514                'bar_gap' => 0,
515                'show_data_values' => 0,
516                'height' => $gheight,
517                'width' => $gwidth,
518                'stagger_x_labels' => 1,
519                'area_fill' => 1,
520                'rollover_values' => 1,
521                'show_data_values' => 1,
522                'x_label_format' => $time_format,
523                'scale_divisions' => $scalediv,
524                'timescale_divisions' => $timediv,
525                });
526        $graph->add_data({
527                'data' => $data,
528                });
529
530        open(SVG,">$graph_dir/$gname".".svg") || die "could not create $graph_dir/$gname".".svg: $!\n";
531        print SVG $graph->burn();
532        close(SVG);
533
534        return sprintf "<object type='image/svg+xml' data='%s.svg' height='%d' width='%d'></object>\n",$gname,$gheight,$gwidth;
535}
536
537###############################################################################
538# produces up to 4 sets of graph
539
540sub graph_count_data
541{
542        my ($field, $max_hrs, $max_days, $max_weeks, $max_months) = @_;
543        my $r = "";
544
545        # hours
546        my @d1;
547        for (my $i = ($max_hrs-1); $i >= 0; $i--) {
548                my $val = numvar($store->{per_hour}->[$i]->{$field},$field,0);
549                my $time_offset = $starttime + $store->{tzdiff} - (60*60*$i);
550                push(@d1, strftime("%Y-%m-%d %H:%M:%S", localtime($time_offset)), $val);
551        }
552
553        # days
554        my @d2;
555        for (my $i = ($max_days-1); $i >= 0; $i--) {
556                my $val = numvar($store->{per_day}->[$i]->{$field},$field,0);
557                my $time_offset = $starttime + $store->{tzdiff} - (60*60*24*$i);
558                push(@d2, strftime("%Y-%m-%d %H:%M:%S", localtime($time_offset)), $val);
559        }
560
561        # weeks
562        my @d3;
563        for (my $i = ($max_weeks-1); $i >= 0; $i--) {
564                my $val = numvar($store->{per_week}->[$i]->{$field},$field,0);
565                my $time_offset = $starttime + $store->{tzdiff} - (60*60*24*7*$i);
566                push(@d3, strftime("%Y-%m-%d %H:%M:%S", localtime($time_offset)), $val);
567        }
568
569        # months
570        my @d4;
571        for (my $i = ($max_months-1); $i >= 0; $i--) {
572                my $val = numvar($store->{per_week}->[$i]->{$field},$field,0);
573                my $time_offset = $starttime + $store->{tzdiff} - (60*60*24*30*$i);
574                push(@d4, strftime("%Y-%m-%d %H:%M:%S", localtime($time_offset)), $val);
575        }
576
577        $r .= &graph_one_count_data($field."_hr", \@d1, "Last $max_hrs hours", "%a %H:%M", "6 hours") if (defined $max_hrs);
578        $r .= &graph_one_count_data($field."_day", \@d2, "Last $max_days days", "%a %e %b", "5 days") if (defined $max_days);
579        $r .= &graph_one_count_data($field."_wk", \@d3, "Last $max_weeks weeks", "%e %b %y", "3 weeks") if (defined $max_weeks);
580        $r .= &graph_one_count_data($field."_mn", \@d4, "Last $max_months months", "%b %y", "4 months") if (defined $max_months);
581        return $r;
582}
583
584###############################################################################
585
586sub generate_reports
587{
588        open(F,">$graph_dir/main.html") || die "couldn't open $graph_dir for writing: $!\n";
589
590        my $now = localtime($starttime);
591
592        print F <<EOF
593<html>
594<head>
595<title>Shepherd</title>
596<style>
597a { color: #F6B620; text-decoration: none; border-bottom: 1px dotted #F6B620; }
598a:hover { border: 1px dotted #FFEE40; }
599html, body, table { margin: 0; padding: 0; color: #c0bcb0; font: normal 12px Verdana; }
600body { text-align: left; margin: 1em 10 10 20; }
601body.home { background: #474642; }
602body.home h1 { font: bold 36px/.8em Slyphaen, Times, serif; color: #F6B620; border-bottom: 3px solid #F6B620; padding: 0; clear: both; text-align: left; }
603body.home h2 { font: normal 24px/.8em Slyphaen, Times, serif; color: #DFB020; padding: 0; clear: both; margin: 20 0 10 0; }
604body.home p { clear: both; }
605input { font: bold 12px Arial; }
606</style>
607</head>
608
609<body class="home">
610<h1>Shepherd Overall statistics <font size=-1>(as of $now)</font></h1>
611<table style='border: 1px dotted; border-collapse: separate; border-spacing: 1px dotted; padding: 3px; cellpadding: 3px;' align=left>
612<tr>
613EOF
614;
615        print F "<tr'><td align=left valign=top nowrap><h2>Shepherd usage</h2>\n";
616        print F "Number of times shepherd reported in.<br>\n";
617        print F report_count_data("visits");
618        print F "</td><td nowrap colspan=4><br>";
619        print F graph_count_data("visits",24,21,12,24);
620        print F "</td></tr>\n";
621
622        print F "<tr><td align=left valign=top nowrap><h2>Unique users</h2>\n";
623        print F "Number of unique systems seen.<br>\n";
624        print F report_count_data("seen_sysid_list");
625        print F "</td><td nowrap colspan=4><br>";
626        print F graph_count_data("seen_sysid_list",24,21,12,24);
627        print F "</td></tr>\n";
628
629        print F "<tr><td align=left valign=top nowrap><h2>Start time</h2>\n";
630        print F "When shepherd checked in.<br>\n";
631        print F "(in 15 minute groups, time in GMT+10)";
632        print F "</td><td nowrap colspan=2><br>";
633        print F graph_hash_data("when_run_list", "per_day", 0, 7, "Last 7 days", 120, 500);
634        print F "</td><td nowrap colspan=2><br>";
635        print F graph_hash_data("when_run_list", "per_month", 0, 1, "Last 30 days", 120, 500);
636        print F "</td></tr>\n";
637
638        print F "<tr><td align=left valign=top nowrap><h2>Duration</h2>\n";
639        print F "How long shepherd took to run.<br>\n";
640        print F "</td><td nowrap align=left valign=top><br>";
641        printf F "<li>in last 60 minutes:</b><ul><li>%s average (mean)<li>%s min<li>%s max</ul>\n",
642                sec2hms(numvar($store->{per_hour}->[0]->{duration},"",0) / $store->{per_hour}->[0]->{visits}),
643                sec2hms(numvar($store->{per_hour}->[0]->{duration_min},"",0)),
644                sec2hms(numvar($store->{per_hour}->[0]->{duration_max},"",0))
645                if ((defined $store->{per_hour}->[0]->{visits}) && ($store->{per_hour}->[0]->{visits} > 0));
646        print F "</td><td nowrap align=left valign=top><br>";
647        printf F "<li>in last 7 days:</b><ul><li>%s average (mean)<li>%s min<li>%s max</ul>\n",
648                sec2hms(numvar($store->{per_day}->[0]->{duration},"",0) / $store->{per_day}->[0]->{visits}),
649                sec2hms(numvar($store->{per_day}->[0]->{duration_min},"",0)),
650                sec2hms(numvar($store->{per_day}->[0]->{duration_max},"",0))
651                if ((defined $store->{per_day}->[0]->{visits}) && ($store->{per_day}->[0]->{visits} > 0));
652        print F "</td><td nowrap align=left valign=top><br>";
653        printf F "<li>in last 30 days:</b><ul><li>%s average (mean)<li>%s min<li>%s max</ul>\n",
654                sec2hms(numvar($store->{per_month}->[0]->{duration},"",0) / $store->{per_month}->[0]->{visits}),
655                sec2hms(numvar($store->{per_month}->[0]->{duration_min},"",0)),
656                sec2hms(numvar($store->{per_month}->[0]->{duration_max},"",0))
657                if ((defined $store->{per_month}->[0]->{visits}) && ($store->{per_month}->[0]->{visits} > 0));
658        print F "</td><td nowrap align=left valign=top><br>";
659        printf F "<li>OVERALL:</b><ul><li>%s average (mean)<li>%s min<li>%s max</ul>\n",
660                sec2hms(numvar($store->{total}->{duration},"",0) / $store->{total}->{visits}),
661                sec2hms(numvar($store->{total}->{duration_min},"",0)),
662                sec2hms(numvar($store->{total}->{duration_max},"",0))
663                if ((defined $store->{total}->{visits}) && ($store->{total}->{visits} > 0));
664        print F "</td></tr>\n";
665
666        print F "<tr><td align=left valign=top nowrap><h2>Timezones</h2>\n";
667        print F "Timezone setting on hosts.<br>\n";
668        print F "</td><td nowrap colspan=4><br>";
669        print F graph_hash_data("gmt_offset_list", "per_day", 0, 7, "Last 7 days", 120, 500);
670        print F graph_hash_data("gmt_offset_list", "per_month", 0, 1, "Last 30 days", 120, 500);
671        print F "</td></tr>\n";
672
673
674# SHEPHERD
675# what components were used (and in what order?)
676# $store->{per_hour}->[0]->{components_used_order_list}->{$components_used}++;
677
678
679# PER-COMPONENT
680# my $g = "c_".$c."_success";
681# my $g = "c_".$c."_fail";
682# $store->{per_hour}->[0]->{$g}->{counter}++;
683# $store->{total}->{$g}->{counter}++;
684
685# duration (total count, min, max, average from duration/counter)
686# &record_duration($store->{per_hour}->[0]->{$g}, $c_dur);
687# &record_duration($store->{total}->{$g}, $c_dur);
688
689# # version count
690# $store->{per_hour}->[0]->{$g}->{version_list}->{$c_ver}++;
691
692        print F "</table><br clear=all>\n";
693
694        print F "</body></html>\n";
695
696}
697
698###############################################################################
699
Note: See TracBrowser for help on using the browser.