Navigation Menu

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

snmp_agent: Implement SNMP Agent plugin. #2105

Merged
merged 11 commits into from May 18, 2017
8 changes: 8 additions & 0 deletions Makefile.am
Expand Up @@ -1456,6 +1456,14 @@ snmp_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBNETSNMP_LDFLAGS)
snmp_la_LIBADD = $(BUILD_WITH_LIBNETSNMP_LIBS)
endif

if BUILD_PLUGIN_SNMP_AGENT
pkglib_LTLIBRARIES += snmp_agent.la
snmp_agent_la_SOURCES = src/snmp_agent.c
snmp_agent_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBNETSNMP_CPPFLAGS)
snmp_agent_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBNETSNMP_LDFLAGS)
snmp_agent_la_LIBADD = $(BUILD_WITH_LIBNETSNMP_LIBS)
endif

if BUILD_PLUGIN_STATSD
pkglib_LTLIBRARIES += statsd.la
statsd_la_SOURCES = src/statsd.c
Expand Down
13 changes: 12 additions & 1 deletion README
Expand Up @@ -490,6 +490,13 @@ Features
updates to the files and write a bunch of updates at once, which lessens
system load a lot.

- snmp_agent
Receives and handles queries from SNMP master agent and returns the data
collected by read plugins. Handles requests only for OIDs specified in
configuration file. To handle SNMP queries the plugin gets data from
collectd and translates requested values from collectd's internal format
to SNMP format.

- unixsock
One can query the values from the unixsock plugin whenever they're
needed. Please read collectd-unixsock(5) for a description on how that's
Expand Down Expand Up @@ -817,7 +824,11 @@ Prerequisites
This library is part of the “Manage ONTAP SDK” published by NetApp.

* libnetsnmp (optional)
For the `snmp' plugin.
For the `snmp' and 'snmp_agent' plugins.
<http://www.net-snmp.org/>

* libnetsnmpagent (optional)
Required for the 'snmp_agent' plugin.
<http://www.net-snmp.org/>

* libnotify (optional)
Expand Down
22 changes: 22 additions & 0 deletions configure.ac
Expand Up @@ -3643,6 +3643,7 @@ AC_SUBST([LIBNETAPP_LIBS])
# }}}

# --with-libnetsnmp {{{
with_libnetsnmpagent="no"
AC_ARG_WITH(libnetsnmp, [AS_HELP_STRING([--with-libnetsnmp@<:@=PREFIX@:>@], [Path to the Net-SNMPD library.])],
[
if test "x$withval" = "xno"
Expand All @@ -3655,6 +3656,7 @@ AC_ARG_WITH(libnetsnmp, [AS_HELP_STRING([--with-libnetsnmp@<:@=PREFIX@:>@], [Pat
with_libnetsnmp_cppflags="-I$withval/include"
with_libnetsnmp_ldflags="-I$withval/lib"
with_libnetsnmp="yes"
with_libnetsnmpagent="yes"
fi; fi
],
[with_libnetsnmp="yes"])
Expand All @@ -3667,6 +3669,10 @@ then
[with_libnetsnmp="yes"],
[with_libnetsnmp="no (net-snmp/net-snmp-config.h not found)"]
)
AC_CHECK_HEADERS(net-snmp/agent/agent_module_config.h,
[],
[with_libnetsnmpagent="no (net-snmp/agent/agent_module_config.h not found)"]
)

CPPFLAGS="$SAVE_CPPFLAGS"
fi
Expand All @@ -3680,6 +3686,11 @@ then
[with_libnetsnmp="no (libnetsnmp not found)"],
[$with_snmp_libs])

AC_CHECK_LIB(netsnmpagent, init_agent,
[with_libnetsnmpagent="yes"],
[with_libnetsnmpagent="no (libnetsnmpagent not found)"],
[$with_snmp_libs])

LDFLAGS="$SAVE_LDFLAGS"
fi
if test "x$with_libnetsnmp" = "xyes"
Expand All @@ -3688,6 +3699,10 @@ then
BUILD_WITH_LIBNETSNMP_LDFLAGS="$with_libnetsnmp_ldflags"
BUILD_WITH_LIBNETSNMP_LIBS="-lnetsnmp"
fi
if test "x$with_libnetsnmpagent" = "xyes"
then
BUILD_WITH_LIBNETSNMP_LIBS+=" -lnetsnmpagent"
fi
AC_SUBST(BUILD_WITH_LIBNETSNMP_CPPFLAGS)
AC_SUBST(BUILD_WITH_LIBNETSNMP_LDFLAGS)
AC_SUBST(BUILD_WITH_LIBNETSNMP_LIBS)
Expand Down Expand Up @@ -5899,6 +5914,7 @@ plugin_processes="no"
plugin_protocols="no"
plugin_python="no"
plugin_serial="no"
plugin_snmp_agent="no"
plugin_smart="no"
plugin_swap="no"
plugin_tape="no"
Expand Down Expand Up @@ -6191,6 +6207,10 @@ if test "x$with_kvm_getswapinfo" = "xyes"; then
plugin_swap="yes"
fi

if test "x$with_libnetsnmp" = "xyes" && test "x$with_libnetsnmpagent" = "xyes"; then
plugin_snmp_agent="yes"
fi

if test "x$have_swapctl" = "xyes" && test "x$c_cv_have_swapctl_two_args" = "xyes"; then
plugin_swap="yes"
fi
Expand Down Expand Up @@ -6350,6 +6370,7 @@ AC_PLUGIN([serial], [$plugin_serial], [serial port traffic
AC_PLUGIN([sigrok], [$with_libsigrok], [sigrok acquisition sources])
AC_PLUGIN([smart], [$plugin_smart], [SMART statistics])
AC_PLUGIN([snmp], [$with_libnetsnmp], [SNMP querying plugin])
AC_PLUGIN([snmp_agent], [$plugin_snmp_agent], [SNMP agent plugin])
AC_PLUGIN([statsd], [yes], [StatsD plugin])
AC_PLUGIN([swap], [$plugin_swap], [Swap usage statistics])
AC_PLUGIN([syslog], [$have_syslog], [Syslog logging plugin])
Expand Down Expand Up @@ -6774,6 +6795,7 @@ AC_MSG_RESULT([ serial . . . . . . . $enable_serial])
AC_MSG_RESULT([ sigrok . . . . . . . $enable_sigrok])
AC_MSG_RESULT([ smart . . . . . . . . $enable_smart])
AC_MSG_RESULT([ snmp . . . . . . . . $enable_snmp])
AC_MSG_RESULT([ snmp_agent . . . . . $enable_snmp_agent])
AC_MSG_RESULT([ statsd . . . . . . . $enable_statsd])
AC_MSG_RESULT([ swap . . . . . . . . $enable_swap])
AC_MSG_RESULT([ syslog . . . . . . . $enable_syslog])
Expand Down
25 changes: 25 additions & 0 deletions src/collectd.conf.in
Expand Up @@ -186,6 +186,7 @@
#@BUILD_PLUGIN_SIGROK_TRUE@LoadPlugin sigrok
#@BUILD_PLUGIN_SMART_TRUE@LoadPlugin smart
#@BUILD_PLUGIN_SNMP_TRUE@LoadPlugin snmp
#@BUILD_PLUGIN_SNMP_AGENT_TRUE@LoadPlugin snmp_agent
#@BUILD_PLUGIN_STATSD_TRUE@LoadPlugin statsd
#@BUILD_PLUGIN_SWAP_TRUE@LoadPlugin swap
#@BUILD_PLUGIN_TABLE_TRUE@LoadPlugin table
Expand Down Expand Up @@ -1228,6 +1229,30 @@
# </Host>
#</Plugin>

#<Plugin snmp_agent>
# <Data "memAvailReal">
# Plugin "memory"
# Type "memory"
# TypeInstance "free"
# OIDs "1.3.6.1.4.1.2021.4.6.0"
# </Data>
# <Table "ifTable">
# IndexOID "IF-MIB::ifIndex"
# SizeOID "IF-MIB::ifNumber"
# <Data "ifDescr">
# Instance true
# Plugin "interface"
# OIDs "IF-MIB::ifDescr"
# </Data>
# <Data "ifOctets">
# Plugin "interface"
# Type "if_octets"
# TypeInstance ""
# OIDs "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
# </Data>
# </Table>
#</Plugin>

#<Plugin statsd>
# Host "::"
# Port "8125"
Expand Down
109 changes: 109 additions & 0 deletions src/collectd.conf.pod
Expand Up @@ -6930,6 +6930,115 @@ Since the configuration of the C<snmp plugin> is a little more complicated than
other plugins, its documentation has been moved to an own manpage,
L<collectd-snmp(5)>. Please see there for details.

=head2 Plugin C<snmp_agent>

The I<snmp_agent> plugin is an AgentX subagent that receives and handles queries
from SNMP master agent and returns the data collected by read plugins.
The I<snmp_agent> plugin handles requests only for OIDs specified in
configuration file. To handle SNMP queries the plugin gets data from collectd
and translates requested values from collectd's internal format to SNMP format.
This plugin is a generic plugin and cannot work without configuration.
For more details on AgentX subagent see
<http://www.net-snmp.org/tutorial/tutorial-5/toolkit/demon/>

B<Synopsis:>

<Plugin snmp_agent>
<Data "memAvailReal">
Plugin "memory"
Type "memory"
TypeInstance "free"
OIDs "1.3.6.1.4.1.2021.4.6.0"
</Data>
<Table "ifTable">
IndexOID "IF-MIB::ifIndex"
SizeOID "IF-MIB::ifNumber"
<Data "ifDescr">
Instance true
Plugin "interface"
OIDs "IF-MIB::ifDescr"
</Data>
<Data "ifOctets">
Plugin "interface"
Type "if_octets"
TypeInstance ""
OIDs "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets"
</Data>
</Table>
</Plugin>

There are two types of blocks that can be contained in the
C<E<lt>PluginE<nbsp> snmp_agentE<gt>> block: B<Data> and B<Table>:

=head3 The B<Data> block

The B<Data> block defines a list OIDs that are to be handled. This block can
define scalar or table OIDs. If B<Data> block is defined inside of B<Table>
block it reperesents table OIDs.
The following options can be set:

=over 4

=item B<Instance> I<true|false>

When B<Instance> is set to B<true>, the value for requested OID is copied from
plugin instance field of corresponding collectd value. If B<Data> block defines
scalar data type B<Instance> has no effect and can be omitted.

=item B<Plugin> I<String>

Read plugin name whose collected data will be mapped to specified OIDs.

=item B<Type> I<String>

Collectd's type that is to be used for specified OID, e.E<nbsp>g. "if_octets"
for example. The types are read from the B<TypesDB> (see L<collectd.conf(5)>).

=item B<TypeInstance> I<String>

Collectd's type-instance that is to be used for specified OID.

=item B<OIDs> I<OID> [I<OID> ...]

Configures the OIDs to be handled by I<snmp_agent> plugin. Values for these OIDs
are taken from collectd data type specified by B<Plugin>, B<Type>,
B<TypeInstance> fields of this B<Data> block. Number of the OIDs configured
should correspond to number of values in specified B<Type>.
For example two OIDs "IF-MIB::ifInOctets" "IF-MIB::ifOutOctets" can be mapped to
"rx" and "tx" values of "if_octets" type.

=item B<Scale> I<Value>

The values taken from collectd are multiplied by I<Value>. The field is optional
and the default is B<1.0>.

=item B<Shift> I<Value>

I<Value> is added to values from collectd after they have been multiplied by
B<Scale> value. The field is optional and the default value is B<0.0>.

=back

=head3 The B<Table> block

The B<Table> block defines a collection of B<Data> blocks that belong to one
snmp table. In addition to multiple B<Data> blocks the following options can be
set:

=over 4

=item B<IndexOID> I<OID>

OID that is handled by the plugin and is mapped to numerical index value that is
generated by the plugin for each table record.

=item B<SizeOID> I<OID>

OID that is handled by the plugin. Returned value is the number of records in
the table. The field is optional.

=back

=head2 Plugin C<statsd>

The I<statsd plugin> listens to a UDP socket, reads "events" in the statsd
Expand Down
2 changes: 2 additions & 0 deletions src/daemon/utils_avltree.c
Expand Up @@ -534,6 +534,8 @@ int c_avl_pick(c_avl_tree_t *t, void **key, void **value) {
c_avl_node_t *n;
c_avl_node_t *p;

assert(t != NULL);

if ((key == NULL) || (value == NULL))
return (-1);
if (t->root == NULL)
Expand Down
69 changes: 69 additions & 0 deletions src/daemon/utils_cache.c
Expand Up @@ -477,6 +477,75 @@ gauge_t *uc_get_rate(const data_set_t *ds, const value_list_t *vl) {
return (ret);
} /* gauge_t *uc_get_rate */

int uc_get_value_by_name(const char *name, value_t **ret_values,
size_t *ret_values_num) {
value_t *ret = NULL;
size_t ret_num = 0;
cache_entry_t *ce = NULL;
int status = 0;

pthread_mutex_lock(&cache_lock);

if (c_avl_get(cache_tree, name, (void *) &ce) == 0) {
assert(ce != NULL);

/* remove missing values from getval */
if (ce->state == STATE_MISSING) {
status = -1;
} else {
ret_num = ce->values_num;
ret = malloc(ret_num * sizeof(*ret));
if (ret == NULL) {
ERROR("utils_cache: uc_get_value_by_name: malloc failed.");
status = -1;
} else {
memcpy(ret, ce->values_raw, ret_num * sizeof(value_t));
}
}
}
else {
DEBUG("utils_cache: uc_get_value_by_name: No such value: %s", name);
status = -1;
}

pthread_mutex_unlock(&cache_lock);

if (status == 0) {
*ret_values = ret;
*ret_values_num = ret_num;
}

return (status);
} /* int uc_get_value_by_name */

value_t *uc_get_value(const data_set_t *ds, const value_list_t *vl) {
char name[6 * DATA_MAX_NAME_LEN];
value_t *ret = NULL;
size_t ret_num = 0;
int status;

if (FORMAT_VL(name, sizeof(name), vl) != 0) {
ERROR("utils_cache: uc_get_value: FORMAT_VL failed.");
return (NULL);
}

status = uc_get_value_by_name(name, &ret, &ret_num);
if (status != 0)
return (NULL);

/* This is important - the caller has no other way of knowing how many
* values are returned. */
if (ret_num != (size_t) ds->ds_num) {
ERROR("utils_cache: uc_get_value: ds[%s] has %zu values, "
"but uc_get_value_by_name returned %zu.", ds->type, ds->ds_num,
ret_num);
sfree(ret);
return (NULL);
}

return (ret);
} /* value_t *uc_get_value */

size_t uc_get_size(void) {
size_t size_arrays = 0;

Expand Down
2 changes: 2 additions & 0 deletions src/daemon/utils_cache.h
Expand Up @@ -42,6 +42,8 @@ int uc_update(const data_set_t *ds, const value_list_t *vl);
int uc_get_rate_by_name(const char *name, gauge_t **ret_values,
size_t *ret_values_num);
gauge_t *uc_get_rate(const data_set_t *ds, const value_list_t *vl);
int uc_get_value_by_name(const char *name, value_t **ret_values, size_t *ret_values_num);
value_t *uc_get_value(const data_set_t *ds, const value_list_t *vl);

size_t uc_get_size(void);
int uc_get_names(char ***ret_names, cdtime_t **ret_times, size_t *ret_number);
Expand Down