18 |
use Data::Dumper; |
use Data::Dumper; |
19 |
|
|
20 |
my $date_fmt = "%Y-%m-%d"; |
my $date_fmt = "%Y-%m-%d"; |
21 |
my $date_time_fmt = "%Y-%m-%d %H:%M:%S"; |
#my $date_time_fmt = "%Y-%m-%d %H:%M:%S"; |
22 |
|
my $date_time_fmt = "<small>%a</small> <nobr>%Y-%m-%d</nobr> %H:%M:%S"; |
23 |
|
|
24 |
my $from_date = "now - 6 months"; |
my $from_date = "now - 6 months"; |
25 |
my $to_date = "now"; |
my $to_date = "now"; |
30 |
my $from_time_interval = "7:00"; |
my $from_time_interval = "7:00"; |
31 |
my $to_time_interval = "17:00"; |
my $to_time_interval = "17:00"; |
32 |
|
|
33 |
my $debug=1; |
my $debug=0; |
34 |
$debug++ if (grep(/-v/,@ARGV)); |
$debug++ if (grep(/-v/,@ARGV)); |
35 |
$debug++ if (grep(/-d/,@ARGV)); |
$debug++ if (grep(/-d/,@ARGV)); |
36 |
|
|
51 |
my @sg_selected = $q->param('sg_filter'); |
my @sg_selected = $q->param('sg_filter'); |
52 |
|
|
53 |
# init misc sort parametars |
# init misc sort parametars |
54 |
my @sort; |
my @sort_rules; |
55 |
my $order; |
my $order; |
56 |
my %sort_param; |
my %sort_param; |
57 |
my ($usort,$dsort); |
my ($usort,$dsort); |
58 |
if ($q->param('usort')) { |
if ($q->param('usort')) { |
59 |
$sort_param{'usort'} = $q->param('usort'); |
$sort_param{'usort'} = $q->param('usort'); |
60 |
$q->delete('usort'); |
$q->delete('usort'); |
61 |
@sort = ( -compare => 'numeric', $sort_param{'usort'} ); |
@sort_rules = ( -compare => 'numeric', scalar $sort_param{'usort'} ); |
62 |
} |
} |
63 |
if ($q->param('dsort')) { |
if ($q->param('dsort')) { |
64 |
$sort_param{'dsort'} = $q->param('dsort'); |
$sort_param{'dsort'} = $q->param('dsort'); |
65 |
$q->delete('dsort'); |
$q->delete('dsort'); |
66 |
@sort = ( -compare => 'numeric', -order=>'reverse', $sort_param{'dsort'} ); |
@sort_rules = ( -compare => 'numeric', -order=>'reverse', scalar $sort_param{'dsort'} ); |
67 |
} |
} |
68 |
|
|
69 |
# make interval |
# make interval |
123 |
# |
# |
124 |
|
|
125 |
my %fail; |
my %fail; |
|
my $downtime; # total downtime |
|
126 |
my $sg_filter; # filter for service/group |
my $sg_filter; # filter for service/group |
|
my $sg_count; # count number of downtimes |
|
127 |
|
|
128 |
my $log_file="/var/log/mon/sap.log"; |
my $log_file="/var/log/mon/sap.log"; |
129 |
|
|
130 |
my $data; |
my $data; |
131 |
|
|
132 |
# generate unique key for this data and options |
# generate unique key for this data and options |
133 |
my $cache_key="monlog".join("",@sg_selected).$print_orphans.$rep_reset; |
my $cache_key="monlog".join("|",@sg_selected)."|".$print_orphans."|".$rep_reset; |
134 |
|
|
135 |
|
# debug disables cache |
136 |
if (! $debug) { |
if (! $debug) { |
137 |
$data = $cache->get( $cache_key ); |
$data = $cache->get( $cache_key ); |
138 |
$downtime = $cache->get("downtime $cache_key"); |
$sg_filter = $cache->get("sg_filter $cache_key"); |
|
$sg_filter = $cache->get("sg_filter $cache_key"); |
|
|
$sg_count = $cache->get("sg_count $cache_key"); |
|
139 |
} |
} |
140 |
|
|
141 |
if (!$data || !$downtime || !$sg_filter || !$sg_count) { |
if (!$data || !$sg_filter) { |
142 |
|
|
143 |
open(LOG, $log_file) || die "$log_file: $!"; |
open(LOG, $log_file) || die "$log_file: $!"; |
144 |
|
|
153 |
'sg'=>"$group/$service", |
'sg'=>"$group/$service", |
154 |
'from'=>$fail{$id}, |
'from'=>$fail{$id}, |
155 |
'to'=>$utime, |
'to'=>$utime, |
156 |
|
'dur'=>($utime-$fail{id}), |
157 |
'desc'=>$desc }; |
'desc'=>$desc }; |
|
$downtime->{"$group/$service"} += ($utime - $fail{$id}), |
|
|
$sg_count->{"$group/$service"}++; |
|
158 |
} |
} |
159 |
$sg_filter->{"$group/$service"}++; |
$sg_filter->{"$group/$service"}++; |
160 |
delete $fail{$id}; |
delete $fail{$id}; |
164 |
'sg'=>"$group/$service", |
'sg'=>"$group/$service", |
165 |
'from'=>-1, |
'from'=>-1, |
166 |
'to'=>$utime, |
'to'=>$utime, |
167 |
|
'dur'=>0, |
168 |
'desc'=>$desc }; |
'desc'=>$desc }; |
|
$sg_count->{"$group/$service"}++; |
|
169 |
} |
} |
170 |
delete $fail{$id}; |
delete $fail{$id}; |
171 |
$sg_filter->{"$group/$service"}++; |
$sg_filter->{"$group/$service"}++; |
175 |
'sg'=>"$group/$service", |
'sg'=>"$group/$service", |
176 |
'from'=>$fail{$id}, |
'from'=>$fail{$id}, |
177 |
'to'=>$utime, |
'to'=>$utime, |
178 |
|
'dur'=>($utime-$fail{id}), |
179 |
'desc'=>'[failure again]'}; |
'desc'=>'[failure again]'}; |
|
$downtime->{"$group/$service"} += ($utime - $fail{$id}), |
|
180 |
$fail{$id} = $utime; |
$fail{$id} = $utime; |
|
$sg_count->{"$group/$service"}++; |
|
181 |
} |
} |
182 |
$sg_filter->{"$group/$service"}++; |
$sg_filter->{"$group/$service"}++; |
183 |
} else { |
} else { |
188 |
close(LOG); |
close(LOG); |
189 |
|
|
190 |
$cache->set($cache_key, $data); |
$cache->set($cache_key, $data); |
|
$cache->set("downtime $cache_key", $downtime); |
|
191 |
$cache->set("sg_filter $cache_key", $sg_filter); |
$cache->set("sg_filter $cache_key", $sg_filter); |
|
$cache->set("sg_count $cache_key", $sg_count); |
|
192 |
|
|
193 |
} |
} |
194 |
|
|
210 |
), |
), |
211 |
),td( |
),td( |
212 |
em("Other options:"),br, |
em("Other options:"),br, |
213 |
$q->checkbox(-name=>'rep_reset',-checked=>0, |
$q->checkbox(-name=>'rep_reset',-default=>0, |
214 |
-label=>"show repeated failures on same service as individual failures"), |
-label=>"show repeated failures on same service as individual failures"), |
215 |
br, |
br, |
216 |
$q->checkbox(-name=>'print_orphans',-checked=>0, |
$q->checkbox(-name=>'print_orphans',-default=>0, |
217 |
-label=>"show records which are not complete in this interval"), |
-label=>"show records which are not complete in this interval"), |
218 |
br, |
br, |
219 |
$q->checkbox(-name=>'use_date_limit',-checked=>1, |
$q->checkbox(-name=>'use_date_limit',-default=>1, |
220 |
-label=>"use date limit from:"), |
-label=>"use date limit from:"), |
221 |
$q->textfield(-name=>'from_date',-size=>20,-default=>$from_date), |
$q->textfield(-name=>'from_date',-size=>20,-default=>$from_date), |
222 |
" to: ", |
" to: ", |
223 |
$q->textfield(-name=>'to_date',-size=>20,-default=>$to_date), |
$q->textfield(-name=>'to_date',-size=>20,-default=>$to_date), |
224 |
small('Using <a href="http://search.cpan.org/search?mode=module&query=Time::ParseDate">Time::ParseDate</a>'), |
small('Using <a href="http://search.cpan.org/search?mode=module&query=Time::ParseDate">Time::ParseDate</a>'), |
225 |
br, |
br, |
226 |
$q->checkbox(-name=>'use_time_limit',-checked=>1, |
$q->checkbox(-name=>'use_time_limit',-default=>1, |
227 |
-label=>"use time limit for each day:"), |
-label=>"use time limit for each day:"), |
228 |
$q->textfield(-name=>'from_time_interval',-size=>8,-default=>$from_time_interval), |
$q->textfield(-name=>'from_time_interval',-size=>8,-default=>$from_time_interval), |
229 |
" to: ", |
" to: ", |
246 |
# dump report |
# dump report |
247 |
# |
# |
248 |
|
|
249 |
|
my %dir_html_entity = ( |
250 |
|
# 'u' => '⇑', |
251 |
|
# 'd' => '⇓' |
252 |
|
'u' => '▲', |
253 |
|
'd' => '▼', |
254 |
|
); |
255 |
|
|
256 |
sub sort_link { |
sub sort_link { |
257 |
my $q = shift || return; |
my $q = shift || return; |
258 |
my $col = shift || return; |
my $col = shift || return; |
259 |
my $dir = lc(shift) || return; |
my $dir = lc(shift) || return; |
260 |
if ($sort_param{$dir.'sort'} && $sort_param{$dir.'sort'} eq $col) { |
if ($sort_param{$dir.'sort'} && $sort_param{$dir.'sort'} eq $col) { |
261 |
return '&'.$dir.'Arr;'; |
return $dir_html_entity{$dir}; |
262 |
} else { |
} else { |
263 |
return '<a href="'.$q->url(-query=>1).'&'.$dir.'sort='.$col.'">&'.$dir.'Arr;</a>'; |
return '<a href="'.$q->url(-query=>1).'&'.$dir.'sort='.$col.'">'.$dir_html_entity{$dir}.'</a>'; |
264 |
} |
} |
265 |
} |
} |
266 |
|
|
277 |
|
|
278 |
# sort data |
# sort data |
279 |
# |
# |
280 |
my @sorted = sorted_array(@$data, @sort); |
my @sorted = sorted_array( @$data, @sort_rules ); |
281 |
|
|
282 |
print "-- sort: ",Dumper(@sort)," (data: ".@$data." sorted: ".@sorted.") --\n",br,"-- dayMask: $dayMask --\n",br,"-- cache_key: $cache_key --\n",br if ($debug); |
print "-- sort: ",Dumper(@sort_rules)," (data: ".@$data." sorted: ".@sorted.") --\n",br,"-- dayMask: $dayMask --\n",br,"-- cache_key: $cache_key --\n",br if ($debug); |
283 |
|
|
284 |
print start_table({-border=>1,-cellspacing=>0,-cellpadding=>2,-width=>'100%'}); |
print start_table({-border=>1,-cellspacing=>0,-cellpadding=>2,-width=>'100%'}); |
285 |
|
|
302 |
th("description") |
th("description") |
303 |
) if (scalar @sorted > 0); |
) if (scalar @sorted > 0); |
304 |
|
|
305 |
|
my $downtime; # total downtime |
306 |
|
my $downinterval; # total downtime in time interval |
307 |
|
my $sg_count; # count number of downtimes |
308 |
|
|
309 |
foreach my $row (@sorted) { |
foreach my $row (@sorted) { |
310 |
next if ($q->param('use_date_limit') && ($row->{from} < $from_time || $row->{to} > $to_time)); |
next if ($q->param('use_date_limit') && ($row->{from} < $from_time || $row->{to} > $to_time)); |
311 |
my ($from,$dur,$int) = ('unknown','unknown','unknown'); |
my ($from,$dur,$int) = ('unknown','unknown','unknown'); |
312 |
|
|
313 |
if ($row->{from} != -1 ) { |
if ($row->{from} != -1 ) { |
314 |
$from = d($row->{from}); |
$from = d($row->{from}); |
315 |
$dur = dur($row->{to} - $row->{from}); |
$dur = $row->{to} - $row->{from}; |
316 |
$int = dur($working_days->interval($row->{to},$row->{from})); |
$downtime->{$row->{sg}} += $dur; |
317 |
|
if ($q->param('use_time_limit')) { |
318 |
|
$int = $working_days->interval($row->{from},$row->{to}); |
319 |
|
$dur = dur($int)."<br><nobr><small>∑ ".dur($dur)."</small></nobr>"; |
320 |
|
$downinterval->{$row->{sg}} += $int; |
321 |
|
} else { |
322 |
|
$dur = dur($dur); |
323 |
|
} |
324 |
} |
} |
325 |
|
$sg_count->{$row->{sg}}++; |
326 |
|
|
327 |
print Tr( |
print Tr( |
328 |
td({-align=>'left',-valign=>'center'},$row->{sg}), |
td({-align=>'left',-valign=>'center'},$row->{sg}), |
329 |
td({-align=>'right',-bgcolor=>'#f0f0f0'},$from), |
td({-align=>'right',-bgcolor=>'#f0f0f0'},$from), |
330 |
td({-align=>'right'},d($row->{to})), |
td({-align=>'right'},d($row->{to})), |
331 |
td({-align=>'center',-bgcolor=>'#e0e0e0'},$dur), |
td({-align=>'center',-bgcolor=>'#e0e0e0'},$dur), |
|
td({-align=>'center',-bgcolor=>'#e0e0e0'},$int), |
|
332 |
td({-align=>'left'},$row->{desc}), |
td({-align=>'left'},$row->{desc}), |
333 |
),"\n"; |
),"\n"; |
334 |
} |
} |
337 |
# |
# |
338 |
|
|
339 |
foreach my $sg (keys %$downtime) { |
foreach my $sg (keys %$downtime) { |
340 |
print Tr(td({-colspan=>3,-align=>'right'},"total for $sg:"), |
my $dur; |
341 |
td({-bgcolor=>'#e0e0e0',-align=>'right'},dur($downtime->{$sg})), |
if ($downinterval->{$sg}) { |
342 |
td(small("in ".$sg_count->{$sg}." failures"))),"\n"; |
$dur=dur($downinterval->{$sg})."<br><nobr><small>∑ ".dur($downtime->{$sg})."</small></nobr>"; |
343 |
|
} else { |
344 |
|
$dur=dur($downtime->{sg}); |
345 |
|
} |
346 |
|
print Tr(td({-colspan=>3,-align=>'right'},"total for $sg:"), |
347 |
|
td({-bgcolor=>'#e0e0e0',-align=>'right'},$dur), |
348 |
|
td(small("in ".$sg_count->{$sg}." failures"))),"\n"; |
349 |
} |
} |
350 |
|
|
351 |
print end_table, |
print end_table, |