1 /*****************************************************************************\
2  *  acct_gather_energy_xcc.c - slurm energy accounting plugin for xcc.
3  *****************************************************************************
4  *  Copyright (C) 2018
5  *  Written by SchedMD - Felip Moll
6  *  Based on IPMI plugin by Thomas Cadeau/Yoann Blein @ Bull
7  *
8  *  This file is part of Slurm, a resource management program.
9  *  For details, see <https://slurm.schedmd.com/>.
10  *  Please also read the included file: DISCLAIMER.
11  *
12  *  Slurm is free software; you can redistribute it and/or modify it under
13  *  the terms of the GNU General Public License as published by the Free
14  *  Software Foundation; either version 2 of the License, or (at your option)
15  *  any later version.
16  *
17  *  In addition, as a special exception, the copyright holders give permission
18  *  to link the code of portions of this program with the OpenSSL library under
19  *  certain conditions as described in each individual source file, and
20  *  distribute linked combinations including the two. You must obey the GNU
21  *  General Public License in all respects for all of the code used other than
22  *  OpenSSL. If you modify file(s) with this exception, you may extend this
23  *  exception to your version of the file(s), but you are not obligated to do
24  *  so. If you do not wish to do so, delete this exception statement from your
25  *  version.  If you delete this exception statement from all source files in
26  *  the program, then also delete it here.
27  *
28  *  Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
29  *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
30  *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
31  *  details.
32  *
33  *  You should have received a copy of the GNU General Public License along
34  *  with Slurm; if not, write to the Free Software Foundation, Inc.,
35  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
36  *
37  *  This file is patterned after jobcomp_linux.c, written by Morris Jette and
38  *  Copyright (C) 2002 The Regents of the University of California.
39 \*****************************************************************************/
40 
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <fcntl.h>
44 #include <sys/stat.h>
45 #include <signal.h>
46 #include <math.h>
47 
48 #include "src/common/slurm_xlator.h"
49 #include "src/common/slurm_acct_gather_energy.h"
50 #include "src/common/slurm_protocol_defs.h"
51 #include "src/common/fd.h"
52 
53 #include "src/slurmd/common/proctrack.h"
54 
55 #include "src/slurmd/slurmd/slurmd.h"
56 
57 /*
58  * freeipmi includes for the lib
59  */
60 #include <freeipmi/freeipmi.h>
61 
62 /* These are defined here so when we link with something other than
63  * the slurmctld we will have these symbols defined.  They will get
64  * overwritten when linking with the slurmctld.
65  */
66 #if defined (__APPLE__)
67 extern slurmd_conf_t *conf __attribute__((weak_import));
68 #else
69 slurmd_conf_t *conf = NULL;
70 #endif
71 
72 #define DEFAULT_IPMI_FREQ 30
73 #define DEFAULT_IPMI_USER "USERID"
74 #define DEFAULT_IPMI_PASS "PASSW0RD"
75 #define DEFAULT_IPMI_TIMEOUT 10
76 
77 #define IPMI_VERSION 2		/* Data structure version number */
78 #define MAX_LOG_ERRORS 5	/* Max sensor reading errors log messages */
79 #define XCC_MIN_RES 50        /* Minimum resolution for XCC readings, in ms */
80 #define IPMI_RAW_MAX_ARGS 256 /* Max XCC response length in bytes*/
81 /*FIXME: Investigate which is the OVERFLOW limit for XCC*/
82 #define IPMI_XCC_OVERFLOW INFINITE /* XCC overflows at X */
83 
84 #define XCC_FLAG_NONE 0x00000000
85 #define XCC_FLAG_FAKE 0x00000001
86 #define XCC_EXPECTED_RSPLEN 16 /* Must match cmd_rq[] response expectations */
87 
88 /*
89  * These variables are required by the generic plugin interface.  If they
90  * are not found in the plugin, the plugin loader will ignore it.
91  *
92  * plugin_name - a string giving a human-readable description of the
93  * plugin.  There is no maximum length, but the symbol must refer to
94  * a valid string.
95  *
96  * plugin_type - a string suggesting the type of the plugin or its
97  * applicability to a particular form of data or method of data handling.
98  * If the low-level plugin API is used, the contents of this string are
99  * unimportant and may be anything.  Slurm uses the higher-level plugin
100  * interface which requires this string to be of the form
101  *
102  *	<application>/<method>
103  *
104  * where <application> is a description of the intended application of
105  * the plugin (e.g., "jobacct" for Slurm job completion logging) and <method>
106  * is a description of how this plugin satisfies that application.  Slurm will
107  * only load job completion logging plugins if the plugin_type string has a
108  * prefix of "jobacct/".
109  *
110  * plugin_version - an unsigned 32-bit integer containing the Slurm version
111  * (major.minor.micro combined into a single number).
112  */
113 
114 const char plugin_name[] = "AcctGatherEnergy XCC plugin";
115 const char plugin_type[] = "acct_gather_energy/xcc";
116 const uint32_t plugin_version = SLURM_VERSION_NUMBER;
117 
118 typedef struct slurm_ipmi_conf {
119 	/* Adjust/approach the consumption
120 	 * in function of time between ipmi update and read call */
121 	bool adjustment;
122 	/* authentication type to use
123 	 *   IPMI_MONITORING_AUTHENTICATION_TYPE_NONE                  = 0x00,
124 	 *   IPMI_MONITORING_AUTHENTICATION_TYPE_STRAIGHT_PASSWORD_KEY = 0x01,
125 	 *   IPMI_MONITORING_AUTHENTICATION_TYPE_MD2                   = 0x02,
126 	 *   IPMI_MONITORING_AUTHENTICATION_TYPE_MD5                   = 0x03,
127 	 * Pass < 0 for default of IPMI_MONITORING_AUTHENTICATION_TYPE_MD5*/
128 	uint32_t authentication_type;
129 	/* Cipher suite identifier to determine authentication, integrity,
130 	 * and confidentiality algorithms to use.
131 	 * Supported Cipher Suite IDs
132 	 * (Key: A - Authentication Algorithm
133 	 *       I - Integrity Algorithm
134 	 *       C - Confidentiality Algorithm)
135 	 *   0 - A = None; I = None; C = None
136 	 *   1 - A = HMAC-SHA1; I = None; C = None
137 	 *   2 - A = HMAC-SHA1; I = HMAC-SHA1-96; C = None
138 	 *   3 - A = HMAC-SHA1; I = HMAC-SHA1-96; C = AES-CBC-128
139 	 *   6 - A = HMAC-MD5; I = None; C = None
140 	 *   7 - A = HMAC-MD5; I = HMAC-MD5-128; C = None
141 	 *   8 - A = HMAC-MD5; I = HMAC-MD5-128; C = AES-CBC-128
142 	 *   11 - A = HMAC-MD5; I = MD5-128; C = None
143 	 *   12 - A = HMAC-MD5; I = MD5-128; C = AES-CBC-128
144 	 *   15 - A = HMAC-SHA256; I = None; C = None
145 	 *   16 - A = HMAC-SHA256; I = HMAC-SHA256-128; C = None
146 	 *   17 - A = HMAC-SHA256; I = HMAC-SHA256-128; C = AES-CBC-128
147 	 * Pass < 0 for default.of 3.*/
148 	uint32_t cipher_suite_id;
149 	/* Flag informs the library if in-band driver information should be
150 	 * probed or not.*/
151 	uint32_t disable_auto_probe;
152 	/* Use this specified driver address instead of a probed one.*/
153 	uint32_t driver_address;
154 	/* Use this driver device for the IPMI driver.*/
155 	char *driver_device;
156 	/* Options for IPMI configuration*/
157 	/* Use a specific in-band driver.
158 	 *   IPMI_MONITORING_DRIVER_TYPE_KCS      = 0x00,
159 	 *   IPMI_MONITORING_DRIVER_TYPE_SSIF     = 0x01,
160 	 *   IPMI_MONITORING_DRIVER_TYPE_OPENIPMI = 0x02,
161 	 *   IPMI_MONITORING_DRIVER_TYPE_SUNBMC   = 0x03,
162 	 *    Pass < 0 for default of IPMI_MONITORING_DRIVER_TYPE_KCS.*/
163 	uint32_t driver_type;
164 	uint32_t flags;
165 	/* frequency for ipmi call*/
166 	uint32_t freq;
167 	uint32_t ipmi_flags;
168 	/* BMC password. Pass NULL ptr for default password.  Standard
169 	 * default is the null (e.g. empty) password.  Maximum length of 20
170 	 * bytes.*/
171 	char *password;
172 	/* privilege level to authenticate with.
173 	 * Supported privilege levels:
174 	 *   0 = IPMICONSOLE_PRIVILEGE_USER
175 	 *   1 = IPMICONSOLE_PRIVILEGE_OPERATOR
176 	 *   2 = IPMICONSOLE_PRIVILEGE_ADMIN
177 	 * Pass < 0 for default of IPMICONSOLE_PRIVILEGE_ADMIN.*/
178 	uint32_t privilege_level;
179 	/* Flag informs the library if in-band driver information should be
180 	 * probed or not.*/
181 	/* Indicate the IPMI protocol version to use
182 	 * IPMI_MONITORING_PROTOCOL_VERSION_1_5 = 0x00,
183 	 * IPMI_MONITORING_PROTOCOL_VERSION_2_0 = 0x01,
184 	 * Pass < 0 for default of IPMI_MONITORING_VERSION_1_5.*/
185 	uint32_t protocol_version;
186 	/* Use this register space instead of the probed one.*/
187 	uint32_t register_spacing;
188 	/* Specifies the packet retransmission timeout length in
189 	 * milliseconds.  Pass <= 0 to default 500 (0.5 seconds).*/
190 	uint32_t retransmission_timeout;
191 	/* Specifies the session timeout length in milliseconds.  Pass <= 0
192 	 * to default 60000 (60 seconds).*/
193 	uint32_t session_timeout;
194 	uint8_t target_channel_number;
195 	bool target_channel_number_is_set;
196 	uint8_t target_slave_address;
197 	bool target_slave_address_is_set;
198 	/* Timeout for the ipmi thread */
199 	uint32_t timeout;
200 	/* BMC username. Pass NULL ptr for default username.  Standard
201 	 * default is the null (e.g. empty) username.  Maximum length of 16
202 	 * bytes.*/
203 	char *username;
204 	/* Bitwise OR of flags indicating IPMI implementation changes.  Some
205 	 * BMCs which are non-compliant and may require a workaround flag
206 	 * for correct operation. Pass IPMICONSOLE_WORKAROUND_DEFAULT for
207 	 * default.  Standard default is 0, no modifications to the IPMI
208 	 * protocol.*/
209 	uint32_t workaround_flags;
210 } slurm_ipmi_conf_t;
211 
212 /* Struct to store the raw single data command reading */
213 typedef struct xcc_raw_single_data {
214 	uint16_t fifo_inx;
215 	uint32_t j;
216 	uint16_t mj;
217 	uint16_t ms;
218 	uint32_t s;
219 } xcc_raw_single_data_t;
220 
221 static acct_gather_energy_t xcc_energy;
222 
223 /* LUN, NetFN, CMD, Data[n]*/
224 static uint8_t cmd_rq[8] = { 0x00, 0x3A, 0x32, 4, 2, 0, 0, 0 };
225 static unsigned int cmd_rq_len = 8;
226 
227 
228 static int dataset_id = -1; /* id of the dataset for profile data */
229 
230 static slurm_ipmi_conf_t slurm_ipmi_conf;
231 static uint64_t debug_flags = 0;
232 static bool flag_energy_accounting_shutdown = false;
233 static bool flag_thread_started = false;
234 static bool flag_init = false;
235 
236 static pthread_mutex_t ipmi_mutex = PTHREAD_MUTEX_INITIALIZER;
237 static pthread_cond_t ipmi_cond = PTHREAD_COND_INITIALIZER;
238 static pthread_mutex_t launch_mutex = PTHREAD_MUTEX_INITIALIZER;
239 static pthread_cond_t launch_cond = PTHREAD_COND_INITIALIZER;
240 static pthread_t thread_ipmi_id_launcher = 0;
241 static pthread_t thread_ipmi_id_run = 0;
242 static stepd_step_rec_t *job = NULL;
243 static int context_id = -1;
244 
245 /* Thread scope global vars */
246 __thread ipmi_ctx_t ipmi_ctx = NULL;
247 
_reset_slurm_ipmi_conf(slurm_ipmi_conf_t * slurm_ipmi_conf)248 static void _reset_slurm_ipmi_conf(slurm_ipmi_conf_t *slurm_ipmi_conf)
249 {
250 	if (slurm_ipmi_conf) {
251 		slurm_ipmi_conf->adjustment = false;
252 		slurm_ipmi_conf->authentication_type = 0;
253 		slurm_ipmi_conf->cipher_suite_id = 0;
254 		slurm_ipmi_conf->disable_auto_probe = 0;
255 		slurm_ipmi_conf->driver_address = 0;
256 		xfree(slurm_ipmi_conf->driver_device);
257 		slurm_ipmi_conf->driver_type = NO_VAL;
258 		slurm_ipmi_conf->flags = XCC_FLAG_NONE;
259 		slurm_ipmi_conf->freq = DEFAULT_IPMI_FREQ;
260 		slurm_ipmi_conf->ipmi_flags = IPMI_FLAGS_DEFAULT;
261 		xfree(slurm_ipmi_conf->password);
262 		slurm_ipmi_conf->password = xstrdup(DEFAULT_IPMI_PASS);
263 		slurm_ipmi_conf->privilege_level = 0;
264 		slurm_ipmi_conf->protocol_version = 0;
265 		slurm_ipmi_conf->register_spacing = 0;
266 		slurm_ipmi_conf->retransmission_timeout = 0;
267 		slurm_ipmi_conf->session_timeout = 0;
268 		slurm_ipmi_conf->target_channel_number = 0x00;
269 		slurm_ipmi_conf->target_channel_number_is_set = false;
270 		slurm_ipmi_conf->target_slave_address = 0x20;
271 		slurm_ipmi_conf->target_slave_address_is_set = false;
272 		slurm_ipmi_conf->timeout = DEFAULT_IPMI_TIMEOUT;
273 		xfree(slurm_ipmi_conf->username);
274 		slurm_ipmi_conf->username = xstrdup(DEFAULT_IPMI_USER);
275 		slurm_ipmi_conf->workaround_flags = 0; // See man 8 ipmi-raw
276 	}
277 }
278 
_running_profile(void)279 static int _running_profile(void)
280 {
281 	static bool run = false;
282 	static uint32_t profile_opt = ACCT_GATHER_PROFILE_NOT_SET;
283 
284 	if (profile_opt == ACCT_GATHER_PROFILE_NOT_SET) {
285 		acct_gather_profile_g_get(ACCT_GATHER_PROFILE_RUNNING,
286 					  &profile_opt);
287 		if (profile_opt & ACCT_GATHER_PROFILE_ENERGY)
288 			run = true;
289 	}
290 
291 	return run;
292 }
293 
294 /*
295  * _init_ipmi_config initializes parameters for freeipmi library
296  */
_init_ipmi_config(void)297 static int _init_ipmi_config (void)
298 {
299 	int ret = 0;
300 	unsigned int workaround_flags_mask =
301 		(IPMI_WORKAROUND_FLAGS_INBAND_ASSUME_IO_BASE_ADDRESS
302 		 | IPMI_WORKAROUND_FLAGS_INBAND_SPIN_POLL);
303 
304 	if (ipmi_ctx) {
305 		debug("ipmi_ctx already initialized\n");
306 		return SLURM_SUCCESS;
307 	}
308 
309 	if (!(ipmi_ctx = ipmi_ctx_create())) {
310 		error("ipmi_ctx_create: %s\n", strerror(errno));
311 		goto cleanup;
312 	}
313 
314 	if (getuid() != 0) {
315 		error ("%s: error : must be root to open ipmi devices\n",
316 		       __func__);
317 		goto cleanup;
318 	}
319 
320 	/* XCC OEM commands always require to use in-band communication */
321 	if (((slurm_ipmi_conf.driver_type > 0) &&
322 	     (slurm_ipmi_conf.driver_type != NO_VAL) &&
323 	     (slurm_ipmi_conf.driver_type != IPMI_DEVICE_KCS) &&
324 	     (slurm_ipmi_conf.driver_type != IPMI_DEVICE_SSIF) &&
325 	     (slurm_ipmi_conf.driver_type != IPMI_DEVICE_OPENIPMI) &&
326 	     (slurm_ipmi_conf.driver_type != IPMI_DEVICE_SUNBMC))
327 	    || (slurm_ipmi_conf.workaround_flags & ~workaround_flags_mask)) {
328 		/* IPMI ERROR PARAMETERS */
329 		error ("%s: error: XCC Lenovo plugin only supports in-band communication, incorrect driver type or workaround flags",
330 		       __func__);
331 
332 		debug("slurm_ipmi_conf.driver_type=%u slurm_ipmi_conf.workaround_flags=%u",
333 		      slurm_ipmi_conf.driver_type,
334 		      slurm_ipmi_conf.workaround_flags);
335 
336 		goto cleanup;
337 	}
338 
339 	if (slurm_ipmi_conf.driver_type == NO_VAL) {
340 		if ((ret = ipmi_ctx_find_inband(
341 			     ipmi_ctx,
342 			     NULL,
343 			     slurm_ipmi_conf.disable_auto_probe,
344 			     slurm_ipmi_conf.driver_address,
345 			     slurm_ipmi_conf.register_spacing,
346 			     slurm_ipmi_conf.driver_device,
347 			     slurm_ipmi_conf.workaround_flags,
348 			     slurm_ipmi_conf.ipmi_flags)) <= 0) {
349 			error("%s: error on ipmi_ctx_find_inband: %s",
350 			      __func__, ipmi_ctx_errormsg(ipmi_ctx));
351 
352 			debug("slurm_ipmi_conf.driver_type=%u\n"
353 			      "slurm_ipmi_conf.disable_auto_probe=%u\n"
354 			      "slurm_ipmi_conf.driver_address=%u\n"
355 			      "slurm_ipmi_conf.register_spacing=%u\n"
356 			      "slurm_ipmi_conf.driver_device=%s\n"
357 			      "slurm_ipmi_conf.workaround_flags=%u\n"
358 			      "slurm_ipmi_conf.ipmi_flags=%u",
359 			      slurm_ipmi_conf.driver_type,
360 			      slurm_ipmi_conf.disable_auto_probe,
361 			      slurm_ipmi_conf.driver_address,
362 			      slurm_ipmi_conf.register_spacing,
363 			      slurm_ipmi_conf.driver_device,
364 			      slurm_ipmi_conf.workaround_flags,
365 			      slurm_ipmi_conf.ipmi_flags);
366 
367 			goto cleanup;
368 		}
369 	} else {
370 		if ((ipmi_ctx_open_inband(ipmi_ctx,
371 					  slurm_ipmi_conf.driver_type,
372 					  slurm_ipmi_conf.disable_auto_probe,
373 					  slurm_ipmi_conf.driver_address,
374 					  slurm_ipmi_conf.register_spacing,
375 					  slurm_ipmi_conf.driver_device,
376 					  slurm_ipmi_conf.workaround_flags,
377 					  slurm_ipmi_conf.ipmi_flags) < 0)) {
378 			error ("%s: error on ipmi_ctx_open_inband: %s",
379 			       __func__, ipmi_ctx_errormsg (ipmi_ctx));
380 
381 			debug("slurm_ipmi_conf.driver_type=%u\n"
382 			      "slurm_ipmi_conf.disable_auto_probe=%u\n"
383 			      "slurm_ipmi_conf.driver_address=%u\n"
384 			      "slurm_ipmi_conf.register_spacing=%u\n"
385 			      "slurm_ipmi_conf.driver_device=%s\n"
386 			      "slurm_ipmi_conf.workaround_flags=%u\n"
387 			      "slurm_ipmi_conf.ipmi_flags=%u",
388 			      slurm_ipmi_conf.driver_type,
389 			      slurm_ipmi_conf.disable_auto_probe,
390 			      slurm_ipmi_conf.driver_address,
391 			      slurm_ipmi_conf.register_spacing,
392 			      slurm_ipmi_conf.driver_device,
393 			      slurm_ipmi_conf.workaround_flags,
394 			      slurm_ipmi_conf.ipmi_flags);
395 			goto cleanup;
396 		}
397 	}
398 
399 	if (slurm_ipmi_conf.target_channel_number_is_set
400 	    || slurm_ipmi_conf.target_slave_address_is_set) {
401 		if (ipmi_ctx_set_target(
402 			    ipmi_ctx,
403 			    slurm_ipmi_conf.target_channel_number_is_set ?
404 			    &slurm_ipmi_conf.target_channel_number : NULL,
405 			    slurm_ipmi_conf.target_slave_address_is_set ?
406 			    &slurm_ipmi_conf.target_slave_address : NULL) < 0) {
407 			error ("%s: error on ipmi_ctx_set_target: %s",
408 			       __func__, ipmi_ctx_errormsg (ipmi_ctx));
409 			goto cleanup;
410 		}
411 	}
412 
413 	return SLURM_SUCCESS;
414 
415 cleanup:
416 	ipmi_ctx_close(ipmi_ctx);
417 	ipmi_ctx_destroy(ipmi_ctx);
418 	return SLURM_ERROR;
419 }
420 
421 /*
422  * _read_ipmi_values read the Power sensor and update last_update_watt and times
423  */
_read_ipmi_values(void)424 static xcc_raw_single_data_t *_read_ipmi_values(void)
425 {
426 	xcc_raw_single_data_t *xcc_reading;
427 	uint8_t buf_rs[IPMI_RAW_MAX_ARGS];
428 	int rs_len = 0;
429 
430 	if (!IPMI_NET_FN_RQ_VALID(cmd_rq[1])) {
431                 error("Invalid netfn value\n");
432 		return NULL;
433 	}
434 
435         rs_len = ipmi_cmd_raw(ipmi_ctx,
436                               cmd_rq[0], // Lun (logical unit number)
437                               cmd_rq[1], // Net Function
438                               &cmd_rq[2], // Command number + request data
439                               cmd_rq_len - 2, // Length (in bytes)
440                               &buf_rs, // response buffer
441                               IPMI_RAW_MAX_ARGS // max response length
442                 );
443 
444         debug3("ipmi_cmd_raw: %s", ipmi_ctx_errormsg(ipmi_ctx));
445 
446         if (rs_len != XCC_EXPECTED_RSPLEN) {
447 		error("Invalid ipmi response length for XCC raw command: "
448 		      "%d bytes, expected %d", rs_len, XCC_EXPECTED_RSPLEN);
449 		return NULL;
450 	}
451 
452 	/* Due to memory alineation we must copy the data from the buffer */
453 	xcc_reading = xmalloc(sizeof(xcc_raw_single_data_t));
454 	if (slurm_ipmi_conf.flags & XCC_FLAG_FAKE) {
455 		static uint32_t fake_past_read = 10774496;
456 		static bool fake_inited = false;
457 
458 		if (!fake_inited) {
459 			srand((unsigned) time(NULL));
460 			fake_inited = true;
461 		}
462 
463 		xcc_reading->fifo_inx = 0;
464 		// Fake metric j
465 		xcc_reading->j = fake_past_read + 550 + rand() % 200;
466 		fake_past_read = xcc_reading->j;
467 		xcc_reading->mj = 0;
468 		xcc_reading->s = time(NULL); //Fake metric timestamp
469 		xcc_reading->ms = 0;
470 	} else {
471 		memcpy(&xcc_reading->fifo_inx, buf_rs+2, 2);
472 		memcpy(&xcc_reading->j, buf_rs+4, 4);
473 		memcpy(&xcc_reading->mj, buf_rs+8, 2);
474 		memcpy(&xcc_reading->s, buf_rs+10, 4);
475 		memcpy(&xcc_reading->ms, buf_rs+14, 2);
476 	}
477 
478 	return xcc_reading;
479 }
480 
481 /*
482  * _thread_update_node_energy calls _read_ipmi_values and updates all values
483  * for node consumption
484  */
_thread_update_node_energy(void)485 static int _thread_update_node_energy(void)
486 {
487 	xcc_raw_single_data_t *xcc_raw;
488 	static uint16_t overflows = 0; /* Number of overflows of the counter */
489         int elapsed = 0;
490 	static uint64_t first_consumed_energy = 0;
491 
492 	xcc_raw = _read_ipmi_values();
493 
494 	if (!xcc_raw) {
495 		error("%s could not read XCC ipmi values", __func__);
496 		return SLURM_ERROR;
497 	}
498 
499 	if (!xcc_energy.poll_time) {
500 		/*
501 		 * First number from the slurmd.  We will figure out the usage
502 		 * by subtracting this each time.
503 		 */
504 		first_consumed_energy =	xcc_raw->j;
505 		xcc_energy.consumed_energy = 0;
506 		xcc_energy.base_consumed_energy = 0;
507 		xcc_energy.previous_consumed_energy = 0;
508 		xcc_energy.ave_watts = 0;
509 	} else {
510 		xcc_energy.previous_consumed_energy =
511 			xcc_energy.consumed_energy;
512 
513 		/* Detect first overflow */
514 		if (!overflows && xcc_raw->j < xcc_energy.consumed_energy) {
515 			xcc_energy.consumed_energy = IPMI_XCC_OVERFLOW -
516 				                     first_consumed_energy +
517 				                     xcc_raw->j;
518 			overflows++;
519 		} else if (!overflows &&
520 			   (xcc_raw->j >= xcc_energy.consumed_energy)) {
521 			xcc_energy.consumed_energy = xcc_raw->j;
522 			xcc_energy.consumed_energy -= first_consumed_energy;
523 		} else if (overflows) {
524 			/*
525 			 * Offset = First overflow + consecutive overflows
526 			 * If it happens that the offset + xcc_raw->j is less
527 			 * than the past consumed energy, it means that we
528 			 * overflowed and must add a new overflow to the count
529 			 */
530 			uint64_t offset = IPMI_XCC_OVERFLOW -
531 				first_consumed_energy +
532 				(IPMI_XCC_OVERFLOW * (overflows-1));
533 
534 			if ((offset + xcc_raw->j) <
535 			    xcc_energy.consumed_energy) {
536 				overflows++;
537 				xcc_energy.consumed_energy = offset +
538 					IPMI_XCC_OVERFLOW + xcc_raw->j;
539 			} else {
540 				xcc_energy.consumed_energy += offset +
541 					xcc_raw->j;
542 			}
543 		}
544 
545 		xcc_energy.base_consumed_energy =
546 			xcc_energy.consumed_energy -
547 			xcc_energy.previous_consumed_energy;
548 
549 		elapsed = xcc_raw->s - xcc_energy.poll_time;
550 	}
551 
552 	xcc_energy.poll_time = xcc_raw->s;
553 
554 	xfree(xcc_raw);
555 
556 	if (elapsed && xcc_energy.base_consumed_energy) {
557 		static uint64_t readings = 0;
558 		xcc_energy.current_watts =
559 			round((double)xcc_energy.base_consumed_energy /
560 			      (double)elapsed);
561 
562 		/* ave_watts is used as TresUsageOutAve (AvePower) */
563 		xcc_energy.ave_watts = ((xcc_energy.ave_watts * readings) +
564 					 xcc_energy.current_watts) /
565 					 (readings + 1);
566 		readings++;
567 	}
568 
569 	if (debug_flags & DEBUG_FLAG_ENERGY) {
570 		info("%s: XCC current_watts: %u consumed energy last interval: %"PRIu64"(current reading %"PRIu64") Joules, elapsed time: %u Seconds, first read energy counter val: %"PRIu64" ave watts: %u",
571 		     __func__,
572 		     xcc_energy.current_watts,
573 		     xcc_energy.base_consumed_energy,
574 		     xcc_energy.consumed_energy,
575 		     elapsed,
576 		     first_consumed_energy,
577 		     xcc_energy.ave_watts);
578 	}
579 	return SLURM_SUCCESS;
580 }
581 
582 /*
583  * _thread_init initializes values and conf for the ipmi thread
584  */
_thread_init(void)585 static int _thread_init(void)
586 {
587 	static bool first = true;
588 	static int first_init = SLURM_ERROR;
589 
590 	/*
591 	 * If we are here we are a new slurmd thread serving
592 	 * a request. In that case we must init a new ipmi_ctx,
593 	 * update the sensor and return because the freeipmi lib
594 	 * context cannot be shared among threads.
595 	 */
596 	if (_init_ipmi_config() != SLURM_SUCCESS)
597 		if (debug_flags & DEBUG_FLAG_ENERGY) {
598 			info("%s thread init error on _init_ipmi_config()",
599 			     plugin_name);
600 			goto cleanup;
601 		}
602 
603 	if (!first)
604 		return first_init;
605 
606 	first = false;
607 
608 	if (debug_flags & DEBUG_FLAG_ENERGY)
609 		info("%s thread init success", plugin_name);
610 
611 	first_init = SLURM_SUCCESS;
612 	return SLURM_SUCCESS;
613 cleanup:
614 	info("%s thread init error", plugin_name);
615 	first_init = SLURM_ERROR;
616 
617 	return SLURM_ERROR;
618 }
619 
_ipmi_send_profile(void)620 static int _ipmi_send_profile(void)
621 {
622 	/*
623 	 * This enum is directly related to the xcc_labels below.  If this is
624 	 * changed it should be altered as well.
625 	 */
626 	enum {
627 		XCC_ENERGY = 0,
628 		XCC_CURR_POWER,
629 		XCC_LABEL_CNT
630 	};
631 
632 	static char *xcc_labels[] = { "Energy",
633 				      "CurrPower",
634 				      NULL };
635 
636 	uint16_t i;
637 	uint64_t data[XCC_LABEL_CNT];
638 
639 	if (!_running_profile())
640 		return SLURM_SUCCESS;
641 
642 	if (dataset_id < 0) {
643 		acct_gather_profile_dataset_t dataset[XCC_LABEL_CNT + 1];
644 		for (i = 0; i < XCC_LABEL_CNT; i++) {
645 			dataset[i].name = xcc_labels[i];
646 			dataset[i].type = PROFILE_FIELD_UINT64;
647 		}
648 		dataset[i].name = NULL;
649 		dataset[i].type = PROFILE_FIELD_NOT_SET;
650 
651 		dataset_id = acct_gather_profile_g_create_dataset(
652 			"Energy", NO_PARENT, dataset);
653 
654 		if (debug_flags & DEBUG_FLAG_ENERGY)
655 			debug("Energy: dataset created (id = %d)", dataset_id);
656 		if (dataset_id == SLURM_ERROR) {
657 			error("Energy: Failed to create the dataset for IPMI");
658 			return SLURM_ERROR;
659 		}
660 	}
661 
662 	/* pack an array of uint64_t with current power of sensors */
663 	memset(data, 0, sizeof(data));
664 	data[XCC_ENERGY] = xcc_energy.base_consumed_energy;
665 	data[XCC_CURR_POWER] = xcc_energy.current_watts;
666 	if (debug_flags & DEBUG_FLAG_PROFILE)
667 		for (i = 0; i < XCC_LABEL_CNT; i++)
668 			info("PROFILE-Energy: %s=%"PRIu64,
669 			     xcc_labels[i], data[i]);
670 
671 	return acct_gather_profile_g_add_sample_data(
672 		dataset_id, (void *)data, xcc_energy.poll_time);
673 }
674 
675 
676 /*
677  * _thread_ipmi_run is the thread calling ipmi and launching _thread_ipmi_write
678  */
_thread_ipmi_run(void * no_data)679 static void *_thread_ipmi_run(void *no_data)
680 {
681 // need input (attr)
682 	struct timeval tvnow;
683 	struct timespec abs;
684 
685 	flag_energy_accounting_shutdown = false;
686 	if (debug_flags & DEBUG_FLAG_ENERGY)
687 		info("ipmi-thread: launched");
688 
689 	(void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
690 	(void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
691 
692 	slurm_mutex_lock(&ipmi_mutex);
693 	if (_thread_init() != SLURM_SUCCESS) {
694 		if (debug_flags & DEBUG_FLAG_ENERGY)
695 			info("ipmi-thread: aborted");
696 		slurm_mutex_unlock(&ipmi_mutex);
697 
698 		slurm_cond_signal(&launch_cond);
699 
700 		return NULL;
701 	}
702 
703 	(void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
704 
705 	slurm_mutex_unlock(&ipmi_mutex);
706 	flag_thread_started = true;
707 
708 	slurm_cond_signal(&launch_cond);
709 
710 	/* setup timer */
711 	gettimeofday(&tvnow, NULL);
712 	abs.tv_sec = tvnow.tv_sec;
713 	abs.tv_nsec = tvnow.tv_usec * 1000;
714 
715 	//loop until slurm stop
716 	while (!flag_energy_accounting_shutdown) {
717 		slurm_mutex_lock(&ipmi_mutex);
718 
719 		_thread_update_node_energy();
720 
721 		/* Sleep until the next time. */
722 		abs.tv_sec += slurm_ipmi_conf.freq;
723 		slurm_cond_timedwait(&ipmi_cond, &ipmi_mutex, &abs);
724 
725 		slurm_mutex_unlock(&ipmi_mutex);
726 	}
727 
728 	if (debug_flags & DEBUG_FLAG_ENERGY)
729 		info("ipmi-thread: ended");
730 
731 	return NULL;
732 }
733 
_thread_launcher(void * no_data)734 static void *_thread_launcher(void *no_data)
735 {
736 	//what arg would countain? frequency, socket?
737 	struct timeval tvnow;
738 	struct timespec abs;
739 
740 	slurm_thread_create(&thread_ipmi_id_run, _thread_ipmi_run, NULL);
741 
742 	/* setup timer */
743 	gettimeofday(&tvnow, NULL);
744 	abs.tv_sec = tvnow.tv_sec + slurm_ipmi_conf.timeout;
745 	abs.tv_nsec = tvnow.tv_usec * 1000;
746 
747 	slurm_mutex_lock(&launch_mutex);
748 	slurm_cond_timedwait(&launch_cond, &launch_mutex, &abs);
749 	slurm_mutex_unlock(&launch_mutex);
750 
751 	if (!flag_thread_started) {
752 		error("%s threads failed to start in a timely manner",
753 		      plugin_name);
754 
755 		flag_energy_accounting_shutdown = true;
756 
757 		/*
758 		 * It is a known thing we can hang up on IPMI calls cancel if
759 		 * we must.
760 		 */
761 		pthread_cancel(thread_ipmi_id_run);
762 
763 		/*
764 		 * Unlock just to make sure since we could have canceled the
765 		 * thread while in the lock.
766 		 */
767 		slurm_mutex_unlock(&ipmi_mutex);
768 	}
769 
770 	return NULL;
771 }
772 
_get_joules_task(uint16_t delta)773 static int _get_joules_task(uint16_t delta)
774 {
775 	acct_gather_energy_t *new = NULL;
776 	uint16_t sensor_cnt = 0;
777 	static bool first = true;
778 	static uint64_t first_consumed_energy = 0;
779 
780 	xassert(context_id != -1);
781 
782         /*
783 	 * 'delta' parameter means "use cache" if data is newer than delta
784 	 * seconds ago, otherwise just inquiry ipmi again.
785 	 */
786 	if (slurm_get_node_energy(NULL, context_id, delta, &sensor_cnt, &new)) {
787 		error("%s: can't get info from slurmd", __func__);
788 		return SLURM_ERROR;
789 	}
790 
791 	if (sensor_cnt != 1) {
792 		error("%s: received %u xcc sensors expected 1",
793 		      __func__, sensor_cnt);
794 		acct_gather_energy_destroy(new);
795 		return SLURM_ERROR;
796 	}
797 
798 	if (first) {
799 		if (!new->consumed_energy) {
800 			info("we got a blank");
801 			goto end_it;
802 		}
803 
804 		/*
805 		 * First number from the slurmd.  We will figure out the usage
806 		 * by subtracting this each time.
807 		 */
808 		first_consumed_energy = new->consumed_energy;
809 		first = false;
810 	}
811 
812 	new->consumed_energy -= first_consumed_energy;
813 	new->previous_consumed_energy = xcc_energy.consumed_energy;
814 	new->base_consumed_energy =
815 		new->consumed_energy - new->previous_consumed_energy;
816 
817 	memcpy(&xcc_energy, new, sizeof(acct_gather_energy_t));
818 
819 	if (debug_flags & DEBUG_FLAG_ENERGY)
820 		info("%s: consumed %"PRIu64" Joules "
821 		     "(received %"PRIu64"(%u watts) from slurmd)",
822 		     __func__,
823 		     xcc_energy.consumed_energy,
824 		     xcc_energy.base_consumed_energy,
825 		     xcc_energy.current_watts);
826 
827 //	new->previous_consumed_energy = xcc_energy.consumed_energy;
828 end_it:
829 	acct_gather_energy_destroy(new);
830 
831 	return SLURM_SUCCESS;
832 }
833 
834 /*
835  * init() is called when the plugin is loaded, before any other functions
836  * are called.  Put global initialization here.
837  */
init(void)838 extern int init(void)
839 {
840 	debug_flags = slurm_get_debug_flags();
841 
842 	memset(&xcc_energy, 0, sizeof(acct_gather_energy_t));
843 
844 	return SLURM_SUCCESS;
845 }
846 
fini(void)847 extern int fini(void)
848 {
849 	if (!running_in_slurmdstepd())
850 		return SLURM_SUCCESS;
851 
852 	flag_energy_accounting_shutdown = true;
853 
854 	/* clean up the launch thread */
855 	slurm_cond_signal(&launch_cond);
856 
857 	if (thread_ipmi_id_launcher)
858 		pthread_join(thread_ipmi_id_launcher, NULL);
859 
860 	/* clean up the run thread */
861 	slurm_cond_signal(&ipmi_cond);
862 
863 	slurm_mutex_lock(&ipmi_mutex);
864 
865 	if (ipmi_ctx)
866 		ipmi_ctx_destroy(ipmi_ctx);
867 	_reset_slurm_ipmi_conf(&slurm_ipmi_conf);
868 
869 	slurm_mutex_unlock(&ipmi_mutex);
870 
871 	if (thread_ipmi_id_run)
872 		pthread_join(thread_ipmi_id_run, NULL);
873 
874 	return SLURM_SUCCESS;
875 }
876 
acct_gather_energy_p_update_node_energy(void)877 extern int acct_gather_energy_p_update_node_energy(void)
878 {
879 	int rc = SLURM_SUCCESS;
880 	xassert(running_in_slurmdstepd());
881 
882 	return rc;
883 }
884 
acct_gather_energy_p_get_data(enum acct_energy_type data_type,void * data)885 extern int acct_gather_energy_p_get_data(enum acct_energy_type data_type,
886 					 void *data)
887 {
888 	int rc = SLURM_SUCCESS;
889 	acct_gather_energy_t *energy = (acct_gather_energy_t *)data;
890 	time_t *last_poll = (time_t *)data;
891 	uint16_t *sensor_cnt = (uint16_t *)data;
892 
893 	xassert(running_in_slurmdstepd());
894 
895 	switch (data_type) {
896 	case ENERGY_DATA_NODE_ENERGY_UP:
897 	case ENERGY_DATA_JOULES_TASK:
898 		slurm_mutex_lock(&ipmi_mutex);
899 		if (running_in_slurmd()) {
900 			if (_thread_init() == SLURM_SUCCESS)
901 				_thread_update_node_energy();
902 		} else {
903 			_get_joules_task(10);
904 		}
905 		memcpy(energy, &xcc_energy, sizeof(acct_gather_energy_t));
906 		slurm_mutex_unlock(&ipmi_mutex);
907 		break;
908 	case ENERGY_DATA_NODE_ENERGY:
909 	case ENERGY_DATA_STRUCT:
910 		slurm_mutex_lock(&ipmi_mutex);
911 		memcpy(energy, &xcc_energy, sizeof(acct_gather_energy_t));
912 		slurm_mutex_unlock(&ipmi_mutex);
913 		break;
914 	case ENERGY_DATA_LAST_POLL:
915 		slurm_mutex_lock(&ipmi_mutex);
916 		*last_poll = xcc_energy.poll_time;
917 		slurm_mutex_unlock(&ipmi_mutex);
918 		break;
919 	case ENERGY_DATA_SENSOR_CNT:
920 		*sensor_cnt = 1;
921 		break;
922 	default:
923 		error("acct_gather_energy_p_get_data: unknown enum %d",
924 		      data_type);
925 		rc = SLURM_ERROR;
926 		break;
927 	}
928 	return rc;
929 }
930 
acct_gather_energy_p_set_data(enum acct_energy_type data_type,void * data)931 extern int acct_gather_energy_p_set_data(enum acct_energy_type data_type,
932 					 void *data)
933 {
934 	int rc = SLURM_SUCCESS;
935 	int *delta = (int *)data;
936 
937 	xassert(running_in_slurmdstepd());
938 
939 	switch (data_type) {
940 	case ENERGY_DATA_RECONFIG:
941 		debug_flags = slurm_get_debug_flags();
942 		break;
943 	case ENERGY_DATA_PROFILE:
944 		slurm_mutex_lock(&ipmi_mutex);
945 		_get_joules_task(*delta);
946 		_ipmi_send_profile();
947 		slurm_mutex_unlock(&ipmi_mutex);
948 		break;
949 	case ENERGY_DATA_STEP_PTR:
950 		/* set global job if needed later */
951 		job = (stepd_step_rec_t *)data;
952 		break;
953 	default:
954 		error("acct_gather_energy_p_set_data: unknown enum %d",
955 		      data_type);
956 		rc = SLURM_ERROR;
957 		break;
958 	}
959 	return rc;
960 }
961 
acct_gather_energy_p_conf_options(s_p_options_t ** full_options,int * full_options_cnt)962 extern void acct_gather_energy_p_conf_options(s_p_options_t **full_options,
963 					      int *full_options_cnt)
964 {
965 //	s_p_options_t *full_options_ptr;
966 	s_p_options_t options[] = {
967 		{"EnergyIPMIAuthenticationType", S_P_UINT32},
968 		{"EnergyIPMICalcAdjustment", S_P_BOOLEAN},
969 		{"EnergyIPMICipherSuiteId", S_P_UINT32},
970 		{"EnergyIPMIDisableAutoProbe", S_P_UINT32},
971 		{"EnergyIPMIDriverAddress", S_P_UINT32},
972 		{"EnergyIPMIDriverDevice", S_P_STRING},
973 		{"EnergyIPMIDriverType", S_P_UINT32},
974 		{"EnergyIPMIFrequency", S_P_UINT32},
975 		{"EnergyIPMIPassword", S_P_STRING},
976 		{"EnergyIPMIPrivilegeLevel", S_P_UINT32},
977 		{"EnergyIPMIProtocolVersion", S_P_UINT32},
978 		{"EnergyIPMIRegisterSpacing", S_P_UINT32},
979 		{"EnergyIPMIRetransmissionTimeout", S_P_UINT32},
980 		{"EnergyIPMISessionTimeout", S_P_UINT32},
981 		{"EnergyIPMITimeout", S_P_UINT32},
982 		{"EnergyIPMIUsername", S_P_STRING},
983 		{"EnergyIPMIWorkaroundFlags", S_P_UINT32},
984 		{"EnergyXCCFake", S_P_BOOLEAN},
985 		{NULL} };
986 
987 	transfer_s_p_options(full_options, options, full_options_cnt);
988 }
989 
acct_gather_energy_p_conf_set(int context_id_in,s_p_hashtbl_t * tbl)990 extern void acct_gather_energy_p_conf_set(int context_id_in,
991 					  s_p_hashtbl_t *tbl)
992 {
993 	bool tmp_bool;
994 
995 	/* Set initial values */
996 	_reset_slurm_ipmi_conf(&slurm_ipmi_conf);
997 
998 	if (tbl) {
999 		/* ipmi initialization parameters */
1000 		s_p_get_uint32(&slurm_ipmi_conf.authentication_type,
1001 			       "EnergyIPMIAuthenticationType", tbl);
1002 		(void) s_p_get_boolean(&(slurm_ipmi_conf.adjustment),
1003 				       "EnergyIPMICalcAdjustment", tbl);
1004 		s_p_get_uint32(&slurm_ipmi_conf.cipher_suite_id,
1005 			       "EnergyIPMICipherSuiteId", tbl);
1006 		s_p_get_uint32(&slurm_ipmi_conf.disable_auto_probe,
1007 			       "EnergyIPMIDisableAutoProbe", tbl);
1008 		s_p_get_uint32(&slurm_ipmi_conf.driver_address,
1009 			       "EnergyIPMIDriverAddress", tbl);
1010 		s_p_get_string(&slurm_ipmi_conf.driver_device,
1011 			       "EnergyIPMIDriverDevice", tbl);
1012 		s_p_get_uint32(&slurm_ipmi_conf.driver_type,
1013 			       "EnergyIPMIDriverType", tbl);
1014 		s_p_get_uint32(&slurm_ipmi_conf.freq,
1015 			       "EnergyIPMIFrequency", tbl);
1016 		if ((int)slurm_ipmi_conf.freq <= 0)
1017 			fatal("EnergyIPMIFrequency must be a positive integer in acct_gather.conf.");
1018 		s_p_get_string(&slurm_ipmi_conf.password,
1019 			       "EnergyIPMIPassword", tbl);
1020 		s_p_get_uint32(&slurm_ipmi_conf.privilege_level,
1021 			       "EnergyIPMIPrivilegeLevel", tbl);
1022 		s_p_get_uint32(&slurm_ipmi_conf.protocol_version,
1023 			       "EnergyIPMIProtocolVersion", tbl);
1024 		s_p_get_uint32(&slurm_ipmi_conf.register_spacing,
1025 			       "EnergyIPMIRegisterSpacing", tbl);
1026 		s_p_get_uint32(&slurm_ipmi_conf.retransmission_timeout,
1027 			       "EnergyIPMIRetransmissionTimeout", tbl);
1028 		s_p_get_uint32(&slurm_ipmi_conf.session_timeout,
1029 			       "EnergyIPMISessionTimeout", tbl);
1030 		s_p_get_uint32(&slurm_ipmi_conf.timeout,
1031 			       "EnergyIPMITimeout", tbl);
1032 		s_p_get_string(&slurm_ipmi_conf.username,
1033 			       "EnergyIPMIUsername", tbl);
1034 		s_p_get_uint32(&slurm_ipmi_conf.workaround_flags,
1035 			       "EnergyIPMIWorkaroundFlags", tbl);
1036 		(void) s_p_get_boolean(&tmp_bool, "EnergyXCCFake", tbl);
1037 		if (tmp_bool) {
1038 			slurm_ipmi_conf.flags |= XCC_FLAG_FAKE;
1039 			/*
1040 			 * This is just to do a random query and get error if
1041 			 * ipmi is not initialized
1042 			 */
1043 			cmd_rq[0] = 0x00;
1044 			cmd_rq[1] = 0x04;
1045 			cmd_rq[2] = 0x2d;
1046 			cmd_rq[3] = 0x36;
1047 			cmd_rq_len = 4;
1048 		}
1049 	}
1050 
1051 	context_id = context_id_in;
1052 
1053 	if (!running_in_slurmdstepd())
1054 		return;
1055 
1056 	if (!flag_init) {
1057 		flag_init = true;
1058 		if (running_in_slurmd()) {
1059 			slurm_thread_create(&thread_ipmi_id_launcher,
1060 					    _thread_launcher, NULL);
1061 			if (debug_flags & DEBUG_FLAG_ENERGY)
1062 				info("%s thread launched", plugin_name);
1063 		} else
1064 			_get_joules_task(0);
1065 	}
1066 
1067 	verbose("%s loaded", plugin_name);
1068 }
1069 
acct_gather_energy_p_conf_values(List * data)1070 extern void acct_gather_energy_p_conf_values(List *data)
1071 {
1072 	config_key_pair_t *key_pair;
1073 
1074 	xassert(*data);
1075 
1076 	key_pair = xmalloc(sizeof(config_key_pair_t));
1077 	key_pair->name = xstrdup("EnergyIPMIAuthenticationType");
1078 	key_pair->value = xstrdup_printf("%u",
1079 					 slurm_ipmi_conf.authentication_type);
1080 	list_append(*data, key_pair);
1081 
1082 	key_pair = xmalloc(sizeof(config_key_pair_t));
1083 	key_pair->name = xstrdup("EnergyIPMICalcAdjustment");
1084 	key_pair->value = xstrdup(slurm_ipmi_conf.adjustment
1085 				  ? "Yes" : "No");
1086 	list_append(*data, key_pair);
1087 
1088 	key_pair = xmalloc(sizeof(config_key_pair_t));
1089 	key_pair->name = xstrdup("EnergyIPMICipherSuiteId");
1090 	key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.cipher_suite_id);
1091 	list_append(*data, key_pair);
1092 
1093 	key_pair = xmalloc(sizeof(config_key_pair_t));
1094 	key_pair->name = xstrdup("EnergyIPMIDisableAutoProbe");
1095 	key_pair->value = xstrdup_printf("%u",
1096 					 slurm_ipmi_conf.disable_auto_probe);
1097 	list_append(*data, key_pair);
1098 
1099 	key_pair = xmalloc(sizeof(config_key_pair_t));
1100 	key_pair->name = xstrdup("EnergyIPMIDriverAddress");
1101 	key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.driver_address);
1102 	list_append(*data, key_pair);
1103 
1104 	key_pair = xmalloc(sizeof(config_key_pair_t));
1105 	key_pair->name = xstrdup("EnergyIPMIDriverDevice");
1106 	key_pair->value = xstrdup(slurm_ipmi_conf.driver_device);
1107 	list_append(*data, key_pair);
1108 
1109 	key_pair = xmalloc(sizeof(config_key_pair_t));
1110 	key_pair->name = xstrdup("EnergyIPMIDriverType");
1111 	key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.driver_type);
1112 	list_append(*data, key_pair);
1113 
1114 	key_pair = xmalloc(sizeof(config_key_pair_t));
1115 	key_pair->name = xstrdup("EnergyIPMIFrequency");
1116 	key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.freq);
1117 	list_append(*data, key_pair);
1118 
1119 	/*
1120 	 * Don't give out the password
1121 	 * key_pair = xmalloc(sizeof(config_key_pair_t));
1122 	 * key_pair->name = xstrdup("EnergyIPMIPassword");
1123          * key_pair->value = xstrdup(slurm_ipmi_conf.password);
1124 	 * list_append(*data, key_pair);
1125 	 */
1126 
1127 	key_pair = xmalloc(sizeof(config_key_pair_t));
1128 	key_pair->name = xstrdup("EnergyIPMIPrivilegeLevel");
1129 	key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.privilege_level);
1130 	list_append(*data, key_pair);
1131 
1132 	key_pair = xmalloc(sizeof(config_key_pair_t));
1133 	key_pair->name = xstrdup("EnergyIPMIProtocolVersion");
1134 	key_pair->value = xstrdup_printf("%u",
1135 					 slurm_ipmi_conf.protocol_version);
1136 	list_append(*data, key_pair);
1137 
1138 	key_pair = xmalloc(sizeof(config_key_pair_t));
1139 	key_pair->name = xstrdup("EnergyIPMIRegisterSpacing");
1140 	key_pair->value = xstrdup_printf("%u",
1141 					 slurm_ipmi_conf.register_spacing);
1142 	list_append(*data, key_pair);
1143 
1144 	key_pair = xmalloc(sizeof(config_key_pair_t));
1145 	key_pair->name = xstrdup("EnergyIPMIRetransmissionTimeout");
1146 	key_pair->value = xstrdup_printf(
1147 		"%u", slurm_ipmi_conf.retransmission_timeout);
1148 	list_append(*data, key_pair);
1149 
1150 	key_pair = xmalloc(sizeof(config_key_pair_t));
1151 	key_pair->name = xstrdup("EnergyIPMISessionTimeout");
1152 	key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.session_timeout);
1153 	list_append(*data, key_pair);
1154 
1155 	key_pair = xmalloc(sizeof(config_key_pair_t));
1156 	key_pair->name = xstrdup("EnergyIPMITimeout");
1157 	key_pair->value = xstrdup_printf("%u", slurm_ipmi_conf.timeout);
1158 	list_append(*data, key_pair);
1159 
1160 	key_pair = xmalloc(sizeof(config_key_pair_t));
1161 	key_pair->name = xstrdup("EnergyIPMIUsername");
1162 	key_pair->value = xstrdup(slurm_ipmi_conf.username);
1163 	list_append(*data, key_pair);
1164 
1165 	key_pair = xmalloc(sizeof(config_key_pair_t));
1166 	key_pair->name = xstrdup("EnergyIPMIWorkaroundFlags");
1167 	key_pair->value = xstrdup_printf(
1168 		"%u", slurm_ipmi_conf.workaround_flags);
1169 	list_append(*data, key_pair);
1170 
1171 	return;
1172 
1173 }
1174