]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MAJOR] proto_uxst rework -> SNMP support
authorKrzysztof Piotr Oledzki <ole@ans.pl>
Sun, 2 Mar 2008 01:42:14 +0000 (02:42 +0100)
committerWilly Tarreau <w@1wt.eu>
Tue, 4 Mar 2008 05:32:16 +0000 (06:32 +0100)
Currently there is a ~16KB limit for a data size passed via unix socket.
It is caused by a trivial bug ttat is going to fixed soon, however
in most cases there is no need to dump a full stats.

This patch makes possible to select a scope of dumped data by extending
current "show stat" to "show stat [<iid> <type> <sid>]":
 - iid is a proxy id, -1 to dump all proxies
 - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for
   server, -1 for all types. Values can be ORed, for example:
     1+2=3   -> frontend+backend.
     1+2+4=7 -> frontend+backend+server.
 - sid is a service id, -1 to dump everything from the selected proxy.

To do this I implemented a new session flag (SN_STAT_BOUND), added three
variables in data_ctx.stats (iid, type, sid), modified dumpstats.c and
completely revorked the process_uxst_stats: now it waits for a "\n"
terminated string, splits args and uses them. BTW: It should be quite easy
to add new commands, for example to enable/disable servers, the only problem
I can see is a not very lucky config name (*stats* socket). :|

During the work I also fixed two bug:
 - s->flags were not initialized for proto_uxst
 - missing comma if throttling not enabled (caused by a stupid change in
     "Implement persistent id for proxies and servers")

Other changes:
 - No more magic type valuse, use STATS_TYPE_FE/STATS_TYPE_BE/STATS_TYPE_SV
 - Don't memset full s->data_ctx (it was clearing s->data_ctx.stats.{iid/type/sid},
    instead initialize stats.sv & stats.sv_st (stats.px and stats.px_st were already
    initialized)

With all that changes it was extremely easy to write a short perl plugin
for a perl-enabled net-snmp (also included in this patch).

29385 is my PEN (Private Enterprise Number) and I'm willing to donate
the SNMPv2-SMI::enterprises.29385.106.* OIDs for HAProxy if there
is nothing assigned already.

contrib/netsnmp-perl/README [new file with mode: 0644]
contrib/netsnmp-perl/haproxy.pl [new file with mode: 0644]
contrib/netsnmp-perl/haproxy_backend.xml [new file with mode: 0644]
contrib/netsnmp-perl/haproxy_frontend.xml [new file with mode: 0644]
doc/configuration.txt
include/common/defaults.h
include/proto/dumpstats.h
include/types/session.h
src/dumpstats.c
src/proto_uxst.c

diff --git a/contrib/netsnmp-perl/README b/contrib/netsnmp-perl/README
new file mode 100644 (file)
index 0000000..f44eb5b
--- /dev/null
@@ -0,0 +1,111 @@
+SNMP support for HAProxy
+Copyright 2007-2008 Krzysztof Piotr Oledzki <ole@ans.pl>
+
+Root OID: 1.3.6.1.4.1.29385.106
+
+Files:
+ - README: this file
+ - haproxy.pl: Net-SNMP embedded perl module
+ - haproxy_backend.xml: Cacti snmp-query definition for backends
+ - haproxy_frontend.xml: Cacti snmp-query definition for frontends
+
+Install:
+ cp haproxy.pl /etc/snmp/
+ grep -q "disablePerl false" /etc/snmp/snmpd.conf || echo "disablePerl false" >> /etc/snmp/snmpd.conf
+ echo "perl do '/etc/snmp/haproxy.pl';" >> /etc/snmp/snmpd.conf
+
+Supported commands:
+ - GET (snmpget, snmpbulkget): quite fast.
+ - GETNEXT (snmpwalk, snmpbulkwalk): not so fast as requires to transfer
+    and parse a lot of data during each step. Always use "get" instead of "walk"
+    if that's possible.
+
+Supported OIDs:
+ - 1.3.6.1.4.1.29385.106.1: get a variable from stats
+    Usage: 1.3.6.1.4.1.29385.106.1.$type.$field.$iid.$sid
+
+     - type is one of:
+       0) frontend
+       1) backend
+       2) server
+
+     - field is one of:
+       0..32) CSV format variable
+       10001) index
+       10002) unique name
+
+     - iid is a proxy id 
+
+     - sid is a service id (sid): 0 for frontends and backends, >= 1 for servers
+
+ - 1.3.6.1.4.1.29385.106.2: get a variable from info
+    Usage: 1.3.6.1.4.1.29385.106.2.$req.$varnr
+
+      - req is one of:
+        0) get variable name
+        1) gat variable value
+
+Examples:
+
+- Get a list of frontends (type: 0) with status (field: 17):
+$ snmpbulkwalk -c public -v2c 192.168.0.1 1.3.6.1.4.1.29385.106.1.0.17
+SNMPv2-SMI::enterprises.29385.106.1.0.17.1.0 = STRING: "OPEN"
+SNMPv2-SMI::enterprises.29385.106.1.0.17.47.0 = STRING: "OPEN"
+
+- Get a list of backends (type: 1) with index (field: 10001):
+$ snmpbulkwalk -c public -v2c 192.168.0.1 1.3.6.1.4.1.29385.106.1.1.10001
+SNMPv2-SMI::enterprises.29385.106.1.1.10001.1.0 = STRING: "1.0"
+SNMPv2-SMI::enterprises.29385.106.1.1.10001.1100.0 = STRING: "1100.0"
+SNMPv2-SMI::enterprises.29385.106.1.1.10001.1101.0 = STRING: "1101.0"
+SNMPv2-SMI::enterprises.29385.106.1.1.10001.1200.0 = STRING: "1200.0"
+SNMPv2-SMI::enterprises.29385.106.1.1.10001.1201.0 = STRING: "1201.0"
+SNMPv2-SMI::enterprises.29385.106.1.1.10001.1300.0 = STRING: "1300.0"
+SNMPv2-SMI::enterprises.29385.106.1.1.10001.1400.0 = STRING: "1400.0"
+SNMPv2-SMI::enterprises.29385.106.1.1.10001.1401.0 = STRING: "1401.0"
+SNMPv2-SMI::enterprises.29385.106.1.1.10001.1500.0 = STRING: "1500.0"
+(...)
+
+- Get a list of servers (type: 2) with unique name (field: 10002):
+$ snmpbulkwalk -c public -v2c 192.168.0.1 1.3.6.1.4.1.29385.106.1.2.10002
+SNMPv2-SMI::enterprises.29385.106.1.2.10002.1100.1001 = STRING: "backend1/s2"
+SNMPv2-SMI::enterprises.29385.106.1.2.10002.1100.1002 = STRING: "backend1/s5"
+SNMPv2-SMI::enterprises.29385.106.1.2.10002.1100.1003 = STRING: "backend1/s6"
+SNMPv2-SMI::enterprises.29385.106.1.2.10002.1100.1012 = STRING: "backend1/s7"
+SNMPv2-SMI::enterprises.29385.106.1.2.10002.1101.1001 = STRING: "backend2/s9"
+SNMPv2-SMI::enterprises.29385.106.1.2.10002.1101.1002 = STRING: "backend2/s10"
+SNMPv2-SMI::enterprises.29385.106.1.2.10002.1101.1003 = STRING: "backend2/s11"
+SNMPv2-SMI::enterprises.29385.106.1.2.10002.1101.1012 = STRING: "backend2/s12"
+SNMPv2-SMI::enterprises.29385.106.1.2.10002.1200.1001 = STRING: "backend3/s8"
+(...)
+
+- Get a list of servers (type: 2) with weight (field: 18) in proxy 4300:
+$ snmpbulkwalk -c public -v2c 192.168.0.1 1.3.6.1.4.1.29385.106.1.2.18.4300
+SNMPv2-SMI::enterprises.29385.106.1.2.18.4300.1001 = STRING: "40"
+SNMPv2-SMI::enterprises.29385.106.1.2.18.4300.1002 = STRING: "25"
+SNMPv2-SMI::enterprises.29385.106.1.2.18.4300.1003 = STRING: "40"
+SNMPv2-SMI::enterprises.29385.106.1.2.18.4300.1012 = STRING: "80"
+
+- Get total sessions count (field: 7) in frontend (type: 1), sid.iid: 47.0 (proxy #47):
+snmpget -c public -v2c 192.168.0.1 enterprises.29385.106.1.0.7.47.0
+SNMPv2-SMI::enterprises.29385.106.1.0.7.47.0 = STRING: "1014019"
+
+- Get a list of available variables (req: 0):
+$ snmpbulkwalk -c public -v2c 192.168.0.1 1.3.6.1.4.1.29385.106.2.0
+SNMPv2-SMI::enterprises.29385.106.2.0.0 = STRING: "Name"
+SNMPv2-SMI::enterprises.29385.106.2.0.1 = STRING: "Version"
+SNMPv2-SMI::enterprises.29385.106.2.0.2 = STRING: "Release_date"
+SNMPv2-SMI::enterprises.29385.106.2.0.3 = STRING: "Nbproc"
+SNMPv2-SMI::enterprises.29385.106.2.0.4 = STRING: "Process_num"
+SNMPv2-SMI::enterprises.29385.106.2.0.5 = STRING: "Pid"
+SNMPv2-SMI::enterprises.29385.106.2.0.6 = STRING: "Uptime"
+SNMPv2-SMI::enterprises.29385.106.2.0.7 = STRING: "Uptime_sec"
+SNMPv2-SMI::enterprises.29385.106.2.0.8 = STRING: "Memmax_MB"
+SNMPv2-SMI::enterprises.29385.106.2.0.9 = STRING: "Ulimit-n"
+SNMPv2-SMI::enterprises.29385.106.2.0.10 = STRING: "Maxsock"
+SNMPv2-SMI::enterprises.29385.106.2.0.11 = STRING: "Maxconn"
+SNMPv2-SMI::enterprises.29385.106.2.0.12 = STRING: "CurrConns"
+
+- Get a variable (req: 1), varnr: 7 (Uptime_sec):
+$ snmpget -c public -v2c 192.168.0.1 1.3.6.1.4.1.29385.106.2.1.7
+SNMPv2-SMI::enterprises.29385.106.2.1.7 = STRING: "18761"
diff --git a/contrib/netsnmp-perl/haproxy.pl b/contrib/netsnmp-perl/haproxy.pl
new file mode 100644 (file)
index 0000000..61b693a
--- /dev/null
@@ -0,0 +1,242 @@
+#
+# Net-SNMP perl plugin for Haproxy
+# Version 0.27
+#
+# Copyright 2007-2008 Krzysztof Piotr Oledzki <ole@ans.pl>
+#
+# 1. get a variable from stat:
+#  1.3.6.1.4.1.29385.106.1.$type.$field.$iid.$sid
+#  type: 0->frontend, 1->backend, 2->server
+#
+# 2. get a variable from 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      => 'CurrConns',
+);
+
+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 > 2 || 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);
+
diff --git a/contrib/netsnmp-perl/haproxy_backend.xml b/contrib/netsnmp-perl/haproxy_backend.xml
new file mode 100644 (file)
index 0000000..9bbde2f
--- /dev/null
@@ -0,0 +1,62 @@
+<interface>
+       <name>Haproxy - backend</name>
+       <oid_index>.1.3.6.1.4.1.29385.106.1.1.10001</oid_index>
+       <fields>
+               <beIID>
+                       <name>Proxy ID</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>input</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.1.27</oid>
+               </beIID>
+               <beSID>
+                       <name>Service ID</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>input</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.1.28</oid>
+               </beSID>
+               <bePxName>
+                       <name>Proxy Name</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>input</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.1.0</oid>
+               </bePxName>
+               <beSvName>
+                       <name>Service Name</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>input</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.1.1</oid>
+               </beSvName>
+               <beSTot>
+                       <name>Total Sessions</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>output</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.1.7</oid>
+               </beSTot>
+               <beEResp>
+                       <name>Response Errors</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>output</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.1.14</oid>
+               </beEResp>
+               <beBIn>
+                       <name>Bytes In</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>output</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.1.8</oid>
+               </beBIn>
+               <beBOut>
+                       <name>Bytes Out</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>output</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.1.9</oid>
+               </beBOut>
+       </fields>
+</interface>
diff --git a/contrib/netsnmp-perl/haproxy_frontend.xml b/contrib/netsnmp-perl/haproxy_frontend.xml
new file mode 100644 (file)
index 0000000..09789c8
--- /dev/null
@@ -0,0 +1,62 @@
+<interface>
+       <name>Haproxy - frontend</name>
+       <oid_index>.1.3.6.1.4.1.29385.106.1.0.10001</oid_index>
+       <fields>
+               <feIID>
+                       <name>Proxy ID</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>input</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.0.27</oid>
+               </feIID>
+               <feSID>
+                       <name>Service ID</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>input</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.0.28</oid>
+               </feSID>
+               <fePxName>
+                       <name>Proxy Name</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>input</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.0.0</oid>
+               </fePxName>
+               <feSvName>
+                       <name>Service Name</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>input</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.0.1</oid>
+               </feSvName>
+               <feSTot>
+                       <name>Total Sessions</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>output</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.0.7</oid>
+               </feSTot>
+               <feEReq>
+                       <name>Request Errors</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>output</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.0.12</oid>
+               </feEReq>
+               <feBIn>
+                       <name>Bytes In</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>output</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.0.8</oid>
+               </feBIn>
+               <feBOut>
+                       <name>Bytes Out</name>
+                       <method>get</method>
+                       <source>value</source>
+                       <direction>output</direction>
+                       <oid>.1.3.6.1.4.1.29385.106.1.0.9</oid>
+               </feBOut>
+       </fields>
+</interface>
index a2c97e31755df4e6ff6e85014d26803ed92f10be..11360ba463a3b0182adce821a09c0928776cde6d 100644 (file)
@@ -4035,10 +4035,10 @@ Notes related to these keywords :
   8. bin: bytes in
   9. bout: bytes out
  10. dreq: denied requests
- 11. dresp: denied responces
+ 11. dresp: denied responses
  12. ereq: request errors
  13. econ: connection errors
- 14. eresp: responce errors
+ 14. eresp: response errors
  15. wretr: retries (warning)
  16. wredis: redispatches (warning)
  17. status: status (UP/DOWN/...)
@@ -4058,6 +4058,19 @@ Notes related to these keywords :
  31. tracked: id of proxy/server if tracking is enabled
  32. type (0=frontend, 1=backend, 2=server)
 
+2.8) Unix Socket commands
+
+ - "show stat [<iid> <type> <sid>]": dump statistics in the cvs format. By
+   passing id, type and sid it is possible to dump only selected items:
+     - iid is a proxy id, -1 to dump everything
+     - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for
+       server, -1 for everything. Values can be ORed, for example:
+          1+2=3   -> frontend+backend.
+          1+2+4=7 -> frontend+backend+server.
+     - sid is a service id, -1 to dump everything from the selected proxy.
+
+ - "show info": dump info about current haproxy status.
+
 /*
  * Local variables:
  *  fill-column: 79
index 1a69f40d9be4e58160afd6b499a9716c412b3c4c..da9bdd8c7201a50a283c43bb98431ed7fa85e920 100644 (file)
@@ -51,6 +51,9 @@
 // max # args on a configuration line
 #define MAX_LINE_ARGS   64
 
+// max # args on a uxts socket
+#define MAX_UXST_ARGS   16
+
 // max # of added headers per request
 #define MAX_NEWHDR      10
 
index 198f0d793492ccac7ba8b58b1dd99302684bb0cf..362650567162151c8a492a962819c4980708fb79 100644 (file)
 #define STAT_SHOW_STAT 0x2
 #define STAT_SHOW_INFO 0x4
 
+#define STATS_TYPE_FE  0
+#define STATS_TYPE_BE  1
+#define STATS_TYPE_SV  2
+
 int stats_parse_global(const char **args, char *err, int errlen);
 int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags);
 int stats_dump_http(struct session *s, struct uri_auth *uri, int flags);
index 9437198d9591db5977c486b7caeaf815934b10e1..18d5115f0a285754fd72562d00d492cf975b290d 100644 (file)
@@ -82,6 +82,7 @@
 #define SN_STAT_HIDEDWN        0x00100000      /* hide 'down' servers in the stats page */
 #define SN_STAT_NORFRSH        0x00200000      /* do not automatically refresh the stats page */
 #define SN_STAT_FMTCSV 0x00400000      /* dump the stats in CSV format instead of HTML */
+#define SN_STAT_BOUND  0x00800000      /* bound statistics to selected proxies/types/services */
 
 
 /* WARNING: if new fields are added, they must be initialized in event_accept()
@@ -126,6 +127,7 @@ struct session {
                        struct proxy *px;
                        struct server *sv;
                        short px_st, sv_st;     /* DATA_ST_INIT or DATA_ST_DATA */
+                       int iid, type, sid;     /* proxy id, type and service id if bounding of stats is enabled */
                } stats;
        } data_ctx;                             /* used by produce_content to dump the stats right now */
        unsigned int uniq_id;                   /* unique ID used for the traces */
index c132246a5190229889671b11d7d7c3fbaea3346d..99ac770f61abdd852d9fc2c785de7713ba97ab81 100644 (file)
@@ -213,7 +213,6 @@ int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags)
 
        case DATA_ST_INFO:
                up = (now.tv_sec - start_date.tv_sec);
-               memset(&s->data_ctx, 0, sizeof(s->data_ctx));
 
                if (flags & STAT_SHOW_INFO) {
                        chunk_printf(&msg, sizeof(trash),
@@ -248,6 +247,10 @@ int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags)
 
                s->data_ctx.stats.px = proxy;
                s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
+
+               s->data_ctx.stats.sv = NULL;
+               s->data_ctx.stats.sv_st = 0;
+
                s->data_state = DATA_ST_LIST;
                /* fall through */
 
@@ -621,6 +624,10 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
                                return 1;
                }
 
+               if ((s->flags & SN_STAT_BOUND) && (s->data_ctx.stats.iid != -1) &&
+                       (px->uuid != s->data_ctx.stats.iid))
+                       return 1;
+
                s->data_ctx.stats.px_st = DATA_ST_PX_TH;
                /* fall through */
 
@@ -660,7 +667,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
 
        case DATA_ST_PX_FE:
                /* print the frontend */
-               if (px->cap & PR_CAP_FE) {
+               if ((px->cap & PR_CAP_FE) && (!(s->flags & SN_STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_FE)))) {
                        if (flags & STAT_FMT_HTML) {
                                chunk_printf(&msg, sizeof(trash),
                                     /* name, queue */
@@ -706,8 +713,8 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
                                     "%s,"
                                     /* rest of server: nothing */
                                     ",,,,,,,,"
-                                    /* pid, iid, sid, throttle, lbtot, tracked, type (0=server)*/
-                                    "%d,%d,0,,,,0,"
+                                    /* pid, iid, sid, throttle, lbtot, tracked, type */
+                                    "%d,%d,0,,,,%d,"
                                     "\n",
                                     px->id,
                                     px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
@@ -716,7 +723,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
                                     px->failed_req,
                                     px->state == PR_STRUN ? "OPEN" :
                                     px->state == PR_STIDLE ? "FULL" : "STOP",
-                                    relative_pid, px->uuid);
+                                    relative_pid, px->uuid, STATS_TYPE_FE);
                        }
 
                        if (buffer_write_chunk(rep, &msg) != 0)
@@ -729,11 +736,20 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
 
        case DATA_ST_PX_SV:
                /* stats.sv has been initialized above */
-               while (s->data_ctx.stats.sv != NULL) {
+               for (; s->data_ctx.stats.sv != NULL; s->data_ctx.stats.sv = sv->next) {
+
                        int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP, 4,5=NOLB, 6=unchecked */
 
                        sv = s->data_ctx.stats.sv;
 
+                       if (s->flags & SN_STAT_BOUND) {
+                               if (!(s->data_ctx.stats.type & (1 << STATS_TYPE_SV)))
+                                       break;
+
+                               if (s->data_ctx.stats.sid != -1 && sv->puid != s->data_ctx.stats.sid)
+                                       continue;
+                       }
+
                        if (sv->tracked)
                                svs = sv->tracked;
                        else
@@ -911,11 +927,11 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
                                    now.tv_sec >= sv->last_change) {
                                        unsigned int ratio;
                                        ratio = MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart);
-                                       chunk_printf(&msg, sizeof(trash), "%d,", ratio);
+                                       chunk_printf(&msg, sizeof(trash), "%d", ratio);
                                }
 
                                /* sessions: lbtot */
-                               chunk_printf(&msg, sizeof(trash), "%d,", sv->cum_lbconn);
+                               chunk_printf(&msg, sizeof(trash), ",%d,", sv->cum_lbconn);
 
                                /* tracked */
                                if (sv->tracked)
@@ -924,21 +940,19 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
                                else
                                        chunk_printf(&msg, sizeof(trash), ",");
 
-                               /* type (2=server), then EOL */
-                               chunk_printf(&msg, sizeof(trash), "2,\n");
+                               /* type, then EOL */
+                               chunk_printf(&msg, sizeof(trash), "%d,\n", STATS_TYPE_SV);
                        }
                        if (buffer_write_chunk(rep, &msg) != 0)
                                return 0;
-
-                       s->data_ctx.stats.sv = sv->next;
-               } /* while sv */
+               } /* for sv */
 
                s->data_ctx.stats.px_st = DATA_ST_PX_BE;
                /* fall through */
 
        case DATA_ST_PX_BE:
                /* print the backend */
-               if (px->cap & PR_CAP_BE) {
+               if ((px->cap & PR_CAP_BE) && (!(s->flags & SN_STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_BE)))) {
                        if (flags & STAT_FMT_HTML) {
                                chunk_printf(&msg, sizeof(trash),
                                     /* name */
@@ -1007,8 +1021,8 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
                                     "%d,%d,%d,"
                                     /* rest of backend: nothing, down transitions, last change, total downtime */
                                     ",%d,%d,%d,,"
-                                    /* pid, iid, sid, throttle, lbtot, tracked, type (1=backend) */
-                                    "%d,%d,0,,%d,,1,"
+                                    /* pid, iid, sid, throttle, lbtot, tracked, type */
+                                    "%d,%d,0,,%d,,%d,"
                                     "\n",
                                     px->id,
                                     px->nbpend /* or px->totpend ? */, px->nbpend_max,
@@ -1023,7 +1037,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri,
                                     px->down_trans, now.tv_sec - px->last_change,
                                     px->srv?be_downtime(px):0,
                                     relative_pid, px->uuid,
-                                    px->cum_lbconn);
+                                    px->cum_lbconn, STATS_TYPE_BE);
                        }
                        if (buffer_write_chunk(rep, &msg) != 0)
                                return 0;
index 90547226889cc0c13dbea3d6c80f4a5d8fd3da70..76709c1372caf2e6370d438ce44b81381271fd55 100644 (file)
@@ -416,6 +416,8 @@ int uxst_event_accept(int fd) {
                        return 0;
                }
 
+               s->flags = 0;
+
                if ((t = pool_alloc2(pool2_task)) == NULL) {
                        Alert("out of memory in uxst_event_accept().\n");
                        close(cfd);
@@ -1381,31 +1383,75 @@ void process_uxst_stats(struct task *t, struct timeval *next)
                }
 
                if (s->data_state == DATA_ST_INIT) {
-                       if ((s->req->l >= 10) && (memcmp(s->req->data, "show stat\n", 10) == 0)) {
-                               /* send the stats, and changes the data_state */
-                               if (stats_dump_raw(s, NULL, STAT_SHOW_STAT) != 0) {
-                                       s->srv_state = SV_STCLOSE;
-                                       fsm_resync |= 1;
+
+                       char *args[MAX_UXST_ARGS + 1];
+                       char *line, *p;
+                       int arg;
+
+                       line = s->req->data;
+                       p = memchr(line, '\n', s->req->l);
+
+                       if (!p)
+                               continue;
+
+                       *p = '\0';
+
+                       while (isspace((unsigned char)*line))
+                               line++;
+
+                       arg = 0;
+                       args[arg] = line;
+
+                       while (*line && arg < MAX_UXST_ARGS) {
+                               if (isspace((unsigned char)*line)) {
+                                       *line++ = '\0';
+
+                                       while (isspace((unsigned char)*line))
+                                               line++;
+
+                                       args[++arg] = line;
                                        continue;
                                }
+
+                               line++;
                        }
-                       if ((s->req->l >= 10) && (memcmp(s->req->data, "show info\n", 10) == 0)) {
-                               /* send the stats, and changes the data_state */
-                               if (stats_dump_raw(s, NULL, STAT_SHOW_INFO) != 0) {
-                                       s->srv_state = SV_STCLOSE;
-                                       fsm_resync |= 1;
+
+                       while (++arg <= MAX_UXST_ARGS)
+                               args[arg] = line;
+
+                       if (!strcmp(args[0], "show")) {
+                               if (!strcmp(args[1], "stat")) {
+                                       if (*args[2] && *args[3] && *args[4]) {
+                                               s->flags |= SN_STAT_BOUND;
+                                               s->data_ctx.stats.iid   = atoi(args[2]);
+                                               s->data_ctx.stats.type  = atoi(args[3]);
+                                               s->data_ctx.stats.sid   = atoi(args[4]);
+                                       }
+
+                                       /* send the stats, and changes the data_state */
+                                       if (stats_dump_raw(s, NULL, STAT_SHOW_STAT) != 0) {
+                                               s->srv_state = SV_STCLOSE;
+                                               fsm_resync |= 1;
+                                       }
+
+                                       continue;
+                               }
+
+                               if (!strcmp(args[1], "info")) {
+                                       /* send the stats, and changes the data_state */
+                                       if (stats_dump_raw(s, NULL, STAT_SHOW_INFO) != 0) {
+                                               s->srv_state = SV_STCLOSE;
+                                               fsm_resync |= 1;
+                                       }
+
                                        continue;
                                }
                        }
-                       else if (s->cli_state == CL_STSHUTR || (s->req->l >= s->req->rlim - s->req->data)) {
-                               s->srv_state = SV_STCLOSE;
-                               fsm_resync |= 1;
-                               continue;
-                       }
-               }
 
-               if (s->data_state == DATA_ST_INIT)
+                       s->srv_state = SV_STCLOSE;
+                       fsm_resync |= 1;
                        continue;
+               }
 
                /* OK we have some remaining data to process. Just for the
                 * sake of an exercice, we copy the req into the resp,