Commit c776dfae authored by Alessandro Rubini's avatar Alessandro Rubini

snmp library: add wrsPstats

This is a squash-up of the whole netsnmp-pain branch, that I kept
on the repository, so to document the steps.
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent c8ca9ade
......@@ -8,11 +8,12 @@ CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
# defer running "net-snmp-config --cflags" so it is visible in make output
CFLAGS = -fPIC $$($(NET_SNMP_CONFIG) --cflags)
CFLAGS += -fPIC -Wall $$($(NET_SNMP_CONFIG) --cflags | sed s,-I/usr/include,,)
LDFLAGS = -shared $$($(NET_SNMP_CONFIG) --ldflags)
CFLAGS += -Iinclude -DWRS_WITH_SNMP_HACKISH_LOG=0
SHLIB = wrsSnmp.so
SOURCES = init.c wrsScalar.c
SOURCES = init.c wrsScalar.c wrsPstats.c
OBJECTS = $(SOURCES:.c=.o)
all: $(SHLIB)
......
The initial version of wrsPstats.c and wrsPstats.h is generated by running
The initial version of wrsScalar.c and wrsScalar.h is generated by running
export BUILD_DIR="$WRS_OUTPUT_DIR/build/buildroot-2011.11/output/build"
export MIBDIRS=$BUILD_DIR/netsnmp-5.6.1.1/mibs
......@@ -8,9 +8,84 @@ And then this "intuitive" command:
export MIBS=./WR-SWITCH-MIB.txt
$BUILD_DIR/netsnmp-5.6.1.1/local/mib2c \
-I $BUILD_DIR//netsnmp-5.6.1.1/local \
-I $BUILD_DIR/netsnmp-5.6.1.1/local \
-c mib2c.scalar.conf \
wrsScalar
Clearly, if you rename the item, the code must be completely rewritten.
Clearly, if you rename the item, the code must be completely rewritten
(and I already did it once).
Pstats code, on the other hand, is based on what ifTable does in the
real snmpd. i.e., no autogeneration was used.
This is one option I tried, but the result was too complex:
export MIBS=./WR-SWITCH-MIB.txt
$BUILD_DIR/netsnmp-5.6.1.1/local/mib2c \
-I $BUILD_DIR/netsnmp-5.6.1.1/local \
-I $BUILD_DIR/netsnmp-5.6.1.1/local/mib2c-conf.d \
-c mib2c.mfd.conf \
pstatsTable
This one spits out a single file, but again too complex for me:
export MIBS=./WR-SWITCH-MIB.txt
$BUILD_DIR/netsnmp-5.6.1.1/local/mib2c \
-I $BUILD_DIR/netsnmp-5.6.1.1/local \
-I $BUILD_DIR/netsnmp-5.6.1.1/local/mib2c-conf.d \
-c mib2c.raw-table.conf \
pstatsTable
Using "mib2c.create-dataset.conf" is even worse, becahse the dataset
is static and hidden in some obscure place, I can't change it after
creation.
With the current code base, I properly get the counters:
tornado% snmpwalk -c public -v 2c wrs SNMPv2-SMI::enterprises.96.100 | wc -l
743
The numeric OID for WRS is:
.1.3.6.1.4.1.96.100
The OID for counters is:
.1.3.6.1.4.1.96.100.2.1
Then, column 1 is the counter names, and colomn N+2 is the counters for
port wrN (0..17). Column M+1 is for port M as written on the device (1..18).
Example: get counter 18 (line 19):
snmpwalk -On -c public -v 2c wrs SNMPv2-SMI::enterprises.96.100.2.1 | \
grep '2\.1\..*\.19 ='
.1.3.6.1.4.1.96.100.2.1.1.19 = STRING: "TX Frames"
.1.3.6.1.4.1.96.100.2.1.2.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.3.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.4.19 = Counter32: 0
.1.3.6.1.4.1.96.100.2.1.5.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.6.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.7.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.8.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.9.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.10.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.11.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.12.19 = Counter32: 1057
.1.3.6.1.4.1.96.100.2.1.13.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.14.19 = Counter32: 0
.1.3.6.1.4.1.96.100.2.1.15.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.16.19 = Counter32: 1025
.1.3.6.1.4.1.96.100.2.1.17.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.18.19 = Counter32: 38
.1.3.6.1.4.1.96.100.2.1.19.19 = Counter32: 38
You can get a symbolic readout by forcing the tool to access the
local WR-SWITCH-MIB.txt. So we can get all counters for port WR14 like
this:
snmpwalk -m $WR_SWITCH_SW/userspace/snmpd/WR-SWITCH-MIB.txt \
-c public -v 2c wrs 1.3.6.1.4.1.96.100.2 | \
grep WR14
WR-SWITCH-MIB DEFINITIONS ::= BEGIN
-- This is blatantly copied from NET-SNMP-TUTORIAL-MIB.txt and adapted
-- This is blatantly copied from NET-SNMP-TUTORIAL-MIB.txt and IF-MIB.txt
-- URL: http://www.net-snmp.org/tutorial/tutorial-5/
-- toolkit/mib_module/NET-SNMP-TUTORIAL-MIB.txt
-- Bugs and swearing by Alessandro Rubini
-- IMPORTS: Include definitions from other mibs here
IMPORTS
OBJECT-TYPE, Integer32,
MODULE-IDENTITY,enterprises FROM SNMPv2-SMI
MODULE-COMPLIANCE, OBJECT-GROUP FROM SNMPv2-CONF;
OBJECT-TYPE, Integer32, Counter32,
MODULE-IDENTITY,enterprises FROM SNMPv2-SMI
DisplayString FROM SNMPv2-TC
MODULE-COMPLIANCE, OBJECT-GROUP FROM SNMPv2-CONF;
CERN OBJECT IDENTIFIER ::= { enterprises 96 }
-- A brief description and update information about this mib.
-- (I follow the CamelCase to be sure I don't mysype. Who knows the rules...
-- (I follow the CamelCase to be sure I don't mistype. Who knows the rules...
wrSwitchMIB MODULE-IDENTITY
LAST-UPDATED "201406181915Z" -- 18 june 2014, 19:15 (why saying again?)
ORGANIZATION "CERN"
CONTACT-INFO "postal: BE-CO-HT, CERN, Geneva
email: ht-drivers@cern.ch
email: ht-drivers@cern.ch
"
DESCRIPTION "White Rabbit Switch internal details
"
......@@ -42,4 +43,204 @@ wrsScalarOne OBJECT-TYPE
"Just an integer"
::= { wrsScalar 1 }
-- This pstats stuff is copied, in a way, from the ifTable stuff (IF-MIB.txt)
-- We have the ports as columns, so they are a fixed number. And the
-- counters as lines. This allows to change counters following gateware.
-- even if it is not standard practice. Otherwise, we'd need to rewrite
-- code every time (I *hate* this obsolete crap: code generators should not
-- be like that).
wrsPstats OBJECT-TYPE
SYNTAX SEQUENCE OF pstatsEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A list of pstat counters. One column per interface.
The number of entries is given by the value of pstatsNumber."
::= { wrSwitchMIB 2 }
pstatsEntry OBJECT-TYPE
SYNTAX PstatsEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"An entry containing a pstat name and values for all WR ports."
INDEX { pstatsIndex }
::= { wrsPstats 1 }
PstatsEntry ::=
SEQUENCE {
pstatsDescr DisplayString,
pstatsWR0 Counter32,
pstatsWR1 Counter32,
pstatsWR2 Counter32,
pstatsWR3 Counter32,
pstatsWR4 Counter32,
pstatsWR5 Counter32,
pstatsWR6 Counter32,
pstatsWR7 Counter32,
pstatsWR8 Counter32,
pstatsWR9 Counter32,
pstatsWR10 Counter32,
pstatsWR11 Counter32,
pstatsWR12 Counter32,
pstatsWR13 Counter32,
pstatsWR14 Counter32,
pstatsWR15 Counter32,
pstatsWR16 Counter32,
pstatsWR17 Counter32
}
pstatsDescr OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..32))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"A textual string describing this counter"
::= { pstatsEntry 1 }
pstatsWR0 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr0."
::= {pstatsEntry 2 }
pstatsWR1 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr1."
::= {pstatsEntry 3 }
pstatsWR2 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr2."
::= {pstatsEntry 4 }
pstatsWR3 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr3."
::= {pstatsEntry 5 }
pstatsWR4 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr4."
::= {pstatsEntry 6 }
pstatsWR5 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr5."
::= {pstatsEntry 7 }
pstatsWR6 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr6."
::= {pstatsEntry 8 }
pstatsWR7 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr7."
::= {pstatsEntry 9 }
pstatsWR8 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr8."
::= {pstatsEntry 10 }
pstatsWR9 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr9."
::= {pstatsEntry 11 }
pstatsWR10 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr10."
::= {pstatsEntry 12 }
pstatsWR11 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr11."
::= {pstatsEntry 13 }
pstatsWR12 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr12."
::= {pstatsEntry 14 }
pstatsWR13 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr13."
::= {pstatsEntry 15 }
pstatsWR14 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr14."
::= {pstatsEntry 16 }
pstatsWR15 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr15."
::= {pstatsEntry 17 }
pstatsWR16 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr16."
::= {pstatsEntry 18 }
pstatsWR17 OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The value of this counter for interface wr17."
::= {pstatsEntry 19 }
END
......@@ -7,9 +7,11 @@
/* The sub-init functions */
#include "wrsScalar.h"
#include "wrsPstats.h"
void
init_wrsSnmp(void)
{
init_wrsScalar();
init_wrsPstats();
}
/*
* White Rabbit Switch pstats table
* Using the netsnmp iterator, like in tcpTable
* Alessandro Rubini for CERN, 2014
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/agent/auto_nlist.h>
#include "wrsPstats.h"
#include <stdarg.h>
static FILE *logf;
/* local hack */
static int logmsg(const char *fmt, ...)
{
va_list args;
int ret;
if (WRS_WITH_SNMP_HACKISH_LOG) {
if (!logf)
logf = fopen("/dev/console", "w");
va_start(args, fmt);
ret = vfprintf(logf, fmt, args);
va_end(args);
return ret;
} else {
return 0;
}
}
int dumpstruct(FILE *dest, char *name, void *ptr, int size)
{
int ret = 0, i;
unsigned char *p = ptr;
if (WRS_WITH_SNMP_HACKISH_LOG) {
ret = fprintf(dest, "dump %s at %p (size 0x%x)\n",
name, ptr, size);
for (i = 0; i < size; ) {
ret += fprintf(dest, "%02x", p[i]);
i++;
ret += fprintf(dest, i & 3 ? " " : i & 0xf ? " " : "\n");
}
if (i & 0xf)
ret += fprintf(dest, "\n");
}
return ret;
}
/* end local hack */
#define PSTATS_CACHE_TIMEOUT 5 /* seconds */
/* Our structure for caching data */
#define PSTATS_N_COUNTERS 39
#define PSTATS_N_PORTS 18 /* this hardwired in the mib too */
struct pstats_per_port {
uint32_t val[PSTATS_N_COUNTERS];
};
static struct pstats_global_data {
struct pstats_per_port port[PSTATS_N_PORTS];
char *pname[PSTATS_N_PORTS];
} pstats_global_data;
static char *pstats_names[] = {
[0] = "TX Underrun",
[1] = "RX Overrun",
[2] = "RX Invalid Code",
[3] = "RX Sync Lost",
[4] = "RX Pause Frames",
[5] = "RX Pfilter Dropped",
[6] = "RX PCS Errors",
[7] = "RX Giant Frames",
[8] = "RX Runt Frames",
[9] = "RX CRC Errors",
[10] = "RX Pclass 0",
[11] = "RX Pclass 1",
[12] = "RX Pclass 2",
[13] = "RX Pclass 3",
[14] = "RX Pclass 4",
[15] = "RX Pclass 5",
[16] = "RX Pclass 6",
[17] = "RX Pclass 7",
[18] = "TX Frames",
[19] = "RX Frames",
[20] = "RX Drop RTU Full",
[21] = "RX PRIO 0",
[22] = "RX PRIO 1",
[23] = "RX PRIO 2",
[24] = "RX PRIO 3",
[25] = "RX PRIO 4",
[26] = "RX PRIO 5",
[27] = "RX PRIO 6",
[28] = "RX PRIO 7",
[29] = "RTU Valid",
[30] = "RTU Responses",
[31] = "RTU Dropped",
[32] = "FastMatch: Priority",
[33] = "FastMatch: FastForward",
[34] = "FastMatch: NonForward",
[35] = "FastMatch: Resp Valid",
[36] = "FullMatch: Resp Valid",
[37] = "Forwarded",
[38] = "TRU Resp Valid"
};
/* FIXME: build error if ARRAY_SIZE(pstats_names) != PSTATS_N_COUNTERS */
static int
wrsPstats_handler(netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
netsnmp_request_info *request;
netsnmp_variable_list *requestvb;
netsnmp_table_request_info *table_info;
struct pstats_global_data *data = &pstats_global_data; /* a shorter name */
int counter;
int wrport;
uint32_t *c;
logmsg("%s: %i\n", __func__, __LINE__);
switch (reqinfo->mode) {
case MODE_GET:
for (request=requests; request; request=request->next) {
requestvb = request->requestvb;
logmsg("%s: %i\n", __func__, __LINE__);
/* our "context" is the counter number; "subid" the column i.e. the port */
counter = (int)netsnmp_extract_iterator_context(request);
table_info = netsnmp_extract_table_info(request);
wrport = table_info->colnum - 2; /* port is 0-based and position 1 is the string */
logmsg("counter %i, port %i\n", counter, wrport);
if (wrport < 0) {
char *s = pstats_names[counter];
snmp_set_var_typed_value(requestvb, ASN_OCTET_STR, s, strlen(s));
continue;
}
/* While most tables do "switch(subid)" we'd better just index */
c = &data->port[wrport].val[counter];
snmp_set_var_typed_value(requestvb, ASN_COUNTER,
(u_char *)c, sizeof(*c));
}
break;
case MODE_GETNEXT:
case MODE_GETBULK:
case MODE_SET_RESERVE1:
case MODE_SET_RESERVE2:
case MODE_SET_ACTION:
case MODE_SET_COMMIT:
case MODE_SET_FREE:
case MODE_SET_UNDO:
/* unsupported mode */
break;
default:
/* unknown mode */
break;
}
return SNMP_ERR_NOERROR;
}
static netsnmp_variable_list *
wrsPstats_next_entry( void **loop_context,
void **data_context,
netsnmp_variable_list *index,
netsnmp_iterator_info *data)
{
intptr_t i;
/* create the line ID from counter number */
i = (intptr_t)*loop_context;
//logmsg("%s: %i (i = %i)\n", __func__, __LINE__, i);
if (i >= PSTATS_N_COUNTERS)
return NULL; /* no more */
i++;
/* Create the row OID: only the counter index */
snmp_set_var_value(index, (u_char*)&i, sizeof(i));
/* Set the data context (1..39 -> 0..38) */
*data_context = (void *)(intptr_t)(i - 1);
/* and set the loop context for the next iteration */
*loop_context = (void *)i;
return index;
}
static netsnmp_variable_list *
wrsPstats_first_entry(void **loop_context,
void **data_context,
netsnmp_variable_list *index,
netsnmp_iterator_info *data)
{
logmsg("%s: %i\n", __func__, __LINE__);
/* reset internal position, so "next" is "first" */
*loop_context = (void*)0; /* first counter */
return wrsPstats_next_entry(loop_context, data_context, index, data);
}
static int
wrsPstats_load(netsnmp_cache *cache, void *vmagic)
{
FILE *f;
char fname[32];
int wrport, counter;
struct pstats_per_port *stat;
for (wrport = 0; wrport < PSTATS_N_PORTS; wrport++) {
sprintf(fname, "/proc/sys/pstats/port%i", wrport);
stat = pstats_global_data.port + wrport;
f = fopen(fname, "r");
if (!f) {
memset(stat, 0x7f, sizeof(*stat));
continue;
}
for (counter = 0; counter < PSTATS_N_COUNTERS; counter++) {
if (fscanf(f, "%u", stat->val + counter) != 1)
stat->val[counter] = 0xffffff;
}
fclose(f);
}
//dumpstruct(logf, "global data", &pstats_global_data, sizeof(pstats_global_data));
return 0;
}
void
init_wrsPstats(void)
{
const oid wrsPstats_oid[] = { 1, 3, 6, 1, 4, 1, 96, 100, 2 };
netsnmp_table_registration_info *table_info;
netsnmp_iterator_info *iinfo;
netsnmp_handler_registration *reginfo;
table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
if (!table_info)
return;
/* Add indexes: we only use one integer OID member as line identifier */
netsnmp_table_helper_add_indexes(table_info, ASN_INTEGER, 0);
table_info->min_column = 1;
table_info->max_column = 1 + PSTATS_N_PORTS;
/* Iterator info */
iinfo = SNMP_MALLOC_TYPEDEF(netsnmp_iterator_info);
if (!iinfo)
return; /* free table_info? */
iinfo->get_first_data_point = wrsPstats_first_entry;
iinfo->get_next_data_point = wrsPstats_next_entry;
iinfo->table_reginfo = table_info;
/* register the table */
reginfo = netsnmp_create_handler_registration("wrsPstats",
wrsPstats_handler,
wrsPstats_oid, OID_LENGTH(wrsPstats_oid),
HANDLER_CAN_RONLY);
netsnmp_register_table_iterator(reginfo, iinfo);
/* and create a local cache */
netsnmp_inject_handler(reginfo,
netsnmp_get_cache_handler(PSTATS_CACHE_TIMEOUT,
wrsPstats_load, NULL,
wrsPstats_oid, OID_LENGTH(wrsPstats_oid)));
}
#ifndef _PSTATSTABLE_H
#define _PSTATSTABLE_H
extern void init_wrsPstats(void);
#endif /* _PSTATSTABLE_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment