3 # haproxyng Munin Plugin
4 # Multigraph plugin which monitors the haproxy service.
5 # (c) 2014-2015 Jonathan Wright <jon@than.io>
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 # Configure the Perl Environment
28 use IO::Socket::UNIX qw( SOCK_STREAM );
31 # Configure Program details
32 our ($_program, $_version, $_author);
33 $_program = 'haproxyng';
35 $_author = 'Jonathan Wright <jon@than.io>';
38 # Field names to locations for all the CSV data provided by HAProxy
40 STAT_SERVICE_NAME => 1,
41 STAT_QUEUED_REQUESTS => 2,
43 STAT_SESSIONS_CURRENT => 4,
44 STAT_SESSIONS_MAX => 5,
45 STAT_SESSIONS_LIMIT => 6,
46 STAT_CONNECTIONS_TOTAL => 7,
49 STAT_REQUESTS_DENIED => 10,
50 STAT_RESPONSES_DENIED => 11,
51 STAT_REQUESTS_ERROR => 12,
52 STAT_CONNECTIONS_ERROR => 13,
53 STAT_RESPONSES_ERROR => 14,
54 STAT_CONNECTIONS_RETRIED => 15,
55 STAT_CONNECTIONS_REDISPATCHED => 16,
58 STAT_SERVER_ACTIVE => 19, STAT_SERVERS_ACTIVE => 19,
59 STAT_SERVER_BACKUP => 20, STAT_SERVERS_BACKUP => 20,
60 STAT_CHECKS_FAIL => 21,
61 STAT_CHECKS_GO_DOWN => 22,
62 STAT_CHECKS_LAST_CHANGE => 23,
63 STAT_CHECKS_DOWNTIME => 24,
64 STAT_QUEUED_LIMIT => 25,
69 STAT_SESSIONS_TOTAL => 30,
71 STAT_SERVICE_TYPE => 32,
72 STAT_SESSIONS_RATE_CURRENT => 33,
73 STAT_SESSIONS_RATE_LIMIT => 34,
74 STAT_SESSIONS_RATE_MAX => 35,
75 STAT_CHECK_STATUS => 36,
76 STAT_CHECK_CODE => 37,
77 STAT_CHECK_DURATION => 38,
78 STAT_RESPONSES_HTTP_1XX => 39,
79 STAT_RESPONSES_HTTP_2XX => 40,
80 STAT_RESPONSES_HTTP_3XX => 41,
81 STAT_RESPONSES_HTTP_4XX => 42,
82 STAT_RESPONSES_HTTP_5XX => 43,
83 STAT_RESPONSES_HTTP_XXX => 44,
84 STAT_CHECK_FAILED_DETAILS => 45,
85 STAT_REQUESTS_RATE_CURRENT => 46,
86 STAT_REQUESTS_RATE_MAX => 47,
87 STAT_REQUESTS_TOTAL => 48,
88 STAT_ABORTS_CLIENT => 49,
89 STAT_ABORTS_SERVER => 50,
90 STAT_COMPRESSOR_IN => 51,
91 STAT_COMPRESSOR_OUT => 52,
92 STAT_COMPRESSOR_BYPASSED => 53,
93 STAT_COMPRESSOR_REQUESTS => 54,
94 STAT_SESSIONS_LAST => 55,
95 STAT_CHECK_HEALTH_LAST => 56,
96 STAT_CHECK_AGENT_LAST => 57,
97 STAT_TIME_QUEUE => 58,
98 STAT_TIME_CONNECT => 59,
99 STAT_TIME_RESPONSE => 60,
100 STAT_TIME_TOTAL => 61,
101 # Types used by HAProxy for some fields
106 # Types used to define which attributed to give a graph
112 # Program-specific constants
113 GRAPH_CATEGORY => 'haproxyng',
117 # Configurable limits
118 MAX_BANDWIDTH => (1024**3),
119 MAX_TIMING => (1200000),
120 MAX_SESSIONS => (10000),
121 MAX_RESPONSES => (100000),
122 MAX_BACKENDS => (1000),
123 # Constants to match Munin keywords
126 AREASTACK => 'AREASTACK',
128 COLOUR_BLACK => '000000',
131 # Configure basic variables for program operation
132 our ($_socket, $_clean, %data);
133 # Get the location of the socket we'll connect to
134 $_socket = exists $ENV{'socket'} ? $ENV{'socket'} : '/var/run/haproxy.sock';
135 $_clean = (exists $ENV{'clean'} ? quotemeta $ENV{'clean'} : '');
138 # This is a special multi-graph plugin, so make sure that whatever version
139 # of Munin we're running in is enabled and configured for it.
142 # Now work out what we are being asked to do, then run it.
143 my $request = $ARGV[0] || "fetch";
144 eval "do_${request}();"
145 or croak "do_${request}: $@";
151 my ($t, %v); $t = shift;
153 my $s = IO::Socket::UNIX->new(
157 ) or croak "Cannot connect to socket ".$_socket;
159 print $s "show stat\n";
161 chomp; next unless length;
162 my @stat = split (',');
164 switch ($stat[STAT_SERVICE_TYPE]) {
165 # Process all FRONTEND type entries; these are singular and are totals
166 # only for each of the frontends configured
168 $v{$stat[STAT_PROXY_NAME]} = {
169 type => TYPE_FRONTEND,
170 connections => ($stat[STAT_CONNECTIONS_TOTAL] || 0),
171 sessions => ($stat[STAT_SESSIONS_CURRENT] || 0),
174 in => ($stat[STAT_BYTES_IN] || 0),
175 out => ($stat[STAT_BYTES_OUT] || 0),
178 total => ($stat[STAT_REQUESTS_TOTAL] || 0),
179 http1xx => ($stat[STAT_RESPONSES_HTTP_1XX] || 0),
180 http2xx => ($stat[STAT_RESPONSES_HTTP_2XX] || 0),
181 http3xx => ($stat[STAT_RESPONSES_HTTP_3XX] || 0),
182 http4xx => ($stat[STAT_RESPONSES_HTTP_4XX] || 0),
183 http5xx => ($stat[STAT_RESPONSES_HTTP_5XX] || 0),
184 httpxxx => ($stat[STAT_RESPONSES_HTTP_XXX] || 0),
188 # Process all BACKEND type entries; these are the totals for each backend
189 # and don't have the same amount of information as SERVERs
191 # We can't 'set' the hash here as the backend totals are normally after
192 # the backend's servers, so would override anything previously set
194 $v{$stat[STAT_PROXY_NAME]}{'type'} = TYPE_BACKEND;
195 $v{$stat[STAT_PROXY_NAME]}{'connections'} = ($stat[STAT_CONNECTIONS_TOTAL] || 0);
196 $v{$stat[STAT_PROXY_NAME]}{'sessions'} = ($stat[STAT_SESSIONS_CURRENT] || 0);
197 $v{$stat[STAT_PROXY_NAME]}{'queued'} = ($stat[STAT_QUEUED_REQUESTS] || 0);
198 $v{$stat[STAT_PROXY_NAME]}{'active'} = ($stat[STAT_SERVERS_ACTIVE] || 0);
199 $v{$stat[STAT_PROXY_NAME]}{'backup'} = ($stat[STAT_SERVERS_BACKUP] || 0);
200 $v{$stat[STAT_PROXY_NAME]}{'bandwidth'} = {
201 in => ($stat[STAT_BYTES_IN] || 0),
202 out => ($stat[STAT_BYTES_OUT] || 0),
204 $v{$stat[STAT_PROXY_NAME]}{'responses'} = {
205 total => ($stat[STAT_RESPONSES_HTTP_1XX] || 0)
206 + ($stat[STAT_RESPONSES_HTTP_2XX] || 0)
207 + ($stat[STAT_RESPONSES_HTTP_3XX] || 0)
208 + ($stat[STAT_RESPONSES_HTTP_4XX] || 0)
209 + ($stat[STAT_RESPONSES_HTTP_5XX] || 0)
210 + ($stat[STAT_RESPONSES_HTTP_XXX] || 0),
211 http1xx => ($stat[STAT_RESPONSES_HTTP_1XX] || 0),
212 http2xx => ($stat[STAT_RESPONSES_HTTP_2XX] || 0),
213 http3xx => ($stat[STAT_RESPONSES_HTTP_3XX] || 0),
214 http4xx => ($stat[STAT_RESPONSES_HTTP_4XX] || 0),
215 http5xx => ($stat[STAT_RESPONSES_HTTP_5XX] || 0),
216 httpxxx => ($stat[STAT_RESPONSES_HTTP_XXX] || 0),
219 # Process all SERVER type entries, which are the most details and give
220 # information about how each server is responding
222 # Only set the server itself directly, otherwise we may override
223 # anything previously set
224 $v{$stat[STAT_PROXY_NAME]}{'servers'}{$stat[STAT_SERVICE_NAME]} = {
225 active => ($stat[STAT_SERVER_ACTIVE] ? 1 : 0),
226 backup => ($stat[STAT_SERVER_BACKUP] ? 1 : 0),
227 up => ($stat[STAT_STATUS] eq 'UP' ? 1 : 0),
228 down => ($stat[STAT_STATUS] eq 'DOWN' ? 1 : 0),
229 disabled => ($stat[STAT_STATUS] =~ /^(MAINT|DRAIN|NOLB)/i ? 1 : 0),
230 connections => ($stat[STAT_CONNECTIONS_TOTAL] || 0),
231 sessions => ($stat[STAT_SESSIONS_CURRENT] || 0),
232 queued => ($stat[STAT_QUEUED_REQUESTS] || 0),
234 in => ($stat[STAT_BYTES_IN] || 0),
235 out => ($stat[STAT_BYTES_OUT] || 0),
237 status => ($stat[STAT_STATUS] || 0),
239 queue => ($stat[STAT_TIME_QUEUE] || 0),
240 connect => ($stat[STAT_TIME_CONNECT] || 0),
241 response => ($stat[STAT_TIME_RESPONSE] || 0),
242 total => ($stat[STAT_TIME_TOTAL] || 0)
245 total => ($stat[STAT_RESPONSES_HTTP_1XX] || 0)
246 + ($stat[STAT_RESPONSES_HTTP_2XX] || 0)
247 + ($stat[STAT_RESPONSES_HTTP_3XX] || 0)
248 + ($stat[STAT_RESPONSES_HTTP_4XX] || 0)
249 + ($stat[STAT_RESPONSES_HTTP_5XX] || 0)
250 + ($stat[STAT_RESPONSES_HTTP_XXX] || 0),
251 http1xx => ($stat[STAT_RESPONSES_HTTP_1XX] || 0),
252 http2xx => ($stat[STAT_RESPONSES_HTTP_2XX] || 0),
253 http3xx => ($stat[STAT_RESPONSES_HTTP_3XX] || 0),
254 http4xx => ($stat[STAT_RESPONSES_HTTP_4XX] || 0),
255 http5xx => ($stat[STAT_RESPONSES_HTTP_5XX] || 0),
256 httpxxx => ($stat[STAT_RESPONSES_HTTP_XXX] || 0),
268 my ($type, $name, $values) = (shift, shift, shift);
270 my %values_ = %{$values};
271 my $type_ = $type eq TYPE_FRONTEND ? '_fe' :
272 $type eq TYPE_BACKEND ? '_be' : '';
275 foreach my $level (@{$name}) {
276 $level =~ s/[^a-z0-9]+/_/g;
277 $name_ .= ($name_ ? '.' : '').$level;
280 printf "multigraph %s%s_%s\n", GRAPH_CATEGORY, $type_, $name_;
281 foreach my $key (sort keys %values_) {
282 (my $key_ = $key) =~ s/[^a-z0-9]+/_/g;
283 printf "%s.value %0.0f\n", $key_, $values_{$key};
289 my ($graph, $type, $name, $title, $metrics) = (shift, shift, shift, shift, shift, shift);
291 my %metrics_ = %{$metrics};
293 my $type_ = $type eq TYPE_FRONTEND ? '_fe' :
294 $type eq TYPE_BACKEND ? '_be' : '';
296 $title =~ s/$_clean//g if $_clean;
297 my $title_ = $type eq TYPE_FRONTEND ? ' Frontend' :
298 $type eq TYPE_BACKEND ? ' Backend' : '';
301 foreach my $level (@{$name}) {
303 $level =~ s/[^a-z0-9]+/_/g;
304 $name_ .= ($name_ ? '.' : '').$level;
309 printf "multigraph %s%s_%s\n", GRAPH_CATEGORY, $type_, $name_;
310 printf "graph_category %s\n", GRAPH_CATEGORY;
311 printf "graph_title HAProxy%s %s\n", $title_, $title;
312 printf "graph_width %d\n", GRAPH_WIDTH;
315 case TYPE_BANDWIDTH {
316 print "graph_args --base 1024\n".
317 "graph_vlabel bits/second [in(-); out(+)]\n";
318 $max = MAX_BANDWIDTH;
321 print "graph_args --base 1000\n".
322 "graph_vlabel Time in Seconds\n";
326 print "graph_args --base 1000\n".
327 "graph_vlabel [Queued(-); Sessions(+)]\n";
330 case TYPE_RESPONSES {
331 print "graph_args --base 1000\n".
332 "graph_vlabel Responses per Second\n";
333 $max = MAX_RESPONSES;
336 print "graph_args --base 1000\n".
337 "graph_vlabel Total Backends\n";
342 foreach my $metric (sort keys %metrics_) {
343 (my $metric_ = $metric) =~ s/[^a-z0-9]+/_/g;
344 foreach my $key (keys %{$metrics_{$metric}}) {
347 my $value = $metrics_{$metric}{$key};
348 $value =~ s/$_clean//g if $_clean;
349 printf "%s.%s %s\n", $metric_, $key, $value;
352 printf "%s.%s %s\n", $metric_, $key, $metric_.','.$metrics_{$metric}{$key};
355 my $value = $metrics_{$metric}{$key};
356 $value =~ s/[^a-z0-9]+/_/g;
357 printf "%s.%s %s\n", $metric_, $key, $value;
360 printf "%s.%s %s\n", $metric_, $key, $metrics_{$metric}{$key};
363 # Always add min/max if max has been defined
365 printf "%s.%s %s\n", $metric_, 'min', 0;
366 printf "%s.%s %s\n", $metric_, 'max', $max if $max;
372 # frontend(bandwidth)
374 # frontend(bandwidth/service)
375 # backend(bandwidth/service)
376 # backend(bandwidth/service/host)
378 sub haproxy_bandwidth {
379 my ($operation, $data_) = (shift, shift);
380 my %data = %{$data_};
382 switch ($operation) {
386 my (%fe_metrics, %be_metrics, %submetrics);
388 foreach my $service (sort keys %data) {
391 # Prepare the field configuration
403 negative => "${service}_in",
406 # This will copy the hash for the main metric
407 switch ($data{$service}{'type'}) {
409 %{$fe_metrics{"${service}_in"}} = %in;
410 %{$fe_metrics{"${service}_out"}} = %out;
414 %{$be_metrics{"${service}_in"}} = %in;
415 %{$be_metrics{"${service}_out"}} = %out;
419 # next unless $data{$service}{'type'} eq TYPE_BACKEND;
421 # Override these for the totals in the sub metric graph
422 $in{'label'} = $out{'label'} = 'Total';
423 $out{'negative'} = 'total_in';
424 $out{'draw'} = LINE1;
426 # This will copy the hash for the submetric
427 %{$submetrics{'total_in'}} = %in;
428 %{$submetrics{'total_out'}} = %out;
429 $submetrics{'total_out'}{'colour'} = COLOUR_BLACK;
431 # Loop through each of the available servers
432 foreach my $server (sort keys %{$data{$service}{'servers'}}) {
433 next if $data{$service}{'servers'}{$server}{'backup'};
434 # Override these for the per-Service graph
435 $in{'label'} = $out{'label'} = $server;
436 $out{'negative'} = "${server}_in";
437 $out{'draw'} = AREASTACK;
439 # This will copy the hash for the submetric
440 %{$submetrics{"${server}_in"}} = %in;
441 %{$submetrics{"${server}_out"}} = %out;
443 # Override these for the per-Host graph
444 $in{'label'} = $out{'label'} = 'Bandwidth';
445 $out{'negative'} = 'total_in';
446 $out{'draw'} = LINE1;
447 $out{'colour'} = COLOUR_BLACK;
449 mg_config(TYPE_BANDWIDTH, $data{$service}{'type'}, ['bandwidth', $service, $server],
450 'Bandwidth for '.$service.' via '.$server, {
455 delete $out{'colour'};
458 mg_config(TYPE_BANDWIDTH, $data{$service}{'type'}, ['bandwidth', $service],
459 'Bandwidth for '.$service, \%submetrics);
462 mg_config(TYPE_BANDWIDTH, TYPE_FRONTEND, ['bandwidth'],
463 'Bandwidth Overview', \%fe_metrics);
464 mg_config(TYPE_BANDWIDTH, TYPE_BACKEND, ['bandwidth'],
465 'Bandwidth Overview', \%be_metrics);
472 foreach my $service (sort keys %data) {
473 next unless $data{$service}{'type'} eq TYPE_FRONTEND;
474 $values{"${service}_in"} = $data{$service}{'bandwidth'}{'in'};
475 $values{"${service}_out"} = $data{$service}{'bandwidth'}{'out'};
478 mg_fetch(TYPE_FRONTEND, ['bandwidth'], \%values);
482 foreach my $service (sort keys %data) {
483 next unless $data{$service}{'type'} eq TYPE_BACKEND;
484 $values{"${service}_in"} = $data{$service}{'bandwidth'}{'in'};
485 $values{"${service}_out"} = $data{$service}{'bandwidth'}{'out'};
488 mg_fetch(TYPE_BACKEND, ['bandwidth'], \%values);
490 foreach my $service (sort keys %data) {
492 total_in => $data{$service}{'bandwidth'}{'in'},
493 total_out => $data{$service}{'bandwidth'}{'out'},
496 foreach my $server (sort keys %{$data{$service}{'servers'}}) {
497 next if $data{$service}{'servers'}{$server}{'backup'};
498 $values{"${server}_in"} = $data{$service}{'servers'}{$server}{'bandwidth'}{'in'};
499 $values{"${server}_out"} = $data{$service}{'servers'}{$server}{'bandwidth'}{'out'};
500 mg_fetch($data{$service}{'type'}, ['bandwidth', $service, $server], {
501 total_in => $data{$service}{'servers'}{$server}{'bandwidth'}{'in'},
502 total_out => $data{$service}{'servers'}{$server}{'bandwidth'}{'out'},
506 mg_fetch($data{$service}{'type'}, ['bandwidth', $service], \%values);
515 # backend(timing/service)
516 # backend(timing/service/host)
519 my ($operation, $data_) = (shift, shift);
520 my %data = %{$data_};
522 switch ($operation) {
526 my (%metrics, %submetrics);
528 foreach my $service (sort keys %data) {
529 next unless $data{$service}{'type'} eq TYPE_BACKEND;
533 # Prepare the field configuration
541 # This will copy the hash for the main metric
542 %{$metrics{$service}} = %time;
544 # Loop through each of the available servers
545 foreach my $server (sort keys %{$data{$service}{'servers'}}) {
546 next if $data{$service}{'servers'}{$server}{'backup'};
547 # Override these for the per-Service graph
548 $time{'label'} = $server;
550 # This will copy the hash for the submetric
551 %{$submetrics{$server}} = %time;
553 # Copy and override these for the per-Host graph
554 my %queue = %time; $queue{'label'} = 'Queue';
555 my %connect = %time; $connect{'label'} = 'Connect';
556 my %response = %time; $response{'label'} = 'Response';
557 my %total = %time; $total{'label'} = 'Total';
559 mg_config(TYPE_TIMING, TYPE_BACKEND, ['timing', $service, $server],
560 'Server Timing for '.$service.' via '.$server, {
562 connect => \%connect,
563 response => \%response,
568 mg_config(TYPE_TIMING, TYPE_BACKEND, ['timing', $service],
569 'Server Timing for '.$service, \%submetrics);
572 mg_config(TYPE_TIMING, TYPE_BACKEND, ['timing'],
573 'Server Timing Overview', \%metrics);
581 foreach my $service (sort keys %data) {
582 next unless $data{$service}{'type'} eq TYPE_BACKEND;
583 my ($count, $total) = (0,0);
584 foreach my $server (sort keys %{$data{$service}{'servers'}}) {
586 $total+=$data{$service}{'servers'}{$server}{'timing'}{'total'};
589 $values{$service} = ($count > 0 ? $total/$count : 0);
592 mg_fetch(TYPE_BACKEND, ['timing'], \%values);
595 foreach my $service (sort keys %data) {
596 next unless $data{$service}{'type'} eq TYPE_BACKEND;
599 foreach my $server (sort keys %{$data{$service}{'servers'}}) {
600 next if $data{$service}{'servers'}{$server}{'backup'};
601 $values{$server} = $data{$service}{'servers'}{$server}{'timing'}{'total'};
602 mg_fetch(TYPE_BACKEND, ['timing', $service, $server],
603 $data{$service}{'servers'}{$server}{'timing'});
606 mg_fetch(TYPE_BACKEND, ['timing', $service], \%values);
615 # frontend(sessions/service)
617 # backend(sessions/service)
618 # backend(sessions/service/host)
620 sub haproxy_sessions {
621 my ($operation, $data_) = (shift, shift);
622 my %data = %{$data_};
624 switch ($operation) {
628 my (%metrics, %submetrics);
630 foreach my $service (sort keys %data) {
631 next unless $data{$service}{'type'} eq TYPE_FRONTEND;
639 %{$metrics{"${service}_sessions"}} = %sessions;
641 # Override these for the per-Service graph
642 $sessions{'label'} = 'Sessions';
644 mg_config(TYPE_SESSIONS, TYPE_FRONTEND, ['sessions', $service],
645 'Sessions for '.$service, {
646 sessions => \%sessions,
650 mg_config(TYPE_SESSIONS, TYPE_FRONTEND, ['sessions'],
651 'Sessions Overview', \%metrics);
655 foreach my $service (sort keys %data) {
656 next unless $data{$service}{'type'} eq TYPE_BACKEND;
660 # Prepare the field configuration
670 negative => "${service}_queued",
673 # This will copy the hash for the main metric
674 %{$metrics{"${service}_queued"}} = %queued;
675 %{$metrics{"${service}_sessions"}} = %sessions;
677 # Override these for the totals in the sub metric graph
678 $queued{'label'} = $sessions{'label'} = 'Total';
679 $out{'negative'} = 'total_queued';
680 $out{'draw'} = LINE1;
682 # This will copy the hash for the submetric
683 %{$submetrics{'total_queued'}} = %queued;
684 %{$submetrics{'total_sessions'}} = %sessions;
686 # Loop through each of the available servers
687 foreach my $server (sort keys %{$data{$service}{'servers'}}) {
688 next if $data{$service}{'servers'}{$server}{'backup'};
689 # Override these for the per-Service graph
690 $in{'label'} = $out{'label'} = $server;
691 $out{'negative'} = "${server}_queued";
692 $out{'draw'} = AREASTACK;
694 # This will copy the hash for the submetric
695 %{$submetrics{"${server}_queued"}} = %queued;
696 %{$submetrics{"${server}_sessions"}} = %sessions;
698 # Override these for the per-Host graph
699 $in{'label'} = $out{'label'} = 'Sessions';
700 $out{'negative'} = 'queued';
701 $out{'draw'} = LINE1;
703 mg_config(TYPE_SESSIONS, TYPE_BACKEND, ['sessions', $service, $server],
704 'Sessions for '.$service.' via '.$server, {
706 sessions => \%sessions,
710 mg_config(TYPE_SESSIONS, TYPE_BACKEND, ['sessions', $service],
711 'Sessions for '.$service, \%submetrics);
714 mg_config(TYPE_SESSIONS, TYPE_BACKEND, ['sessions'],
715 'Sessions Overview', \%metrics);
723 foreach my $service (sort keys %data) {
724 next unless $data{$service}{'type'} eq TYPE_FRONTEND;
725 $values{"${service}_sessions"} = $data{$service}{'sessions'};
726 mg_fetch(TYPE_FRONTEND, ['sessions', $service], {
727 sessions => $data{$service}{'sessions'},
731 mg_fetch(TYPE_FRONTEND, ['sessions'], \%values);
735 foreach my $service (sort keys %data) {
736 next unless $data{$service}{'type'} eq TYPE_BACKEND;
737 $values{"${service}_sessions"} = $data{$service}{'sessions'};
738 $values{"${service}_queued"} = $data{$service}{'queued'};
741 mg_fetch(TYPE_BACKEND, ['sessions'], \%values);
743 foreach my $service (sort keys %data) {
744 next unless $data{$service}{'type'} eq TYPE_BACKEND;
747 total_sessions => $data{$service}{'sessions'},
748 total_queued => $data{$service}{'queued'},
751 foreach my $server (sort keys %{$data{$service}{'servers'}}) {
752 next if $data{$service}{'servers'}{$server}{'backup'};
754 $values{"${server}_sessions"} = $data{$service}{'servers'}{$server}{'sessions'};
755 $values{"${server}_queued"} = $data{$service}{'servers'}{$server}{'queued'};
757 mg_fetch(TYPE_BACKEND, ['sessions', $service, $server], {
758 sessions => $data{$service}{'servers'}{$server}{'sessions'},
759 queued => $data{$service}{'servers'}{$server}{'queued'},
763 mg_fetch(TYPE_BACKEND, ['sessions', $service], \%values);
771 # frontend(responses)
772 # frontend(responses/service(status))
773 # frontend(responses/service(status)/status)
775 # backend(responses/service(server))
776 # backend(responses/service(server)/server)
777 # backend(responses/service(status))
778 # backend(responses/service(status)/status)
779 # backend(responses/service(status)/status/server)
781 sub haproxy_responses {
782 my ($operation, $data_) = (shift, shift);
783 my %data = %{$data_};
785 switch ($operation) {
789 my (%fe_metrics, %be_metrics, %submetrics, $subsubmetrics);
791 foreach my $service (sort keys %data) {
798 switch ($data{$service}{'type'}) {
800 %{$fe_metrics{$service}} = %response;
803 %{$be_metrics{$service}} = %response;
809 foreach my $response (sort keys %{$data{$service}{'responses'}}) {
810 $response_ = ($response =~ /([12345])xx$/ ? "HTTP ${1}xx" :
811 $response eq 'total' ? "Total" : "Other");
812 $draw_ = ($response_ eq 'Total' ? LINE1 : AREASTACK);
814 %{$submetrics{$response}} = (
820 $submetrics{$response}{'colour'} = COLOUR_BLACK if $response eq 'total';
822 switch ($data{$service}{'type'}) {
824 mg_config(TYPE_RESPONSES, $data{$service}{'type'}, ['responses', $service, $response],
825 "${response_} Responses for ${service}", {
827 label => 'Responses',
835 undef %subsubmetrics;
837 foreach my $server (sort keys %{$data{$service}{'servers'}}) {
838 next if $data{$service}{'servers'}{$server}{'backup'};
839 %{$subsubmetrics{$server}} = (
844 mg_config(TYPE_RESPONSES, $data{$service}{'type'}, ['responses', $service, $response, $server],
845 "${response_} Responses for ${service} via ${server}", {
847 label => 'Responses',
854 %{$subsubmetrics{'total'}} = (
858 colour => COLOUR_BLACK,
861 mg_config(TYPE_RESPONSES, $data{$service}{'type'}, ['responses', $service, $response],
862 "${response_} Responses for ${service}", \%subsubmetrics);
868 mg_config(TYPE_RESPONSES, $data{$service}{'type'}, ['responses', $service],
869 'Responses for '.$service, \%submetrics);
872 mg_config(TYPE_RESPONSES, TYPE_FRONTEND, ['responses'],
873 'Responses Overview', \%fe_metrics);
874 mg_config(TYPE_RESPONSES, TYPE_BACKEND, ['responses'],
875 'Responses Overview', \%be_metrics);
881 my (%values, %subvalues);
883 foreach my $service (sort keys %data) {
884 next unless $data{$service}{'type'} eq TYPE_FRONTEND;
885 $values{$service} = $data{$service}{'responses'}{'total'};
886 mg_fetch(TYPE_FRONTEND, ['responses', $service], $data{$service}{'responses'});
887 foreach my $response (sort keys %{$data{$service}{'responses'}}) {
888 mg_fetch(TYPE_FRONTEND, ['responses', $service, $response], {
889 responses => $data{$service}{'responses'}{$response}
894 mg_fetch(TYPE_FRONTEND, ['responses'], \%values);
898 foreach my $service (sort keys %data) {
899 next unless $data{$service}{'type'} eq TYPE_BACKEND;
901 $values{$service} = $data{$service}{'responses'}{'total'};
904 total => $data{$service}{'responses'}{'total'},
907 mg_fetch(TYPE_BACKEND, ['responses', $service], $data{$service}{'responses'});
909 foreach my $response (sort keys %{$data{$service}{'responses'}}) {
912 foreach my $server (sort keys %{$data{$service}{'servers'}}) {
913 next if $data{$service}{'servers'}{$server}{'backup'};
915 $subvalues{$server} = $data{$service}{'servers'}{$server}{'responses'}{$response};
916 mg_fetch(TYPE_BACKEND, ['responses', $service, $response, $server], {
917 responses => $data{$service}{'servers'}{$server}{'responses'}{$response},
921 $subvalues{'total'} = $data{$service}{'responses'}{$response};
923 mg_fetch(TYPE_BACKEND, ['responses', $service, $response], \%subvalues);
927 mg_fetch(TYPE_BACKEND, ['responses'], \%values);
935 my ($operation, $data_) = (shift, shift);
936 my %data = %{$data_};
938 switch ($operation) {
942 my (%metrics, %submetrics);
944 foreach my $service (sort keys %data) {
945 next unless $data{$service}{'type'} eq TYPE_BACKEND;
947 # Prepare the field configuration
954 # This will copy the hash for the main metric
955 %{$metrics{$service}} = %count;
956 $metrics{$service}{'draw'} = LINE1;
958 # Copy and override these for the per-Service graph
959 my %backup = %count; $backup{'label'} = 'Backup';
960 my %down = %count; $down{'label'} = 'Down';
961 my %disabled = %count; $disabled{'label'} = 'Disabled';
962 my %up = %count; $up{'label'} = 'Up';
964 mg_config(TYPE_BACKEND, TYPE_BACKEND, ['count', $service],
965 'Server Count for '.$service, {
968 disabled => \%disabled,
973 mg_config(TYPE_BACKEND, TYPE_BACKEND, ['count'],
974 'Server Count Overview', \%metrics);
982 foreach my $service (sort keys %data) {
983 next unless $data{$service}{'type'} eq TYPE_BACKEND;
985 my ($backup, $disabled, $down, $up) = (0,0,0,0);
986 foreach my $server (sort keys %{$data{$service}{'servers'}}) {
987 ++$backup if $data{$service}{'servers'}{$server}{'backup'}
988 and not $data{$service}{'servers'}{$server}{'down'}
989 and not $data{$service}{'servers'}{$server}{'disabled'};
990 ++$disabled if $data{$service}{'servers'}{$server}{'disabled'};
991 ++$down if $data{$service}{'servers'}{$server}{'down'};
992 ++$up if $data{$service}{'servers'}{$server}{'up'}
993 and not $data{$service}{'servers'}{$server}{'backup'};
996 $values{$service} = $up;
998 mg_fetch(TYPE_BACKEND, ['count', $service], {
1000 disabled => $disabled,
1006 mg_fetch(TYPE_BACKEND, ['count'], \%values);
1014 my %data = get_data;
1015 exit 1 unless scalar(keys %data);
1017 haproxy_bandwidth FETCH, \%data;
1018 haproxy_timing FETCH, \%data;
1019 haproxy_sessions FETCH, \%data;
1020 haproxy_count FETCH, \%data;
1021 haproxy_responses FETCH, \%data;
1023 # Return something or the eval() will fail above
1028 my %data = get_data;
1029 exit 1 unless scalar(keys %data);
1031 haproxy_bandwidth CONFIG, \%data;
1032 haproxy_timing CONFIG, \%data;
1033 haproxy_sessions CONFIG, \%data;
1034 haproxy_count CONFIG, \%data;
1035 haproxy_responses CONFIG, \%data;
1041 # All Munin wants to know is will this plugin provide all the configuration
1042 # settings and options itself? Well, yes...
1043 print "".(-S $_socket ? "yes" : "no")."\n";