/*****************************************************************************\ * acct_gather_energy_ipmi.c - slurm energy accounting plugin for ipmi. ***************************************************************************** * Copyright (C) 2012 * Initially written by Thomas Cadeau @ Bull. Adapted by Yoann Blein @ Bull. * * This file is part of Slurm, a resource management program. * For details, see . * Please also read the included file: DISCLAIMER. * * Slurm 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. * * In addition, as a special exception, the copyright holders give permission * to link the code of portions of this program with the OpenSSL library under * certain conditions as described in each individual source file, and * distribute linked combinations including the two. You must obey the GNU * General Public License in all respects for all of the code used other than * OpenSSL. If you modify file(s) with this exception, you may extend this * exception to your version of the file(s), but you are not obligated to do * so. If you do not wish to do so, delete this exception statement from your * version. If you delete this exception statement from all source files in * the program, then also delete it here. * * Slurm 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 Slurm; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This file is patterned after jobcomp_linux.c, written by Morris Jette and * Copyright (C) 2002 The Regents of the University of California. \*****************************************************************************/ /* acct_gather_energy_ipmi * This plugin initiates a node-level thread to periodically * issue reads to a BMC over an ipmi interface driver 'kipmi0' * * TODO .. the library is under ../plugins/ipmi/lib and its * headers under ../plugins/ipmi/include * Currently the code is still using the 'getwatts' app. * This must be changed to utilize this library directly .. * including adjusting the makefiles for SLUrM building . */ #include #include #include #include #include #include "src/common/slurm_xlator.h" #include "src/common/slurm_acct_gather_energy.h" #include "src/common/slurm_protocol_api.h" #include "src/common/slurm_protocol_defs.h" #include "src/common/fd.h" #include "src/slurmd/common/proctrack.h" #include "src/slurmd/slurmd/slurmd.h" #include "acct_gather_energy_ipmi_config.h" /* * freeipmi includes for the lib */ #include #include /* These are defined here so when we link with something other than * the slurmctld we will have these symbols defined. They will get * overwritten when linking with the slurmctld. */ #if defined (__APPLE__) extern slurmd_conf_t *conf __attribute__((weak_import)); #else slurmd_conf_t *conf = NULL; #endif #define _DEBUG 1 #define _DEBUG_ENERGY 1 #define IPMI_VERSION 2 /* Data structure version number */ #define MAX_LOG_ERRORS 5 /* Max sensor reading errors log messages */ /* * These variables are required by the generic plugin interface. If they * are not found in the plugin, the plugin loader will ignore it. * * plugin_name - a string giving a human-readable description of the * plugin. There is no maximum length, but the symbol must refer to * a valid string. * * plugin_type - a string suggesting the type of the plugin or its * applicability to a particular form of data or method of data handling. * If the low-level plugin API is used, the contents of this string are * unimportant and may be anything. Slurm uses the higher-level plugin * interface which requires this string to be of the form * * / * * where is a description of the intended application of * the plugin (e.g., "jobacct" for Slurm job completion logging) and * is a description of how this plugin satisfies that application. Slurm will * only load job completion logging plugins if the plugin_type string has a * prefix of "jobacct/". * * plugin_version - an unsigned 32-bit integer containing the Slurm version * (major.minor.micro combined into a single number). */ const char plugin_name[] = "AcctGatherEnergy IPMI plugin"; const char plugin_type[] = "acct_gather_energy/ipmi"; const uint32_t plugin_version = SLURM_VERSION_NUMBER; /* * freeipmi variable declaration */ /* Global structure */ struct ipmi_monitoring_ipmi_config ipmi_config; ipmi_monitoring_ctx_t ipmi_ctx = NULL; unsigned int sensor_reading_flags = 0; /* Hostname, NULL for In-band communication, non-null for a hostname */ char *hostname = NULL; /* Set to an appropriate alternate if desired */ char *sdr_cache_directory = "/tmp"; char *sensor_config_file = NULL; /* * internal variables */ static time_t last_update_time = 0; static time_t previous_update_time = 0; static stepd_step_rec_t *job = NULL; static int context_id = -1; /* array of struct to track the status of multiple sensors */ typedef struct sensor_status { uint32_t id; uint32_t last_update_watt; acct_gather_energy_t energy; } sensor_status_t; static sensor_status_t *sensors = NULL; static uint16_t sensors_len = 0; static uint64_t *start_current_energies = NULL; /* array of struct describing the configuration of the sensors */ typedef struct description { const char* label; uint16_t sensor_cnt; uint16_t *sensor_idxs; } description_t; static description_t *descriptions; static uint16_t descriptions_len; static const char *NODE_DESC = "Node"; static int dataset_id = -1; /* id of the dataset for profile data */ static slurm_ipmi_conf_t slurm_ipmi_conf; static uint64_t debug_flags = 0; static bool flag_energy_accounting_shutdown = false; static bool flag_thread_started = false; static bool flag_init = false; static pthread_mutex_t ipmi_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t ipmi_cond = PTHREAD_COND_INITIALIZER; static pthread_mutex_t launch_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t launch_cond = PTHREAD_COND_INITIALIZER; pthread_t thread_ipmi_id_launcher = 0; pthread_t thread_ipmi_id_run = 0; static int _running_profile(void) { static bool run = false; static uint32_t profile_opt = ACCT_GATHER_PROFILE_NOT_SET; if (profile_opt == ACCT_GATHER_PROFILE_NOT_SET) { acct_gather_profile_g_get(ACCT_GATHER_PROFILE_RUNNING, &profile_opt); if (profile_opt & ACCT_GATHER_PROFILE_ENERGY) run = true; } return run; } /* * _get_additional_consumption computes consumption between 2 times * method is set to third method strongly */ static uint64_t _get_additional_consumption(time_t time0, time_t time1, uint32_t watt0, uint32_t watt1) { return (uint64_t) ((time1 - time0)*(watt1 + watt0)/2); } /* * _init_ipmi_config initializes parameters for freeipmi library */ static int _init_ipmi_config (void) { int errnum; /* Initialization flags * Most commonly bitwise OR IPMI_MONITORING_FLAGS_DEBUG and/or * IPMI_MONITORING_FLAGS_DEBUG_IPMI_PACKETS for extra debugging * information. */ unsigned int ipmimonitoring_init_flags = 0; memset(&ipmi_config, 0, sizeof(struct ipmi_monitoring_ipmi_config)); ipmi_config.driver_type = (int) slurm_ipmi_conf.driver_type; ipmi_config.disable_auto_probe = (int) slurm_ipmi_conf.disable_auto_probe; ipmi_config.driver_address = (unsigned int) slurm_ipmi_conf.driver_address; ipmi_config.register_spacing = (unsigned int) slurm_ipmi_conf.register_spacing; ipmi_config.driver_device = slurm_ipmi_conf.driver_device; ipmi_config.protocol_version = (int) slurm_ipmi_conf.protocol_version; ipmi_config.username = slurm_ipmi_conf.username; ipmi_config.password = slurm_ipmi_conf.password; ipmi_config.k_g = slurm_ipmi_conf.k_g; ipmi_config.k_g_len = (unsigned int) slurm_ipmi_conf.k_g_len; ipmi_config.privilege_level = (int) slurm_ipmi_conf.privilege_level; ipmi_config.authentication_type = (int) slurm_ipmi_conf.authentication_type; ipmi_config.cipher_suite_id = (int) slurm_ipmi_conf.cipher_suite_id; ipmi_config.session_timeout_len = (int) slurm_ipmi_conf.session_timeout; ipmi_config.retransmission_timeout_len = (int) slurm_ipmi_conf.retransmission_timeout; ipmi_config.workaround_flags = (unsigned int) slurm_ipmi_conf.workaround_flags; if (ipmi_monitoring_init(ipmimonitoring_init_flags, &errnum) < 0) { error("ipmi_monitoring_init: %s", ipmi_monitoring_ctx_strerror(errnum)); return SLURM_ERROR; } if (!(ipmi_ctx = ipmi_monitoring_ctx_create())) { error("ipmi_monitoring_ctx_create"); return SLURM_ERROR; } if (sdr_cache_directory) { if (ipmi_monitoring_ctx_sdr_cache_directory( ipmi_ctx, sdr_cache_directory) < 0) { error("ipmi_monitoring_ctx_sdr_cache_directory: %s", ipmi_monitoring_ctx_errormsg(ipmi_ctx)); return SLURM_ERROR; } } /* Must call otherwise only default interpretations ever used */ if (ipmi_monitoring_ctx_sensor_config_file( ipmi_ctx, sensor_config_file) < 0) { error("ipmi_monitoring_ctx_sensor_config_file: %s", ipmi_monitoring_ctx_errormsg(ipmi_ctx)); return SLURM_ERROR; } if (slurm_ipmi_conf.reread_sdr_cache) sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_REREAD_SDR_CACHE; if (slurm_ipmi_conf.ignore_non_interpretable_sensors) sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_IGNORE_NON_INTERPRETABLE_SENSORS; if (slurm_ipmi_conf.bridge_sensors) sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_BRIDGE_SENSORS; if (slurm_ipmi_conf.interpret_oem_data) sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_INTERPRET_OEM_DATA; if (slurm_ipmi_conf.shared_sensors) sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_SHARED_SENSORS; if (slurm_ipmi_conf.discrete_reading) sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_DISCRETE_READING; if (slurm_ipmi_conf.ignore_scanning_disabled) sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_IGNORE_SCANNING_DISABLED; if (slurm_ipmi_conf.assume_bmc_owner) sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_ASSUME_BMC_OWNER; /* FIXME: This is not included until later versions of IPMI, so don't always have it. */ /* if (slurm_ipmi_conf.entity_sensor_names) */ /* sensor_reading_flags |= */ /* IPMI_MONITORING_SENSOR_READING_FLAGS_ENTITY_SENSOR_NAMES; */ return SLURM_SUCCESS; } /* * _check_power_sensor check if the sensor is in Watt */ static int _check_power_sensor(void) { /* check the sensors list */ void *sensor_reading; int rc; int sensor_units; uint16_t i; unsigned int ids[sensors_len]; static uint8_t check_err_cnt = 0; for (i = 0; i < sensors_len; ++i) ids[i] = sensors[i].id; rc = ipmi_monitoring_sensor_readings_by_record_id(ipmi_ctx, hostname, &ipmi_config, sensor_reading_flags, ids, sensors_len, NULL, NULL); if (rc != sensors_len) { if (check_err_cnt < MAX_LOG_ERRORS) { error("ipmi_monitoring_sensor_readings_by_record_id: " "%s", ipmi_monitoring_ctx_errormsg(ipmi_ctx)); check_err_cnt++; } else if (check_err_cnt == MAX_LOG_ERRORS) { error("ipmi_monitoring_sensor_readings_by_record_id: " "%s. Stop logging these errors after %d attempts", ipmi_monitoring_ctx_errormsg(ipmi_ctx), MAX_LOG_ERRORS); check_err_cnt++; } return SLURM_ERROR; } check_err_cnt = 0; i = 0; do { /* check if the sensor unit is watts */ sensor_units = ipmi_monitoring_sensor_read_sensor_units(ipmi_ctx); if (sensor_units < 0) { error("ipmi_monitoring_sensor_read_sensor_units: %s", ipmi_monitoring_ctx_errormsg(ipmi_ctx)); return SLURM_ERROR; } if (sensor_units != slurm_ipmi_conf.variable) { error("Configured sensor is not in Watt, " "please check ipmi.conf"); return SLURM_ERROR; } /* update current value of the sensor */ sensor_reading = ipmi_monitoring_sensor_read_sensor_reading(ipmi_ctx); if (sensor_reading) { sensors[i].last_update_watt = (uint32_t) (*((double *)sensor_reading)); } else { error("ipmi read an empty value for power consumption"); return SLURM_ERROR; } ++i; } while (ipmi_monitoring_sensor_iterator_next(ipmi_ctx)); previous_update_time = last_update_time; last_update_time = time(NULL); return SLURM_SUCCESS; } /* * _find_power_sensor reads all sensors and find sensor in Watt */ static int _find_power_sensor(void) { int sensor_count; int i; int rc = SLURM_ERROR; void* sensor_reading; int sensor_units, record_id; static uint8_t find_err_cnt = 0; sensor_count = ipmi_monitoring_sensor_readings_by_record_id( ipmi_ctx, hostname, &ipmi_config, sensor_reading_flags, NULL, 0, NULL, NULL); if (sensor_count < 0) { if (find_err_cnt < MAX_LOG_ERRORS) { error("ipmi_monitoring_sensor_readings_by_record_id: " "%s", ipmi_monitoring_ctx_errormsg(ipmi_ctx)); find_err_cnt++; } else if (find_err_cnt == MAX_LOG_ERRORS) { error("ipmi_monitoring_sensor_readings_by_record_id: " "%s. Stop logging these errors after %d attempts", ipmi_monitoring_ctx_errormsg(ipmi_ctx), MAX_LOG_ERRORS); find_err_cnt++; } return SLURM_ERROR; } find_err_cnt = 0; for (i = 0; i < sensor_count; i++, ipmi_monitoring_sensor_iterator_next(ipmi_ctx)) { sensor_units = ipmi_monitoring_sensor_read_sensor_units(ipmi_ctx); if (sensor_units < 0) { error("ipmi_monitoring_sensor_read_sensor_units: %s", ipmi_monitoring_ctx_errormsg(ipmi_ctx)); return SLURM_ERROR; } if (sensor_units != slurm_ipmi_conf.variable) continue; record_id = ipmi_monitoring_sensor_read_record_id(ipmi_ctx); if (record_id < 0) { error("ipmi_monitoring_sensor_read_record_id: %s", ipmi_monitoring_ctx_errormsg(ipmi_ctx)); return SLURM_ERROR; } sensor_reading = ipmi_monitoring_sensor_read_sensor_reading(ipmi_ctx); if (sensor_reading) { /* we found a valid sensor, allocate room for its * status and its description as the main sensor */ sensors_len = 1; sensors = xmalloc(sizeof(sensor_status_t)); sensors[0].id = (uint32_t)record_id; sensors[0].last_update_watt = (uint32_t) (*((double *)sensor_reading)); descriptions_len = 1; descriptions = xmalloc(sizeof(description_t)); descriptions[0].label = xstrdup(NODE_DESC); descriptions[0].sensor_cnt = 1; descriptions[0].sensor_idxs = xmalloc(sizeof(uint16_t)); descriptions[0].sensor_idxs[0] = 0; previous_update_time = last_update_time; last_update_time = time(NULL); } else { error("ipmi read an empty value for power consumption"); rc = SLURM_ERROR; continue; } rc = SLURM_SUCCESS; break; } if (rc != SLURM_SUCCESS) info("Power sensor not found."); else if (debug_flags & DEBUG_FLAG_ENERGY) info("Power sensor found: %d", sensors_len); return rc; } /* * _read_ipmi_values read the Power sensor and update last_update_watt and times */ static int _read_ipmi_values(void) { /* read sensors list */ void *sensor_reading; int rc; uint16_t i; unsigned int ids[sensors_len]; static uint8_t read_err_cnt = 0; for (i = 0; i < sensors_len; ++i) ids[i] = sensors[i].id; rc = ipmi_monitoring_sensor_readings_by_record_id(ipmi_ctx, hostname, &ipmi_config, sensor_reading_flags, ids, sensors_len, NULL, NULL); if (rc != sensors_len) { if (read_err_cnt < MAX_LOG_ERRORS) { error("ipmi_monitoring_sensor_readings_by_record_id: " "%s", ipmi_monitoring_ctx_errormsg(ipmi_ctx)); read_err_cnt++; } else if (read_err_cnt == MAX_LOG_ERRORS) { error("ipmi_monitoring_sensor_readings_by_record_id: " "%s. Stop logging these errors after %d attempts", ipmi_monitoring_ctx_errormsg(ipmi_ctx), MAX_LOG_ERRORS); read_err_cnt++; } return SLURM_ERROR; } read_err_cnt = 0; i = 0; do { sensor_reading = ipmi_monitoring_sensor_read_sensor_reading(ipmi_ctx); if (sensor_reading) { sensors[i].last_update_watt = (uint32_t) (*((double *)sensor_reading)); } else { error("ipmi read an empty value for power consumption"); return SLURM_ERROR; } ++i; } while (ipmi_monitoring_sensor_iterator_next(ipmi_ctx)); previous_update_time = last_update_time; last_update_time = time(NULL); return SLURM_SUCCESS; } /* updates the given energy according to the last watt reading of the sensor */ static void _update_energy(acct_gather_energy_t *e, uint32_t last_update_watt, uint32_t readings) { uint32_t prev_watts; if (e->current_watts) { prev_watts = e->current_watts; e->ave_watts = ((e->ave_watts * readings) + e->current_watts) / (readings + 1); e->current_watts = last_update_watt; if (previous_update_time == 0) e->base_consumed_energy = 0; else e->base_consumed_energy = _get_additional_consumption( previous_update_time, last_update_time, prev_watts, e->current_watts); e->previous_consumed_energy = e->consumed_energy; e->consumed_energy += e->base_consumed_energy; } else { e->consumed_energy = 0; e->ave_watts = 0; e->current_watts = last_update_watt; } e->poll_time = time(NULL); } /* * _thread_update_node_energy calls _read_ipmi_values and updates all values * for node consumption */ static int _thread_update_node_energy(void) { int rc = SLURM_SUCCESS; uint16_t i; static uint32_t readings = 0; rc = _read_ipmi_values(); if (rc == SLURM_SUCCESS) { /* sensors list */ for (i = 0; i < sensors_len; ++i) { if (sensors[i].energy.current_watts == NO_VAL) return rc; _update_energy(&sensors[i].energy, sensors[i].last_update_watt, readings); } if (previous_update_time == 0) previous_update_time = last_update_time; } readings++; if (debug_flags & DEBUG_FLAG_ENERGY) { for (i = 0; i < sensors_len; ++i) info("ipmi-thread: sensor %u current_watts: %u, consumed %"PRIu64" Joules %"PRIu64" new, ave watts %u", sensors[i].id, sensors[i].energy.current_watts, sensors[i].energy.consumed_energy, sensors[i].energy.base_consumed_energy, sensors[i].energy.ave_watts); } return rc; } /* * _thread_init initializes values and conf for the ipmi thread */ static int _thread_init(void) { static bool first = true; static bool first_init = SLURM_ERROR; int rc = SLURM_SUCCESS; uint16_t i; if (!first) return first_init; first = false; if (_init_ipmi_config() != SLURM_SUCCESS) { //TODO verbose error? rc = SLURM_ERROR; } else { if ((sensors_len == 0 && _find_power_sensor() != SLURM_SUCCESS) || _check_power_sensor() != SLURM_SUCCESS) { /* no valid sensors found */ for (i = 0; i < sensors_len; ++i) { sensors[i].energy.current_watts = NO_VAL; } } else { for (i = 0; i < sensors_len; ++i) { sensors[i].energy.current_watts = sensors[i].last_update_watt; } } if (slurm_ipmi_conf.reread_sdr_cache) //IPMI cache is reread only on initialization //This option need a big EnergyIPMITimeout sensor_reading_flags ^= IPMI_MONITORING_SENSOR_READING_FLAGS_REREAD_SDR_CACHE; } if (rc != SLURM_SUCCESS) if (ipmi_ctx) ipmi_monitoring_ctx_destroy(ipmi_ctx); if (debug_flags & DEBUG_FLAG_ENERGY) info("%s thread init", plugin_name); first_init = SLURM_SUCCESS; return rc; } static int _ipmi_send_profile(void) { uint16_t i, j; uint64_t data[descriptions_len]; uint32_t id; time_t last_time = last_update_time; if (!_running_profile()) return SLURM_SUCCESS; if (dataset_id < 0) { acct_gather_profile_dataset_t dataset[descriptions_len+1]; for (i = 0; i < descriptions_len; i++) { dataset[i].name = xstrdup_printf( "%sPower", descriptions[i].label); dataset[i].type = PROFILE_FIELD_UINT64; } dataset[i].name = NULL; dataset[i].type = PROFILE_FIELD_NOT_SET; dataset_id = acct_gather_profile_g_create_dataset( "Energy", NO_PARENT, dataset); for (i = 0; i < descriptions_len; ++i) xfree(dataset[i].name); if (debug_flags & DEBUG_FLAG_ENERGY) debug("Energy: dataset created (id = %d)", dataset_id); if (dataset_id == SLURM_ERROR) { error("Energy: Failed to create the dataset for IPMI"); return SLURM_ERROR; } } /* pack an array of uint64_t with current power of sensors */ memset(data, 0, sizeof(data)); for (i = 0; i < descriptions_len; ++i) { for (j = 0; j < descriptions[i].sensor_cnt; ++j) { id = descriptions[i].sensor_idxs[j]; data[i] += sensors[id].energy.current_watts; } if (descriptions[i].sensor_cnt) last_time = sensors[id].energy.poll_time; } if (debug_flags & DEBUG_FLAG_PROFILE) { for (i = 0; i < descriptions_len; i++) { info("PROFILE-Energy: %sPower=%"PRIu64"", descriptions[i].label, data[i]); } } return acct_gather_profile_g_add_sample_data(dataset_id, (void *)data, last_time); } /* * _thread_ipmi_run is the thread calling ipmi and launching _thread_ipmi_write */ static void *_thread_ipmi_run(void *no_data) { // need input (attr) struct timeval tvnow; struct timespec abs; flag_energy_accounting_shutdown = false; if (debug_flags & DEBUG_FLAG_ENERGY) info("ipmi-thread: launched"); (void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); (void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); slurm_mutex_lock(&ipmi_mutex); if (_thread_init() != SLURM_SUCCESS) { if (debug_flags & DEBUG_FLAG_ENERGY) info("ipmi-thread: aborted"); slurm_mutex_unlock(&ipmi_mutex); slurm_mutex_lock(&launch_mutex); slurm_cond_signal(&launch_cond); slurm_mutex_unlock(&launch_mutex); return NULL; } (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); slurm_mutex_unlock(&ipmi_mutex); flag_thread_started = true; slurm_mutex_lock(&launch_mutex); slurm_cond_signal(&launch_cond); slurm_mutex_unlock(&launch_mutex); /* setup timer */ gettimeofday(&tvnow, NULL); abs.tv_sec = tvnow.tv_sec; abs.tv_nsec = tvnow.tv_usec * 1000; //loop until slurm stop while (!flag_energy_accounting_shutdown) { slurm_mutex_lock(&ipmi_mutex); _thread_update_node_energy(); /* Sleep until the next time. */ abs.tv_sec += slurm_ipmi_conf.freq; slurm_cond_timedwait(&ipmi_cond, &ipmi_mutex, &abs); slurm_mutex_unlock(&ipmi_mutex); } if (debug_flags & DEBUG_FLAG_ENERGY) info("ipmi-thread: ended"); return NULL; } static void *_thread_launcher(void *no_data) { //what arg would countain? frequency, socket? struct timeval tvnow; struct timespec abs; slurm_thread_create(&thread_ipmi_id_run, _thread_ipmi_run, NULL); /* setup timer */ gettimeofday(&tvnow, NULL); abs.tv_sec = tvnow.tv_sec + slurm_ipmi_conf.timeout; abs.tv_nsec = tvnow.tv_usec * 1000; slurm_mutex_lock(&launch_mutex); slurm_cond_timedwait(&launch_cond, &launch_mutex, &abs); slurm_mutex_unlock(&launch_mutex); if (!flag_thread_started) { error("%s threads failed to start in a timely manner", plugin_name); flag_energy_accounting_shutdown = true; /* * It is a known thing we can hang up on IPMI calls cancel if * we must. */ pthread_cancel(thread_ipmi_id_run); /* * Unlock just to make sure since we could have canceled the * thread while in the lock. */ slurm_mutex_unlock(&ipmi_mutex); } return NULL; } static int _get_joules_task(uint16_t delta) { time_t now = time(NULL); static bool first = true; uint64_t adjustment = 0; uint16_t i; acct_gather_energy_t *new, *old; /* sensors list */ acct_gather_energy_t *energies = NULL; uint16_t sensor_cnt = 0; xassert(context_id != -1); if (slurm_get_node_energy( NULL, context_id, delta, &sensor_cnt, &energies)) { error("_get_joules_task: can't get info from slurmd"); return SLURM_ERROR; } if (first) { sensors_len = sensor_cnt; sensors = xmalloc(sizeof(sensor_status_t) * sensors_len); start_current_energies = xmalloc(sizeof(uint64_t) * sensors_len); } if (sensor_cnt != sensors_len) { error("_get_joules_task: received %u sensors, %u expected", sensor_cnt, sensors_len); acct_gather_energy_destroy(energies); return SLURM_ERROR; } for (i = 0; i < sensor_cnt; ++i) { new = &energies[i]; old = &sensors[i].energy; new->previous_consumed_energy = old->consumed_energy; if (slurm_ipmi_conf.adjustment) adjustment = _get_additional_consumption( new->poll_time, now, new->current_watts, new->current_watts); if (!first) { new->consumed_energy -= start_current_energies[i]; new->base_consumed_energy = adjustment + (new->consumed_energy - old->consumed_energy); } else { /* This is just for the step, so take all the pervious consumption out of the mix. */ start_current_energies[i] = new->consumed_energy + adjustment; new->base_consumed_energy = 0; } new->consumed_energy = new->previous_consumed_energy + new->base_consumed_energy; memcpy(old, new, sizeof(acct_gather_energy_t)); if (debug_flags & DEBUG_FLAG_ENERGY) info("_get_joules_task: consumed %"PRIu64" Joules " "(received %"PRIu64"(%u watts) from slurmd)", new->consumed_energy, new->base_consumed_energy, new->current_watts); } acct_gather_energy_destroy(energies); first = false; return SLURM_SUCCESS; } static void _get_node_energy(acct_gather_energy_t *energy) { uint16_t i, j, id; acct_gather_energy_t *e; /* find the "Node" description */ for (i = 0; i < descriptions_len; ++i) if (xstrcmp(descriptions[i].label, NODE_DESC) == 0) break; /* not found, init is not finished or there is no watt sensors */ if (i >= descriptions_len) return; /* sum the energy of all sensors described for "Node" */ memset(energy, 0, sizeof(acct_gather_energy_t)); for (j = 0; j < descriptions[i].sensor_cnt; ++j) { id = descriptions[i].sensor_idxs[j]; e = &sensors[id].energy; energy->base_consumed_energy += e->base_consumed_energy; energy->ave_watts += e->ave_watts; energy->consumed_energy += e->consumed_energy; energy->current_watts += e->current_watts; energy->previous_consumed_energy += e->previous_consumed_energy; /* node poll_time is computed as the oldest poll_time of the sensors */ if (energy->poll_time == 0 || energy->poll_time > e->poll_time) energy->poll_time = e->poll_time; } } /* * init() is called when the plugin is loaded, before any other functions * are called. Put global initialization here. */ extern int init(void) { debug_flags = slurm_get_debug_flags(); /* put anything that requires the .conf being read in acct_gather_energy_p_conf_parse */ return SLURM_SUCCESS; } extern int fini(void) { uint16_t i; if (!running_in_slurmdstepd()) return SLURM_SUCCESS; flag_energy_accounting_shutdown = true; slurm_mutex_lock(&launch_mutex); /* clean up the launch thread */ slurm_cond_signal(&launch_cond); slurm_mutex_unlock(&launch_mutex); if (thread_ipmi_id_launcher) pthread_join(thread_ipmi_id_launcher, NULL); slurm_mutex_lock(&ipmi_mutex); /* clean up the run thread */ slurm_cond_signal(&ipmi_cond); if (ipmi_ctx) ipmi_monitoring_ctx_destroy(ipmi_ctx); reset_slurm_ipmi_conf(&slurm_ipmi_conf); slurm_mutex_unlock(&ipmi_mutex); if (thread_ipmi_id_run) pthread_join(thread_ipmi_id_run, NULL); xfree(sensors); xfree(start_current_energies); for (i = 0; i < descriptions_len; ++i) { xfree(descriptions[i].label); xfree(descriptions[i].sensor_idxs); } xfree(descriptions); return SLURM_SUCCESS; } extern int acct_gather_energy_p_update_node_energy(void) { int rc = SLURM_SUCCESS; xassert(running_in_slurmdstepd()); return rc; } extern int acct_gather_energy_p_get_data(enum acct_energy_type data_type, void *data) { uint16_t i; int rc = SLURM_SUCCESS; acct_gather_energy_t *energy = (acct_gather_energy_t *)data; time_t *last_poll = (time_t *)data; uint16_t *sensor_cnt = (uint16_t *)data; xassert(running_in_slurmdstepd()); switch (data_type) { case ENERGY_DATA_NODE_ENERGY_UP: slurm_mutex_lock(&ipmi_mutex); if (running_in_slurmd()) { if (_thread_init() == SLURM_SUCCESS) _thread_update_node_energy(); } else { _get_joules_task(10); } _get_node_energy(energy); slurm_mutex_unlock(&ipmi_mutex); break; case ENERGY_DATA_NODE_ENERGY: slurm_mutex_lock(&ipmi_mutex); _get_node_energy(energy); slurm_mutex_unlock(&ipmi_mutex); break; case ENERGY_DATA_LAST_POLL: slurm_mutex_lock(&ipmi_mutex); *last_poll = last_update_time; slurm_mutex_unlock(&ipmi_mutex); break; case ENERGY_DATA_SENSOR_CNT: *sensor_cnt = sensors_len; break; case ENERGY_DATA_STRUCT: slurm_mutex_lock(&ipmi_mutex); for (i = 0; i < sensors_len; ++i) memcpy(&energy[i], &sensors[i].energy, sizeof(acct_gather_energy_t)); slurm_mutex_unlock(&ipmi_mutex); break; case ENERGY_DATA_JOULES_TASK: slurm_mutex_lock(&ipmi_mutex); if (running_in_slurmd()) { if (_thread_init() == SLURM_SUCCESS) _thread_update_node_energy(); } else { _get_joules_task(10); } for (i = 0; i < sensors_len; ++i) memcpy(&energy[i], &sensors[i].energy, sizeof(acct_gather_energy_t)); slurm_mutex_unlock(&ipmi_mutex); break; default: error("acct_gather_energy_p_get_data: unknown enum %d", data_type); rc = SLURM_ERROR; break; } return rc; } extern int acct_gather_energy_p_set_data(enum acct_energy_type data_type, void *data) { int rc = SLURM_SUCCESS; int *delta = (int *)data; xassert(running_in_slurmdstepd()); switch (data_type) { case ENERGY_DATA_RECONFIG: debug_flags = slurm_get_debug_flags(); break; case ENERGY_DATA_PROFILE: slurm_mutex_lock(&ipmi_mutex); _get_joules_task(*delta); _ipmi_send_profile(); slurm_mutex_unlock(&ipmi_mutex); break; case ENERGY_DATA_STEP_PTR: /* set global job if needed later */ job = (stepd_step_rec_t *)data; break; default: error("acct_gather_energy_p_set_data: unknown enum %d", data_type); rc = SLURM_ERROR; break; } return rc; } /* Parse the sensor descriptions stored into slurm_ipmi_conf.power_sensors. * Expected format: comma-separated sensors ids and semi-colon-separated * sensors descriptions. Also expects a mandatory description with label * "Node". */ static int _parse_sensor_descriptions(void) { /* TODO: error propagation */ const char *sep1 = ";"; const char *sep2 = ","; char *str_desc_list, *str_desc, *str_id, *mid, *endptr; char *saveptr1, *saveptr2; // pointers for strtok_r storage uint16_t i, j, k; uint16_t id; uint16_t *idx; description_t *d; bool found; if (!slurm_ipmi_conf.power_sensors || !slurm_ipmi_conf.power_sensors[0]) return SLURM_SUCCESS; /* count the number of descriptions */ str_desc_list = xstrdup(slurm_ipmi_conf.power_sensors); descriptions_len = 0; str_desc = strtok_r(str_desc_list, sep1, &saveptr1); while (str_desc) { ++descriptions_len; str_desc = strtok_r(NULL, sep1, &saveptr1); } descriptions = xmalloc(sizeof(description_t) * descriptions_len); /* parse descriptions */ strcpy(str_desc_list, slurm_ipmi_conf.power_sensors); i = 0; str_desc = strtok_r(str_desc_list, sep1, &saveptr1); while (str_desc) { mid = xstrchr(str_desc, '='); if (!mid || mid == str_desc) { goto error; } /* label */ *mid = '\0'; d = &descriptions[i]; d->label = xstrdup(str_desc); /* associated sensors */ ++mid; str_id = strtok_r(mid, sep2, &saveptr2); /* parse sensor ids of the current description */ while (str_id) { id = strtol(str_id, &endptr, 10); if (*endptr != '\0') goto error; d->sensor_cnt++; xrealloc(d->sensor_idxs, sizeof(uint16_t) * d->sensor_cnt); d->sensor_idxs[d->sensor_cnt - 1] = id; str_id = strtok_r(NULL, sep2, &saveptr2); } ++i; str_desc = strtok_r(NULL, sep1, &saveptr1); } xfree(str_desc_list); /* Ensure that the "Node" description is provided */ found = false; for (i = 0; i < descriptions_len && !found; ++i) found = (xstrcasecmp(descriptions[i].label, NODE_DESC) == 0); if (!found) goto error; /* Here we have the list of descriptions with sensors ids in the * sensors_idxs field instead of their indexes. We still have to * gather the unique sensors ids and replace sensors_idxs by their * indexes in the sensors array */ for (i = 0; i < descriptions_len; ++i) { for (j = 0; j < descriptions[i].sensor_cnt; ++j) { idx = &descriptions[i].sensor_idxs[j]; found = false; for (k = 0; k < sensors_len && !found; ++k) found = (*idx == sensors[k].id); if (found) { *idx = k - 1; } else { ++sensors_len; xrealloc(sensors, sensors_len * sizeof(sensor_status_t)); sensors[sensors_len - 1].id = *idx; *idx = sensors_len - 1;; } } } return SLURM_SUCCESS; error: error("Configuration of EnergyIPMIPowerSensors is malformed. " "Make sure that the expected format is respected and that " "the \"Node\" label is provided."); for (i = 0; i < descriptions_len; ++i) { xfree(descriptions[i].label); xfree(descriptions[i].sensor_idxs); } xfree(descriptions); descriptions = NULL; return SLURM_ERROR; } extern void acct_gather_energy_p_conf_options(s_p_options_t **full_options, int *full_options_cnt) { // s_p_options_t *full_options_ptr; s_p_options_t options[] = { {"EnergyIPMIDriverType", S_P_UINT32}, {"EnergyIPMIDisableAutoProbe", S_P_UINT32}, {"EnergyIPMIDriverAddress", S_P_UINT32}, {"EnergyIPMIRegisterSpacing", S_P_UINT32}, {"EnergyIPMIDriverDevice", S_P_STRING}, {"EnergyIPMIProtocolVersion", S_P_UINT32}, {"EnergyIPMIUsername", S_P_STRING}, {"EnergyIPMIPassword", S_P_STRING}, /* FIXME: remove these from the structure? */ // {"EnergyIPMIk_g", S_P_STRING}, // {"EnergyIPMIk_g_len", S_P_UINT32}, {"EnergyIPMIPrivilegeLevel", S_P_UINT32}, {"EnergyIPMIAuthenticationType", S_P_UINT32}, {"EnergyIPMICipherSuiteId", S_P_UINT32}, {"EnergyIPMISessionTimeout", S_P_UINT32}, {"EnergyIPMIRetransmissionTimeout", S_P_UINT32}, {"EnergyIPMIWorkaroundFlags", S_P_UINT32}, {"EnergyIPMIRereadSdrCache", S_P_BOOLEAN}, {"EnergyIPMIIgnoreNonInterpretableSensors", S_P_BOOLEAN}, {"EnergyIPMIBridgeSensors", S_P_BOOLEAN}, {"EnergyIPMIInterpretOemData", S_P_BOOLEAN}, {"EnergyIPMISharedSensors", S_P_BOOLEAN}, {"EnergyIPMIDiscreteReading", S_P_BOOLEAN}, {"EnergyIPMIIgnoreScanningDisabled", S_P_BOOLEAN}, {"EnergyIPMIAssumeBmcOwner", S_P_BOOLEAN}, {"EnergyIPMIEntitySensorNames", S_P_BOOLEAN}, {"EnergyIPMIFrequency", S_P_UINT32}, {"EnergyIPMICalcAdjustment", S_P_BOOLEAN}, {"EnergyIPMIPowerSensors", S_P_STRING}, {"EnergyIPMITimeout", S_P_UINT32}, {"EnergyIPMIVariable", S_P_STRING}, {NULL} }; transfer_s_p_options(full_options, options, full_options_cnt); } extern void acct_gather_energy_p_conf_set(int context_id_in, s_p_hashtbl_t *tbl) { char *tmp_char; /* Set initial values */ reset_slurm_ipmi_conf(&slurm_ipmi_conf); if (tbl) { /* ipmi initialization parameters */ s_p_get_uint32(&slurm_ipmi_conf.driver_type, "EnergyIPMIDriverType", tbl); s_p_get_uint32(&slurm_ipmi_conf.disable_auto_probe, "EnergyIPMIDisableAutoProbe", tbl); s_p_get_uint32(&slurm_ipmi_conf.driver_address, "EnergyIPMIDriverAddress", tbl); s_p_get_uint32(&slurm_ipmi_conf.register_spacing, "EnergyIPMIRegisterSpacing", tbl); s_p_get_string(&slurm_ipmi_conf.driver_device, "EnergyIPMIDriverDevice", tbl); s_p_get_uint32(&slurm_ipmi_conf.protocol_version, "EnergyIPMIProtocolVersion", tbl); if (!s_p_get_string(&slurm_ipmi_conf.username, "EnergyIPMIUsername", tbl)) slurm_ipmi_conf.username = xstrdup(DEFAULT_IPMI_USER); s_p_get_string(&slurm_ipmi_conf.password, "EnergyIPMIPassword", tbl); if (!slurm_ipmi_conf.password) slurm_ipmi_conf.password = xstrdup("foopassword"); s_p_get_uint32(&slurm_ipmi_conf.privilege_level, "EnergyIPMIPrivilegeLevel", tbl); s_p_get_uint32(&slurm_ipmi_conf.authentication_type, "EnergyIPMIAuthenticationType", tbl); s_p_get_uint32(&slurm_ipmi_conf.cipher_suite_id, "EnergyIPMICipherSuiteId", tbl); s_p_get_uint32(&slurm_ipmi_conf.session_timeout, "EnergyIPMISessionTimeout", tbl); s_p_get_uint32(&slurm_ipmi_conf.retransmission_timeout, "EnergyIPMIRetransmissionTimeout", tbl); s_p_get_uint32(&slurm_ipmi_conf.workaround_flags, "EnergyIPMIWorkaroundFlags", tbl); if (!s_p_get_boolean(&slurm_ipmi_conf.reread_sdr_cache, "EnergyIPMIRereadSdrCache", tbl)) slurm_ipmi_conf.reread_sdr_cache = false; if (!s_p_get_boolean(&slurm_ipmi_conf. ignore_non_interpretable_sensors, "EnergyIPMIIgnoreNonInterpretableSensors", tbl)) slurm_ipmi_conf.ignore_non_interpretable_sensors = false; if (!s_p_get_boolean(&slurm_ipmi_conf.bridge_sensors, "EnergyIPMIBridgeSensors", tbl)) slurm_ipmi_conf.bridge_sensors = false; if (!s_p_get_boolean(&slurm_ipmi_conf.interpret_oem_data, "EnergyIPMIInterpretOemData", tbl)) slurm_ipmi_conf.interpret_oem_data = false; if (!s_p_get_boolean(&slurm_ipmi_conf.shared_sensors, "EnergyIPMISharedSensors", tbl)) slurm_ipmi_conf.shared_sensors = false; if (!s_p_get_boolean(&slurm_ipmi_conf.discrete_reading, "EnergyIPMIDiscreteReading", tbl)) slurm_ipmi_conf.discrete_reading = false; if (!s_p_get_boolean(&slurm_ipmi_conf.ignore_scanning_disabled, "EnergyIPMIIgnoreScanningDisabled", tbl)) slurm_ipmi_conf.ignore_scanning_disabled = false; if (!s_p_get_boolean(&slurm_ipmi_conf.assume_bmc_owner, "EnergyIPMIAssumeBmcOwner", tbl)) slurm_ipmi_conf.assume_bmc_owner = false; if (!s_p_get_boolean(&slurm_ipmi_conf.entity_sensor_names, "EnergyIPMIEntitySensorNames", tbl)) slurm_ipmi_conf.entity_sensor_names = false; s_p_get_uint32(&slurm_ipmi_conf.freq, "EnergyIPMIFrequency", tbl); if ((int)slurm_ipmi_conf.freq <= 0) fatal("EnergyIPMIFrequency must be a positive integer " "in acct_gather.conf."); if (!s_p_get_boolean(&(slurm_ipmi_conf.adjustment), "EnergyIPMICalcAdjustment", tbl)) slurm_ipmi_conf.adjustment = false; s_p_get_string(&slurm_ipmi_conf.power_sensors, "EnergyIPMIPowerSensors", tbl); s_p_get_uint32(&slurm_ipmi_conf.timeout, "EnergyIPMITimeout", tbl); if (s_p_get_string(&tmp_char, "EnergyIPMIVariable", tbl)) { if (!xstrcmp(tmp_char, "Temp")) slurm_ipmi_conf.variable = IPMI_MONITORING_SENSOR_UNITS_CELSIUS; else if (!xstrcmp(tmp_char, "Voltage")) slurm_ipmi_conf.variable = IPMI_MONITORING_SENSOR_UNITS_VOLTS; else if (!xstrcmp(tmp_char, "Fan")) slurm_ipmi_conf.variable = IPMI_MONITORING_SENSOR_UNITS_RPM; xfree(tmp_char); } } context_id = context_id_in; if (!running_in_slurmdstepd()) return; if (!flag_init) { /* try to parse the PowerSensors settings */ _parse_sensor_descriptions(); flag_init = true; if (running_in_slurmd()) { slurm_thread_create(&thread_ipmi_id_launcher, _thread_launcher, NULL); if (debug_flags & DEBUG_FLAG_ENERGY) info("%s thread launched", plugin_name); } else _get_joules_task(0); } verbose("%s loaded", plugin_name); } extern void acct_gather_energy_p_conf_values(List *data) { config_key_pair_t *key_pair; xassert(*data); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIDriverType"); key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.driver_type); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIDisableAutoProbe"); key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.disable_auto_probe); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIDriverAddress"); key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.driver_address); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIRegisterSpacing"); key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.register_spacing); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIDriverDevice"); key_pair->value = xstrdup(slurm_ipmi_conf.driver_device); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIProtocolVersion"); key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.protocol_version); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIUsername"); key_pair->value = xstrdup(slurm_ipmi_conf.username); list_append(*data, key_pair); /* Don't give out the password */ /* key_pair = xmalloc(sizeof(config_key_pair_t)); */ /* key_pair->name = xstrdup("EnergyIPMIPassword"); */ /* key_pair->value = xstrdup(slurm_ipmi_conf.password); */ /* list_append(*data, key_pair); */ key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIPrivilegeLevel"); key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.privilege_level); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIAuthenticationType"); key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.authentication_type); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMICipherSuiteId"); key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.cipher_suite_id); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMISessionTimeout"); key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.session_timeout); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIRetransmissionTimeout"); key_pair->value = xstrdup_printf( "%u", slurm_ipmi_conf.retransmission_timeout); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIWorkaroundFlags"); key_pair->value = xstrdup_printf( "%u", slurm_ipmi_conf.workaround_flags); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIRereadSdrCache"); key_pair->value = xstrdup(slurm_ipmi_conf.reread_sdr_cache ? "Yes" : "No"); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIIgnoreNonInterpretableSensors"); key_pair->value = xstrdup( slurm_ipmi_conf.ignore_non_interpretable_sensors ? "Yes" : "No"); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIBridgeSensors"); key_pair->value = xstrdup(slurm_ipmi_conf.bridge_sensors ? "Yes" : "No"); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIInterpretOemData"); key_pair->value = xstrdup(slurm_ipmi_conf.interpret_oem_data ? "Yes" : "No"); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMISharedSensors"); key_pair->value = xstrdup(slurm_ipmi_conf.shared_sensors ? "Yes" : "No"); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIDiscreteReading"); key_pair->value = xstrdup(slurm_ipmi_conf.discrete_reading ? "Yes" : "No"); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIIgnoreScanningDisabled"); key_pair->value = xstrdup(slurm_ipmi_conf.ignore_scanning_disabled ? "Yes" : "No"); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIAssumeBmcOwner"); key_pair->value = xstrdup(slurm_ipmi_conf.assume_bmc_owner ? "Yes" : "No"); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIEntitySensorNames"); key_pair->value = xstrdup(slurm_ipmi_conf.entity_sensor_names ? "Yes" : "No"); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIFrequency"); key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.freq); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMICalcAdjustment"); key_pair->value = xstrdup(slurm_ipmi_conf.adjustment ? "Yes" : "No"); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIPowerSensors"); key_pair->value = xstrdup_printf("%s", slurm_ipmi_conf.power_sensors); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMITimeout"); key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.timeout); list_append(*data, key_pair); key_pair = xmalloc(sizeof(config_key_pair_t)); key_pair->name = xstrdup("EnergyIPMIVariable"); switch (slurm_ipmi_conf.variable) { case IPMI_MONITORING_SENSOR_UNITS_CELSIUS: key_pair->value = xstrdup("Temp"); break; case IPMI_MONITORING_SENSOR_UNITS_RPM: key_pair->value = xstrdup("Fan"); break; case IPMI_MONITORING_SENSOR_UNITS_VOLTS: key_pair->value = xstrdup("Voltage"); break; case IPMI_MONITORING_SENSOR_UNITS_WATTS: key_pair->value = xstrdup("Watts"); break; default: key_pair->value = xstrdup("Unknown"); break; } list_append(*data, key_pair); return; }