Commit e0b1b310 authored by Micha Mueller's avatar Micha Mueller
Browse files

Add commit-history of ipmipusher belated

parent 6f5cfae0
/*
* Configuration.cpp
*
* Created on: 26 Jan 2017
* Author: ottmi
*/
#include "Configuration.h"
#include <boost/property_tree/info_parser.hpp>
#include <boost/foreach.hpp>
#include <boost/optional.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include "IPMIHost.h"
#include "IPMISensor.h"
namespace DCDB {
Configuration::Configuration(std::string cfgFile) {
_global.threads = 1;
_global.sessionTimeout = 0;
_global.retransmissionTimeout = 0;
_global.mqttBrokerPort = 1883;
boost::property_tree::iptree cfg;
boost::property_tree::read_info(cfgFile, cfg);
BOOST_FOREACH(boost::property_tree::iptree::value_type &global, cfg.get_child("global")) {
std::cout << global.first << " " << global.second.data() << std::endl;
if (boost::iequals(global.first, "threads")) {
_global.threads = stoi(global.second.data());
} else if (boost::iequals(global.first, "SessionTimeout")) {
_global.sessionTimeout = stoi(global.second.data());
} else if(boost::iequals(global.first, "RetransmissionTimeout")) {
_global.retransmissionTimeout = stoi(global.second.data());
} else if(boost::iequals(global.first, "mqttBroker")) {
_global.mqttBrokerHost = global.second.data();
std::size_t pos = _global.mqttBrokerHost.find(":");
if (pos != std::string::npos) {
_global.mqttBrokerPort = stoi(_global.mqttBrokerHost.substr(pos+1));
_global.mqttBrokerHost.erase(pos);
}
}
}
BOOST_FOREACH(boost::property_tree::iptree::value_type &sensor, cfg.get_child("sensors")) {
if (boost::iequals(sensor.first, "sensor")) {
std::cout << "Sensor " << sensor.second.data() << std::endl;
if (!sensor.second.empty()) {
DCDB::IPMISensor ipmiSensor(sensor.second.data());
if (boost::iequals(sensor.second.get<std::string>("type"), "raw")) {
BOOST_FOREACH(boost::property_tree::iptree::value_type &s, sensor.second) {
std::cout << " " << s.first << " " << s.second.data() << std::endl;
if (boost::iequals(s.first, ("cmd"))) {
ipmiSensor.setRawCmd(s.second.data());
} else if (boost::iequals(s.first, "freq")) {
ipmiSensor.setFreq(stoi(s.second.data()));
} else if (boost::iequals(s.first, "start")) {
ipmiSensor.setStart(stoi(s.second.data()));
} else if (boost::iequals(s.first, "stop")) {
ipmiSensor.setStop(stoi(s.second.data()));
} else if (boost::iequals(s.first, "mqttsuffix")) {
ipmiSensor.setMqttSuffix(s.second.data());
}
}
}
_sensors.insert(sensorMap_t::value_type(ipmiSensor.getName(), ipmiSensor));
}
}
}
BOOST_FOREACH(boost::property_tree::iptree::value_type &host, cfg.get_child("hosts")) {
if (boost::iequals(host.first, ("host"))) {
std::cout << "Host " << host.second.data() << std::endl;
_hosts.push_back(DCDB::IPMIHost(host.second.data(), *this));
DCDB::IPMIHost &ipmiHost = _hosts.back();
if (!host.second.empty()) {
BOOST_FOREACH(boost::property_tree::iptree::value_type &h, host.second) {
std::cout << " " << h.first << " " << h.second.data() << std::endl;
if (boost::iequals(h.first, "username")) {
ipmiHost.setUserName(h.second.data());
} else if (boost::iequals(h.first, "password")) {
ipmiHost.setPassword(h.second.data());
} else if (boost::iequals(h.first, "sensors")) {
BOOST_FOREACH(boost::property_tree::iptree::value_type &s, h.second) {
std::cout << " " << s.first << " " << s.second.data() << std::endl;
sensorMap_t::iterator it = _sensors.find(s.second.data());
if (it != _sensors.end()) {
DCDB::IPMISensor sensor = it->second;
boost::optional< boost::property_tree::iptree& > mqtt = s.second.get_child_optional("mqttsuffix");
if (mqtt) {
sensor.setMqttSuffix(mqtt.get().data());
}
ipmiHost.addSensor(sensor);
}
}
} else if (boost::iequals(h.first, "mqttprefix")) {
ipmiHost.setMqttPrefix(h.second.data());
}
}
}
}
}
}
Configuration::~Configuration() {
// TODO Auto-generated destructor stub
}
} /* namespace DCDB */
/*
* Configuration.h
*
* Created on: 26 Jan 2017
* Author: ottmi
*/
#ifndef CONFIGURATION_H_
#define CONFIGURATION_H_
#include <boost/property_tree/ptree.hpp>
#include <string>
#include <map>
#include <list>
namespace DCDB {
class IPMIHost;
class IPMISensor;
class Configuration {
typedef std::list<DCDB::IPMIHost> hostList_t;
typedef std::map<std::string, DCDB::IPMISensor> sensorMap_t;
typedef struct {
uint32_t threads;
uint32_t sessionTimeout;
uint32_t retransmissionTimeout;
std::string mqttBrokerHost;
uint16_t mqttBrokerPort;
} global_t;
public:
Configuration(std::string cfgFile);
virtual ~Configuration();
sensorMap_t _sensors;
hostList_t _hosts;
global_t _global;
};
} /* namespace DCDB */
#endif /* CONFIGURATION_H_ */
/*
* IPMIConnection.cpp
*
* Created on: 18 Jan 2017
* Author: ottmi
*/
#include "IPMIHost.h"
#include "timestamp.h"
#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <exception>
#include <freeipmi/freeipmi.h>
#include <freeipmi/api/ipmi-api.h>
namespace DCDB {
IPMIHost::IPMIHost(const std::string& hostName, const Configuration& cfg) {
_ipmiCtx = NULL;
_hostName = hostName;
_userName = std::string("admin");
_password = std::string("admin");
_auth = IPMI_AUTHENTICATION_TYPE_MD5;
_priv = IPMI_PRIVILEGE_LEVEL_ADMIN;
_retransmissionTimeout = cfg._global.retransmissionTimeout;
_sessionTimeout = cfg._global.sessionTimeout;
_strand = NULL;
_errorCount = 0;
_delayNextReadUntil = 0;
}
IPMIHost::~IPMIHost() {
}
int IPMIHost::connect() {
if (!(_ipmiCtx = ipmi_ctx_create())) {
throw std::runtime_error("freeipmi Error: " + std::string(strerror(errno)));
return 1;
}
int workaround_flags = 0;
int flags = 0;
if (ipmi_ctx_open_outofband(_ipmiCtx, _hostName.c_str(), _userName.c_str(), _password.c_str(), _auth, _priv, _sessionTimeout, _retransmissionTimeout, workaround_flags, flags) < 0) {
std::string errorMsg(ipmi_ctx_errormsg(_ipmiCtx));
ipmi_ctx_close(_ipmiCtx);
ipmi_ctx_destroy(_ipmiCtx);
_ipmiCtx = NULL;
throw std::runtime_error("freeipmi Error: " + errorMsg);
return 2;
}
return 0;
}
uint64_t IPMIHost::sendRawCmd(const std::vector<uint8_t>& rawCmd, uint16_t start, uint16_t stop) {
uint8_t buf[256];
int len;
int i;
if (!IPMI_NET_FN_RQ_VALID(rawCmd[1])) {
std::cout << "Invalid netfn value" << std::endl;
return 0;
}
if (_ipmiCtx == NULL) {
try {
connect();
}
catch (const std::runtime_error& e) {
increaseErrorCount();
throw e;
return 0;
}
}
if ((len = ipmi_cmd_raw(_ipmiCtx, rawCmd[0], rawCmd[1], &rawCmd[2], rawCmd.size() - 2, buf, sizeof(buf))) < 0) {
increaseErrorCount();
throw std::runtime_error("freeipmi Error: " + std::string(ipmi_ctx_errormsg(_ipmiCtx)));
return 0;
}
disconnect();
_errorCount = 0;
#if 0
std::cout << "IPMIHost::sendRawCmd() received " << len << " bytes: " << std::setw(2) << std::setfill('0') << std::hex;
for (i = 0; i < len; i++) {
std::cout << (unsigned) buf[i] << " ";
}
std::cout << std::dec << std::endl;
#endif
if (stop == 0 || stop >= len) {
stop = len - 1;
}
if ((stop - start) >= 8) {
stop = start + 7;
}
uint64_t val = 0;
for (i = start; i <= stop; i++) {
val |= ((uint64_t) buf[i]) << (stop - i) * 8;
}
return val;
}
void IPMIHost::increaseErrorCount() {
uint64_t now = getTimestamp();
if (_errorCount < 25) {
_errorCount++;
}
if (_errorCount > 5) {
_delayNextReadUntil = now + MS_TO_NS(500 * (_errorCount-5));
}
}
int IPMIHost::disconnect() {
if (_ipmiCtx) {
ipmi_ctx_close(_ipmiCtx);
ipmi_ctx_destroy(_ipmiCtx);
_ipmiCtx = NULL;
return 0;
}
return 1;
}
void IPMIHost::addSensor(const DCDB::IPMISensor& sensor) {
_sensors.push_back(sensor);
_sensors.back().setHost(this);
std::cerr << "IPMIHost::addSensor host=" << this << " sensor=" << &_sensors.back() << std::endl;
}
void IPMIHost::initalizeStrand(boost::asio::io_service& io) {
_strand = new boost::asio::io_service::strand(io);
}
} /* namespace DCDB */
/*
* IPMIConnection.h
*
* Created on: 18 Jan 2017
* Author: ottmi
*/
#ifndef IPMIHOST_H_
#define IPMIHOST_H_
#include <string>
#include <list>
#include <freeipmi/freeipmi.h>
#include "IPMISensor.h"
#include "Configuration.h"
namespace DCDB {
class IPMIHost {
public:
IPMIHost(const std::string& hostName, const Configuration& cfg);
virtual ~IPMIHost();
int connect();
uint64_t sendRawCmd(const std::vector<uint8_t>& rawCmd, uint16_t start, uint16_t stop);
void increaseErrorCount();
const uint64_t getDelayFactor() const;
int disconnect();
uint8_t getAuth() const {
return _auth;
}
void setAuth(uint8_t auth) {
_auth = auth;
}
const std::string& getHostName() const {
return _hostName;
}
void setHostName(const std::string& hostName) {
_hostName = hostName;
}
const std::string& getPassword() const {
return _password;
}
void setPassword(const std::string& password) {
_password = password;
}
uint8_t getPriv() const {
return _priv;
}
void setPriv(uint8_t priv) {
_priv = priv;
}
const std::string& getUserName() const {
return _userName;
}
void setUserName(const std::string& userName) {
_userName = userName;
}
void addSensor(const DCDB::IPMISensor& sensor);
std::list<DCDB::IPMISensor>& getSensors() {
return _sensors;
}
const std::string& getMqttPrefix() const {
return _mqttPrefix;
}
void setMqttPrefix(const std::string& mqttPrefix) {
_mqttPrefix = mqttPrefix;
if (_mqttPrefix.front() != '/') {
_mqttPrefix.insert(0, "/");
}
if (_mqttPrefix.back() == '/') {
_mqttPrefix.erase(_mqttPrefix.size()-1);
}
}
void initalizeStrand(boost::asio::io_service& io);
boost::asio::io_service::strand* getStrand() const {
return _strand;
}
uint64_t getDelayNextReadUntil() const {
return _delayNextReadUntil;
}
void setDelayNextReadUntil(uint64_t delayNextReadUntil) {
_delayNextReadUntil = delayNextReadUntil;
}
private:
ipmi_ctx_t _ipmiCtx;
std::string _hostName;
std::string _userName;
std::string _password;
uint8_t _auth;
uint8_t _priv;
std::string _mqttPrefix;
std::list<DCDB::IPMISensor> _sensors;
uint32_t _sessionTimeout;
uint32_t _retransmissionTimeout;
boost::asio::io_service::strand* _strand;
uint32_t _errorCount;
volatile uint64_t _delayNextReadUntil;
};
} /* namespace DCDB */
#endif /* IPMIHOST_H_ */
/*
* IPMISensor.cpp
*
* Created on: 18 Jan 2017
* Author: ottmi
*/
#include "IPMISensor.h"
#include "IPMIHost.h"
#include "timestamp.h"
#include <boost/tokenizer.hpp>
#include <boost/foreach.hpp>
#include <boost/regex.hpp>
#include <boost/bind.hpp>
#include <boost/lockfree/spsc_queue.hpp>
#include <iostream>
#include <exception>
#include <chrono>
extern volatile int keepRunning;
namespace DCDB {
IPMISensor::IPMISensor(const std::string& name) {
_name = name;
_freq = 1000;
_start = 0;
_stop = 0;
_host = NULL;
_timer = NULL;
_latestReading = 0;
_readingQueue = NULL;
}
IPMISensor::~IPMISensor() {
if (_readingQueue != NULL) {
delete _readingQueue;
_readingQueue = NULL;
}
}
void IPMISensor::query() {
if (_host != NULL) {
reading_t reading;
reading.timestamp = getTimestamp();
try {
reading.value = _host->sendRawCmd(_rawCmd, _start, _stop);
}
catch (const std::exception& e) {
#ifdef DEBUG
std::cout << prettyPrintTimestamp(reading.timestamp) << " " << _host->getHostName() << "::" << _name << " " << _host->getMqttPrefix() << _mqttSuffix << " => " << e.what() << std::endl;
#endif
return;
}
#ifdef DEBUG
std::cout << prettyPrintTimestamp(reading.timestamp) << " " << _host->getHostName() << "::" << _name << " " << _host->getMqttPrefix() << _mqttSuffix << " (" << _readingQueue->read_available() << "/" << _readingQueue->write_available() << ") => " << reading.value << std::endl;
#endif
_readingQueue->push(reading);
}
}
void IPMISensor::queryAsync() {
uint64_t now = getTimestamp();
if (now >= _host->getDelayNextReadUntil()) {
query();
}
if (_timer != NULL && keepRunning) {
uint64_t next = now + MS_TO_NS(_freq);
while (next < _host->getDelayNextReadUntil()) {
next+= MS_TO_NS(_freq);
}
_timer->expires_at(timestamp2ptime(next));
_timer->async_wait(_host->getStrand()->wrap(boost::bind(&IPMISensor::queryAsync, this)));
}
}
void IPMISensor::startPolling(boost::asio::io_service& io) {
if (_readingQueue == NULL) {
_readingQueue = new boost::lockfree::spsc_queue<reading_t>(1024);
}
_timer = new boost::asio::deadline_timer(io, boost::posix_time::seconds(0));
_timer->async_wait(_host->getStrand()->wrap(boost::bind(&IPMISensor::queryAsync, this)));
}
void IPMISensor::setRawCmd(std::string& rawCmd) {
boost::regex expr("(?:0x)?([0-9a-fA-F]+)");
boost::regex_token_iterator<std::string::iterator> it{rawCmd.begin(), rawCmd.end(), expr, 1};
boost::regex_token_iterator<std::string::iterator> end;
while (it != end) {
_rawCmd.push_back(stoi(*it++, NULL, 16));
}
}
void IPMISensor::setMqttSuffix(const std::string& mqttSuffix) {
_mqttSuffix = mqttSuffix;
if (_mqttSuffix.front() != '/') {
_mqttSuffix.insert(0, "/");
}
if (_mqttSuffix.back() == '/') {
_mqttSuffix.erase(_mqttSuffix.size()-1);
}
}
} /* namespace DCDB */
/*
* IPMISensor.h
*
* Created on: 18 Jan 2017
* Author: ottmi
*/
#ifndef IPMISENSOR_H_
#define IPMISENSOR_H_
#include <string>
#include <vector>
#include <boost/asio.hpp>
#include <boost/lockfree/spsc_queue.hpp>
namespace DCDB {
class IPMIHost;
typedef struct {
uint64_t value;
uint64_t timestamp;
} reading_t;
class IPMISensor {
public:
IPMISensor(const std::string& name);
virtual ~IPMISensor();
void query();
void queryAsync();
void startPolling(boost::asio::io_service& io);
const std::string& getName() const {
return _name;
}
void setName(const std::string& name) {
_name = name;
}
void setRawCmd(std::string& rawCmd);
uint8_t getStart() const {
return _start;
}
void setStart(uint8_t start) {
_start = start;
}
uint8_t getStop() const {
return _stop;
}
void setStop(uint8_t stop) {
_stop = stop;
}
const std::vector<uint8_t>& getRawCmd() const {
return _rawCmd;
}
const DCDB::IPMIHost* getHost() const {
return _host;
}
void setHost(DCDB::IPMIHost* host) {
_host = host;
}
const std::string& getMqttSuffix() const {
return _mqttSuffix;
}
void setMqttSuffix(const std::string& mqttSuffix);
uint64_t getFreq() const {
return _freq;
}
void setFreq(uint64_t freq) {
_freq = freq;
}
const std::size_t getSizeOfReadingQueue() const {
return _readingQueue->read_available();
}