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 |
