Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / system / cpu_linux_multi @ 17f78427

Historique | Voir | Annoter | Télécharger (10,9 ko)

1 44e66720 Adrien "ze" Urban
#! /usr/bin/perl
2
########################################################################
3 5a92255c Adrien "ze" Urban
# Copyright (c) 2012, Adrien Urban
4
# All rights reserved.
5 17f78427 Lars Kruse
#
6 5a92255c Adrien "ze" Urban
# Redistribution and use in source and binary forms, with or without
7
# modification, are permitted provided that the following conditions are
8 17f78427 Lars Kruse
# met:
9
#
10 5a92255c Adrien "ze" Urban
# 1. Redistributions of source code must retain the above copyright
11 17f78427 Lars Kruse
#    notice, this list of conditions and the following disclaimer.
12 5a92255c Adrien "ze" Urban
# 2. Redistributions in binary form must reproduce the above copyright
13
#    notice, this list of conditions and the following disclaimer in the
14
#    documentation and/or other materials provided with the
15 17f78427 Lars Kruse
#    distribution.
16
#
17 5a92255c Adrien "ze" Urban
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
#
29
########################################################################
30 44e66720 Adrien "ze" Urban
#                                                                      #
31
#    WARNING    WARNING    WARNING    WARNING    WARNING    WARNING    #
32
#                                                                      #
33
#       This plugin does not work properly with multiple master        #
34
#                                                                      #
35
########################################################################
36
#
37 8589c6df klemens
# multigraph, supersampling, extended cpu information
38 44e66720 Adrien "ze" Urban
#
39
# require: mpstat (to actually collect the data)
40
#
41
#
42
# ENV (default):
43
#  MUNIN_PLUGSTATE  - pid and cache files gets there
44
#
45
# ENV (user defined):
46
#  MUNIN_UPDATERATE - rate at which to update (default: 1s)
47
#  MUNIN_CACHEFLUSH_RATE - flush data every N batch (default: 1)
48
#  MUNIN_MPSTAT - binary to use as mpstat
49
#
50
#  increase cache flush rate if you have i/o performance issues
51 5a92255c Adrien "ze" Urban
#  warning: increasing flushrate too much might cause partial write, and
52
#  loss of data. 0 to disable flush
53 44e66720 Adrien "ze" Urban
#
54
#
55
# Parent graph: cpu usage per core/thread
56
# child graph(1): detailed cpu usage overall
57
# child graph(n): detailed cpu usage per thread
58
#
59
# Known bugs:
60
#
61
#   Multi-Master
62
#     If there are many masters, the data is only sent once. Each master will
63
#     only have part of the data.
64
#
65
#   Everlasting
66
#     The daemon is launched on first config/fetch. A touch of the pidfile is
67
#     done on every following config/fetch. The daemon should check if the
68
#     pidfile is recent (configurable) enough, and stop itself if not.
69
#
70
#   Graph Order
71
#     There is currently (2.0.6) noway to order childgraphs.
72
#
73
#   RRD file
74
#     The master currently (2.0.6) generate rrd file for aggregate values, and
75
#     complains that no data is provided for them (but the graph still works
76
#     fine)
77 5a92255c Adrien "ze" Urban
#
78 44e66720 Adrien "ze" Urban
79
#%# family=auto
80
#%# capabilities=autoconf
81
82
use strict;
83
use warnings;
84
85
my $plugin = $0;
86
$plugin =~ s/.*\///;
87
88 5a92255c Adrien "ze" Urban
# quick failsafe
89
if (!defined $ENV{MUNIN_PLUGSTATE}) {
90 5d4c3e8e Adrien "ze" Urban
	die "This plugin should be run via munin. Try munin-run $plugin\n";
91 5a92255c Adrien "ze" Urban
}
92
93
########################################################################
94
# If you want to change something, it's probably doable here
95
#
96
97 44e66720 Adrien "ze" Urban
# order to display
98
my $fields_order = [
99 5a92255c Adrien "ze" Urban
	'sys', 'usr', 'nice', 'idle', 'iowait', 'irq', 'soft', 'steal', 'guest',
100 44e66720 Adrien "ze" Urban
];
101
# order is the order given by mpstat
102
my $fields_info = [
103
	{
104
		name => 'usr',
105
		label => 'usr',
106
		info => "%s time spent in normal programs and daemons",
107
	}, {
108
		name => 'nice',
109
		label => 'nice',
110
		info => "%s time spent in nice(1)d programs and daemons",
111
	}, {
112
		name => 'sys',
113
		label => 'sys',
114
		info => "%s time spent in kernel system activity",
115
	}, {
116
		name => 'iowait',
117
		label => 'iowait',
118
		info => "%s time spent waiting for blocking I/O operations",
119
	}, {
120
		name => 'irq',
121
		label => 'irq',
122
		info => "%s time spent handling interrupts",
123
	}, {
124
		name => 'soft',
125
		label => 'soft',
126
		info => "%s time spent handling software interrupts",
127
	}, {
128
		name => 'steal',
129
		label => 'steal',
130
		info => "%s time spent elsewhere (stolen from us)",
131
	}, {
132
		name => 'guest',
133
		label => 'guest',
134
		info => "%s time spent in a guest operating system",
135
	}, {
136
		name => 'idle',
137
		label => 'idle',
138
		info => "%s time spent idling (waiting to get something to do)",
139
	}
140
];
141
142 5a92255c Adrien "ze" Urban
sub pidfile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.pid" }
143
sub cachefile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.cache" }
144
145
sub graph_name() { "cpu_extended_multi_1s" };
146
sub graph_title() { "CPU usage" };
147
sub graph_title_all() { "Overall CPU usage" };
148 d38eda28 Steve Schnepp
sub graph_title_n($) { "CPU#" . (shift) . " usage" };
149 5a92255c Adrien "ze" Urban
sub acquire_name() { "<$plugin> collecting information" }
150
151
########################################################################
152
# if you need to change something after that line, It should probably be
153
# changed to be configurable above it.
154
#
155
156 44e66720 Adrien "ze" Urban
# mpstat sampling interval
157
my $update_rate = 1;
158
if (defined $ENV{MUNIN_UPDATERATE}) {
159
	if ($ENV{MUNIN_UPDATERATE} =~ /^[1-9][0-9]*$/) {
160
		$update_rate = int($ENV{MUNIN_UPDATERATE});
161
	} else {
162
		print STDERR "Invalid update_rate: $ENV{MUNIN_UPDATERATE}";
163
	}
164
}
165
166
my $flush_interval = 1;
167
if (defined $ENV{MUNIN_CACHEFLUSH_RATE}) {
168
	if ($ENV{MUNIN_CACHEFLUSH_RATE} =~ /^[0-9]+$/) {
169
		$update_rate = int($ENV{MUNIN_CACHEFLUSH_RATE});
170
	} else {
171
		print STDERR "Invalid flush rate: $ENV{MUNIN_CACHEFLUSH_RATE}";
172
	}
173
}
174
175
my $mpstat = "mpstat";
176
if (defined $ENV{MUNIN_MPSTAT}) {
177
	if (-f $ENV{MUNIN_MPSTAT}) {
178
		print STDERR "MUNIN_STAT: file not found: $ENV{MUNIN_MPSTAT}";
179
	} else {
180
		$mpstat = defined $ENV{MUNIN_MPSTAT};
181
	}
182
}
183
184
my $cpu_count_cache = undef;
185
sub cpu_count() {
186
	if (not defined $cpu_count_cache) {
187 6d2da7ad Steve Schnepp
               open MPSTAT, "$mpstat -P ALL |" or die "open mpstat|: $!\n";
188
               $cpu_count_cache = 0;
189
               while (<MPSTAT>) {
190
                       chomp;
191
                       my @field = split();
192
                       next unless ($field[1] && ($field[1] =~ /^([0-9]+)$/));
193
                       $cpu_count_cache ++;
194
               }
195
               close(MPSTAT);
196 44e66720 Adrien "ze" Urban
	}
197
	return $cpu_count_cache;
198
}
199
200
sub is_running() {
201
	if (-f pidfile()) {
202
		my $pid = undef;
203
		if (open FILE, "<", pidfile()) {
204
			$pid = <FILE>;
205
			close FILE;
206
			chomp $pid;
207
		}
208
		if ($pid) {
209
			# does not exist ? kill it
210
			if (kill 0, $pid) {
211
				return 1;
212
			}
213
		}
214
 		unlink(pidfile());
215
	}
216
	return 0;
217
}
218
219
220
# FIXME: should also trap kill sigint and sigterm
221
# FIXME: check pidfile got touched recently
222
sub acquire() {
223
	$0 = acquire_name();
224
	$ARGV = [ '<daemon>' ];
225
	$0 = "<$plugin> collecting information";
226
	open PIDFILE, '>', pidfile() or die "open: @{[ pidfile() ]}: $!\n";
227
	print PIDFILE $$, "\n";
228
	close PIDFILE;
229
	open CACHE, ">>", cachefile() or die "open: @{[ cachefile() ]}: $!\n";
230
	open MPSTAT, "-|", "$mpstat -P ALL $update_rate" or
231
		die "open mpstat|: $!\n";
232
	my $flush_count = 0;
233
	while (<MPSTAT>) {
234
		chomp;
235
		my @field = split();
236
		if (!($field[1] =~ /^(all|[0-9]+)$/)) {
237
			next;
238
		}
239
		$field[0] = $field[1];
240
		$field[1] = time();
241
		print CACHE join(" ", @field), "\n";
242
		if ($flush_interval) {
243
			if ($flush_interval == ++$flush_count) {
244
				CACHE->flush();
245
				$flush_count = 0;
246
			}
247
		}
248
	}
249
	unlink(pidfile());
250
	unlink(cachefile());
251
}
252
253
sub run_daemon() {
254
	if (is_running()) {
255
		my $atime;
256
		my $mtime;
257
		$atime = $mtime = time;
258
		utime $atime, $mtime, pidfile();
259
	} else {
260
		if (0 == fork()) {
261
			close(STDIN);
262
			close(STDOUT);
263
			close(STDERR);
264
			open STDIN, "<", "/dev/null";
265
			open STDOUT, ">", "/dev/null";
266
			open STDERR, ">", "/dev/null";
267
			acquire();
268
			exit(0);
269
		}
270
	}
271
}
272
273
274
sub run_autoconf() {
275
	# in case we have specified args, check the file before that
276
	my $file = $mpstat;
277
	$file =~ s/ .*//;
278
	my $path = `which "$file"`;
279
	if ($path) {
280
		print "yes\n";
281
	} else {
282
		print "no\n";
283
	}
284
}
285
286
sub show_config($$$) {
287
	my $i = shift;
288
	my $name = shift;
289
	my $title = shift;
290
	my $graph_order = "graph_order";
291
	for my $field (@$fields_order) {
292
		$graph_order .= " $field";
293
	}
294
	print <<EOF;
295
multigraph @{[ graph_name() ]}.cpu$i
296
graph_title $title
297
graph_vlabel cpu use %
298
graph_scale no
299
update_rate 1
300
graph_data_size custom 1d, 10s for 1w, 1m for 1t, 5m for 1y
301
$graph_order
302
EOF
303
	for my $field (@$fields_info) {
304
		my $style = "STACK";
305
		if ($field->{name} eq $fields_order->[0]) {
306
			$style = "AREA";
307
		}
308
		print <<EOF;
309
$field->{name}.label $field->{label}
310
$field->{name}.draw $style
311
$field->{name}.info @{[ sprintf($field->{info}, $name) ]}
312
$field->{name}.min 0
313
$field->{name}.cdef $field->{name}
314
EOF
315
	}
316
}
317
318
sub run_config() {
319
	run_daemon();
320
	my $cpus = cpu_count();
321
	my $graph_order = "graph_order";
322
	my $sub_order = "order cpuall";
323
	for (my $i = 0; $i < $cpus; ++$i) {
324
		$graph_order .= " use$i=@{[ graph_name() ]}.cpu$i.idle";
325
		$sub_order .= " cpu$i";
326
	}
327
# none of those seems to have any effect
328
#domain_$sub_order
329
#node_$sub_order
330
#graph_$sub_order
331
#service_$sub_order
332
#category_$sub_order
333
#group_$sub_order
334
335
	print <<EOF;
336
multigraph @{[ graph_name() ]}
337 34eeebbe Lars Kruse
graph_category cpu
338 44e66720 Adrien "ze" Urban
graph_title @{[ graph_title() ]}
339
graph_vlabel cpu use %
340
graph_scale no
341
graph_total All CPUs
342
update_rate 1
343
graph_data_size custom 1d, 10s for 1w, 1m for 1t, 5m for 1y
344
$graph_order
345
EOF
346
	my $style="AREA";
347
	for (my $i = 0; $i < $cpus; ++$i) {
348
		print <<EOF;
349
use$i.label CPU#$i
350
use$i.draw $style
351
use$i.cdef 100,use$i,-,${cpus},/
352
EOF
353
		$style = 'STACK';
354
	}
355
	# detailed sub graphs - 1 for all, and 1 per cpu
356
	show_config("all", "all CPU", graph_title_all());
357
	for (my $i = 0; $i < $cpus; ++$i) {
358
		show_config($i, "CPU$i", graph_title_n($i));
359
	}
360
}
361
362
sub fetch_showline($) {
363
	my $line = shift;
364
	my $n = 2;
365
	for my $field (@$fields_info) {
366
		print <<EOF;
367
$field->{name}.value $line->[1]:$line->[$n]
368
EOF
369
		++$n;
370
	}
371
}
372
sub run_fetch() {
373
	run_daemon();
374
	if (open CACHE, "+<", cachefile()) {
375
		my $cpus = {};
376
		while (<CACHE>) {
377
			chomp;
378
			my $field = [];
379
			@$field = split(/ /);
380
			if (not defined $cpus->{$field->[0]}) {
381
				$cpus->{$field->[0]} = [];
382
			}
383
			push @{$cpus->{$field->[0]}}, $field;
384
		}
385 fba800ae Veres Lajos
		# finished reading ? truncate it right away
386 44e66720 Adrien "ze" Urban
		truncate CACHE, 0;
387
		close CACHE;
388
		foreach my $cpu (keys %$cpus) {
389
			print <<EOF;
390
multigraph @{[ graph_name() ]}.cpu$cpu
391
EOF
392
			foreach my $line (@{$cpus->{$cpu}}) {
393
				fetch_showline($line);
394
			}
395
		}
396
	}
397
}
398
399
my $cmd = 'fetch';
400
if (defined $ARGV[0]) {
401
	$cmd = $ARGV[0];
402
}
403
if ('fetch' eq $cmd) {
404
	run_fetch();
405
} elsif ('config' eq $cmd) {
406
	run_config();
407
} elsif ('autoconf' eq $cmd) {
408
	run_autoconf();
409
} elsif ('daemon' eq $cmd) {
410
	run_daemon();
411
} else {
412
	print STDERR <<EOF;
413
$0: unrecognized command
414
415
Usage:
416
	$0 autoconf - check if we have everything we need
417
	$0 config - show plugin configuration
418
	$0 fetch - fetch latest data
419
	$0 daemon - launch daemon
420
EOF
421
	exit(1);
422
}
423
exit(0);
424 7e562477 dipohl
425
# for Munin Plugin Gallery
426
# graph_category cpu