# # Net-SNMP perl plugin for HAProxy # Version 0.30 # # Copyright 2007-2010 Krzysztof Piotr Oledzki # # 1. get a variable from "show stat": # 1.3.6.1.4.1.29385.106.1.$type.$field.$iid.$sid # type: 0->frontend, 1->backend, 2->server, 3->socket # # 2. get a variable from "show info": # 1.3.6.1.4.1.29385.106.2.$req.$varnr # # TODO: # - implement read timeout # use NetSNMP::agent (':all'); use NetSNMP::ASN qw(:all); use IO::Socket::UNIX; use strict; my $agent = new NetSNMP::agent('Name' => 'HAProxy'); my $sa = "/var/run/haproxy.stat"; use constant OID_HAPROXY => '1.3.6.1.4.1.29385.106'; use constant OID_HAPROXY_STATS => OID_HAPROXY . '.1'; use constant OID_HAPROXY_INFO => OID_HAPROXY . '.2'; my $oid_stat = new NetSNMP::OID(OID_HAPROXY_STATS); my $oid_info = new NetSNMP::OID(OID_HAPROXY_INFO); use constant STATS_PXNAME => 0; use constant STATS_SVNAME => 1; use constant STATS_IID => 27; use constant STATS_SID => 28; use constant STATS_TYPE => 32; use constant FIELD_INDEX => 10001; use constant FIELD_NAME => 10002; my %info_vars = ( 0 => 'Name', 1 => 'Version', 2 => 'Release_date', 3 => 'Nbproc', 4 => 'Process_num', 5 => 'Pid', 6 => 'Uptime', 7 => 'Uptime_sec', 8 => 'Memmax_MB', 9 => 'Ulimit-n', 10 => 'Maxsock', 11 => 'Maxconn', 12 => 'Maxpipes', 13 => 'CurrConns', 14 => 'PipesUsed', 15 => 'PipesFree', 16 => 'Tasks', 17 => 'Run_queue', 18 => 'node', 19 => 'description', ); sub find_next_stat_id { my($type, $field, $proxyid, $sid) = @_; my $obj = 1 << $type; my $np = -1; my $nl = -1; my $sock = new IO::Socket::UNIX (Peer => $sa, Type => SOCK_STREAM, Timeout => 1); next if !$sock; print $sock "show stat -1 $obj -1\n"; while(<$sock>) { chomp; my @d = split(','); last if !$d[$field] && $field != FIELD_INDEX && $field != FIELD_NAME && /^#/; next if /^#/; next if $d[STATS_TYPE] != $type; next if ($d[STATS_IID] < $proxyid) || ($d[STATS_IID] == $proxyid && $d[STATS_SID] <= $sid); if ($np == -1 || $d[STATS_IID] < $np || ($d[STATS_IID] == $np && $d[STATS_SID] < $nl)) { $np = $d[STATS_IID]; $nl = $d[STATS_SID]; next; } } close($sock); return 0 if ($np == -1); return "$type.$field.$np.$nl" } sub haproxy_stat { my($handler, $registration_info, $request_info, $requests) = @_; for(my $request = $requests; $request; $request = $request->next()) { my $oid = $request->getOID(); $oid =~ s/$oid_stat//; $oid =~ s/^\.//; my $mode = $request_info->getMode(); my($type, $field, $proxyid, $sid, $or) = split('\.', $oid, 5); next if $type > 3 || defined($or); if ($mode == MODE_GETNEXT) { $type = 0 if !$type; $field = 0 if !$field; $proxyid = 0 if !$proxyid; $sid = 0 if !$sid; my $nextid = find_next_stat_id($type, $field, $proxyid, $sid); $nextid = find_next_stat_id($type, $field+1, 0, 0) if !$nextid; $nextid = find_next_stat_id($type+1, 0, 0, 0) if !$nextid; if ($nextid) { ($type, $field, $proxyid, $sid) = split('\.', $nextid); $request->setOID(sprintf("%s.%s", OID_HAPROXY_STATS, $nextid)); $mode = MODE_GET; } } if ($mode == MODE_GET) { next if !defined($proxyid) || !defined($type) || !defined($sid) || !defined($field); my $obj = 1 << $type; my $sock = new IO::Socket::UNIX (Peer => $sa, Type => SOCK_STREAM, Timeout => 1); next if !$sock; print $sock "show stat $proxyid $obj $sid\n"; while(<$sock>) { chomp; my @data = split(','); last if !defined($data[$field]) && $field != FIELD_INDEX && $field != FIELD_NAME; if ($proxyid) { next if $data[STATS_IID] ne $proxyid; next if $data[STATS_SID] ne $sid; next if $data[STATS_TYPE] ne $type; } if ($field == FIELD_INDEX) { $request->setValue(ASN_OCTET_STR, sprintf("%s.%s", $data[STATS_IID], $data[STATS_SID])); } elsif ($field == FIELD_NAME) { $request->setValue(ASN_OCTET_STR, sprintf("%s/%s", $data[STATS_PXNAME], $data[STATS_SVNAME])); } else { $request->setValue(ASN_OCTET_STR, $data[$field]); } close($sock); last; } close($sock); next; } } } sub haproxy_info { my($handler, $registration_info, $request_info, $requests) = @_; for(my $request = $requests; $request; $request = $request->next()) { my $oid = $request->getOID(); $oid =~ s/$oid_info//; $oid =~ s/^\.//; my $mode = $request_info->getMode(); my($req, $nr, $or) = split('\.', $oid, 3); next if $req >= 2 || defined($or); if ($mode == MODE_GETNEXT) { $req = 0 if !defined($req); $nr = -1 if !defined($nr); if (!defined($info_vars{$nr+1})) { $req++; $nr = -1; } next if $req >= 2; $request->setOID(sprintf("%s.%s.%s", OID_HAPROXY_INFO, $req, ++$nr)); $mode = MODE_GET; } if ($mode == MODE_GET) { next if !defined($req) || !defined($nr); if ($req == 0) { next if !defined($info_vars{$nr}); $request->setValue(ASN_OCTET_STR, $info_vars{$nr}); next; } if ($req == 1) { next if !defined($info_vars{$nr}); my $sock = new IO::Socket::UNIX (Peer => $sa, Type => SOCK_STREAM, Timeout => 1); next if !$sock; print $sock "show info\n"; while(<$sock>) { chomp; my ($key, $val) = /(.*):\s*(.*)/; next if $info_vars{$nr} ne $key; $request->setValue(ASN_OCTET_STR, $val); last; } close($sock); } } } } $agent->register('HAProxy stat', OID_HAPROXY_STATS, \&haproxy_stat); $agent->register('HAProxy info', OID_HAPROXY_INFO, \&haproxy_info);