Commit 62974ab6 authored by Micha Mueller's avatar Micha Mueller
Browse files

Fill SNMP plugin with life

parent 797377ab
......@@ -15,7 +15,7 @@ CXXFLAGS = -std=c++11 -DBOOST_DATE_TIME_POSIX_TIME_STD_CONFIG -DBOOST_NETWORK_EN
LIBS = -L../deps/mosquitto_build/lib -L$(DCDBDEPLOYPATH)/lib/ -ldl -lmosquitto -lboost_system -lboost_thread -lboost_log_setup -lboost_log -lpthread -lcrypto -lssl -lcppnetlib-server-parsers -lcppnetlib-uri -rdynamic
OBJS = src/dcdbpusher.o src/Configuration.o src/Sensor.o src/MQTTPusher.o src/HttpsServer.o
PLUGINS_BASE = libdcdbplugin_pdu libdcdbplugin_sysfs libdcdbplugin_ipmi libdcdbplugin_bacnet
PLUGINS_BASE = libdcdbplugin_pdu libdcdbplugin_sysfs libdcdbplugin_ipmi libdcdbplugin_bacnet libdcdbplugin_snmp
ifeq ($(OS),Darwin)
BACNET_PORT = bsd
......@@ -77,3 +77,6 @@ libdcdbplugin_pdu.$(LIBEXT): src/Sensor.o src/sensors/pdu/PDUSensor.o src/sensor
libdcdbplugin_bacnet.$(LIBEXT): src/Sensor.o src/sensors/bacnet/BACnetSensor.o src/sensors/bacnet/BACnetClient.o src/sensors/bacnet/BACnetConfigurator.o
$(CXX) $(LIBFLAGS)$@ -o $@ $^ -L$(DCDBDEPLOYPATH)/lib/ -lboost_log -lboost_system -lbacnet
libdcdbplugin_snmp.$(LIBEXT): src/Sensor.o src/sensors/snmp/SNMPSensor.o src/sensors/snmp/SNMPConnection.o src/sensors/snmp/SNMPConfigurator.o
$(CXX) $(LIBFLAGS)$@ -o $@ $^ -L$(DCDBDEPLOYPATH)/lib/ -lboost_log -lboost_system -lnetsnmp -lnetsnmpagent
......@@ -26,6 +26,11 @@ restAPI {
plugins {
plugin snmp {
path ./
config
}
plugin sysfs {
path ./
config
......
global {
mqttprefix /00112233445566778899AABBCC
}
templates {
sensor temp1 {
interval 1000
minValues 3
}
}
; Connections contains a list of connections
; So far, we only support "Agent" mode in which we connect to a
; SNMP agent and issue GET commands to read the data.
connections {
; Each connection is identified by a unique name. However, this
; name is only useful when we print error messages.
connection name1 {
Type Agent ; Type of connection
Host HostNameOfSNMPAgent ; Hostname of the SNMP agent
Port 161 ; Port of the SNMP agent
Community public ; SNMP community string
OIDPrefix 1.3.6.1.4.1.1000 ; When querying OIDs, we'll
; always use this prefix
mqttPart BB ; When generating MQTT topics,
; we'll append this part to
; the prefix
sensor name1 {
OID 100.1 ; OID of the sensor
mqttsuffix 0001 ; MQTT suffix of the sensor
interval 1000 ; Read sensor every 1000ms
}
sensor name2 {
OID 100.2
mqttsuffix 0002
default temp1
}
}
connection name2
{
Type Agent
Host HostNameOfSNMPAgent2
Port 161
Community public
OIDPrefix 1.3.6.1.4.1.1000.15
mqttPart CC
sensor name1 {
OID 3
mqttsuffix 0017
interval 5000
}
}
}
......@@ -65,7 +65,8 @@ void HttpsServer::requestHandler::operator()(server::request const &request, ser
}
//split query part into the individual queries (key-value pairs)
{
{ //do not remove the enclosing brackets
//need to encapsulate this code block to keep queryStrs local
std::vector<std::string> queryStrs;
boost::split(queryStrs, query, boost::is_any_of(";"), boost::token_compress_on);
for(auto& key : queryStrs) {
......
......@@ -9,22 +9,137 @@
#include <boost/foreach.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/algorithm/string.hpp>
SNMPConfigurator::SNMPConfigurator() {
// TODO Auto-generated constructor stub
/* Initialize SNMP library */
init_snmp("dcdbpusher_SNMPplugin");
}
SNMPConfigurator::~SNMPConfigurator() {
// TODO Auto-generated destructor stub
}
SNMPConfigurator::~SNMPConfigurator() {}
bool SNMPConfigurator::readConfig(std::string cfgPath) {
//TODO
Configurator::readConfig(cfgPath);
boost::property_tree::iptree cfg;
boost::property_tree::read_info(cfgPath, cfg);
std::string mqttPartConnection;
//read global variables (if present overwrite those from global.conf)
BOOST_FOREACH(boost::property_tree::iptree::value_type &global, cfg.get_child("global")) {
if (boost::iequals(global.first, "mqttprefix")) {
_mqttPrefix = global.second.data();
if (_mqttPrefix[_mqttPrefix.length()-1] != '/') {
_mqttPrefix.append("/");
}
LOG(debug) << " Using own MQTT-Prefix " << _mqttPrefix;
} else if (boost::iequals(global.first, "cacheInterval")) {
_cacheInterval = stoul(global.second.data());
LOG(debug) << " Using own caching interval " << _cacheInterval << " [s]";
_cacheInterval *= 1000;
} else {
LOG(error) << " Value \"" << global.first << "\" not recognized. Omitting...";
}
}
//read template sensors
BOOST_FOREACH(boost::property_tree::iptree::value_type &sensor, cfg.get_child("templates")) {
if (boost::iequals(sensor.first, "sensor")) {
LOG(debug) << "Template Sensor \"" << sensor.second.data() << "\"";
if (!sensor.second.empty()) {
SNMPSensor snmpSensor(sensor.second.data());
if(readSensor(snmpSensor, sensor.second)) {
_templateSensors.insert(sensorMap_t::value_type(snmpSensor.getName(), snmpSensor));
} else {
LOG(warning) << "Template sensor \"" << sensor.second.data() << "\" has bad values! Ignoring...";
}
}
}
}
BOOST_FOREACH(boost::property_tree::iptree::value_type &connection, cfg.get_child("connections")) {
if (boost::iequals(connection.first, ("connection"))) {
LOG(debug) << "Connection " << connection.second.data();
_connections.push_back(SNMPConnection());
SNMPConnection &conn = _connections.back();
if (!connection.second.empty()) {
BOOST_FOREACH(boost::property_tree::iptree::value_type &c, connection.second) {
if (boost::iequals(c.first, "Type")) {
//TODO would be relevant if we would support more than just agent mode...
// at the moment we just ignore it, as it can be only "Agent" anyways
} else if (boost::iequals(c.first, "Host")) {
conn.setHost(c.second.data());
} else if (boost::iequals(c.first, "Port")) {
conn.setPort(stoi(c.second.data()));
} else if (boost::iequals(c.first, "Community")) {
conn.setSNMPCommunity(c.second.data());
} else if (boost::iequals(c.first, "OIDPrefix")) {
conn.setOIDPrefix(c.second.data());
} else if (boost::iequals(c.first, "mqttPart")) {
mqttPartConnection = c.second.data();
if (mqttPartConnection[mqttPartConnection.length()-1] != '/') {
mqttPartConnection.append("/");
}
} else if (boost::iequals(c.first, "sensor")) {
LOG(debug) << "Sensor \"" << c.second.data() << "\"";
if (!c.second.empty()) {
std::string name = connection.second.data() + "_" + c.second.data();
SNMPSensor * snmpSensor = new SNMPSensor(name);
//first check if default sensor is given
boost::optional<boost::property_tree::iptree&> defaultC = c.second.get_child_optional("default");
if(defaultC) {
LOG(debug) << " Using \"" << defaultC.get().data() << "\" as default.";
sensorMap_t::iterator it = _templateSensors.find(defaultC.get().data());
if(it != _templateSensors.end()) {
*snmpSensor = it->second;
snmpSensor->setName(name);
} else {
LOG(warning) << " Template sensor \"" << defaultC.get().data() << "\" not found! Using standard values.";
}
}
snmpSensor->setMqtt(_mqttPrefix + mqttPartConnection);
snmpSensor->setConnection(&conn);
//read remaining values
if(readSensor(*snmpSensor, c.second)) {
_sensors.push_back(snmpSensor);
} else {
LOG(warning) << " Sensor \"" << c.second.data() << "\" has bad values! Ignoring..." << std::endl;
}
}
}
}
}
conn.init();
}
}
return true;
}
bool SNMPConfigurator::readSensor(SNMPSensor& sensor, boost::property_tree::iptree& config) {
//TODO
BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
if (boost::iequals(val.first, "interval")) {
sensor.setInterval(stoull(val.second.data()));
} else if (boost::iequals(val.first, "mqttsuffix")) {
sensor.setMqtt(sensor.getMqtt() + val.second.data());
} else if (boost::iequals(val.first, "minValues")) {
sensor.setMinValues(stoull(val.second.data()));
} else if (boost::iequals(val.first, "OID")) {
sensor.setOID(sensor.getConnection()->getOIDPrefix() + "." + val.second.data());
} else if (boost::iequals(val.first, "default")) {
//avoid unnecessary "Value not recognized" message
} else {
LOG(warning) << " Value \"" << val.first << "\" not recognized. Omitting...";
}
}
sensor.setCacheInterval(_cacheInterval);
LOG(debug) << " MQTT : " << sensor.getMqtt();
LOG(debug) << " Interval : " << sensor.getInterval();
LOG(debug) << " minValues: " << sensor.getMinValues();
LOG(debug) << " OID : " << sensor.getOIDString();
return true;
}
......@@ -16,10 +16,12 @@
#include <string>
#include <vector>
#include <map>
#include <list>
#include <boost/property_tree/ptree.hpp>
class SNMPConfigurator : public Configurator {
typedef std::list<SNMPConnection> connectionList_t;
typedef std::map<std::string, SNMPSensor> sensorMap_t;
public:
......@@ -35,7 +37,10 @@ public:
//Overwrite from Configurator
bool reReadConfig() {
//TODO
for (auto s : _sensors) {
s->stopPolling();
}
_connections.clear();
_templateSensors.clear();
return Configurator::reReadConfig();
}
......@@ -48,6 +53,7 @@ private:
*/
bool readSensor(SNMPSensor& sensor, boost::property_tree::iptree& config);
connectionList_t _connections;
sensorMap_t _templateSensors;
};
......
......@@ -7,13 +7,24 @@
#include "SNMPConnection.h"
#include <string>
SNMPConnection::SNMPConnection() {
// TODO Auto-generated constructor stub
_snmpCommunity = "";
_oidPrefix = "";
_host = "";
_port = 0;
_snmpSession.community_len = 0;
_strand = NULL;
}
SNMPConnection::~SNMPConnection() {
// TODO Auto-generated destructor stub
if (_snmpSession.community_len) {
free(_snmpSession.peername);
free(_snmpSession.community);
_snmpSession.community_len = 0;
}
}
void SNMPConnection::initializeStrand(boost::asio::io_service& io) {
......@@ -21,3 +32,62 @@ void SNMPConnection::initializeStrand(boost::asio::io_service& io) {
_strand = new boost::asio::io_service::strand(io);
}
}
void SNMPConnection::init() {
snmp_sess_init(&_snmpSession);
_snmpSession.version = SNMP_VERSION_2c;
_snmpSession.peername = strdup(_host.c_str());
_snmpSession.community = (u_char*) strdup(_snmpCommunity.c_str());
_snmpSession.community_len = _snmpCommunity.length();
}
uint64_t SNMPConnection::issueGet(oid* OID, size_t OIDLen) {
struct snmp_session* ss;
struct snmp_pdu *pdu, *response;
struct variable_list *vp;
int status;
uint64_t ret = 0;
ss = snmp_open(&_snmpSession);
if (!ss) {
throw std::runtime_error("Could not open SNMP Session!");
}
pdu = snmp_pdu_create(SNMP_MSG_GET);
snmp_add_null_var(pdu, OID, OIDLen);
status = snmp_synch_response(ss, pdu, &response);
if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) {
vp = response->variables;
if (vp) {
if (vp->type == ASN_INTEGER) {
/* Trigger callback in SNMPPusher.cpp */
ret = *vp->val.integer;
}
else if (vp->type == 0x43) { /* Timeticks: No idea why there is no definition for this in the libray */
/* Trigger callback in SNMPPusher.cpp */
ret = *vp->val.integer;
}
else {
LOG(warning) << "Non-Integer and non-Timetick SNMP data received (type=" << std::to_string((int)vp->type) << "):";
char buf[1024];
while (vp) {
snprint_variable(buf, sizeof(buf), vp->name, vp->name_length, vp);
LOG(warning) << ss->peername << ": " << buf;
vp = vp->next_variable;
}
}
}
} else {
throw std::runtime_error("Request failed!");
}
if (response) {
snmp_free_pdu(response);
}
snmp_close(ss);
return ret;
}
......@@ -12,6 +12,7 @@
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <boost/asio.hpp>
class SNMPConnection {
......@@ -19,13 +20,72 @@ public:
SNMPConnection();
virtual ~SNMPConnection();
void setSNMPCommunity(const std::string& community) {
_snmpCommunity = community;
}
const std::string& getSNMPCommunity() const {
return _snmpCommunity;
}
void setOIDPrefix(const std::string& oidPrefix) {
/* Ensure that the oidPrefix does not contain a trailing . */
_oidPrefix = oidPrefix;
if (_oidPrefix.length() > 0) {
if (_oidPrefix.at(_oidPrefix.length() - 1) == '.') {
_oidPrefix.resize(_oidPrefix.size() - 1);
}
}
}
const std::string& getOIDPrefix() const {
return _oidPrefix;
}
void setHost(const std::string& host) {
_host = host;
}
const std::string& getHost() const {
return _host;
}
void setPort(int port) {
_port = port;
}
int getPort() const {
return _port;
}
void initializeStrand(boost::asio::io_service& io);
boost::asio::io_service::strand* getStrand() const {
return _strand;
}
/**
* Initializes the connection. Must be called once before the connection can be used
*/
void init();
/**
* Issues a get request to _host for the value specified by the OID.
*
* @param OID Pointer to the OID buffer which identifies the value to request
* @param OIDLen Lenght of the OID buffer
*
* @return The requested value
*/
uint64_t issueGet(oid* OID, size_t OIDLen);
private:
std::string _snmpCommunity;
std::string _oidPrefix;
std::string _host;
int _port;
struct snmp_session _snmpSession;
boost::asio::io_service::strand* _strand;
boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
};
......
......@@ -10,27 +10,30 @@
SNMPSensor::SNMPSensor(const std::string& name) :
Sensor(name) {
// TODO Auto-generated constructor stub
memset(_oid, 0x0, sizeof(oid) * MAX_OID_LEN);
_oidLen = 0;
_connection = NULL;
}
SNMPSensor::~SNMPSensor() {
// TODO Auto-generated destructor stub
// Nothing to do
}
void SNMPSensor::read() {
reading_t reading;
char buf[1024];
reading.timestamp = getTimestamp();
//TODO
try {
reading.value = _connection->issueGet(_oid, _oidLen);
storeReading(reading);
_latestValue.value = reading.value;
_latestValue.timestamp = reading.timestamp;
#ifdef DEBUG
LOG(debug) << _name << ": \"" << reading.value << "\"";
#endif
} catch (const std::exception& e) {
LOG(error) << _name << " could not read value: " << e.what();
}
storeReading(reading);
}
void SNMPSensor::readAsync() {
......@@ -59,8 +62,6 @@ void SNMPSensor::startPolling() {
return;
}
//TODO
if (_connection) {
_keepRunning = 1;
_timer->async_wait(_connection->getStrand()->wrap(std::bind(&SNMPSensor::readAsync, this)));
......@@ -77,8 +78,5 @@ void SNMPSensor::stopPolling() {
_timer->cancel();
//wait until read() finished before closing _file
_timer->wait();
//TODO
LOG(info) << "Sensor " << _name << " stopped.";
}
......@@ -9,6 +9,7 @@
#define SNMPSENSOR_H_
#include "../../Sensor.h"
#include "SNMPConnection.h"
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
......@@ -19,6 +20,37 @@ public:
SNMPSensor(const std::string& name);
virtual ~SNMPSensor();
void setOID(std::string oid) {
size_t newoidnamelen = MAX_OID_LEN;
if (read_objid(oid.c_str(), _oid, &newoidnamelen)) {
_oidLen = newoidnamelen;
} else {
LOG(error) << _name << ": Cannot convert OID string: " << oid;
}
}
const oid* getOID() const {
return _oid;
}
std::string getOIDString() {
char buf[255];
int len = snprint_objid(buf, 255, _oid, _oidLen);
if (len == -1) {
LOG(error) << _name << ": getOIDString buffer not large enough!";
return std::string("");
}
return std::string(buf, len);
}
void setConnection(SNMPConnection* connection) {
_connection = connection;
}
SNMPConnection* getConnection() const {
return _connection;
}
void init(boost::asio::io_service& io);
void read();
......@@ -30,8 +62,8 @@ public:
void stopPolling();
private:
oid _oidName[MAX_OID_LEN];
size_t _oidNameLen;
oid _oid[MAX_OID_LEN];
size_t _oidLen;
SNMPConnection* _connection;
};
......
Supports Markdown
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