Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit 91e3805b authored by Micha Müller's avatar Micha Müller
Browse files

Pusher: introduce EntityInterface

parent 2fa55ec2
//================================================================================
// Name : EntityInterface.h
// Author : Micha Mueller
// Copyright : Leibniz Supercomputing Centre
// Description : Abstract interface defining sensor entity functionality.
//================================================================================
//================================================================================
// This file is part of DCDB (DataCenter DataBase)
// Copyright (C) 2019-2019 Leibniz Supercomputing Centre
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//================================================================================
#ifndef DCDBPUSHER_INCLUDES_ENTITYINTERFACE_H_
#define DCDBPUSHER_INCLUDES_ENTITYINTERFACE_H_
#include <memory>
#include <boost/asio.hpp>
#include "logging.h"
/** Indentation when printing configuration */
#define eInd " "
/**
* @brief Abstract interface defining sensor entity functionality.
*
* @ingroup pusherplugins
*/
class EntityInterface {
public:
using strand = boost::asio::io_service::strand;
/**
* @brief Constructor
*
* @details Does not initialize _strand.
*
* @param name Name of the entity.
*/
EntityInterface(const std::string& name) :
_name(name),
_mqttPart(""),
_strand(nullptr) {}
/**
* @brief Copy constructor
*
* @details Does not initialize _strand.
*
* @param other Entity to copy construct from.
*/
EntityInterface(const EntityInterface& other) :
_name(other._name),
_mqttPart(other._mqttPart),
_strand(nullptr) {}
/**
* @brief Destructor
*/
virtual ~EntityInterface() {}
/**
* @brief Assignment operator
*
* @details _strand is uninitialized afterwards.
*
* @param other Entity to assign from.
*
* @return EntityInterface
*/
EntityInterface& operator=(const EntityInterface& other) {
_name = other._name;
_mqttPart = other._mqttPart;
_strand = nullptr;
return *this;
}
const std::string& getEntityName() const { return _name; }
const std::string& getMqttPart() const { return _mqttPart; }
const std::unique_ptr<strand>& getStrand() const { return _strand; }
void setEntityName(const std::string& name) { _name = name; }
void setMqttPart(const std::string& mqttPart) {
_mqttPart = mqttPart;
//sanitize mqttPart into uniform /xxxx format
if (_mqttPart.front() != '/') {
_mqttPart.insert(0, "/");
}
if (_mqttPart.back() == '/') {
_mqttPart.erase(_mqttPart.size()-1);
}
}
/**
* @brief Initialize this entity.
*
* @details Initializes base class and subsequently calls init().
*
* @param io IO service to initialize _strand with.
*/
void initEntity(boost::asio::io_service& io) {
if (!_strand) {
_strand.reset(new strand(io));
}
this->init();
}
/**
* @brief Print complete configuration of this entity.
*
* @details Prints configuration of base class and subsequently calls
* printConfig().
*
* @param ll Log severity level to be used from logger.
*/
void printEntityConfig(LOG_LEVEL ll) {
LOG_VAR(ll) << " " << "Entity " << _name;
if (_mqttPart != "") {
LOG_VAR(ll) << eInd << "MQTT part: " << _mqttPart;
}
this->printConfig(ll);
}
/**
* @brief Initialize derived class (if necessary).
*/
virtual void init() { /* do nothing if not overwritten */ };
/**
* @brief Print configuration of derived class.
*
* @param ll Log severity level to be used from logger.
*/
virtual void printConfig(LOG_LEVEL ll) = 0;
protected:
std::string _name; /**< Name of the entity */
std::string _mqttPart; /**< Partial MQTT topic identifying this entity */
std::unique_ptr<strand> _strand; /**< Provides serialized handler execution
to avoid race conditions */
LOGGER lg; /**< Logging instance */
};
#endif /* DCDBPUSHER_INCLUDES_ENTITYINTERFACE_H_ */
......@@ -40,18 +40,15 @@
double BACnetClient::_presentValue;
uint8_t BACnetClient::_handlerTransmitBuffer[MAX_PDU];
BACnetClient::BACnetClient() {
_strand = NULL;
_invokeId = 0;
_presentValue = 0;
_timeout = 1000;
BACnetClient::BACnetClient(const std::string& name) :
EntityInterface(name),
_invokeId(0),
_timeout(1000) {
_presentValue = 0;
_targetAddress = {0};
}
BACnetClient::~BACnetClient() {
if (_strand) {
delete _strand;
}
datalink_cleanup();
}
......@@ -194,13 +191,6 @@ double BACnetClient::readProperty(uint32_t deviceObjInstance, uint32_t objInstan
return _presentValue;
}
void BACnetClient::initializeStrand(boost::asio::io_service& io) {
if (!_strand) {
_strand = new boost::asio::io_service::strand(io);
}
}
void BACnetClient::unrecognizedServiceHandler(uint8_t * service_request, uint16_t service_len, BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data) {
boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
int pdu_len = 0;
......@@ -309,7 +299,6 @@ void BACnetClient::rejectHandler(BACNET_ADDRESS * src, uint8_t invokeId, uint8_t
}
void BACnetClient::printConfig(LOG_LEVEL ll) {
LOG_VAR(ll) << " BACnetClient here";
LOG_VAR(ll) << " Timeout: " << _timeout;
LOG_VAR(ll) << eInd << "Timeout: " << _timeout;
}
......@@ -27,88 +27,121 @@
#ifndef BACNETCLIENT_H_
#define BACNETCLIENT_H_
#include "../../includes/EntityInterface.h"
#include "bacnet/apdu.h"
#include "bacnet/bacenum.h"
#include "bacnet/datalink.h"
#include <boost/asio.hpp>
#include "logging.h"
/*
* NOTE
* Had to make some member variables static because of BACnet Stack handler function requirements.
* This should be no problem as we are using only one instance of BACnetClient with serialized access (_strand).
* One should ensure to keep the single instance property in the future.
* Had to make some member variables static because of BACnet Stack handler
* function requirements. This should be no problem as we are using only one
* instance of BACnetClient with serialized access (_strand). One should ensure
* to keep the single instance property in the future.
*/
/**
* @brief Client to handle BACnet protocol communication. Only one instance allowed!
* @brief Client to handle BACnet protocol communication. Only one instance
* allowed!
*
* @ingroup bacnet
*/
class BACnetClient {
public:
BACnetClient();
virtual ~BACnetClient();
class BACnetClient : public EntityInterface {
void initializeStrand(boost::asio::io_service& io);
public:
boost::asio::io_service::strand* getStrand() const {
return _strand;
}
BACnetClient(const std::string& name = "BACnetClient");
BACnetClient(const BACnetClient& other) = delete;
virtual ~BACnetClient();
BACnetClient& operator=(const BACnetClient& other) = delete;
/**
* Initialize datalink layer and address cache.
* We assume BACnet/IP protocol is used. Also you need to compile with environment variable BACNET_ADDRESS_CACHE_FILE set
* to enable initialization of address cache from file "address_cache".
* @brief Initialize datalink layer and address cache.
*
* @details We assume BACnet/IP protocol is used. Also you need to compile
* with environment variable BACNET_ADDRESS_CACHE_FILE set to
* enable initialization of address cache from file "address_cache".
*
* @param interface Name of network interface to use
* @param address_cache (Path and) filename of the address cache file where the addresses of BACnet devices are stored
* @param port Which port to use of the interface
* @param timeout Number of milliseconds to wait for a packet when receiving
* @param apdu_timeout Number of milliseconds before timeout when sending
* @param retries Number of retries after an apdu timeout occurs
* @param interface Name of network interface to use.
* @param address_cache (Path and) filename of the address cache file where
* the addresses of BACnet devices are stored.
* @param port Which port to use of the interface.
* @param timeout Number of milliseconds to wait for a packet when
* receiving.
* @param apdu_timeout Number of milliseconds before timeout when sending.
* @param retries Number of retries after an apdu timeout occurs.
*/
void init(std::string interface, const std::string& address_cache, unsigned port = 47808, unsigned timeout = 1000, unsigned apdu_timeout = 200, unsigned retries = 0);
void init(std::string interface,
const std::string& address_cache,
unsigned port = 47808,
unsigned timeout = 1000,
unsigned apdu_timeout = 200,
unsigned retries = 0);
/**
* Sends a READ_PROPERTY request for PROP_PRESENT_VALUE to specified device and decodes the response (READ_PROPERTY_ACK).
* @brief Sends a READ_PROPERTY request for PROP_PRESENT_VALUE to specified
* device and decodes the response (READ_PROPERTY_ACK).
*
* @param deviceObjectInstance Number of device from which to request the current value.
* @param objectInstance
* @param objectType
* @param objectProperty
* @param objectIndex
* @param deviceObjInstance Number of device from which to request
* the current value.
* @param objInstance Object instance
* @param objType Object type
* @param objProperty Object property
* @param objIndex Object index
*
* @return The value sent as response from the device. If an error occurs a runtime exception is thrown.
* @return The value sent as response from the device. If an error occurs a
* runtime exception is thrown.
*
* @throws Runtime error
*/
double readProperty(uint32_t deviceObjInstance, uint32_t objInstance = 0, BACNET_OBJECT_TYPE objType = OBJECT_DEVICE, BACNET_PROPERTY_ID objProperty = PROP_PRESENT_VALUE, int32_t objIndex = BACNET_ARRAY_ALL);
double readProperty(uint32_t deviceObjInstance,
uint32_t objInstance = 0,
BACNET_OBJECT_TYPE objType = OBJECT_DEVICE,
BACNET_PROPERTY_ID objProperty = PROP_PRESENT_VALUE,
int32_t objIndex = BACNET_ARRAY_ALL);
/**
* Print information about configured attributes
*
* @param ll Severity level of the log messages.
*/
void printConfig(LOG_LEVEL ll);
* @brief Print configuration of this class.
*
* @param ll Log severity level to be used from logger.
*/
void printConfig(LOG_LEVEL ll) override;
private:
/* Handler to process incoming BACnet data */
static void unrecognizedServiceHandler(uint8_t * service_request, uint16_t service_len, BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data);
/**
* Handler for a ReadProperty ACK.
* Here the actual processing of a valid response takes place
* @brief Handler for a ReadProperty ACK.
* @details Here the actual processing of a valid response takes place.
*
* @param service_request The contents of the service request.
* @param service_len The length of the service_request.
* @param src BACNET_ADDRESS of the source of the message
* @param service_data The BACNET_CONFIRMED_SERVICE_DATA information decoded from the APDU header of this message.
* @param src BACNET_ADDRESS of the source of the message.
* @param service_data The BACNET_CONFIRMED_SERVICE_DATA information
* decoded from the APDU header of this message.
*/
static void readPropertyAckHandler(uint8_t * service_request, uint16_t service_len, BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data);
static void errorHandler(BACNET_ADDRESS * src, uint8_t invokeId, BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code);
static void abortHandler(BACNET_ADDRESS * src, uint8_t invokeId, uint8_t abort_reason, bool server);
static void rejectHandler(BACNET_ADDRESS * src, uint8_t invokeId, uint8_t reject_reason);
static void readPropertyAckHandler(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data);
static void unrecognizedServiceHandler(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data);
static void errorHandler(BACNET_ADDRESS * src,
uint8_t invokeId,
BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code);
static void abortHandler(BACNET_ADDRESS * src,
uint8_t invokeId,
uint8_t abort_reason,
bool server);
static void rejectHandler(BACNET_ADDRESS * src,
uint8_t invokeId,
uint8_t reject_reason);
uint8_t _invokeId;
unsigned _timeout;
......@@ -116,10 +149,6 @@ private:
static double _presentValue;
static uint8_t _handlerTransmitBuffer[MAX_PDU];
BACNET_ADDRESS _targetAddress; //store as member variable to enable access in handler methods
boost::asio::io_service::strand* _strand;
LOGGER lg;
};
#endif /* BACNETCLIENT_H_ */
......@@ -49,7 +49,7 @@ BACnetSensorGroup& BACnetSensorGroup::operator=(const BACnetSensorGroup& other)
void BACnetSensorGroup::init(boost::asio::io_service& io) {
SensorGroupTemplate::init(io);
if(_bacClient) {
_bacClient->initializeStrand(io);
_bacClient->initEntity(io);
} else {
LOG(error) << "No BACnetClient set for sensor " << _groupName << "! Cannot initialize sensor.";
}
......
......@@ -43,29 +43,59 @@
#define RETRIES 2
IPMIHost::IPMIHost() {
_ipmiCtx = nullptr;
_sensorReadCtx = nullptr;
_hostName = "";
_userName = std::string("admin");
_password = std::string("admin");
_cache = "";
_auth = IPMI_AUTHENTICATION_TYPE_MD5;
_priv = IPMI_PRIVILEGE_LEVEL_ADMIN;
_cipher = 3;
_ipmiVersion = 1;
_mqttPart = "";
_retransmissionTimeout = 0;
_sessionTimeout = 0;
_strand = nullptr;
_errorCount = 0;
_delayNextReadUntil = 0;
IPMIHost::IPMIHost(const std::string& name) :
EntityInterface(name),
_ipmiCtx(nullptr),
_sensorReadCtx(nullptr),
_userName("admin"),
_password("admin"),
_cache(""),
_auth(IPMI_AUTHENTICATION_TYPE_MD5),
_priv(IPMI_PRIVILEGE_LEVEL_ADMIN),
_cipher(3),
_ipmiVersion(1),
_sessionTimeout(0),
_retransmissionTimeout(0),
_errorCount(0),
_delayNextReadUntil(0) {
}
IPMIHost::~IPMIHost() {
if (_strand) {
delete _strand;
}
IPMIHost::IPMIHost(const IPMIHost& other) :
EntityInterface(other),
_ipmiCtx(nullptr),
_sensorReadCtx(nullptr),
_userName(other._userName),
_password(other._password),
_cache(other._cache),
_auth(other._auth),
_priv(other._priv),
_cipher(other._cipher),
_ipmiVersion(other._ipmiVersion),
_retransmissionTimeout(other._retransmissionTimeout),
_sessionTimeout(other._sessionTimeout),
_errorCount(0),
_delayNextReadUntil(other._delayNextReadUntil) {
}
IPMIHost::~IPMIHost() {}
IPMIHost& IPMIHost::operator=(const IPMIHost& other) {
EntityInterface::operator=(other);
_ipmiCtx = nullptr;
_sensorReadCtx = nullptr;
_userName = other._userName;
_password = other._password;
_cache = other._cache;
_auth = other._auth;
_priv = other._priv;
_cipher = other._cipher;
_ipmiVersion = other._ipmiVersion;
_sessionTimeout = other._sessionTimeout;
_retransmissionTimeout = other._retransmissionTimeout;
_errorCount = 0;
_delayNextReadUntil = other._delayNextReadUntil;
return *this;
}
int IPMIHost::connect() {
......@@ -82,9 +112,9 @@ int IPMIHost::connect() {
int flags = IPMI_FLAGS_DEFAULT;
int rc;
if (_ipmiVersion == 1) {
rc = ipmi_ctx_open_outofband(_ipmiCtx, _hostName.c_str(), _userName.c_str(), _password.c_str(), _auth, _priv, _sessionTimeout, _retransmissionTimeout, workaround_flags, flags);
rc = ipmi_ctx_open_outofband(_ipmiCtx, _name.c_str(), _userName.c_str(), _password.c_str(), _auth, _priv, _sessionTimeout, _retransmissionTimeout, workaround_flags, flags);
} else {
rc = ipmi_ctx_open_outofband_2_0(_ipmiCtx, _hostName.c_str(), _userName.c_str(), _password.c_str(), NULL, 0, _priv, _cipher, _sessionTimeout, _retransmissionTimeout, workaround_flags, flags);
rc = ipmi_ctx_open_outofband_2_0(_ipmiCtx, _name.c_str(), _userName.c_str(), _password.c_str(), NULL, 0, _priv, _cipher, _sessionTimeout, _retransmissionTimeout, workaround_flags, flags);
}
if (rc < 0) {
_errorMsg = "Error opening IPMI connection: " + std::string(ipmi_ctx_errormsg(_ipmiCtx));
......@@ -126,14 +156,14 @@ bool IPMIHost::getSdrRecord(uint16_t recordId, std::vector<uint8_t>& record) {
if (ipmi_sdr_cache_open(sdrCtx, _ipmiCtx, _cache.c_str()) < 0) {
if ((ipmi_sdr_ctx_errnum(sdrCtx) == IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) || (ipmi_sdr_ctx_errnum(sdrCtx) == IPMI_SDR_ERR_CACHE_INVALID) || (ipmi_sdr_ctx_errnum(sdrCtx) == IPMI_SDR_ERR_CACHE_OUT_OF_DATE)) {
if ((ipmi_sdr_ctx_errnum(sdrCtx) == IPMI_SDR_ERR_CACHE_INVALID) || (ipmi_sdr_ctx_errnum(sdrCtx) == IPMI_SDR_ERR_CACHE_OUT_OF_DATE)) {
LOG(debug) << _hostName << "Deleting SDR cache " << _cache;
LOG(debug) << _name << "Deleting SDR cache " << _cache;
ipmi_sdr_cache_close(sdrCtx);
ipmi_sdr_cache_delete(sdrCtx, _cache.c_str());
}
if (ipmi_sdr_cache_create(sdrCtx, _ipmiCtx, _cache.c_str(), IPMI_SDR_CACHE_CREATE_FLAGS_DEFAULT, NULL, NULL) == 0) {
LOG(debug) << _hostName << ": Created new SDR cache " << _cache;
LOG(debug) << _name << ": Created new SDR cache " << _cache;
} else {
LOG(debug) << _hostName << ": Error creating new SDR cache " << _cache;
LOG(debug) << _name << ": Error creating new SDR cache " << _cache;
}
} else {
_errorMsg = "Error opening SDR cache: " + std::string(ipmi_sdr_ctx_errormsg(sdrCtx));
......@@ -289,26 +319,19 @@ void IPMIHost::increaseErrorCount() {
}
}
void IPMIHost::initializeStrand(boost::asio::io_service& io) {
if (!_strand) {
_strand = new boost::asio::io_service::strand(io);
}
}
void IPMIHost::printConfig(LOG_LEVEL ll) {
LOG_VAR(ll) << " IPMIHost: " << _hostName;
LOG_VAR(ll) << " UserName: " << _userName;
LOG_VAR(ll) << eInd << "IPMIHost: " << getHostName();
LOG_VAR(ll) << eInd << "UserName: " << getUserName();
#ifdef DEBUG
LOG_VAR(ll) << " Password: " << _password;
LOG_VAR(ll) << eInd << "Password: " << getPassword();
#else
LOG_VAR(ll) << " Password not shown";
LOG_VAR(ll) << eInd << "Password not shown";
#endif
LOG_VAR(ll) << " Cache: " << _cache;
LOG_VAR(ll) << " Auth: " << _auth;
LOG_VAR(ll) << " Priv: " << _priv;
LOG_VAR(ll) << " Cipher: " << _cipher;
LOG_VAR(ll) << " IPMI Version: " << _ipmiVersion;
LOG_VAR(ll) << " MQTT Part: " << _mqttPart;
LOG_VAR(ll) << " Session Timeout: " << _sessionTimeout;
LOG_VAR(ll) << " Retransmission Timeout: " << _retransmissionTimeout;
LOG_VAR(ll) << eInd << "Cache: " << getCache();
LOG_VAR(ll) << eInd << "Auth: " << getAuth();
LOG_VAR(ll) << eInd << "Priv: " << getPriv();
LOG_VAR(ll) << eInd << "Cipher: " << getCipher();
LOG_VAR(ll) << eInd << "IPMI Version: " << getIpmiVersion();
LOG_VAR(ll) << eInd << "Session Timeout: " << _sessionTimeout;
LOG_VAR(ll) << eInd << "Retransmission Timeout: " << _retransmissionTimeout;
}
......@@ -27,21 +27,23 @@
#ifndef IPMIHOST_H_
#define IPMIHOST_H_
#include "../../includes/EntityInterface.h"
#include <string>
#include <list>
#include <freeipmi/freeipmi.h>
#include <boost/asio.hpp>
#include "logging.h"
/**
* @brief Handles all connections to the same IPMI host.
*
* @ingroup ipmi
*/
class IPMIHost {
class IPMIHost : public EntityInterface {
public:
IPMIHost();
IPMIHost(const std::string& name = "");
IPMIHost(const IPMIHost& other);
virtual ~IPMIHost();
IPMIHost& operator=(const IPMIHost& other);
/* Translate recordId to SDR record */
bool getSdrRecord(uint16_t recordId, std::vector<uint8_t>& record);
......@@ -54,40 +56,28 @@ public:
const uint64_t getDelayFactor() const;
void setAuth(uint8_t auth) { _auth = auth; }
void setHostName(const std::string& hostName) { _hostName = hostName; }
void setHostName(const std::string& hostName) { _name = hostName; }
void setPassword(const std::string& password) { _password = password; }
void setCache(const std::string& cacheDir) { _cache = cacheDir + ".ipmiPluginSdrCache." + _hostName; }
void setCache(const std::string& cacheDir) { _cache = cacheDir + ".ipmiPluginSdrCache." + _name; }
void setPriv(uint8_t priv) { _priv = priv; }
void setCipher(const std::string& cipher) { _cipher = stoi(cipher); }
void setIpmiVersion(const std::string& ipmiVersion) { _ipmiVersion = stoi(ipmiVersion); }
void setUserName(const std::string& userName) { _userName = userName; }
void setMqttPart(const std::string& mqttPart) {
_mqttPart = mqttPart;
if (_mqttPart.front() != '/') {
_mqttPart.insert(0, "/");
}
if (_mqttPart.back() == '/') {
_mqttPart.erase(_mqttPart.size()-1);
}
}
void setSessionTimeout(uint32_t sessionTimeout) { _sessionTimeout = sessionTimeout; }
void setRetransmissionTimeout(uint32_t retransmissionTimeout) { _retransmissionTimeout = retransmissionTimeout; }
void initializeStrand(boost::asio::io_service& io);
void setDelayNextReadUntil(uint64_t delayNextReadUntil) { _delayNextReadUntil = delayNextReadUntil; }
uint8_t getAuth() const { return _auth; }