1 |
dpavlin |
1 |
#!/bin/env perl |
2 |
|
|
############################################################ |
3 |
|
|
# |
4 |
|
|
# $Id: rrd-server.pl 1092 2008-01-23 14:23:51Z nicolaw $ |
5 |
|
|
# rrd-server.pl - Data gathering script for RRD::Simple |
6 |
|
|
# |
7 |
|
|
# Copyright 2006, 2007, 2008 Nicola Worthington |
8 |
|
|
# |
9 |
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
10 |
|
|
# you may not use this file except in compliance with the License. |
11 |
|
|
# You may obtain a copy of the License at |
12 |
|
|
# |
13 |
|
|
# http://www.apache.org/licenses/LICENSE-2.0 |
14 |
|
|
# |
15 |
|
|
# Unless required by applicable law or agreed to in writing, software |
16 |
|
|
# distributed under the License is distributed on an "AS IS" BASIS, |
17 |
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
18 |
|
|
# See the License for the specific language governing permissions and |
19 |
|
|
# limitations under the License. |
20 |
|
|
# |
21 |
|
|
############################################################ |
22 |
|
|
# vim:ts=4:sw=4:tw=78 |
23 |
|
|
|
24 |
|
|
BEGIN { |
25 |
|
|
# User defined constants |
26 |
|
|
use constant BASEDIR => '/home/nicolaw/webroot/www/rrd.me.uk'; |
27 |
|
|
use constant THEME => ('BACK#F5F5FF','SHADEA#C8C8FF','SHADEB#9696BE', |
28 |
|
|
'ARROW#61B51B','GRID#404852','MGRID#67C6DE'); |
29 |
|
|
} |
30 |
|
|
|
31 |
|
|
|
32 |
|
|
|
33 |
|
|
BEGIN { |
34 |
|
|
# Ensure we can find RRDs.so for RRDs.pm |
35 |
|
|
eval "use RRDs"; |
36 |
|
|
if ($@ && !defined $ENV{LD_LIBRARY_PATH}) { |
37 |
|
|
$ENV{LD_LIBRARY_PATH} = BASEDIR.'/lib'; |
38 |
|
|
exec($0,@ARGV); |
39 |
|
|
} |
40 |
|
|
} |
41 |
|
|
|
42 |
|
|
use 5.004; |
43 |
|
|
use strict; |
44 |
|
|
use warnings; |
45 |
|
|
use lib qw(../lib); |
46 |
|
|
use RRD::Simple 1.41; |
47 |
|
|
use RRDs; |
48 |
|
|
use Memoize; |
49 |
|
|
use Getopt::Std qw(); |
50 |
|
|
use File::Basename qw(basename); |
51 |
|
|
use File::Path qw(); |
52 |
|
|
use Config::General qw(); |
53 |
|
|
use File::Spec::Functions qw(catfile catdir); |
54 |
|
|
use vars qw($VERSION); |
55 |
|
|
|
56 |
|
|
$VERSION = '1.43' || sprintf('%d', q$Revision: 1092 $ =~ /(\d+)/g); |
57 |
|
|
|
58 |
|
|
# Get command line options |
59 |
|
|
my %opt = (); |
60 |
|
|
$Getopt::Std::STANDARD_HELP_VERSION = 1; |
61 |
|
|
$Getopt::Std::STANDARD_HELP_VERSION = 1; |
62 |
|
|
Getopt::Std::getopts('u:G:T:gthvVf?', \%opt); |
63 |
|
|
|
64 |
|
|
$opt{g} ||= $opt{G}; |
65 |
|
|
$opt{t} ||= $opt{T}; |
66 |
|
|
|
67 |
|
|
# Display help or version |
68 |
|
|
(VERSION_MESSAGE() && exit) if defined $opt{v}; |
69 |
|
|
(HELP_MESSAGE() && exit) if defined $opt{h} || defined $opt{'?'} || |
70 |
|
|
!(defined $opt{u} || defined $opt{g} || defined $opt{t}); |
71 |
|
|
|
72 |
|
|
# cd to the righr location and define directories |
73 |
|
|
chdir BASEDIR || die sprintf("Unable to chdir to '%s': %s", BASEDIR, $!); |
74 |
|
|
my %dir = map { ( $_ => BASEDIR."/$_" ) } qw(bin data etc graphs cgi-bin thumbnails); |
75 |
|
|
|
76 |
|
|
# Create an RRD::Simple object |
77 |
|
|
my $rrd = RRD::Simple->new(rrdtool => "$dir{bin}/rrdtool"); |
78 |
|
|
|
79 |
|
|
# Cache results from read_create_data() |
80 |
|
|
memoize('read_create_data'); |
81 |
|
|
memoize('read_graph_data'); |
82 |
|
|
memoize('basename'); |
83 |
|
|
memoize('graph_def'); |
84 |
|
|
|
85 |
|
|
# Update the RRD if we've been asked to |
86 |
|
|
my $hostname = defined $opt{u} ? update_rrd($rrd,\%dir,$opt{u}) : undef; |
87 |
|
|
|
88 |
|
|
# Generate some graphs |
89 |
|
|
my @hosts; |
90 |
|
|
for my $host (($hostname, $opt{G}, $opt{T})) { |
91 |
|
|
next unless defined $host; |
92 |
|
|
for (split(/\s*[,:]\s*/,$host)) { |
93 |
|
|
push(@hosts, $_) if defined($_) && length($_); |
94 |
|
|
} |
95 |
|
|
} |
96 |
|
|
@hosts = list_dir($dir{data}) unless @hosts; |
97 |
|
|
|
98 |
|
|
for my $hostname (@hosts) { |
99 |
|
|
create_thumbnails($rrd,\%dir,$hostname) if defined $opt{t}; |
100 |
|
|
create_graphs($rrd,\%dir,$hostname) if defined $opt{g}; |
101 |
|
|
} |
102 |
|
|
|
103 |
|
|
exit; |
104 |
|
|
|
105 |
|
|
|
106 |
|
|
|
107 |
|
|
|
108 |
|
|
sub create_graphs { |
109 |
|
|
my ($rrd,$dir,$hostname,@options) = @_; |
110 |
|
|
|
111 |
|
|
my ($caller) = ((caller(1))[3] || '') =~ /.*::(.+)$/; |
112 |
|
|
my $thumbnails = defined $caller && $caller eq 'create_thumbnails' ? 1 : 0; |
113 |
|
|
my $destdir = $thumbnails ? $dir->{thumbnails} : $dir->{graphs}; |
114 |
|
|
|
115 |
|
|
my @colour_theme = (color => [ THEME ]); |
116 |
|
|
my $gdefs = read_graph_data("$dir->{etc}/graph.defs"); |
117 |
|
|
my @hosts = defined $hostname ? ($hostname) |
118 |
|
|
: grep { -d catdir($dir->{data}, $_) } list_dir("$dir->{data}"); |
119 |
|
|
|
120 |
|
|
# For each hostname |
121 |
|
|
for my $hostname (sort @hosts) { |
122 |
|
|
# Create the graph directory for this hostname |
123 |
|
|
my $destination = "$destdir/$hostname"; |
124 |
|
|
File::Path::mkpath($destination) unless -d $destination; |
125 |
|
|
|
126 |
|
|
# For each RRD |
127 |
|
|
for my $file (grep { $_ =~ /\.rrd$/i && !-d catfile($dir->{data},$hostname,$_) } |
128 |
|
|
list_dir(catdir($dir->{data},$hostname)) |
129 |
|
|
) { |
130 |
|
|
|
131 |
|
|
# next unless $file =~ /cpu_utilisation/; |
132 |
|
|
|
133 |
|
|
my $rrdfile = catfile($dir->{data},$hostname,$file); |
134 |
|
|
my $graph = basename($file,'.rrd'); |
135 |
|
|
my $gdef = graph_def($gdefs,$graph); |
136 |
|
|
|
137 |
|
|
# Make sure we parse these raw commands with care |
138 |
|
|
my @raw_cmd_list = qw(DEF CDEF VDEF TEXTALIGN AREA STACK LINE\d* HRULE\d* VRULE\d* TICK SHIFT GPRINT PRINT COMMENT); |
139 |
|
|
my $raw_cmd_regex = '('.join('|',@raw_cmd_list).')'; |
140 |
|
|
# my $raw_cmd_regex = qr/^(?:[VC]?DEF|G?PRINT|COMMENT|[HV]RULE\d*|LINE\d*|AREA|TICK|SHIFT|STACK|TEXTALIGN)$/i; |
141 |
|
|
my @raw_commands; |
142 |
|
|
my @def_sources; |
143 |
|
|
my @def_sources_draw; |
144 |
|
|
|
145 |
|
|
# Allow users to put raw commands in the graph.defs file |
146 |
|
|
for my $raw_cmd (@raw_cmd_list) { |
147 |
|
|
for my $cmd (grep(/^$raw_cmd$/i, keys %{$gdef})) { |
148 |
|
|
my $values = $gdef->{$cmd}; |
149 |
|
|
$values = [($values)] unless ref($values); |
150 |
|
|
for my $v (@{$values}) { |
151 |
|
|
push @raw_commands, (sprintf('%s:%s', uc($cmd), $v) => ''); |
152 |
|
|
if ($cmd =~ /^[CV]?DEF$/i && $v =~ /^([a-z0-9\_\-]{1,30})=/) { |
153 |
|
|
push @def_sources, $1; |
154 |
|
|
} elsif ($cmd =~ /^(?:LINE\d*|AREA|G?PRINT|TICK|STACK)$/i && $v =~ /^([a-z0-9\_\-]{1,30})[#:]/) { |
155 |
|
|
push @def_sources_draw, $1; |
156 |
|
|
} |
157 |
|
|
} |
158 |
|
|
} |
159 |
|
|
} |
160 |
|
|
|
161 |
|
|
# Wrap the RRD::Simple calls in an eval() block just in case |
162 |
|
|
# the explode in a big nasty smelly heap! |
163 |
|
|
eval { |
164 |
|
|
|
165 |
|
|
# Anything that doesn't start with ^source(?:s|_) should just |
166 |
|
|
# be pushed on to the RRD::Simple->graph option stack (So this |
167 |
|
|
# would NOT include the "sources" option). |
168 |
|
|
my @graph_opts = map { ($_ => $gdef->{$_}) } |
169 |
|
|
grep(!/^source(s|_)/ && !/^$raw_cmd_regex$/i, keys %{$gdef}); |
170 |
|
|
|
171 |
|
|
# Anything that starts with ^source_ should be split up and passed |
172 |
|
|
# as a hash reference in to the RRD::Simple->graph option stack |
173 |
|
|
# (This would NOT include the "sources" option). |
174 |
|
|
push @graph_opts, map { |
175 |
|
|
# If we see a value from a key/value pair that looks |
176 |
|
|
# like it might be quoted and comma seperated, |
177 |
|
|
# "like this", 'then we should','split especially' |
178 |
|
|
if ($gdef->{$_} =~ /["']\s*,\s*["']/) { |
179 |
|
|
($_ => [ split(/\s*["']\s*,\s*["']\s*/,$gdef->{$_}) ]) |
180 |
|
|
|
181 |
|
|
# Otherwise just split on whitespace like the old |
182 |
|
|
# version of rrd-server.pl used to do. |
183 |
|
|
} else { |
184 |
|
|
($_ => [ split(/\s+/,$gdef->{$_}) ]) |
185 |
|
|
} |
186 |
|
|
} grep(/^source_/,keys %{$gdef}); |
187 |
|
|
|
188 |
|
|
# By default we want to tell RRDtool to be lazy and only generate |
189 |
|
|
# graphs when it's actually necessary. If we have the -f for force |
190 |
|
|
# flag then we won't let RRDtool be economical. |
191 |
|
|
push @graph_opts, ('lazy','') unless exists $opt{f}; |
192 |
|
|
|
193 |
|
|
# Only draw the sources we've been told to, and only |
194 |
|
|
# those that actually exist in the RRD file |
195 |
|
|
my @rrd_sources = $rrd->sources($rrdfile); |
196 |
|
|
if (defined $gdef->{sources}) { |
197 |
|
|
my @sources; |
198 |
|
|
for my $ds (split(/(?:\s+|\s*,\s*)/,$gdef->{sources})) { |
199 |
|
|
push @sources, $ds if grep(/^$ds$/,@rrd_sources); |
200 |
|
|
} |
201 |
|
|
push @graph_opts, ('sources',\@sources); |
202 |
|
|
} elsif (!@def_sources && !@def_sources_draw) { |
203 |
|
|
push @graph_opts, ('sources', [ sort @rrd_sources ]); |
204 |
|
|
} else { |
205 |
|
|
push @graph_opts, ('sources', undef); |
206 |
|
|
} |
207 |
|
|
|
208 |
|
|
printf "Generating %s/%s/%s ...\n", |
209 |
|
|
$hostname, |
210 |
|
|
($thumbnails ? 'thumbnails' : 'graphs'), |
211 |
|
|
$graph if $opt{V}; |
212 |
|
|
|
213 |
|
|
# Generate the graph and capture the results to |
214 |
|
|
# write the text file output in the same directory |
215 |
|
|
my @stack = ($rrdfile); |
216 |
|
|
push @stack, @raw_commands if @raw_commands; |
217 |
|
|
push @stack, ( destination => $destination ); |
218 |
|
|
push @stack, ( timestamp => 'both' ); |
219 |
|
|
push @stack, @colour_theme if @colour_theme; |
220 |
|
|
push @stack, @options if @options; |
221 |
|
|
push @stack, @graph_opts if @graph_opts; |
222 |
|
|
write_txt($rrd->graph(@stack)); |
223 |
|
|
|
224 |
|
|
my $glob = catfile($destination,"$graph*.png"); |
225 |
|
|
my @images = glob($glob); |
226 |
|
|
warn "[Warning] $rrdfile: Looks like \$rrd->graph() failed to generate any images in '$glob'\n." |
227 |
|
|
unless @images; |
228 |
|
|
}; |
229 |
|
|
warn "[Warning] $rrdfile: => $@" if $@; |
230 |
|
|
} |
231 |
|
|
} |
232 |
|
|
} |
233 |
|
|
|
234 |
|
|
sub graph_def { |
235 |
|
|
my ($gdefs,$graph) = @_; |
236 |
|
|
|
237 |
|
|
my $rtn = {}; |
238 |
|
|
for (keys %{$gdefs->{graph}}) { |
239 |
|
|
my $graph_key = qr(^$_$); |
240 |
|
|
if (my ($var) = $graph =~ /$graph_key/) { |
241 |
|
|
$rtn = { %{$gdefs->{graph}->{$_}} }; |
242 |
|
|
unless (defined $var && "$var" ne "1") { |
243 |
|
|
($var) = $graph =~ /_([^_]+)$/; |
244 |
|
|
} |
245 |
|
|
for my $key (keys %{$rtn}) { |
246 |
|
|
$rtn->{$key} =~ s/\$1/$var/g; |
247 |
|
|
} |
248 |
|
|
last; |
249 |
|
|
} |
250 |
|
|
} |
251 |
|
|
|
252 |
|
|
return $rtn; |
253 |
|
|
} |
254 |
|
|
|
255 |
|
|
sub list_dir { |
256 |
|
|
my $dir = shift; |
257 |
|
|
my @items = (); |
258 |
|
|
opendir(DH,$dir) || die "Unable to open file handle for directory '$dir': $!"; |
259 |
|
|
@items = grep(!/^\./,readdir(DH)); |
260 |
|
|
closedir(DH) || die "Unable to close file handle for directory '$dir': $!"; |
261 |
|
|
return @items; |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
sub create_thumbnails { |
265 |
|
|
my ($rrd,$dir,$hostname) = @_; |
266 |
|
|
my @thumbnail_options = (only_graph => '', width => 125, height => 32); |
267 |
|
|
create_graphs($rrd,$dir,$hostname,@thumbnail_options); |
268 |
|
|
} |
269 |
|
|
|
270 |
|
|
sub update_rrd { |
271 |
|
|
my ($rrd,$dir,$hostname) = @_; |
272 |
|
|
my $filename = shift @ARGV || undef; |
273 |
|
|
|
274 |
|
|
# Check out the input data |
275 |
|
|
die "Input data file '$filename' does not exist.\n" |
276 |
|
|
if defined $filename && !-f $filename; |
277 |
|
|
die "No data recieved while expecting STDIN data from rrd-client.pl.\n" |
278 |
|
|
if !$filename && !key_ready(); |
279 |
|
|
|
280 |
|
|
# Check the hostname is sane |
281 |
|
|
die "Hostname '$hostname' contains disallowed characters.\n" |
282 |
|
|
if $hostname =~ /[^\w\-\.\d]/ || $hostname =~ /^\.|\.$/; |
283 |
|
|
|
284 |
|
|
# Create the data directory for the RRD file if it doesn't exist |
285 |
|
|
File::Path::mkpath(catdir($dir->{data},$hostname)) unless -d catdir($dir->{data},$hostname); |
286 |
|
|
|
287 |
|
|
# Open the input file if specified |
288 |
|
|
if (defined $filename) { |
289 |
|
|
open(FH,'<',$filename) || die "[Error] $rrd: Unable to open file handle for file '$filename': $!"; |
290 |
|
|
select FH; |
291 |
|
|
}; |
292 |
|
|
|
293 |
|
|
# Parse the data |
294 |
|
|
my %data = (); |
295 |
|
|
while (local $_ = <>) { |
296 |
|
|
my ($path,$value) = split(/\s+/,$_); |
297 |
|
|
my ($time,@path) = split(/\./,$path); |
298 |
|
|
my $key = pop @path; |
299 |
|
|
|
300 |
|
|
# Check that none of the data is bogus or bollocks |
301 |
|
|
my $bogus = 0; |
302 |
|
|
$bogus++ unless $time =~ /^\d+$/; |
303 |
|
|
$bogus++ unless $value =~ /^[\d\.]+$/; |
304 |
|
|
for (@path) { |
305 |
|
|
$bogus++ unless /^[\w\-\_\.\d]+$/; |
306 |
|
|
} |
307 |
|
|
next if $bogus; |
308 |
|
|
|
309 |
|
|
my $rrdfile = catfile($dir->{data},$hostname,join('_',@path).'.rrd'); |
310 |
|
|
$data{$rrdfile}->{$time}->{$key} = $value; |
311 |
|
|
} |
312 |
|
|
|
313 |
|
|
# Process the data |
314 |
|
|
for my $rrdfile (sort keys %data) { |
315 |
|
|
for my $time (sort keys %{$data{$rrdfile}}) { |
316 |
|
|
eval { |
317 |
|
|
create_rrd($rrd,$dir,$rrdfile,$data{$rrdfile}->{$time}) |
318 |
|
|
unless -f $rrdfile; |
319 |
|
|
$rrd->update($rrdfile, $time, %{$data{$rrdfile}->{$time}}); |
320 |
|
|
}; |
321 |
|
|
warn "[Warning] $rrdfile: $@" if $@; |
322 |
|
|
} |
323 |
|
|
} |
324 |
|
|
|
325 |
|
|
# Close the input file if specified |
326 |
|
|
if (defined $filename) { |
327 |
|
|
select STDOUT; |
328 |
|
|
close(FH) || warn "[Warning] $rrd: Unable to close file handle for file '$filename': $!"; |
329 |
|
|
} |
330 |
|
|
|
331 |
|
|
return $hostname; |
332 |
|
|
} |
333 |
|
|
|
334 |
|
|
sub create_rrd { |
335 |
|
|
my ($rrd,$dir,$rrdfile,$data) = @_; |
336 |
|
|
my $defs = read_create_data(catfile($dir->{etc},'create.defs')); |
337 |
|
|
|
338 |
|
|
# Figure out what DS types to use |
339 |
|
|
my %create = map { ($_ => 'GAUGE') } sort keys %{$data}; |
340 |
|
|
while (my ($match,$def) = each %{$defs}) { |
341 |
|
|
next unless basename($rrdfile,qw(.rrd)) =~ /$match/; |
342 |
|
|
for my $ds (keys %create) { |
343 |
|
|
$create{$ds} = $def->{'*'}->{type} if defined $def->{'*'}->{type}; |
344 |
|
|
$create{$ds} = $def->{lc($ds)}->{type} if defined $def->{lc($ds)}->{type}; |
345 |
|
|
} |
346 |
|
|
} |
347 |
|
|
|
348 |
|
|
# Create the RRD file |
349 |
|
|
$rrd->create($rrdfile, %create); |
350 |
|
|
|
351 |
|
|
# Tune to use min and max values if specified |
352 |
|
|
while (my ($match,$def) = each %{$defs}) { |
353 |
|
|
next unless basename($rrdfile,qw(.rrd)) =~ /$match/; |
354 |
|
|
for my $ds ($rrd->sources($rrdfile)) { |
355 |
|
|
my $min = defined $def->{lc($ds)}->{min} ? $def->{lc($ds)}->{min} : |
356 |
|
|
defined $def->{'*'}->{min} ? $def->{'*'}->{min} : undef; |
357 |
|
|
RRDs::tune($rrdfile,'-i',"$ds:$min") if defined $min; |
358 |
|
|
|
359 |
|
|
my $max = defined $def->{lc($ds)}->{max} ? $def->{lc($ds)}->{max} : |
360 |
|
|
defined $def->{'*'}->{max} ? $def->{'*'}->{max} : undef; |
361 |
|
|
RRDs::tune($rrdfile,'-a',"$ds:$max") if defined $max; |
362 |
|
|
} |
363 |
|
|
} |
364 |
|
|
} |
365 |
|
|
|
366 |
|
|
sub HELP_MESSAGE { |
367 |
|
|
print qq{Syntax: rrd-server.pl <-u hostname,-g,-t,-V|-h|-v> [inputfile] |
368 |
|
|
-u <hostname> Update RRD data for <hostname> |
369 |
|
|
-g Create graphs from RRD data |
370 |
|
|
-t Create thumbnails from RRD data |
371 |
|
|
-V Display verbose progress information |
372 |
|
|
-v Display version information |
373 |
|
|
-h Display this help\n}; |
374 |
|
|
} |
375 |
|
|
|
376 |
|
|
# Display version |
377 |
|
|
sub VERSION { &VERSION_MESSAGE; } |
378 |
|
|
sub VERSION_MESSAGE { |
379 |
|
|
print "$0 version $VERSION ".'($Id: rrd-server.pl 1092 2008-01-23 14:23:51Z nicolaw $)'."\n"; |
380 |
|
|
} |
381 |
|
|
|
382 |
|
|
sub key_ready { |
383 |
|
|
my ($rin, $nfd) = ('',''); |
384 |
|
|
vec($rin, fileno(STDIN), 1) = 1; |
385 |
|
|
return $nfd = select($rin,undef,undef,3); |
386 |
|
|
} |
387 |
|
|
|
388 |
|
|
sub read_graph_data { |
389 |
|
|
my $filename = shift || undef; |
390 |
|
|
|
391 |
|
|
my %config = (); |
392 |
|
|
eval { |
393 |
|
|
my $conf = new Config::General( |
394 |
|
|
-ConfigFile => $filename, |
395 |
|
|
-LowerCaseNames => 1, |
396 |
|
|
-UseApacheInclude => 1, |
397 |
|
|
-IncludeRelative => 1, |
398 |
|
|
-MergeDuplicateBlocks => 1, |
399 |
|
|
-AllowMultiOptions => 1, |
400 |
|
|
-AutoTrue => 1, |
401 |
|
|
); |
402 |
|
|
%config = $conf->getall; |
403 |
|
|
}; |
404 |
|
|
warn "[Warning] $@" if $@; |
405 |
|
|
|
406 |
|
|
return \%config; |
407 |
|
|
} |
408 |
|
|
|
409 |
|
|
sub read_create_data { |
410 |
|
|
my $filename = shift || undef; |
411 |
|
|
my %defs = (); |
412 |
|
|
|
413 |
|
|
# Open the input file if specified |
414 |
|
|
my @data; |
415 |
|
|
if (defined $filename && -f $filename) { |
416 |
|
|
open(FH,'<',$filename) || die "Unable to open file handle for file '$filename': $!"; |
417 |
|
|
@data = <FH>; |
418 |
|
|
close(FH) || warn "Unable to close file handle for file '$filename': $!"; |
419 |
|
|
} else { |
420 |
|
|
@data = <DATA>; |
421 |
|
|
} |
422 |
|
|
|
423 |
|
|
# Parse the file that you've just selected |
424 |
|
|
for (@data) { |
425 |
|
|
last if /^__END__\s*$/; |
426 |
|
|
next if /^\s*$/ || /^\s*#/; |
427 |
|
|
|
428 |
|
|
my %def = (); |
429 |
|
|
@def{qw(rrdfile ds type min max)} = split(/\s+/,$_); |
430 |
|
|
next unless defined $def{ds}; |
431 |
|
|
$def{ds} = lc($def{ds}); |
432 |
|
|
$def{rrdfile} = qr($def{rrdfile}); |
433 |
|
|
for (keys %def) { |
434 |
|
|
if (!defined $def{$_} || $def{$_} eq '-') { |
435 |
|
|
delete $def{$_}; |
436 |
|
|
} elsif ($_ =~ /^(min|max)$/ && $def{$_} !~ /^[\d\.]+$/) { |
437 |
|
|
delete $def{$_}; |
438 |
|
|
} elsif ($_ eq 'type' && $def{$_} !~ /^(GAUGE|COUNTER|DERIVE|ABSOLUTE|COMPUTE)$/i) { |
439 |
|
|
delete $def{$_}; |
440 |
|
|
} |
441 |
|
|
} |
442 |
|
|
|
443 |
|
|
$defs{$def{rrdfile}}->{$def{ds}} = { |
444 |
|
|
map { ($_ => $def{$_}) } grep(!/^(rrdfile|ds)$/,keys %def) |
445 |
|
|
}; |
446 |
|
|
} |
447 |
|
|
|
448 |
|
|
return \%defs; |
449 |
|
|
} |
450 |
|
|
|
451 |
|
|
|
452 |
|
|
|
453 |
|
|
|
454 |
|
|
## |
455 |
|
|
## This processing and robustness of this routine is pretty |
456 |
|
|
## bloody dire and awful. It needs to be rewritten with crap |
457 |
|
|
## input data in mind rather than patching it every time I |
458 |
|
|
## find a new scenario for the data to not be as expected!! ;-) |
459 |
|
|
## |
460 |
|
|
|
461 |
|
|
sub write_txt { |
462 |
|
|
my %rtn = @_; |
463 |
|
|
while (my ($period,$data) = each %rtn) { |
464 |
|
|
my $filename = shift @{$data}; |
465 |
|
|
last if $filename =~ m,/thumbnails/,; |
466 |
|
|
|
467 |
|
|
my %values = (); |
468 |
|
|
my $max_len = 0; |
469 |
|
|
for (@{$data->[0]}) { |
470 |
|
|
my ($ds,$k,$v) = split(/\s+/,$_); |
471 |
|
|
next unless defined($ds) && length($ds) && defined($k); |
472 |
|
|
$values{$ds}->{$k} = $v; |
473 |
|
|
$max_len = length($ds) if length($ds) > $max_len; |
474 |
|
|
} |
475 |
|
|
|
476 |
|
|
if (open(FH,'>',"$filename.txt")) { |
477 |
|
|
printf FH "%s (%dx%d) %dK\n\n", |
478 |
|
|
basename($filename), |
479 |
|
|
(defined($data->[1]) ? $data->[1] : -1), |
480 |
|
|
(defined($data->[2]) ? $data->[2] : -1), |
481 |
|
|
(-e $filename ? (stat($filename))[7]/1024 : 0); |
482 |
|
|
|
483 |
|
|
for my $ds (sort keys %values) { |
484 |
|
|
for (qw(min max last)) { |
485 |
|
|
$values{$ds}->{$_} = '' |
486 |
|
|
unless defined $values{$ds}->{$_}; |
487 |
|
|
} |
488 |
|
|
printf FH "%-${max_len}s min: %s, max: %s, last: %s\n", $ds, |
489 |
|
|
$values{$ds}->{min}, $values{$ds}->{max}, $values{$ds}->{last}; |
490 |
|
|
} |
491 |
|
|
close(FH); |
492 |
|
|
} |
493 |
|
|
} |
494 |
|
|
} |
495 |
|
|
|
496 |
|
|
|
497 |
|
|
|
498 |
|
|
|
499 |
|
|
1; |
500 |
|
|
|
501 |
|
|
|
502 |
|
|
__DATA__ |
503 |
|
|
|
504 |
|
|
# * means all |
505 |
|
|
# - means undef/na |
506 |
|
|
|
507 |
|
|
# rrdfile ds type min max |
508 |
|
|
^net_traffic_.+ Transmit DERIVE 0 - |
509 |
|
|
^net_traffic_.+ Receive DERIVE 0 - |
510 |
|
|
|
511 |
|
|
# 10000000000 = 10 gigabit |
512 |
|
|
# 1000000000 = 1 gigabit |
513 |
|
|
# 100000000 = 100 megabit |
514 |
|
|
# rrdfile ds type min max |
515 |
|
|
^switch_traffic$ ifInOctets COUNTER 0 10000000000 |
516 |
|
|
^switch_traffic$ ifOutOctets COUNTER 0 10000000000 |
517 |
|
|
^switch_traffic_port\d+$ ifInOctets COUNTER 0 1000000000 |
518 |
|
|
^switch_traffic_port\d+$ ifOutOctets COUNTER 0 1000000000 |
519 |
|
|
|
520 |
|
|
# rrdfile ds type min max |
521 |
|
|
^hw_irq_interrupts_cpu\d+$ * DERIVE 0 - |
522 |
|
|
|
523 |
|
|
# rrdfile ds type min max |
524 |
|
|
^hdd_io_.+ * DERIVE 0 - |
525 |
|
|
|
526 |
|
|
# rrdfile ds type min max |
527 |
|
|
^net_nfs_operations$ * DERIVE 0 - |
528 |
|
|
|
529 |
|
|
# rrdfile ds type min max |
530 |
|
|
^apache_status$ ReqPerSec DERIVE 0 - |
531 |
|
|
^apache_status$ BytesPerSec DERIVE 0 - |
532 |
|
|
^apache_logs$ * DERIVE 0 - |
533 |
|
|
|
534 |
|
|
# rrdfile ds type min max |
535 |
|
|
^db_mysql_activity$ * DERIVE 0 - |
536 |
|
|
^db_mysql_activity_com$ * DERIVE 0 - |
537 |
|
|
^db_mysql_activity_scan$ * DERIVE 0 - |
538 |
|
|
^db_mysql_threads$ Threads_created DERIVE 0 - |
539 |
|
|
^db_mysql_traffic$ * DERIVE 0 - |
540 |
|
|
^db_mysql_connections$ * DERIVE 0 - |
541 |
|
|
^db_mysql_created_tmp$ * DERIVE 0 - |
542 |
|
|
|
543 |
|
|
# rrdfile ds type min max |
544 |
|
|
^mail_postfix_traffic$ * ABSOLUTE 0 - |
545 |
|
|
|
546 |
|
|
__END__ |
547 |
|
|
|
548 |
|
|
|