From 23cd5d3cbbd2381b20738f2568147585cbb0b3a0 Mon Sep 17 00:00:00 2001 From: Pavel Rochnyack Date: Fri, 15 Jun 2018 17:31:07 +0700 Subject: [PATCH] snmp plugin: FilterOID option implementation Added new options `FilterOID`, `FilterValues` and `FilterIgnoreSelected`. These allows to ignore parts of the MIB by its values. Closes: #2637 --- Makefile.am | 2 +- src/collectd-snmp.pod | 28 +++++++++- src/collectd.conf.in | 2 + src/snmp.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 165 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index 83b76a03..190ce8ec 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1558,7 +1558,7 @@ pkglib_LTLIBRARIES += snmp.la snmp_la_SOURCES = src/snmp.c snmp_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBNETSNMP_CPPFLAGS) snmp_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBNETSNMP_LDFLAGS) -snmp_la_LIBADD = $(BUILD_WITH_LIBNETSNMP_LIBS) +snmp_la_LIBADD = libignorelist.la $(BUILD_WITH_LIBNETSNMP_LIBS) endif if BUILD_PLUGIN_SNMP_AGENT diff --git a/src/collectd-snmp.pod b/src/collectd-snmp.pod index e8f0d6b3..90b5c0ed 100644 --- a/src/collectd-snmp.pod +++ b/src/collectd-snmp.pod @@ -26,6 +26,8 @@ collectd-snmp - Documentation of collectd's C Table true Type "if_octets" TypeInstanceOID "IF-MIB::ifDescr" + #FilterOID "IF-MIB::ifOperStatus" + #FilterValues "1", "2" Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets" @@ -113,8 +115,9 @@ queried using the C SNMP command (see L) and transmitted to collectd. B value list is dispatched and, eventually, one file will be written. -When B is set to B, the OIDs given to B (see below) are -queried using the C SNMP command until the subtree is left. After all +When B
is set to B, the OIDs given to B, B, +B, B and B (see below) are queried using +the C SNMP command until the subtree is left. After all the lists (think: all columns of the table) have been read B values sets will be dispatches and, eventually, several files will be written. If you configure a B (see above) which needs more than one data source (for @@ -269,6 +272,27 @@ The invertmatch value should be use in combination of the Ignore option. It changes the behaviour of the Ignore option, from a blacklist behaviour when InvertMatch is set to false, to a whitelist when specified to true. +=item B I + +=item B I [, I ...] + +=item B I + +When B
is set to I, these options allow to configure filtering +based on MIB values. + +The B declares I to fill table column with values. +The B declares values list to do match. Whether table row will be +collected or ignored depends on the B setting. +As with other plugins that use the daemon's ignorelist functionality, a string +that starts and ends with a slash is interpreted as a regular expression. + +If no selection is configured at all, B table rows are selected. + +When B
is set to I then these options has no effect. + +See B
and F for details. + =back =head2 The Host block diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 28d79811..74b6c888 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -1354,6 +1354,8 @@ # TypeInstanceOID "IF-MIB::ifDescr" # #TypeInstancePrefix "port" # Values "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets" +# #FilterOID "IF-MIB::ifOperStatus" +# #FilterValues "1", "2" # # # Table true diff --git a/src/snmp.c b/src/snmp.c index 9aa63f79..d629259d 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -29,6 +29,7 @@ #include "common.h" #include "plugin.h" #include "utils_complain.h" +#include "utils_ignorelist.h" #include #include @@ -59,6 +60,8 @@ struct data_definition_s { instance_t type_instance; instance_t plugin_instance; instance_t host; + oid_t filter_oid; + ignorelist_t *ignorelist; char *plugin_name; oid_t *values; size_t values_len; @@ -122,6 +125,7 @@ typedef enum { OID_TYPE_TYPEINSTANCE, OID_TYPE_PLUGININSTANCE, OID_TYPE_HOST, + OID_TYPE_FILTER, } csnmp_oid_type_t; /* @@ -223,6 +227,7 @@ static void csnmp_data_definition_destroy(data_definition_t *dd) { sfree(dd->host.value); sfree(dd->values); sfree(dd->ignores); + ignorelist_free(dd->ignorelist); sfree(dd); } /* void csnmp_data_definition_destroy */ @@ -336,6 +341,41 @@ static int csnmp_config_add_data_blacklist(data_definition_t *dd, return 0; } /* int csnmp_config_add_data_blacklist */ +static int csnmp_config_add_data_filter_values(data_definition_t *data, + oconfig_item_t *ci) { + if (ci->values_num < 1) { + WARNING("snmp plugin: `FilterValues' needs at least one argument."); + return -1; + } + + for (int i = 0; i < ci->values_num; i++) { + if (ci->values[i].type != OCONFIG_TYPE_STRING) { + WARNING("snmp plugin: All arguments to `FilterValues' must be strings."); + return -1; + } + ignorelist_add(data->ignorelist, ci->values[i].value.string); + } + + return 0; +} /* int csnmp_config_add_data_filter_values */ + +static int csnmp_config_add_data_filter_oid(data_definition_t *data, + oconfig_item_t *ci) { + + char buffer[DATA_MAX_NAME_LEN]; + int status = cf_util_get_string_buffer(ci, buffer, sizeof(buffer)); + if (status != 0) + return status; + + data->filter_oid.oid_len = MAX_OID_LEN; + + if (!read_objid(buffer, data->filter_oid.oid, &data->filter_oid.oid_len)) { + ERROR("snmp plugin: read_objid (%s) failed.", buffer); + return -1; + } + return 0; +} /* int csnmp_config_add_data_filter_oid */ + static int csnmp_config_add_data(oconfig_item_t *ci) { data_definition_t *dd = calloc(1, sizeof(*dd)); if (dd == NULL) @@ -352,6 +392,14 @@ static int csnmp_config_add_data(oconfig_item_t *ci) { dd->ignores_len = 0; dd->ignores = NULL; + dd->ignorelist = ignorelist_create(/* invert = */ 1); + if (dd->ignorelist == NULL) { + sfree(dd->name); + sfree(dd); + ERROR("snmp plugin: ignorelist_create() failed."); + return ENOMEM; + } + dd->plugin_name = strdup("snmp"); if (dd->plugin_name == NULL) { ERROR("snmp plugin: Can't allocate memory"); @@ -417,7 +465,16 @@ static int csnmp_config_add_data(oconfig_item_t *ci) { status = csnmp_config_add_data_blacklist(dd, option); else if (strcasecmp("InvertMatch", option->key) == 0) status = cf_util_get_boolean(option, &dd->invert_match); - else { + else if (strcasecmp("FilterOID", option->key) == 0) { + status = csnmp_config_add_data_filter_oid(dd, option); + } else if (strcasecmp("FilterValues", option->key) == 0) { + status = csnmp_config_add_data_filter_values(dd, option); + } else if (strcasecmp("FilterIgnoreSelected", option->key) == 0) { + bool t; + status = cf_util_get_boolean(option, &t); + if (status == 0) + ignorelist_set_invert(dd->ignorelist, /* invert = */ !t); + } else { WARNING("snmp plugin: data %s: Option `%s' not allowed here.", dd->name, option->key); status = -1; @@ -1229,6 +1286,7 @@ static int csnmp_dispatch_table(host_definition_t *host, csnmp_cell_char_t *type_instance_cells, csnmp_cell_char_t *plugin_instance_cells, csnmp_cell_char_t *hostname_cells, + csnmp_cell_char_t *filter_cells, csnmp_cell_value_t **value_cells) { const data_set_t *ds; value_list_t vl = VALUE_LIST_INIT; @@ -1236,6 +1294,7 @@ static int csnmp_dispatch_table(host_definition_t *host, csnmp_cell_char_t *type_instance_cell_ptr = type_instance_cells; csnmp_cell_char_t *plugin_instance_cell_ptr = plugin_instance_cells; csnmp_cell_char_t *hostname_cell_ptr = hostname_cells; + csnmp_cell_char_t *filter_cell_ptr = filter_cells; csnmp_cell_value_t *value_cell_ptr[data->values_len]; size_t i; @@ -1330,6 +1389,25 @@ static int csnmp_dispatch_table(host_definition_t *host, } } + /* Update filter_cell_ptr to point expected suffix */ + if (filter_cells != NULL) { + while ((filter_cell_ptr != NULL) && + (csnmp_oid_compare(&filter_cell_ptr->suffix, ¤t_suffix) < 0)) + filter_cell_ptr = filter_cell_ptr->next; + + if (filter_cell_ptr == NULL) { + have_more = 0; + continue; + } + + if (csnmp_oid_compare(&filter_cell_ptr->suffix, ¤t_suffix) > 0) { + /* This suffix is missing in the subtree. Indicate this with the + * "suffix_skipped" flag and try the next instance / suffix. */ + suffix_skipped = 1; + NOTICE("snmp plugin: suffix skipped, variable not found for HostOID."); + } + } + /* Update all the value_cell_ptr to point at the entry with the same * trailing partial OID */ for (i = 0; i < data->values_len; i++) { @@ -1383,8 +1461,21 @@ static int csnmp_dispatch_table(host_definition_t *host, assert((hostname_cell_ptr == NULL) || (csnmp_oid_compare(&hostname_cell_ptr->suffix, &value_cell_ptr[0]->suffix) == 0)); + assert((filter_cell_ptr == NULL) || + (csnmp_oid_compare(&filter_cell_ptr->suffix, + &value_cell_ptr[0]->suffix) == 0)); #endif + if (filter_cell_ptr && + ignorelist_match(data->ignorelist, filter_cell_ptr->value) != 0) { + if (type_instance_cells != NULL) + type_instance_cell_ptr = type_instance_cell_ptr->next; + else + value_cell_ptr[0] = value_cell_ptr[0]->next; + + continue; + } + sstrncpy(vl.type, data->type, sizeof(vl.type)); { @@ -1478,6 +1569,9 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) { if (data->host.oid.oid_len > 0) oid_list_len++; + if (data->filter_oid.oid_len > 0) + oid_list_len++; + /* Holds the last OID returned by the device. We use this in the GETNEXT * request to proceed. */ oid_t oid_list[oid_list_len]; @@ -1497,6 +1591,8 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) { csnmp_cell_char_t *plugin_instance_cells_tail = NULL; csnmp_cell_char_t *hostname_cells_head = NULL; csnmp_cell_char_t *hostname_cells_tail = NULL; + csnmp_cell_char_t *filter_cells_head = NULL; + csnmp_cell_char_t *filter_cells_tail = NULL; csnmp_cell_value_t **value_cells_head; csnmp_cell_value_t **value_cells_tail; @@ -1547,6 +1643,12 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) { i++; } + if (data->filter_oid.oid_len > 0) { + memcpy(oid_list + i, &data->filter_oid, sizeof(oid_t)); + oid_list_todo[i] = OID_TYPE_FILTER; + i++; + } + /* We're going to construct n linked lists, one for each "value". * value_cells_head will contain pointers to the heads of these linked lists, * value_cells_tail will contain pointers to the tail of the lists. */ @@ -1760,6 +1862,32 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) { DEBUG("snmp plugin: il->hostname = `%s';", cell->value); csnmp_cells_append(&hostname_cells_head, &hostname_cells_tail, cell); + } else if (oid_list_todo[i] == OID_TYPE_FILTER) { + if ((vb->type == SNMP_ENDOFMIBVIEW) || + (snmp_oid_ncompare(data->filter_oid.oid, data->filter_oid.oid_len, + vb->name, vb->name_length, + data->filter_oid.oid_len) != 0)) { + DEBUG("snmp plugin: host = %s; data = %s; Host left its subtree.", + host->name, data->name); + oid_list_todo[i] = 0; + continue; + } + + /* Allocate a new `csnmp_cell_char_t', insert the instance name and + * add it to the list */ + csnmp_cell_char_t *cell = + csnmp_get_char_cell(vb, &data->filter_oid, host, data); + if (cell == NULL) { + ERROR("snmp plugin: host %s: csnmp_get_char_cell() failed.", + host->name); + status = -1; + break; + } + + csnmp_cell_replace_reserved_chars(cell); + + DEBUG("snmp plugin: il->filter = `%s';", cell->value); + csnmp_cells_append(&filter_cells_head, &filter_cells_tail, cell); } else /* The variable we are processing is a normal value */ { assert(oid_list_todo[i] == OID_TYPE_VARIABLE); @@ -1836,7 +1964,7 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) { if (status == 0) csnmp_dispatch_table(host, data, type_instance_cells_head, plugin_instance_cells_head, hostname_cells_head, - value_cells_head); + filter_cells_head, value_cells_head); /* Free all allocated variables here */ while (type_instance_cells_head != NULL) { @@ -1857,6 +1985,12 @@ static int csnmp_read_table(host_definition_t *host, data_definition_t *data) { hostname_cells_head = next; } + while (filter_cells_head != NULL) { + csnmp_cell_char_t *next = filter_cells_head->next; + sfree(filter_cells_head); + filter_cells_head = next; + } + for (i = 0; i < data->values_len; i++) { while (value_cells_head[i] != NULL) { csnmp_cell_value_t *next = value_cells_head[i]->next; -- 2.11.0