From 422ec2e879b6c1f5577d1602394d5bd654b41187 Mon Sep 17 00:00:00 2001
From: Krzysztof Piotr Oledzki <ole#ans.pl>
Date: Tue, 26 Feb 2008 01:37:10 +0100
Subject: [MAJOR] proto_uxst rework -> SNMP support
Currently there is a ~16KB limit for data size passed via unix socket. It is probably caused by a trivial bug that can be easily fixed, 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 stats [<iid> <type> <sid>]":
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:
Other changes:
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). To use it
you need a perl enabled net-snmpd and:
cp examples/haproxy.pl /etc/snmp/
echo "perl do '/etc/snmp/haproxy.pl';" >> /etc/snmp/snmpd.conf
Examples:
29385 is my PEN (Private Enterprise Number) and I'm willing to donate the SNMPv2-SMI::enterprises.29385.106.* OIDs for the Haproxy if there is nothing assigned already.
In the future I'm going to add support for snmpwalk and access to all "show info" variables.
--- doc/configuration.txt | 13 +++++++ examples/haproxy.pl | 80 +++++++++++++++++++++++++++++++++++++++++++++ include/common/defaults.h | 3 ++ include/proto/dumpstats.h | 4 ++ include/types/session.h | 2 + src/dumpstats.c | 48 +++++++++++++++++--------- src/proto_uxst.c | 80 +++++++++++++++++++++++++++++++++++--------- 7 files changed, 196 insertions(+), 34 deletions(-) create mode 100644 examples/haproxy.pl diff --git a/doc/configuration.txt b/doc/configuration.txt index 8621f4e..6a8f9a0 100644 --- a/doc/configuration.txtReceived on 2008/02/26 02:07
+++ b/doc/configuration.txt
@@ -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 stats [<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 diff --git a/examples/haproxy.pl b/examples/haproxy.pl new file mode 100644 index 0000000..bc0cfb0 --- /dev/null
+++ b/examples/haproxy.pl
@@ -0,0 +1,80 @@
+#
+# Net-SNMP perl plugin for Haproxy
+# Verision 0.03
+#
+# Copyright 2007-2008 Krzysztof Piotr Oledzki <ole#ans.pl>
+#
+# 1. get a variable from statistics:
+# 1.3.6.1.4.1.29385.106.1.$proxy.$type.$lid.$field
+# type: 0->frontend, 1->backend, 2->server
+#
+
+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 HAPROXYOID => '1.3.6.1.4.1.29385.106';
+
+my $myoid = new NetSNMP::OID(HAPROXYOID);
+
+sub myroutine {
+ my($handler, $registration_info, $request_info, $requests) = @_;
+
+ for(my $request = $requests; $request; $request = $request->next()) {
+ my $oid = $request->getOID();
+
+ $oid =~ s/$myoid//;
+
+ return if (!$oid);
+
+ $oid =~ s/^.//;
+
+ if ($request_info->getMode() == MODE_GET) {
+
+ my($cmd, $or1) = split('\.', $oid, 2);
+
+ if ($cmd == 1) {
+
+ my($proxyid, $type, $lid, $field, $or2) = split('\.', $or1, 5);
+
+ next if defined($or2);
+ next if !defined($proxyid) || !defined($type) || !defined($lid) || !defined($field);
+
+ # FIXME: check/verify input values ($proxyid, $type, $lid, $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 $lid\n";
+
+ while(<$sock>) {
+ chomp;
+
+ my @data = split(',');
+
+ if ($proxyid) {
+ next if $data[27] ne $proxyid;
+ next if $data[28] ne $lid;
+ next if $data[32] ne $type;
+ }
+
+ $request->setValue(ASN_OCTET_STR, $data[$field]);
+ close($sock);
+ last;
+ }
+
+ close($sock);
+ next;
+ }
+ }
+ }
+}
+
+$agent->register('Haproxy', HAPROXYOID, \&myroutine);
diff --git a/include/common/defaults.h b/include/common/defaults.h index 1a69f40..da9bdd8 100644 --- a/include/common/defaults.h
+++ b/include/common/defaults.h
@@ -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 diff --git a/include/proto/dumpstats.h b/include/proto/dumpstats.h index 198f0d7..3626505 100644 --- a/include/proto/dumpstats.h
+++ b/include/proto/dumpstats.h
@@ -31,6 +31,10 @@ #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); diff --git a/include/types/session.h b/include/types/session.h index 9437198..18d5115 100644 --- a/include/types/session.h
+++ b/include/types/session.h
@@ -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 */ diff --git a/src/dumpstats.c b/src/dumpstats.c index c132246..99ac770 100644 --- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -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; diff --git a/src/proto_uxst.c b/src/proto_uxst.c index 9054722..76709c1 100644 --- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -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, -- 1.5.3.7
This archive was generated by hypermail 2.2.0 : 2008/02/26 02:15 CET