@cryptotaxi247 / netdata-1 / commits / d8fc41b06

collectors/freeipmi: add ipmi-sensors function (#16363)

* add ipmi-sensors function * rename the fields to reflect the output of the ipmi-sensors * reorder columns * update help

Ilya Mashchenko committed Nov 9, 2023 at 11:16 UTC d8fc41b065ed82e1433103405bec7f3b84a67af3
1 file changed +214 -12
collectors/freeipmi.plugin/freeipmi_plugin.c
+214 -12
@@ -22,6 +22,10 @@
22 #include "libnetdata/libnetdata.h"
23 #include "libnetdata/required_dummies.h"
24
25 +#define FREEIPMI_GLOBAL_FUNCTION_SENSORS() do { \
26 + fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"ipmi-sensors\" %d \"%s\"\n", 5, "Displays current sensor state and readings"); \
27 + } while(0)
28 +
29 // component names, based on our patterns
30 #define NETDATA_SENSOR_COMPONENT_MEMORY_MODULE "Memory Module"
31 #define NETDATA_SENSOR_COMPONENT_MEMORY "Memory"
@@ -83,6 +87,12 @@ static void netdata_update_ipmi_sel_events_count(struct netdata_ipmi_state *stat
87
88 /* Communication Configuration - Initialize accordingly */
89
90 +static netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER;
91 +static bool function_plugin_should_exit = false;
92 +
93 +int update_every = IPMI_SENSORS_MIN_UPDATE_EVERY; // this is the minimum update frequency
94 +int update_every_sel = IPMI_SEL_MIN_UPDATE_EVERY; // this is the minimum update frequency for SEL events
95 +
96 /* Hostname, NULL for In-band communication, non-null for a hostname */
97 char *hostname = NULL;
98
@@ -707,6 +717,8 @@ struct netdata_ipmi_state {
717 } updates;
718 };
719
720 +struct netdata_ipmi_state state = {0};
721 +
722 // ----------------------------------------------------------------------------
723 // excluded record ids maintenance (both for sensor data and state)
724
@@ -1297,6 +1309,7 @@ static size_t send_ipmi_sensor_metrics_to_netdata(struct netdata_ipmi_state *sta
1309 int update_every = (int)(state->sensors.freq_ut / USEC_PER_SEC);
1310 struct sensor *sn;
1311
1312 + netdata_mutex_lock(&stdout_mutex);
1313 // generate the CHART/DIMENSION lines, if we have to
1314 dfe_start_reentrant(state->sensors.dict, sn) {
1315 if(unlikely(!sn->do_metric && !sn->do_state))
@@ -1396,12 +1409,16 @@ static size_t send_ipmi_sensor_metrics_to_netdata(struct netdata_ipmi_state *sta
1409 }
1410 dfe_done(sn);
1411
1412 + netdata_mutex_unlock(&stdout_mutex);
1413 +
1414 return total_sensors_sent;
1415 }
1416
1417 static size_t send_ipmi_sel_metrics_to_netdata(struct netdata_ipmi_state *state) {
1418 static bool sel_chart_generated = false;
1419
1420 + netdata_mutex_lock(&stdout_mutex);
1421 +
1422 if(likely(state->sel.status == ICS_RUNNING)) {
1423 if(unlikely(!sel_chart_generated)) {
1424 sel_chart_generated = true;
@@ -1422,20 +1439,194 @@ static size_t send_ipmi_sel_metrics_to_netdata(struct netdata_ipmi_state *state)
1439 );
1440 }
1441
1442 + netdata_mutex_unlock(&stdout_mutex);
1443 +
1444 return state->sel.events;
1445 }
1446
1447 +// ----------------------------------------------------------------------------
1448 +
1449 +static const char *get_sensor_state_string(struct sensor *sn) {
1450 + switch (sn->sensor_state) {
1451 + case IPMI_MONITORING_STATE_NOMINAL:
1452 + return "nominal";
1453 + case IPMI_MONITORING_STATE_WARNING:
1454 + return "warning";
1455 + case IPMI_MONITORING_STATE_CRITICAL:
1456 + return "critical";
1457 + default:
1458 + return "unknown";
1459 + }
1460 +}
1461 +
1462 +static const char *get_sensor_function_priority(struct sensor *sn) {
1463 + switch (sn->sensor_state) {
1464 + case IPMI_MONITORING_STATE_WARNING:
1465 + return "warning";
1466 + case IPMI_MONITORING_STATE_CRITICAL:
1467 + return "critical";
1468 + default:
1469 + return "normal";
1470 + }
1471 +}
1472 +
1473 +static void freeimi_function_sensors(const char *transaction, char *function __maybe_unused, int timeout __maybe_unused, bool *cancelled __maybe_unused) {
1474 + time_t expires = now_realtime_sec() + update_every;
1475 +
1476 + BUFFER *wb = buffer_create(PLUGINSD_LINE_MAX, NULL);
1477 + buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS);
1478 + buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
1479 + buffer_json_member_add_string(wb, "type", "table");
1480 + buffer_json_member_add_time_t(wb, "update_every", update_every);
1481 + buffer_json_member_add_string(wb, "help", "View IPMI sensor readings and its state");
1482 + buffer_json_member_add_array(wb, "data");
1483 +
1484 + struct sensor *sn;
1485 + dfe_start_reentrant(state.sensors.dict, sn) {
1486 + if (unlikely(!sn->do_metric && !sn->do_state))
1487 + continue;
1488 +
1489 + double reading = NAN;
1490 + switch (sn->sensor_reading_type) {
1491 + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32:
1492 + reading = (double)sn->sensor_reading.uint32_value;
1493 + break;
1494 + case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE:
1495 + reading = (double)(sn->sensor_reading.double_value);
1496 + break;
1497 + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL:
1498 + reading = (double)sn->sensor_reading.bool_value;
1499 + break;
1500 + }
1501 +
1502 + buffer_json_add_array_item_array(wb);
1503 +
1504 + buffer_json_add_array_item_string(wb, sn->sensor_name);
1505 + buffer_json_add_array_item_string(wb, sn->type);
1506 + buffer_json_add_array_item_string(wb, sn->component);
1507 + buffer_json_add_array_item_double(wb, reading);
1508 + buffer_json_add_array_item_string(wb, sn->units);
1509 + buffer_json_add_array_item_string(wb, get_sensor_state_string(sn));
1510 +
1511 + buffer_json_add_array_item_object(wb);
1512 + buffer_json_member_add_string(wb, "severity", get_sensor_function_priority(sn));
1513 + buffer_json_object_close(wb);
1514 +
1515 + buffer_json_array_close(wb);
1516 + }
1517 + dfe_done(sn);
1518 +
1519 + buffer_json_array_close(wb); // data
1520 + buffer_json_member_add_object(wb, "columns");
1521 + {
1522 + size_t field_id = 0;
1523 +
1524 + buffer_rrdf_table_add_field(wb, field_id++, "Sensor", "Sensor Name",
1525 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1526 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1527 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1528 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY | RRDF_FIELD_OPTS_FULL_WIDTH,
1529 + NULL);
1530 + buffer_rrdf_table_add_field(wb, field_id++, "Type", "Sensor Type",
1531 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1532 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1533 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1534 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY,
1535 + NULL);
1536 + buffer_rrdf_table_add_field(wb, field_id++, "Component", "Sensor Component",
1537 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1538 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1539 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1540 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY,
1541 + NULL);
1542 + buffer_rrdf_table_add_field(wb, field_id++, "Reading", "Sensor Current Reading",
1543 + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER,
1544 + 2, NULL, 0, RRDF_FIELD_SORT_DESCENDING, NULL,
1545 + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE,
1546 + RRDF_FIELD_OPTS_VISIBLE,
1547 + NULL);
1548 + buffer_rrdf_table_add_field(wb, field_id++, "Units", "Sensor Reading Units",
1549 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1550 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1551 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1552 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY,
1553 + NULL);
1554 + buffer_rrdf_table_add_field(wb, field_id++, "State", "Sensor State",
1555 + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1556 + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1557 + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
1558 + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY,
1559 + NULL);
1560 + buffer_rrdf_table_add_field(
1561 + wb, field_id++,
1562 + "rowOptions", "rowOptions",
1563 + RRDF_FIELD_TYPE_NONE,
1564 + RRDR_FIELD_VISUAL_ROW_OPTIONS,
1565 + RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
1566 + RRDF_FIELD_SORT_FIXED,
1567 + NULL,
1568 + RRDF_FIELD_SUMMARY_COUNT,
1569 + RRDF_FIELD_FILTER_NONE,
1570 + RRDF_FIELD_OPTS_DUMMY,
1571 + NULL);
1572 + }
1573 +
1574 + buffer_json_object_close(wb); // columns
1575 + buffer_json_member_add_string(wb, "default_sort_column", "Type");
1576 +
1577 + buffer_json_member_add_object(wb, "charts");
1578 + {
1579 + buffer_json_member_add_object(wb, "Sensors");
1580 + {
1581 + buffer_json_member_add_string(wb, "name", "Sensors");
1582 + buffer_json_member_add_string(wb, "type", "stacked-bar");
1583 + buffer_json_member_add_array(wb, "columns");
1584 + {
1585 + buffer_json_add_array_item_string(wb, "Sensor");
1586 + }
1587 + buffer_json_array_close(wb);
1588 + }
1589 + buffer_json_object_close(wb);
1590 + }
1591 + buffer_json_object_close(wb); // charts
1592 +
1593 + buffer_json_member_add_array(wb, "default_charts");
1594 + {
1595 + buffer_json_add_array_item_array(wb);
1596 + buffer_json_add_array_item_string(wb, "Sensors");
1597 + buffer_json_add_array_item_string(wb, "Component");
1598 + buffer_json_array_close(wb);
1599 +
1600 + buffer_json_add_array_item_array(wb);
1601 + buffer_json_add_array_item_string(wb, "Sensors");
1602 + buffer_json_add_array_item_string(wb, "State");
1603 + buffer_json_array_close(wb);
1604 + }
1605 + buffer_json_array_close(wb);
1606 +
1607 + buffer_json_member_add_time_t(wb, "expires", now_realtime_sec() + 1);
1608 + buffer_json_finalize(wb);
1609 +
1610 + pluginsd_function_result_to_stdout(transaction, HTTP_RESP_OK, "application/json", expires, wb);
1611 +
1612 + buffer_free(wb);
1613 +}
1614 +
1615 // ----------------------------------------------------------------------------
1616 // main, command line arguments parsing
1617
1618 +static void plugin_exit(int code) {
1619 + fflush(stdout);
1620 + function_plugin_should_exit = true;
1621 + exit(code);
1622 +}
1623 +
1624 int main (int argc, char **argv) {
1625 bool netdata_do_sel = IPMI_ENABLE_SEL_BY_DEFAULT;
1626
1627 stderror = stderr;
1628 clocks_init();
1629
1437 - int update_every = IPMI_SENSORS_MIN_UPDATE_EVERY; // this is the minimum update frequency
1438 - int update_every_sel = IPMI_SEL_MIN_UPDATE_EVERY; // this is the minimum update frequency for SEL events
1630 bool debug = false;
1631
1632 // ------------------------------------------------------------------------
@@ -1801,16 +1992,17 @@ int main (int argc, char **argv) {
1992
1993 heartbeat_t hb;
1994 heartbeat_init(&hb);
1995 +
1996 for(iteration = 0; 1 ; iteration++) {
1997 usec_t dt = heartbeat_next(&hb, step);
1998
1999 if (!tty) {
2000 + netdata_mutex_lock(&stdout_mutex);
2001 fprintf(stdout, "\n"); // keepalive to avoid parser read timeout (2 minutes) during ipmi_detect_speed_secs()
2002 fflush(stdout);
2003 + netdata_mutex_unlock(&stdout_mutex);
2004 }
2005
1812 - struct netdata_ipmi_state state = {0 };
1813 -
2006 spinlock_lock(&sensors_data.spinlock);
2007 state.sensors = sensors_data.state.sensors;
2008 spinlock_unlock(&sensors_data.spinlock);
@@ -1827,8 +2019,7 @@ int main (int argc, char **argv) {
2019 __FUNCTION__, (size_t)((now_monotonic_usec() - state.sensors.last_iteration_ut) / USEC_PER_SEC));
2020
2021 fprintf(stdout, "EXIT\n");
1830 - fflush(stdout);
1831 - exit(0);
2022 + plugin_exit(0);
2023 }
2024 break;
2025
@@ -1838,14 +2029,12 @@ int main (int argc, char **argv) {
2029 case ICS_INIT_FAILED:
2030 collector_error("%s(): sensors failed to initialize. Calling DISABLE.", __FUNCTION__);
2031 fprintf(stdout, "DISABLE\n");
1841 - fflush(stdout);
1842 - exit(0);
2032 + plugin_exit(0);
2033
2034 case ICS_FAILED:
2035 collector_error("%s(): sensors fails repeatedly to collect metrics. Exiting to restart.", __FUNCTION__);
2036 fprintf(stdout, "EXIT\n");
1847 - fflush(stdout);
1848 - exit(0);
2037 + plugin_exit(0);
2038 }
2039
2040 if(netdata_do_sel) {
@@ -1865,6 +2054,16 @@ int main (int argc, char **argv) {
2054 if(unlikely(debug))
2055 fprintf(stderr, "%s: calling send_ipmi_sensor_metrics_to_netdata()\n", program_name);
2056
2057 + static bool add_func_sensors = true;
2058 + if (add_func_sensors) {
2059 + add_func_sensors = false;
2060 + struct functions_evloop_globals *wg =
2061 + functions_evloop_init(1, "FREEIPMI", &stdout_mutex, &function_plugin_should_exit);
2062 + functions_evloop_add_function(
2063 + wg, "ipmi-sensors", freeimi_function_sensors, PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT);
2064 + FREEIPMI_GLOBAL_FUNCTION_SENSORS();
2065 + }
2066 +
2067 state.updates.now_ut = now_monotonic_usec();
2068 send_ipmi_sensor_metrics_to_netdata(&state);
2069
@@ -1880,6 +2079,8 @@ int main (int argc, char **argv) {
2079 , state.sensors.collected
2080 );
2081
2082 + netdata_mutex_lock(&stdout_mutex);
2083 +
2084 if (!global_chart_created) {
2085 global_chart_created = true;
2086
@@ -1899,10 +2100,11 @@ int main (int argc, char **argv) {
2100 if (now_monotonic_sec() - started_t > IPMI_RESTART_EVERY_SECONDS) {
2101 collector_info("%s(): reached my lifetime expectancy. Exiting to restart.", __FUNCTION__);
2102 fprintf(stdout, "EXIT\n");
1902 - fflush(stdout);
1903 - exit(0);
2103 + plugin_exit(0);
2104 }
2105
2106 fflush(stdout);
2107 +
2108 + netdata_mutex_unlock(&stdout_mutex);
2109 }
2110 }