diff options
Diffstat (limited to 'collectors/freeipmi.plugin')
-rw-r--r-- | collectors/freeipmi.plugin/freeipmi_plugin.c | 244 | ||||
-rw-r--r-- | collectors/freeipmi.plugin/integrations/intelligent_platform_management_interface_ipmi.md | 49 |
2 files changed, 240 insertions, 53 deletions
diff --git a/collectors/freeipmi.plugin/freeipmi_plugin.c b/collectors/freeipmi.plugin/freeipmi_plugin.c index 63147d62..6ec9b698 100644 --- a/collectors/freeipmi.plugin/freeipmi_plugin.c +++ b/collectors/freeipmi.plugin/freeipmi_plugin.c @@ -22,6 +22,10 @@ #include "libnetdata/libnetdata.h" #include "libnetdata/required_dummies.h" +#define FREEIPMI_GLOBAL_FUNCTION_SENSORS() do { \ + fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"ipmi-sensors\" %d \"%s\"\n", 5, "Displays current sensor state and readings"); \ + } while(0) + // component names, based on our patterns #define NETDATA_SENSOR_COMPONENT_MEMORY_MODULE "Memory Module" #define NETDATA_SENSOR_COMPONENT_MEMORY "Memory" @@ -83,6 +87,12 @@ static void netdata_update_ipmi_sel_events_count(struct netdata_ipmi_state *stat /* Communication Configuration - Initialize accordingly */ +static netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER; +static bool function_plugin_should_exit = false; + +int update_every = IPMI_SENSORS_MIN_UPDATE_EVERY; // this is the minimum update frequency +int update_every_sel = IPMI_SEL_MIN_UPDATE_EVERY; // this is the minimum update frequency for SEL events + /* Hostname, NULL for In-band communication, non-null for a hostname */ char *hostname = NULL; @@ -707,6 +717,8 @@ struct netdata_ipmi_state { } updates; }; +struct netdata_ipmi_state state = {0}; + // ---------------------------------------------------------------------------- // excluded record ids maintenance (both for sensor data and state) @@ -1297,6 +1309,7 @@ static size_t send_ipmi_sensor_metrics_to_netdata(struct netdata_ipmi_state *sta int update_every = (int)(state->sensors.freq_ut / USEC_PER_SEC); struct sensor *sn; + netdata_mutex_lock(&stdout_mutex); // generate the CHART/DIMENSION lines, if we have to dfe_start_reentrant(state->sensors.dict, sn) { 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 } dfe_done(sn); + netdata_mutex_unlock(&stdout_mutex); + return total_sensors_sent; } static size_t send_ipmi_sel_metrics_to_netdata(struct netdata_ipmi_state *state) { static bool sel_chart_generated = false; + netdata_mutex_lock(&stdout_mutex); + if(likely(state->sel.status == ICS_RUNNING)) { if(unlikely(!sel_chart_generated)) { sel_chart_generated = true; @@ -1422,39 +1439,197 @@ static size_t send_ipmi_sel_metrics_to_netdata(struct netdata_ipmi_state *state) ); } + netdata_mutex_unlock(&stdout_mutex); + return state->sel.events; } // ---------------------------------------------------------------------------- -// main, command line arguments parsing -int main (int argc, char **argv) { - bool netdata_do_sel = IPMI_ENABLE_SEL_BY_DEFAULT; +static const char *get_sensor_state_string(struct sensor *sn) { + switch (sn->sensor_state) { + case IPMI_MONITORING_STATE_NOMINAL: + return "nominal"; + case IPMI_MONITORING_STATE_WARNING: + return "warning"; + case IPMI_MONITORING_STATE_CRITICAL: + return "critical"; + default: + return "unknown"; + } +} - stderror = stderr; - clocks_init(); +static const char *get_sensor_function_priority(struct sensor *sn) { + switch (sn->sensor_state) { + case IPMI_MONITORING_STATE_WARNING: + return "warning"; + case IPMI_MONITORING_STATE_CRITICAL: + return "critical"; + default: + return "normal"; + } +} - int update_every = IPMI_SENSORS_MIN_UPDATE_EVERY; // this is the minimum update frequency - int update_every_sel = IPMI_SEL_MIN_UPDATE_EVERY; // this is the minimum update frequency for SEL events - bool debug = false; +static void freeimi_function_sensors(const char *transaction, char *function __maybe_unused, int timeout __maybe_unused, bool *cancelled __maybe_unused) { + time_t expires = now_realtime_sec() + update_every; - // ------------------------------------------------------------------------ - // initialization of netdata plugin + BUFFER *wb = buffer_create(PLUGINSD_LINE_MAX, NULL); + buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS); + buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK); + buffer_json_member_add_string(wb, "type", "table"); + buffer_json_member_add_time_t(wb, "update_every", update_every); + buffer_json_member_add_string(wb, "help", "View IPMI sensor readings and its state"); + buffer_json_member_add_array(wb, "data"); - program_name = "freeipmi.plugin"; + struct sensor *sn; + dfe_start_reentrant(state.sensors.dict, sn) { + if (unlikely(!sn->do_metric && !sn->do_state)) + continue; + + double reading = NAN; + switch (sn->sensor_reading_type) { + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32: + reading = (double)sn->sensor_reading.uint32_value; + break; + case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE: + reading = (double)(sn->sensor_reading.double_value); + break; + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL: + reading = (double)sn->sensor_reading.bool_value; + break; + } - // disable syslog - error_log_syslog = 0; + buffer_json_add_array_item_array(wb); - // set errors flood protection to 100 logs per hour - error_log_errors_per_period = 100; - error_log_throttle_period = 3600; + buffer_json_add_array_item_string(wb, sn->sensor_name); + buffer_json_add_array_item_string(wb, sn->type); + buffer_json_add_array_item_string(wb, sn->component); + buffer_json_add_array_item_double(wb, reading); + buffer_json_add_array_item_string(wb, sn->units); + buffer_json_add_array_item_string(wb, get_sensor_state_string(sn)); - log_set_global_severity_for_external_plugins(); + buffer_json_add_array_item_object(wb); + buffer_json_member_add_string(wb, "severity", get_sensor_function_priority(sn)); + buffer_json_object_close(wb); - // initialize the threads + buffer_json_array_close(wb); + } + dfe_done(sn); + + buffer_json_array_close(wb); // data + buffer_json_member_add_object(wb, "columns"); + { + size_t field_id = 0; + + buffer_rrdf_table_add_field(wb, field_id++, "Sensor", "Sensor Name", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY | RRDF_FIELD_OPTS_FULL_WIDTH, + NULL); + buffer_rrdf_table_add_field(wb, field_id++, "Type", "Sensor Type", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY, + NULL); + buffer_rrdf_table_add_field(wb, field_id++, "Component", "Sensor Component", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY, + NULL); + buffer_rrdf_table_add_field(wb, field_id++, "Reading", "Sensor Current Reading", + RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, + 2, NULL, 0, RRDF_FIELD_SORT_DESCENDING, NULL, + RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE, + RRDF_FIELD_OPTS_VISIBLE, + NULL); + buffer_rrdf_table_add_field(wb, field_id++, "Units", "Sensor Reading Units", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY, + NULL); + buffer_rrdf_table_add_field(wb, field_id++, "State", "Sensor State", + RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, + 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, + RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, + RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY, + NULL); + buffer_rrdf_table_add_field( + wb, field_id++, + "rowOptions", "rowOptions", + RRDF_FIELD_TYPE_NONE, + RRDR_FIELD_VISUAL_ROW_OPTIONS, + RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN, + RRDF_FIELD_SORT_FIXED, + NULL, + RRDF_FIELD_SUMMARY_COUNT, + RRDF_FIELD_FILTER_NONE, + RRDF_FIELD_OPTS_DUMMY, + NULL); + } + + buffer_json_object_close(wb); // columns + buffer_json_member_add_string(wb, "default_sort_column", "Type"); + + buffer_json_member_add_object(wb, "charts"); + { + buffer_json_member_add_object(wb, "Sensors"); + { + buffer_json_member_add_string(wb, "name", "Sensors"); + buffer_json_member_add_string(wb, "type", "stacked-bar"); + buffer_json_member_add_array(wb, "columns"); + { + buffer_json_add_array_item_string(wb, "Sensor"); + } + buffer_json_array_close(wb); + } + buffer_json_object_close(wb); + } + buffer_json_object_close(wb); // charts + + buffer_json_member_add_array(wb, "default_charts"); + { + buffer_json_add_array_item_array(wb); + buffer_json_add_array_item_string(wb, "Sensors"); + buffer_json_add_array_item_string(wb, "Component"); + buffer_json_array_close(wb); + + buffer_json_add_array_item_array(wb); + buffer_json_add_array_item_string(wb, "Sensors"); + buffer_json_add_array_item_string(wb, "State"); + buffer_json_array_close(wb); + } + buffer_json_array_close(wb); + + buffer_json_member_add_time_t(wb, "expires", now_realtime_sec() + 1); + buffer_json_finalize(wb); + + pluginsd_function_result_to_stdout(transaction, HTTP_RESP_OK, "application/json", expires, wb); + + buffer_free(wb); +} + +// ---------------------------------------------------------------------------- +// main, command line arguments parsing + +static void plugin_exit(int code) { + fflush(stdout); + function_plugin_should_exit = true; + exit(code); +} + +int main (int argc, char **argv) { + clocks_init(); + nd_log_initialize_for_external_plugins("freeipmi.plugin"); netdata_threads_init_for_external_plugins(0); // set the default threads stack size here + bool netdata_do_sel = IPMI_ENABLE_SEL_BY_DEFAULT; + + bool debug = false; + // ------------------------------------------------------------------------ // parse command line parameters @@ -1728,7 +1903,7 @@ int main (int argc, char **argv) { errno = 0; if(freq_s && freq_s < update_every) - collector_error("%s(): update frequency %d seconds is too small for IPMI. Using %d.", + collector_info("%s(): update frequency %d seconds is too small for IPMI. Using %d.", __FUNCTION__, freq_s, update_every); update_every = freq_s = MAX(freq_s, update_every); @@ -1801,16 +1976,17 @@ int main (int argc, char **argv) { heartbeat_t hb; heartbeat_init(&hb); + for(iteration = 0; 1 ; iteration++) { usec_t dt = heartbeat_next(&hb, step); if (!tty) { + netdata_mutex_lock(&stdout_mutex); fprintf(stdout, "\n"); // keepalive to avoid parser read timeout (2 minutes) during ipmi_detect_speed_secs() fflush(stdout); + netdata_mutex_unlock(&stdout_mutex); } - struct netdata_ipmi_state state = {0 }; - spinlock_lock(&sensors_data.spinlock); state.sensors = sensors_data.state.sensors; spinlock_unlock(&sensors_data.spinlock); @@ -1827,8 +2003,7 @@ int main (int argc, char **argv) { __FUNCTION__, (size_t)((now_monotonic_usec() - state.sensors.last_iteration_ut) / USEC_PER_SEC)); fprintf(stdout, "EXIT\n"); - fflush(stdout); - exit(0); + plugin_exit(0); } break; @@ -1838,14 +2013,12 @@ int main (int argc, char **argv) { case ICS_INIT_FAILED: collector_error("%s(): sensors failed to initialize. Calling DISABLE.", __FUNCTION__); fprintf(stdout, "DISABLE\n"); - fflush(stdout); - exit(0); + plugin_exit(0); case ICS_FAILED: collector_error("%s(): sensors fails repeatedly to collect metrics. Exiting to restart.", __FUNCTION__); fprintf(stdout, "EXIT\n"); - fflush(stdout); - exit(0); + plugin_exit(0); } if(netdata_do_sel) { @@ -1865,6 +2038,16 @@ int main (int argc, char **argv) { if(unlikely(debug)) fprintf(stderr, "%s: calling send_ipmi_sensor_metrics_to_netdata()\n", program_name); + static bool add_func_sensors = true; + if (add_func_sensors) { + add_func_sensors = false; + struct functions_evloop_globals *wg = + functions_evloop_init(1, "FREEIPMI", &stdout_mutex, &function_plugin_should_exit); + functions_evloop_add_function( + wg, "ipmi-sensors", freeimi_function_sensors, PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT); + FREEIPMI_GLOBAL_FUNCTION_SENSORS(); + } + state.updates.now_ut = now_monotonic_usec(); send_ipmi_sensor_metrics_to_netdata(&state); @@ -1880,6 +2063,8 @@ int main (int argc, char **argv) { , state.sensors.collected ); + netdata_mutex_lock(&stdout_mutex); + if (!global_chart_created) { global_chart_created = true; @@ -1899,10 +2084,11 @@ int main (int argc, char **argv) { if (now_monotonic_sec() - started_t > IPMI_RESTART_EVERY_SECONDS) { collector_info("%s(): reached my lifetime expectancy. Exiting to restart.", __FUNCTION__); fprintf(stdout, "EXIT\n"); - fflush(stdout); - exit(0); + plugin_exit(0); } fflush(stdout); + + netdata_mutex_unlock(&stdout_mutex); } } diff --git a/collectors/freeipmi.plugin/integrations/intelligent_platform_management_interface_ipmi.md b/collectors/freeipmi.plugin/integrations/intelligent_platform_management_interface_ipmi.md index 6d894667..c0293fc3 100644 --- a/collectors/freeipmi.plugin/integrations/intelligent_platform_management_interface_ipmi.md +++ b/collectors/freeipmi.plugin/integrations/intelligent_platform_management_interface_ipmi.md @@ -4,6 +4,7 @@ meta_yaml: "https://github.com/netdata/netdata/edit/master/collectors/freeipmi.p sidebar_label: "Intelligent Platform Management Interface (IPMI)" learn_status: "Published" learn_rel_path: "Data Collection/Hardware Devices and Sensors" +most_popular: True message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE" endmeta--> @@ -170,30 +171,30 @@ To display a help message listing the available command line options: | Name | Description | Default | Required | |:----|:-----------|:-------|:--------:| -| SECONDS | Data collection frequency. | | False | -| debug | Enable verbose output. | disabled | False | -| no-sel | Disable System Event Log (SEL) collection. | disabled | False | -| reread-sdr-cache | Re-read SDR cache on every iteration. | disabled | False | -| interpret-oem-data | Attempt to parse OEM data. | disabled | False | -| assume-system-event-record | treat illegal SEL events records as normal. | disabled | False | -| ignore-non-interpretable-sensors | Do not read sensors that cannot be interpreted. | disabled | False | -| bridge-sensors | Bridge sensors not owned by the BMC. | disabled | False | -| shared-sensors | Enable shared sensors if found. | disabled | False | -| no-discrete-reading | Do not read sensors if their event/reading type code is invalid. | enabled | False | -| ignore-scanning-disabled | Ignore the scanning bit and read sensors no matter what. | disabled | False | -| assume-bmc-owner | Assume the BMC is the sensor owner no matter what (usually bridging is required too). | disabled | False | -| hostname HOST | Remote IPMI hostname or IP address. | local | False | -| username USER | Username that will be used when connecting to the remote host. | | False | -| password PASS | Password that will be used when connecting to the remote host. | | False | -| noauthcodecheck / no-auth-code-check | Don't check the authentication codes returned. | | False | -| driver-type IPMIDRIVER | Specify the driver type to use instead of doing an auto selection. The currently available outofband drivers are LAN and LAN_2_0, which perform IPMI 1.5 and IPMI 2.0 respectively. The currently available inband drivers are KCS, SSIF, OPENIPMI and SUNBMC. | | False | -| sdr-cache-dir PATH | SDR cache files directory. | /tmp | False | -| sensor-config-file FILE | Sensors configuration filename. | system default | False | -| sel-config-file FILE | SEL configuration filename. | system default | False | -| ignore N1,N2,N3,... | Sensor IDs to ignore. | | False | -| ignore-status N1,N2,N3,... | Sensor IDs to ignore status (nominal/warning/critical). | | False | -| -v | Print version and exit. | | False | -| --help | Print usage message and exit. | | False | +| SECONDS | Data collection frequency. | | no | +| debug | Enable verbose output. | disabled | no | +| no-sel | Disable System Event Log (SEL) collection. | disabled | no | +| reread-sdr-cache | Re-read SDR cache on every iteration. | disabled | no | +| interpret-oem-data | Attempt to parse OEM data. | disabled | no | +| assume-system-event-record | treat illegal SEL events records as normal. | disabled | no | +| ignore-non-interpretable-sensors | Do not read sensors that cannot be interpreted. | disabled | no | +| bridge-sensors | Bridge sensors not owned by the BMC. | disabled | no | +| shared-sensors | Enable shared sensors if found. | disabled | no | +| no-discrete-reading | Do not read sensors if their event/reading type code is invalid. | enabled | no | +| ignore-scanning-disabled | Ignore the scanning bit and read sensors no matter what. | disabled | no | +| assume-bmc-owner | Assume the BMC is the sensor owner no matter what (usually bridging is required too). | disabled | no | +| hostname HOST | Remote IPMI hostname or IP address. | local | no | +| username USER | Username that will be used when connecting to the remote host. | | no | +| password PASS | Password that will be used when connecting to the remote host. | | no | +| noauthcodecheck / no-auth-code-check | Don't check the authentication codes returned. | | no | +| driver-type IPMIDRIVER | Specify the driver type to use instead of doing an auto selection. The currently available outofband drivers are LAN and LAN_2_0, which perform IPMI 1.5 and IPMI 2.0 respectively. The currently available inband drivers are KCS, SSIF, OPENIPMI and SUNBMC. | | no | +| sdr-cache-dir PATH | SDR cache files directory. | /tmp | no | +| sensor-config-file FILE | Sensors configuration filename. | system default | no | +| sel-config-file FILE | SEL configuration filename. | system default | no | +| ignore N1,N2,N3,... | Sensor IDs to ignore. | | no | +| ignore-status N1,N2,N3,... | Sensor IDs to ignore status (nominal/warning/critical). | | no | +| -v | Print version and exit. | | no | +| --help | Print usage message and exit. | | no | </details> |