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

[DNMY] IPMPROG-9304 Sensors alerts refactoring #300

Open
wants to merge 1 commit into
base: release/IPM-2.8.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 66 additions & 74 deletions lib/src/alert_device.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ void Device::fixAlertLimits(DeviceAlert& alert)
}
}

void Device::addAlert(const std::string& quantity, const std::map<std::string, std::vector<std::string>>& variables)
void Device::addAlert(const std::string& quantity, const std::string& alertName, const std::map<std::string, std::vector<std::string>>& variables)
{
log_debug("aa: device %s provides %s alert", assetName().c_str(), quantity.c_str());
std::string prefix = daisychainPrefix() + quantity;

DeviceAlert alert;
alert.name = quantity;
alert.name = alertName;

// Is there an existing alert which we can change?
const auto& _existingalert = _alerts.find(quantity);
Expand Down Expand Up @@ -152,8 +152,9 @@ std::map<std::string, DeviceAlert>& Device::alerts()
int Device::scanCapabilities(nut::TcpClient& conn)
{
log_debug("aa: scanning capabilities for %s", assetName().c_str());
if (!conn.isConnected())
if (!conn.isConnected()) {
return 0;
}

std::string prefix = daisychainPrefix();
int retval = -1;
Expand All @@ -171,49 +172,16 @@ int Device::scanCapabilities(nut::TcpClient& conn)
if (vars.empty())
return 0;

// Sensors handling
if (vars.find(prefix + "ambient.count") != vars.cend()) {
// New style sensor(s) (EMP002: ambient collection, with index)
auto sensor_count_var = vars.find(prefix + "ambient.count");
int sensors_count = std::stoi(sensor_count_var->second[0]);
log_debug("aa: found %i sensor(s)", sensors_count);

for (int a = 1; a <= sensors_count; a++) {
std::string q = "ambient." + std::to_string(a) + ".temperature";
if (vars.find(prefix + q + ".status") != vars.cend()) {
addAlert(q, vars);
_scanned = true;
}
q = "ambient." + std::to_string(a) + ".humidity";
if (vars.find(prefix + q + ".status") != vars.cend()) {
addAlert(q, vars);
_scanned = true;
}
}
} else {
// Legacy sensor (EMP001: ambient collection, without index)
std::string q = "ambient.temperature";
if (vars.find(prefix + q + ".status") != vars.cend()) {
addAlert(q, vars);
_scanned = true;
}
q = "ambient.humidity";
if (vars.find(prefix + q + ".status") != vars.cend()) {
addAlert(q, vars);
_scanned = true;
}
}

// Input handling
for (int a = 1; a <= 3; a++) {
std::string q = "input.L" + std::to_string(a) + ".current";
if (vars.find(prefix + q + ".status") != vars.cend()) {
addAlert(q, vars);
addAlert(q, q, vars);
_scanned = true;
}
q = "input.L" + std::to_string(a) + ".voltage";
if (vars.find(prefix + q + ".status") != vars.cend()) {
addAlert(q, vars);
addAlert(q, q, vars);
_scanned = true;
}
}
Expand All @@ -223,13 +191,13 @@ int Device::scanCapabilities(nut::TcpClient& conn)
bool found = false;
std::string q = "outlet.group." + std::to_string(a) + ".current";
if (vars.find(prefix + q + ".status") != vars.cend()) {
addAlert(q, vars);
addAlert(q, q, vars);
found = true;
_scanned = true;
}
q = "outlet.group." + std::to_string(a) + ".voltage";
if (vars.find(prefix + q + ".status") != vars.cend()) {
addAlert(q, vars);
addAlert(q, q, vars);
found = true;
_scanned = true;
}
Expand Down Expand Up @@ -258,41 +226,73 @@ int Device::scanCapabilities(nut::TcpClient& conn)

void Device::publishAlerts(mlm_client_t* client, uint64_t ttl)
{
if (!client)
if (!client) {
log_error("publishAlerts: no client defined");
return;
}

log_debug("aa: publishing %zu alerts on %s", _alerts.size(), assetName().c_str());
for (auto& it : _alerts) {
publishAlert(client, it.second, ttl);
}
}

// HOTFIX arrange as we can the alert name displayed (en_US)
// TODO use translation string instead
// NOTE: alertname modified on return
// ex.: "input.L3.voltage" -> "Input L3 voltage"

static void makeAlertNameMoreHumanReadable(const char* alertname)
{
if (!alertname) return;

bool capitalize = true;
for (char* p = const_cast<char*>(alertname); (*p) != 0; p++) {
if (capitalize) { // capitalize 1st char
capitalize = false;
*p = char(toupper(*p));
}
if ((*p)== '.') { // subs '.' with ' '
*p = ' ';
}
}
}

void Device::publishAlert(mlm_client_t* client, DeviceAlert& alert, uint64_t ttl)
{
if (!client)
if (!client) {
log_error("publishAlert: no client defined");
return;
if (alert.status.empty())
}
if (alert.status.empty()) {
log_error("publishAlert: alert status empty");
return;
}

const char *state = "ACTIVE", *severity = NULL;
std::string description;

std::string alertNameLabelStr = alert.name; // cpy
const char* alert_name_label = alertNameLabelStr.c_str();
makeAlertNameMoreHumanReadable(alert_name_label);

log_debug("aa: alert status '%s'", alert.status.c_str());
if (alert.status == "good") {
state = "RESOLVED";
severity = "ok";
description = TRANSLATE_ME("%s is resolved", alert.name.c_str());
description = TRANSLATE_ME("%s is resolved", alert_name_label);
} else if (alert.status == "warning-low") {
severity = "WARNING";
description = TRANSLATE_ME("%s is low", alert.name.c_str());
description = TRANSLATE_ME("%s is low", alert_name_label);
} else if (alert.status == "critical-low") {
severity = "CRITICAL";
description = TRANSLATE_ME("%s is critically low", alert.name.c_str());
description = TRANSLATE_ME("%s is critically low", alert_name_label);
} else if (alert.status == "warning-high") {
severity = "WARNING";
description = TRANSLATE_ME("%s is high", alert.name.c_str());
description = TRANSLATE_ME("%s is high", alert_name_label);
} else if (alert.status == "critical-high") {
severity = "CRITICAL";
description = TRANSLATE_ME("%s is critically high", alert.name.c_str());
description = TRANSLATE_ME("%s is critically high", alert_name_label);
}
std::string rule = alert.name + "@" + assetName();

Expand All @@ -301,6 +301,10 @@ void Device::publishAlert(mlm_client_t* client, DeviceAlert& alert, uint64_t ttl
severity = "WARNING";
}

zlist_t *listAction = zlist_new();
zlist_append(listAction, const_cast<char*>("EMAIL"));
zlist_append(listAction, const_cast<char*>("SMS"));

log_debug("aa: publishing alert %s", rule.c_str());
zmsg_t* message = fty_proto_encode_alert(nullptr, // aux
uint64_t(alert.timestamp), // timestamp
Expand All @@ -310,19 +314,23 @@ void Device::publishAlert(mlm_client_t* client, DeviceAlert& alert, uint64_t ttl
state, // state
severity, // severity
description.c_str(), // description
NULL // action ?email
listAction // action list
);

if (message) {
std::string topic = rule + "/" + severity + "@" + assetName();
mlm_client_send(client, topic.c_str(), &message);
}
zmsg_destroy(&message);
zlist_destroy(&listAction);
}

void Device::publishRules(mlm_client_t* client)
{
if (!client)
if (!client) {
log_error("publishRules: no client defined");
return;
}

for (auto& it : _alerts) {
publishRule(client, it.second);
Expand Down Expand Up @@ -353,31 +361,11 @@ static std::string s_rule_desc(const std::string& alert_name)
return "{}";
}

// HOTFIX arrange as we can the alert name displayed (en_US)
// TODO use translation string instead
// NOTE: alertname modified on return
// ex.: "input.L3.voltage" -> "Input L3 voltage"

static void makeAlertNameMoreHumanReadable(const char* alertname)
{
if (!alertname) return;

bool capitalize = true;
for (char* p = const_cast<char*>(alertname); (*p) != 0; p++) {
if (capitalize) { // capitalize 1st char
capitalize = false;
*p = char(toupper(*p));
}
if ((*p)== '.') { // subs '.' with ' '
*p = ' ';
}
}
}

void Device::publishRule(mlm_client_t* client, DeviceAlert& alert)
{
if (!client || alert.rulePublished)
if (!client || alert.rulePublished) {
return;
}

zmsg_t* message = zmsg_new();
assert(message);
Expand Down Expand Up @@ -508,8 +496,11 @@ void Device::publishRule(mlm_client_t* client, DeviceAlert& alert)
void Device::update(nut::TcpClient& conn)
{
auto nutDevice = conn.getDevice(_nutName);
if (!nutDevice.isOk())
if (!nutDevice.isOk()) {
log_error("aa: device %s is not present", _nutName.c_str());
return;
}

for (auto& it : _alerts) {
try {
std::string prefix = daisychainPrefix();
Expand All @@ -531,7 +522,8 @@ void Device::update(nut::TcpClient& conn)

std::string Device::daisychainPrefix() const
{
if (chain() == 0)
if (chain() == 0) {
return "";
}
return "device." + std::to_string(chain()) + ".";
}
2 changes: 1 addition & 1 deletion lib/src/alert_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class Device
void publishRules(mlm_client_t* client);

public:
void addAlert(const std::string& quantity, const std::map<std::string, std::vector<std::string>>& variables);
void addAlert(const std::string& quantity, const std::string& alertName, const std::map<std::string, std::vector<std::string>>& variables);
const std::map<std::string, DeviceAlert>& alerts() const;
std::map<std::string, DeviceAlert>& alerts();

Expand Down
2 changes: 1 addition & 1 deletion lib/src/alert_device_list.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ void Devices::updateDeviceList()
return;

const AssetState& deviceState = _state_reader->getState();
auto& devices = deviceState.getPowerDevices();
auto& devices = deviceState.getPowerDevices();

log_debug("aa: updating device list");
for (auto i : devices) {
Expand Down
15 changes: 8 additions & 7 deletions lib/src/nut_mlm.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@
#include <malamute.h>

// zactor_new takes a void* pointer
#define MLM_ENDPOINT_VOID (const_cast<void*>(static_cast<const void*>(MLM_ENDPOINT)))
#define ACTOR_NUT_NAME "fty-nut"
#define ACTOR_ALERT_NAME "bios-nut-alert"
#define ACTOR_ALERT_MB_NAME ACTOR_ALERT_NAME "-mb"
#define ACTOR_SENSOR_NAME "agent-nut-sensor"
#define MLM_ENDPOINT_VOID (const_cast<void*>(static_cast<const void*>(MLM_ENDPOINT)))
#define ACTOR_NUT_NAME "fty-nut"
#define ACTOR_ALERT_NAME "bios-nut-alert"
#define ACTOR_ALERT_MB_NAME ACTOR_ALERT_NAME "-mb"
#define ACTOR_SENSOR_NAME "agent-nut-sensor"
#define ACTOR_SENSOR_NAME_INVENTORY ACTOR_SENSOR_NAME "-inventory" // to publish sensors inventory
#define ACTOR_CONFIGURATOR_NAME "nut-configurator"
#define ACTOR_CONFIGURATOR_MB_NAME ACTOR_CONFIGURATOR_NAME "-mb"
#define ACTOR_SENSOR_NAME_ALERT ACTOR_SENSOR_NAME "-alert" // to publish sensors alerts
#define ACTOR_CONFIGURATOR_NAME "nut-configurator"
#define ACTOR_CONFIGURATOR_MB_NAME ACTOR_CONFIGURATOR_NAME "-mb"

#define CONFIG_POLLING "nut/polling_interval"
#define ACTION_POLLING "POLLING"
Expand Down
18 changes: 18 additions & 0 deletions lib/src/sensor_actor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,20 @@ void sensor_actor(zsock_t* pipe, void* args)
return;
}

MlmClientGuard clientAlert(mlm_client_new());
if (!clientAlert) {
log_fatal("mlm_client_new () failed");
return;
}
if (mlm_client_connect(clientAlert, endpoint, 5000, ACTOR_SENSOR_NAME_ALERT) < 0) {
log_error("client %s failed to connect", ACTOR_SENSOR_NAME_ALERT);
return;
}
if (mlm_client_set_producer(clientAlert, FTY_PROTO_STREAM_ALERTS_SYS) < 0) {
log_error("mlm_client_set_producer (stream = '%s') failed", FTY_PROTO_STREAM_ALERTS_SYS);
return;
}

ZpollerGuard poller(zpoller_new(pipe, mlm_client_msgpipe(client), NULL));
if (!poller) {
log_fatal("zpoller_new () failed");
Expand All @@ -138,13 +152,17 @@ void sensor_actor(zsock_t* pipe, void* args)
nutClient.connect("localhost", 3493);

sensors.updateSensorList(nutClient, client);
sensors.updateDeviceList(nutClient);
sensors.updateDevicesValues(nutClient);
sensors.updateFromNUT(nutClient);

sensors.advertiseInventory(clientInventory);

// hotfix IPMVAL-2713 (data stale on device which host sensors cause communication failure alarms on
// sensors) increase ttl from 60 to 240 sec (polling period is equal to 30 sec).
sensors.publish(int((timeout * 8) / 1000));
sensors.publishRules(client);
sensors.publishAlerts(clientAlert);

nutClient.disconnect();
}
Expand Down
Loading