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

First implementation of IPMI sensor reads with record-id (not tested)

parent 7edd780b
......@@ -65,6 +65,17 @@ namespace DCDB {
ipmiSensor.setMqtt(s.second.data());
}
}
} else if (boost::iequals(sensor.second.get<std::string>("type"), "recordId")) {
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, ("id"))) {
ipmiSensor.setRecordId(stoul(s.second.data()));
} else if (boost::iequals(s.first, "freq")) {
ipmiSensor.setInterval(stoi(s.second.data()));
} else if (boost::iequals(s.first, "mqttsuffix")) {
ipmiSensor.setMqtt(s.second.data());
}
}
}
_templateSensors.insert(sensorMap_t::value_type(ipmiSensor.getName(), ipmiSensor));
}
......
......@@ -23,125 +23,222 @@
namespace DCDB {
IPMIHost::IPMIHost(const std::string& hostName, uint32_t retransmissionTimeout, uint32_t sessionTimeout) {
_ipmiCtx = NULL;
_hostName = hostName;
_userName = std::string("admin");
_password = std::string("admin");
_auth = IPMI_AUTHENTICATION_TYPE_MD5;
_priv = IPMI_PRIVILEGE_LEVEL_ADMIN;
_retransmissionTimeout = retransmissionTimeout;
_sessionTimeout = 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;
IPMIHost::IPMIHost(const std::string& hostName, uint32_t retransmissionTimeout,
uint32_t sessionTimeout) {
_ipmiCtx = NULL;
_sdrCtx = NULL;
_sensorReadCtx = NULL;
_hostName = hostName;
_userName = std::string("admin");
_password = std::string("admin");
_auth = IPMI_AUTHENTICATION_TYPE_MD5;
_priv = IPMI_PRIVILEGE_LEVEL_ADMIN;
_retransmissionTimeout = retransmissionTimeout;
_sessionTimeout = 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;
}
int IPMIHost::disconnect() {
if (_ipmiCtx) {
ipmi_ctx_close(_ipmiCtx);
ipmi_ctx_destroy(_ipmiCtx);
_ipmiCtx = NULL;
return 0;
}
return 1;
}
void IPMIHost::createSdrCache() {
if (!_ipmiCtx) {
try {
connect();
} catch (const std::runtime_error& e) {
increaseErrorCount();
throw e;
return;
}
}
std::string cacheName = ".ipmiPluginSdrCache." + _hostName;
_sensorReadCtx = ipmi_sensor_read_ctx_create(_ipmiCtx);
_sdrCtx = ipmi_sdr_ctx_create();
std::string errorMsg;
if (ipmi_sdr_cache_create(_sdrCtx, _ipmiCtx, cacheName.c_str(), IPMI_SDR_CACHE_CREATE_FLAGS_DEFAULT, NULL, NULL) < 0) {
errorMsg = ipmi_sdr_ctx_errormsg(_sdrCtx);
} else if (ipmi_sdr_cache_open(_sdrCtx, _ipmiCtx, cacheName.c_str()) < 0) {
errorMsg = ipmi_sdr_ctx_errormsg(_sdrCtx);
} else {
return;
}
destroySdrCache();
throw std::runtime_error("freeipmi Error: " + errorMsg);
return;
}
void IPMIHost::destroySdrCache() {
if (_sdrCtx) {
ipmi_sdr_cache_close(_sdrCtx);
std::string cacheName = ".ipmiPluginSdrCache." + _hostName;
ipmi_sdr_cache_delete(_sdrCtx, cacheName.c_str());
ipmi_sdr_ctx_destroy(_sdrCtx);
_sdrCtx = NULL;
}
if (_sensorReadCtx) {
ipmi_sensor_read_ctx_destroy(_sensorReadCtx);
_sensorReadCtx = NULL;
}
return;
}
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;
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) {
if (!_strand) {
_strand = new boost::asio::io_service::strand(io);
}
}
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;
}
uint64_t IPMIHost::readSensor_recordId(uint16_t recordId) {
if (!_ipmiCtx) {
try {
connect();
} catch (const std::runtime_error& e) {
increaseErrorCount();
throw e;
return 0;
}
}
if (!_sensorReadCtx || !_sdrCtx) {
try {
createSdrCache();
} catch (const std::runtime_error& e) {
increaseErrorCount();
throw e;
return 0;
}
}
int recordLength = 0;
uint8_t record[IPMI_SDR_MAX_RECORD_LENGTH];
uint8_t rawReading = 0;
double *reading = NULL;
uint16_t eventBitmask = 0;
if (ipmi_sdr_cache_search_record_id(_sdrCtx, recordId) < 0) {
increaseErrorCount();
throw std::runtime_error("freeipmi Error: " + std::string(ipmi_sdr_ctx_errormsg(_sdrCtx)));
return 0;
}
if ((recordLength = ipmi_sdr_cache_record_read(_sdrCtx, record, IPMI_SDR_MAX_RECORD_LENGTH)) < 0) {
increaseErrorCount();
throw std::runtime_error("freeipmi Error: " + std::string(ipmi_sdr_ctx_errormsg(_sdrCtx)));
return 0;
}
if (ipmi_sensor_read(_sensorReadCtx, record, recordLength, 0, &rawReading, &reading, &eventBitmask)) {
increaseErrorCount();
throw std::runtime_error("freeipmi Error: " + std::string(ipmi_sensor_read_ctx_errormsg(_sensorReadCtx)));
return 0;
}
return *reading;
}
void IPMIHost::increaseErrorCount() {
uint64_t now = getTimestamp();
if (_errorCount < 25) {
_errorCount++;
}
if (_errorCount > 5) {
_delayNextReadUntil = now + MS_TO_NS(500 * (_errorCount - 5));
}
}
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) {
if (!_strand) {
_strand = new boost::asio::io_service::strand(io);
}
}
} /* namespace DCDB */
......@@ -16,106 +16,120 @@
namespace DCDB {
class IPMIHost {
public:
IPMIHost(const std::string& hostName, uint32_t retransmissionTimeout, uint32_t sessionTimeout);
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;
};
class IPMIHost {
public:
IPMIHost(const std::string& hostName, uint32_t retransmissionTimeout, uint32_t sessionTimeout);
virtual ~IPMIHost();
/* Send raw command to BMC. Returns sensor reading as responded by BMC. */
uint64_t sendRawCmd(const std::vector<uint8_t>& rawCmd, uint16_t start, uint16_t stop);
/* Read the sensor specified by its record id. Returns sensor reading. */
uint64_t readSensor_recordId(uint16_t recordId);
void increaseErrorCount();
const uint64_t getDelayFactor() const;
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:
/* Open/close connection to BMC (sets/destroys _ipmiCtx) */
int connect();
int disconnect();
/* Open and create/destroy SDR cache (sets/destroys _sdrCtx and _sensorReadCtx) */
void createSdrCache();
void destroySdrCache();
/* Various context structs, required to make use of FreeIPMI */
ipmi_ctx_t _ipmiCtx;
ipmi_sdr_ctx_t _sdrCtx;
ipmi_sensor_read_ctx_t _sensorReadCtx;
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 */
......
......@@ -25,6 +25,7 @@ namespace DCDB {
IPMISensor::IPMISensor(const std::string& name) :
Sensor(name) {
_recordId = 0;
_start = 0;
_stop = 0;
_host = NULL;
......@@ -39,7 +40,11 @@ namespace DCDB {
reading_t reading;
reading.timestamp = getTimestamp();
try {
reading.value = _host->sendRawCmd(_rawCmd, _start, _stop);
if (_recordId != 0) { /* recordId was set */
reading.value = _host->readSensor_recordId(_recordId);
} else { /* use raw command */
reading.value = _host->sendRawCmd(_rawCmd, _start, _stop);
}
}
catch (const std::exception& e) {
#ifdef DEBUG
......
......@@ -22,6 +22,10 @@ namespace DCDB {
void setRawCmd(std::string& rawCmd);
void setRecordId(uint16_t recordId) {
_recordId = recordId;
}
uint8_t getStart() const {
return _start;
}
......@@ -57,6 +61,8 @@ namespace DCDB {
void startPolling(boost::asio::io_service& io);