root / plugins / system / cpu_linux_multi @ 17f78427
Historique | Voir | Annoter | Télécharger (10,9 ko)
| 1 |
#! /usr/bin/perl |
|---|---|
| 2 |
######################################################################## |
| 3 |
# Copyright (c) 2012, Adrien Urban |
| 4 |
# All rights reserved. |
| 5 |
# |
| 6 |
# Redistribution and use in source and binary forms, with or without |
| 7 |
# modification, are permitted provided that the following conditions are |
| 8 |
# met: |
| 9 |
# |
| 10 |
# 1. Redistributions of source code must retain the above copyright |
| 11 |
# notice, this list of conditions and the following disclaimer. |
| 12 |
# 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 |
# distribution. |
| 16 |
# |
| 17 |
# 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 |
# # |
| 31 |
# WARNING WARNING WARNING WARNING WARNING WARNING # |
| 32 |
# # |
| 33 |
# This plugin does not work properly with multiple master # |
| 34 |
# # |
| 35 |
######################################################################## |
| 36 |
# |
| 37 |
# multigraph, supersampling, extended cpu information |
| 38 |
# |
| 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 |
# warning: increasing flushrate too much might cause partial write, and |
| 52 |
# loss of data. 0 to disable flush |
| 53 |
# |
| 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 |
# |
| 78 |
|
| 79 |
#%# family=auto |
| 80 |
#%# capabilities=autoconf |
| 81 |
|
| 82 |
use strict; |
| 83 |
use warnings; |
| 84 |
|
| 85 |
my $plugin = $0; |
| 86 |
$plugin =~ s/.*\///; |
| 87 |
|
| 88 |
# quick failsafe |
| 89 |
if (!defined $ENV{MUNIN_PLUGSTATE}) {
|
| 90 |
die "This plugin should be run via munin. Try munin-run $plugin\n"; |
| 91 |
} |
| 92 |
|
| 93 |
######################################################################## |
| 94 |
# If you want to change something, it's probably doable here |
| 95 |
# |
| 96 |
|
| 97 |
# order to display |
| 98 |
my $fields_order = [ |
| 99 |
'sys', 'usr', 'nice', 'idle', 'iowait', 'irq', 'soft', 'steal', 'guest', |
| 100 |
]; |
| 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 |
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 |
sub graph_title_n($) { "CPU#" . (shift) . " usage" };
|
| 149 |
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 |
# 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 |
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 |
} |
| 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 |
graph_category cpu |
| 338 |
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 |
# finished reading ? truncate it right away |
| 386 |
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 |
|
| 425 |
# for Munin Plugin Gallery |
| 426 |
# graph_category cpu |
| 427 |
|
