Projet

Général

Profil

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

root / plugins / other / multipng_async @ b9dfa6b0

Historique | Voir | Annoter | Télécharger (6,65 ko)

1
#!/usr/bin/env perl
2
package Munin::Plugin::Multiping::Async;
3
use 5.10.0;
4
use MooseX::POE;
5
use MooseX::POE::SweetArgs qw(event);
6
use POE::Quickie;
7
use Munin::Plugin;
8
use Storable;
9
use Digest;
10

    
11
=head1 NAME
12

    
13
multiping_async - Like the multiping plugin but runs asynchronously
14

    
15
=head1 SYNOPSIS
16

    
17
    munin-run multiping_async
18

    
19
=head1 CONFIGURATION
20

    
21
The following environment variables are used:
22

    
23
    host     - Whitespace-seperated list of hosts to ping
24
    times    - How many times to ping the hosts, 3 by default
25
    timeout  - The ping timeout (ping -W), 1 by default (ignored on Solaris)
26
    title    - The graph_title to use for the munin RRD graph
27
    category - What category the graph should be in, network by default
28

    
29
Configuration example, ping all the Linode clusters:
30

    
31
    # An optional custom category
32
    [multiping_async_*]
33
    env.category ping
34

    
35
    [multiping_async_linode]
36
    # From http://www.linode.com/speedtest/
37
    env.title Ping times to all the Linode clusters
38
    env.host  london1.linode.com newark1.linode.com atlanta1.linode.com dallas1.linode.com fremont1.linode.com
39

    
40
=head1 DESCRIPTION
41

    
42
Like the L<munin
43
multiping|http://munin-monitoring.org/browser/people/janl/src/node/node.d/multiping>
44
plugin except that it runs L<ping(1)> asynchronously with POE, and you
45
can add/remove hosts later on without screwing up your RRD files
46
(multiping reports statistics based on the order of hosts in
47
C<hosts=>).
48

    
49
This plugin used to use L<POE::Component::Client::Ping> but I switched
50
away from it due to having odd timing issues with it, and it had to
51
run as root.
52

    
53
This plugin requires the L<MooseX::POE> and L<POE::Quickie> modules
54
from CPAN. It has been tested with the Linux, FreeBSD and Solaris
55
L<ping(1)> implementations.
56

    
57
=head1 AUTHOR
58

    
59
E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org>
60

    
61
=head1 LICENSE
62

    
63
This program is in the public domain.
64

    
65
=head1 MAGIC MARKERS
66

    
67
 #%# family=manual
68

    
69
=cut
70

    
71
has graph_title => (
72
    isa => 'Str',
73
    is  => 'ro',
74
    default => $ENV{title} // 'Ping times',
75
    documentation => 'The munin graph_title',
76
);
77

    
78
has hosts => (
79
    isa        => 'ArrayRef',
80
    is         => 'ro',
81
    auto_deref => 1,
82
    default    => sub {
83
        my $host = $ENV{host} // '';
84
        return [ split /\s+/, $host ]
85
    },
86
    documentation => "Hosts we're going to ping",
87
);
88

    
89
has times => (
90
    isa           => 'Int',
91
    is            => 'ro',
92
    default       => $ENV{times} // 3,
93
    documentation => "How many times we ping each host (ping -c)",
94
);
95

    
96
has timeout => (
97
    isa           => 'Int',
98
    is            => 'ro',
99
    default       => $ENV{timeout} // 1,
100
    documentation => "How long until ping timeouts (ping -W)",
101
);
102

    
103
has category => (
104
    isa           => 'Str',
105
    is            => 'ro',
106
    default       => $ENV{category} // 'network',
107
    documentation => "What munin category we should appear in",
108
);
109

    
110
has should_config => (
111
    isa => 'Bool',
112
    is => 'ro',
113
    default => sub { defined $ARGV[0] and $ARGV[0] eq "config" },
114
    documentation => 'Spew out config section?',
115
);
116

    
117
has response => (
118
    isa        => 'HashRef',
119
    is         => 'ro',
120
    auto_deref => 0,
121
    default    => sub { +{} },
122
    documentation => 'To store ping responses',
123
);
124

    
125
has statefile => (
126
    isa           => 'Str',
127
    is            => 'ro',
128
    default       => $ENV{MUNIN_STATEFILE},
129
    documentation => 'Where we store state between invocations',
130
);
131

    
132
sub START {
133
    my ($self) = @_;
134

    
135
    die "You must supply some hosts" unless @{ $self->hosts } > 0;
136

    
137
    if ($self->should_config) {
138
        $self->print_config;
139
        return;
140
    }
141

    
142
    for my $host ($self->hosts) {
143
        POE::Quickie->new->run(
144
            Program     => [ $self->ping_arguments($host) ],
145
            StdoutEvent => 'stdout',
146
            ExitEvent   => 'exit',
147
            Context     => $host,
148
        );
149
    }
150
}
151

    
152
sub ping_arguments {
153
    my ($self, $host) = @_;
154

    
155
    given ($^O) {
156
        when ('solaris') {
157
            return ('ping', '-s', $host, '64', $self->times);
158
        }
159
        default {
160
            # Linux and FreeBSD
161
            return ('ping', '-c', $self->times, '-W', $self->timeout, => $host);
162
        }
163
    }
164
}
165

    
166
event stdout => sub {
167
    my ($self, $output, undef, $context) = @_;
168

    
169
    given ($output) {
170
        my $noslash = qr{[^/]+};
171
        # Linux output: rtt min/avg/max/mdev = 7.218/7.255/7.293/0.030 ms
172
        # BSD output  : round-trip min/avg/max/stddev = 34.935/35.665/36.684/0.743 ms
173
        # Solaris     : round-trip (ms)  min/avg/max/stddev = 5.82/5.95/6.01/0.11
174
        when (m[= (?<min>$noslash)/(?<avg>$noslash)/(?<max>$noslash)/]) {
175
            $self->response->{ $context } = $+{avg};
176
        }
177
    }
178
};
179

    
180
event exit => sub {
181
    my ($self, $code, $x, $context) = @_;
182

    
183
    given ($code) {
184
        when (0) {
185
            die "Got no response from $context" unless exists $self->response->{ $context };
186
            $self->yield( print_host => $context => $self->response->{ $context } );
187
        }
188
        default {
189
            # Host down, probably
190
            $self->yield( print_host => $context => 0 );
191
        }
192
    }
193

    
194
    return;
195
};
196

    
197
sub STOP {
198
    my ($self) = @_;
199

    
200
    if (not $self->should_config and my $file = $self->statefile) {
201
        my $res = $self->response;
202
        my $ret = store($res, $file);
203
        # use Data::Dumper;
204
        # say Dumper { gonna_store => $res, ret => $ret, file => $file };
205
    }
206
}
207

    
208
sub print_config {
209
    my ($self) = @_;
210
    my $title = $self->graph_title;
211
    my $times = $self->times;
212
    my $category = $self->category;
213
    print <<GRAPH;
214
graph_title $title
215
graph_args --base 1000 -l 0
216
graph_vlabel milliseconds
217
graph_category $category
218
graph_info Average ping times (over $times pings)
219
GRAPH
220
    for my $host ($self->sorted_hosts) {
221
        my $fieldname = $self->fieldname($host);
222
        print <<HOST;
223
$fieldname.label $host
224
$fieldname.info Average ping time over $times pings for $host
225
$fieldname.draw LINE2
226
HOST
227
    }
228
};
229

    
230
sub sorted_hosts {
231
    my ($self) = @_;
232

    
233
    my @hosts = $self->hosts;
234
    my $state = $self->statefile;
235

    
236
    given ($self->statefile) {
237
        when (-e and -r) {
238
            my $last_res = retrieve($_);
239
            my @sorted = sort { $last_res->{$b} <=> $last_res->{$a} } keys %$last_res;
240
            if ($last_res and @hosts == @sorted) {
241
                return @sorted;
242
            }
243
        }
244
    }
245

    
246
    return @hosts;
247
}
248

    
249
event print_host => sub {
250
    my ($self, $context, $time) = @_;
251

    
252
    my $fieldname = $self->fieldname($context);
253
    my $value = sprintf "%6.6f", $time;
254

    
255
    say "$fieldname.value $value";
256
};
257

    
258
sub fieldname {
259
    my ($self, $name) = @_;
260
    my $sha1 = substr Digest->new("SHA-1")->add($name)->hexdigest, 0, 10;
261
    return clean_fieldname($name) . '_' . $sha1;
262
}
263

    
264
no MooseX::POE;
265
Munin::Plugin::Multiping::Async->new;
266
POE::Kernel->run;