| /*****************************************************************************\ | 
 |  *  bb_info.c - Functions related to Burst Buffer display mode of sview. | 
 |  ***************************************************************************** | 
 |  *  Copyright (C) SchedMD LLC. | 
 |  *  Written by Nathan Yee <nyee32@shedmd.com> | 
 |  * | 
 |  *  This file is part of Slurm, a resource management program. | 
 |  *  For details, see <https://slurm.schedmd.com/>. | 
 |  *  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. | 
 |  * | 
 |  *  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. | 
 | \*****************************************************************************/ | 
 |  | 
 | #include "src/common/uid.h" | 
 | #include "src/sview/sview.h" | 
 | #include "src/common/parse_time.h" | 
 | #include "src/common/proc_args.h" | 
 | #include "src/common/strlcpy.h" | 
 |  | 
 | #define _DEBUG 0 | 
 |  | 
 | /* Collection of data for printing reports. Like data is combined here */ | 
 | typedef struct { | 
 | 	char *		bb_name; | 
 | 	burst_buffer_resv_t *bb_ptr; | 
 | 	int		color_inx; | 
 | 	GtkTreeIter	iter_ptr; | 
 | 	bool		iter_set; | 
 | 	char *		plugin; | 
 | 	int		pos; | 
 | } sview_bb_info_t; | 
 |  | 
 | enum { | 
 | 	EDIT_REMOVE = 1, | 
 | 	EDIT_EDIT | 
 | }; | 
 |  | 
 | /* These need to be in alpha order (except POS and CNT) */ | 
 | enum { | 
 | 	SORTID_POS = POS_LOC, | 
 | 	SORTID_ACCOUNT, | 
 | 	SORTID_COLOR, | 
 | 	SORTID_COLOR_INX, | 
 | 	SORTID_CREATE_TIME, | 
 | 	SORTID_NAME, | 
 | 	SORTID_PARTITION, | 
 | 	SORTID_PLUGIN, | 
 | 	SORTID_POOL, | 
 | 	SORTID_QOS, | 
 | 	SORTID_SIZE, | 
 | 	SORTID_STATE, | 
 | 	SORTID_UPDATED, | 
 | 	SORTID_USERID, | 
 | 	SORTID_CNT | 
 | }; | 
 |  | 
 | /* extra field here is for choosing the type of edit you that will | 
 |  * take place.  If you choose EDIT_MODEL (means only display a set of | 
 |  * known options) create it in function create_model_*. | 
 |  */ | 
 |  | 
 | /*these are the settings to apply for the user | 
 |  * on the first startup after a fresh slurm install. | 
 |  * s/b a const probably*/ | 
 | static char *_initial_page_opts = "Name/JobID,Pool,Size,State,StateTime,UserID"; | 
 |  | 
 | static display_data_t display_data_bb[] = { | 
 | 	{G_TYPE_INT, SORTID_POS, NULL, false, EDIT_NONE, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_STRING, SORTID_PLUGIN, "Plugin", false, EDIT_NONE, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_STRING, SORTID_NAME, "Name/JobID", false, EDIT_NONE, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_STRING, SORTID_COLOR, NULL, true, EDIT_COLOR, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_INT, SORTID_COLOR_INX, NULL, false, EDIT_NONE, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_STRING, SORTID_ACCOUNT, "Account", false, EDIT_NONE, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_STRING, SORTID_CREATE_TIME, "CreateTime", false, EDIT_NONE, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_STRING, SORTID_PARTITION, "Partition", false, EDIT_NONE, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_STRING, SORTID_POOL, "Pool", false, EDIT_NONE, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_STRING, SORTID_QOS, "QOS", false, EDIT_NONE, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_STRING, SORTID_SIZE, "Size", false, EDIT_NONE, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_STRING, SORTID_STATE, "State", false, EDIT_NONE, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_INT, SORTID_UPDATED, NULL, false, EDIT_NONE, refresh_bb, | 
 | 	 create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_STRING, SORTID_USERID, "UserID", false, EDIT_NONE, | 
 | 	 refresh_bb, create_model_bb, admin_edit_bb}, | 
 | 	{G_TYPE_NONE, -1, NULL, false, EDIT_NONE} | 
 | }; | 
 |  | 
 | /*Burst buffer options list*/ | 
 | static display_data_t options_data_bb[] = { | 
 | 	{G_TYPE_INT, SORTID_POS, NULL, false, EDIT_NONE}, | 
 | 	{G_TYPE_STRING, INFO_PAGE, "Full Info", true, BB_PAGE}, | 
 | 	{G_TYPE_NONE, -1, NULL, false, EDIT_NONE} | 
 | }; | 
 |  | 
 | static display_data_t *local_display_data = NULL; | 
 | //Variable for Admin edit if needed | 
 | /* static char *got_edit_signal = NULL; */ | 
 | static GtkTreeModel *last_model = NULL; | 
 |  | 
 | static void _get_size_str(char *buf, size_t buf_size, uint64_t num); | 
 |  | 
 | /*Functions for admin edit*/ | 
 | /* static void _admin_bb(GtkTreeModel *model, GtkTreeIter *iter, char *type); */ | 
 | /* static void _process_each_bb(GtkTreeModel *model, GtkTreePath *path, */ | 
 | /*			       GtkTreeIter*iter, gpointer userdata); */ | 
 |  | 
 | /* static void _set_active_combo_bb(GtkComboBox *combo, */ | 
 | /*				 GtkTreeModel *model, GtkTreeIter *iter, */ | 
 | /*				 int type) */ | 
 | /* { */ | 
 | /* NOP */ | 
 | /* Function for admin edit */ | 
 | /* } */ | 
 |  | 
 | // Function for admin edit | 
 | //don't free this char | 
 | /* static const char *_set_bb_msg(burst_buffer_info_msg_t *bb_msg, */ | 
 | /*				 const char *new_text, */ | 
 | /*				 int column) */ | 
 | /* { */ | 
 | /* NOP */ | 
 | /* Function for admin edit */ | 
 | /* } */ | 
 |  | 
 | /* Free the burst buffer information */ | 
 | static void _bb_info_free(sview_bb_info_t *sview_bb_info) | 
 | { | 
 | 	if (sview_bb_info) { | 
 | 		xfree(sview_bb_info->bb_name); | 
 | 		xfree(sview_bb_info->plugin); | 
 | 	} | 
 | } | 
 |  | 
 | /* Free the Burst Buffer information list */ | 
 | static void _bb_info_list_del(void *object) | 
 | { | 
 | 	sview_bb_info_t *sview_bb_info = (sview_bb_info_t *)object; | 
 |  | 
 | 	if (sview_bb_info) { | 
 | 		_bb_info_free(sview_bb_info); | 
 | 		xfree(sview_bb_info); | 
 | 	} | 
 | } | 
 |  | 
 | /* static void _admin_edit_combo_box_bb(GtkComboBox *combo, */ | 
 | /*				     resv_desc_msg_t *resv_msg) */ | 
 | /* { */ | 
 | /* NOP */ | 
 | /* Function for admin edit */ | 
 | /* } */ | 
 |  | 
 |  | 
 |  | 
 | /* static gboolean _admin_focus_out_bb(GtkEntry *entry, */ | 
 | /*				      GdkEventFocus *event, */ | 
 | /*				      resv_desc_msg_t *resv_msg) */ | 
 | /* { */ | 
 | /* NOP */ | 
 | /* Function for admin edit */ | 
 | /* } */ | 
 |  | 
 | /* static GtkWidget *_admin_full_edit_bb(resv_desc_msg_t *resv_msg, */ | 
 | /*					GtkTreeModel *model, GtkTreeIter *iter) */ | 
 | /* { */ | 
 | /* NOP */ | 
 | /* Function for admin edit */ | 
 | /* } */ | 
 |  | 
 | /* Function creates the record menu when you double click on a record */ | 
 | static void _layout_bb_record(GtkTreeView *treeview, | 
 | 			      sview_bb_info_t *sview_bb_info, int update) | 
 | { | 
 | 	GtkTreeIter iter; | 
 | 	char time_buf[256], tmp_user_id[60], tmp_size[20]; | 
 | 	char bb_name_id[32]; | 
 | 	char *tmp_state, *tmp_user_name; | 
 | 	burst_buffer_resv_t *bb_ptr = sview_bb_info->bb_ptr; | 
 | 	GtkTreeStore *treestore; | 
 |  | 
 | 	treestore = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); | 
 |  | 
 | 	if (bb_ptr->name) { | 
 | 		strlcpy(bb_name_id, bb_ptr->name, sizeof(bb_name_id)); | 
 | 	} else if (bb_ptr->array_task_id == NO_VAL) { | 
 | 		convert_num_unit(bb_ptr->job_id, bb_name_id, sizeof(bb_name_id), | 
 | 				 UNIT_NONE, NO_VAL, | 
 | 				 working_sview_config.convert_flags); | 
 | 	} else { | 
 | 		snprintf(bb_name_id, sizeof(bb_name_id), | 
 | 			 "%u_%u(%u)", | 
 | 			 bb_ptr->array_job_id, | 
 | 			 bb_ptr->array_task_id, | 
 | 			 bb_ptr->job_id); | 
 | 	} | 
 | 	add_display_treestore_line(update, treestore, &iter, | 
 | 				   find_col_name(display_data_bb, | 
 | 						 SORTID_NAME), | 
 | 				   bb_name_id); | 
 |  | 
 | 	add_display_treestore_line(update, treestore, &iter, | 
 | 				   find_col_name(display_data_bb, | 
 | 						 SORTID_PLUGIN), | 
 | 				   sview_bb_info->plugin); | 
 |  | 
 | 	add_display_treestore_line(update, treestore, &iter, | 
 | 				   find_col_name(display_data_bb, | 
 | 						 SORTID_ACCOUNT), | 
 | 				   bb_ptr->account); | 
 |  | 
 | 	add_display_treestore_line(update, treestore, &iter, | 
 | 				   find_col_name(display_data_bb, | 
 | 						 SORTID_PARTITION), | 
 | 				   bb_ptr->partition); | 
 |  | 
 | 	add_display_treestore_line(update, treestore, &iter, | 
 | 				   find_col_name(display_data_bb, | 
 | 						 SORTID_POOL), | 
 | 				   bb_ptr->pool); | 
 |  | 
 | 	add_display_treestore_line(update, treestore, &iter, | 
 | 				   find_col_name(display_data_bb, | 
 | 						 SORTID_QOS), | 
 | 				   bb_ptr->qos); | 
 |  | 
 | 	tmp_state = bb_state_string(bb_ptr->state); | 
 | 	add_display_treestore_line(update, treestore, &iter, | 
 | 				   find_col_name(display_data_bb, | 
 | 						 SORTID_STATE), | 
 | 				   tmp_state); | 
 |  | 
 | 	_get_size_str(tmp_size, sizeof(tmp_size), bb_ptr->size); | 
 | 	add_display_treestore_line(update, treestore, &iter, | 
 | 				   find_col_name(display_data_bb, | 
 | 						 SORTID_SIZE), | 
 | 				   tmp_size); | 
 |  | 
 | 	if (bb_ptr->create_time) { | 
 | 		slurm_make_time_str((time_t *)&bb_ptr->create_time, time_buf, | 
 | 				    sizeof(time_buf)); | 
 | 	} else { | 
 | 		time_t now = time(NULL); | 
 | 		slurm_make_time_str(&now, time_buf, sizeof(time_buf)); | 
 | 	} | 
 | 	add_display_treestore_line(update, treestore, &iter, | 
 | 				   find_col_name(display_data_bb, | 
 | 						 SORTID_CREATE_TIME), | 
 | 				   time_buf); | 
 |  | 
 | 	tmp_user_name = uid_to_string(bb_ptr->user_id); | 
 | 	snprintf(tmp_user_id, sizeof(tmp_user_id), "%s(%u)", tmp_user_name, | 
 | 		 bb_ptr->user_id); | 
 | 	xfree(tmp_user_name); | 
 | 	add_display_treestore_line(update, treestore, &iter, | 
 | 				   find_col_name(display_data_bb, | 
 | 						 SORTID_USERID), | 
 | 				   tmp_user_id); | 
 | } | 
 |  | 
 | /* Reformat a numeric value with an appropriate suffix. | 
 |  * The input units are GB */ | 
 | static void _get_size_str(char *buf, size_t buf_size, uint64_t num) | 
 | { | 
 | 	uint64_t tmp64; | 
 |  | 
 | 	if ((num == NO_VAL64) || (num == INFINITE64)) { | 
 | 		snprintf(buf, buf_size, "INFINITE"); | 
 | 	} else if (num == 0) { | 
 | 		snprintf(buf, buf_size, "0GB"); | 
 | 	} else if ((num % ((uint64_t) 1024 * 1024 * 1024 * 1024 * 1024)) == 0) { | 
 | 		tmp64 = num / ((uint64_t) 1024 * 1024 * 1024 * 1024 * 1024); | 
 | 		snprintf(buf, buf_size, "%"PRIu64"PB", tmp64); | 
 | 	} else if ((num % ((uint64_t) 1024 * 1024 * 1024 * 1024)) == 0) { | 
 | 		tmp64 = num / ((uint64_t) 1024 * 1024 * 1024 * 1024); | 
 | 		snprintf(buf, buf_size, "%"PRIu64"TB", tmp64); | 
 | 	} else if ((num % ((uint64_t) 1024 * 1024 * 1024)) == 0) { | 
 | 		tmp64 = num / ((uint64_t) 1024 * 1024 * 1024); | 
 | 		snprintf(buf, buf_size, "%"PRIu64"GB", tmp64); | 
 | 	} else if ((num % ((uint64_t) 1024 * 1024)) == 0) { | 
 | 		tmp64 = num / ((uint64_t) 1024 * 1024); | 
 | 		snprintf(buf, buf_size, "%"PRIu64"MB", tmp64); | 
 | 	} else if ((num % 1024) == 0) { | 
 | 		tmp64 = num / 1024; | 
 | 		snprintf(buf, buf_size, "%"PRIu64"KB", tmp64); | 
 | 	} else { | 
 | 		tmp64 = num; | 
 | 		snprintf(buf, buf_size, "%"PRIu64"B", tmp64); | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | /* updates the burst buffer record on sview */ | 
 | static void _update_bb_record(sview_bb_info_t *sview_bb_info_ptr, | 
 | 			      GtkTreeStore *treestore) | 
 | { | 
 | 	char tmp_create_time[256]; | 
 | 	char tmp_size[20], tmp_user_id[60], bb_name_id[32]; | 
 | 	char *tmp_state, *tmp_user_name; | 
 | 	burst_buffer_resv_t *bb_ptr = sview_bb_info_ptr->bb_ptr; | 
 |  | 
 | 	if (bb_ptr->name) { | 
 | 		strlcpy(bb_name_id, bb_ptr->name, sizeof(bb_name_id)); | 
 | 	} else if (bb_ptr->array_task_id == NO_VAL) { | 
 | 		convert_num_unit(bb_ptr->job_id, bb_name_id, sizeof(bb_name_id), | 
 | 				 UNIT_NONE, NO_VAL, | 
 | 				 working_sview_config.convert_flags); | 
 | 	} else { | 
 | 		snprintf(bb_name_id, sizeof(bb_name_id), | 
 | 			 "%u_%u(%u)", | 
 | 			 bb_ptr->array_job_id, | 
 | 			 bb_ptr->array_task_id, | 
 | 			 bb_ptr->job_id); | 
 | 	} | 
 |  | 
 | 	if (bb_ptr->create_time) { | 
 | 		slurm_make_time_str((time_t *)&bb_ptr->create_time, | 
 | 				    tmp_create_time, sizeof(tmp_create_time)); | 
 | 	} else { | 
 | 		time_t now = time(NULL); | 
 | 		slurm_make_time_str(&now, tmp_create_time, | 
 | 				    sizeof(tmp_create_time)); | 
 | 	} | 
 |  | 
 | 	_get_size_str(tmp_size, sizeof(tmp_size), bb_ptr->size); | 
 |  | 
 | 	tmp_state = bb_state_string(bb_ptr->state); | 
 |  | 
 | 	tmp_user_name = uid_to_string(bb_ptr->user_id); | 
 | 	snprintf(tmp_user_id, sizeof(tmp_user_id), "%s(%u)", tmp_user_name, | 
 | 		 bb_ptr->user_id); | 
 | 	xfree(tmp_user_name); | 
 |  | 
 | 	/* Combining these records provides a slight performance improvement */ | 
 | 	gtk_tree_store_set(treestore, &sview_bb_info_ptr->iter_ptr, | 
 | 			   SORTID_COLOR, | 
 | 			   sview_colors[sview_bb_info_ptr->color_inx], | 
 | 			   SORTID_COLOR_INX,     sview_bb_info_ptr->color_inx, | 
 | 			   SORTID_PLUGIN,        sview_bb_info_ptr->plugin, | 
 | 			   SORTID_ACCOUNT,       bb_ptr->account, | 
 | 			   SORTID_CREATE_TIME,   tmp_create_time, | 
 | 			   SORTID_NAME,          bb_name_id, | 
 | 			   SORTID_PARTITION,     bb_ptr->partition, | 
 | 			   SORTID_POOL,          bb_ptr->pool, | 
 | 			   SORTID_QOS,           bb_ptr->qos, | 
 | 			   SORTID_SIZE,          tmp_size, | 
 | 			   SORTID_STATE,         tmp_state, | 
 | 			   SORTID_UPDATED,       1, | 
 | 			   SORTID_USERID,        tmp_user_id, | 
 | 			   -1); | 
 |  | 
 | 	return; | 
 | } | 
 |  | 
 | /* Append the give Burst Record to the list */ | 
 | static void _append_bb_record(sview_bb_info_t *sview_bb_info_ptr, | 
 | 				GtkTreeStore *treestore) | 
 | { | 
 | 	gtk_tree_store_append(treestore, &sview_bb_info_ptr->iter_ptr, NULL); | 
 | 	gtk_tree_store_set(treestore, &sview_bb_info_ptr->iter_ptr, | 
 | 			   SORTID_POS, sview_bb_info_ptr->pos, -1); | 
 | 	_update_bb_record(sview_bb_info_ptr, treestore); | 
 | } | 
 |  | 
 | /* Update the Burst Buffer information record */ | 
 | static void _update_info_bb(list_t *info_list, GtkTreeView *tree_view) | 
 | { | 
 | 	GtkTreeModel *model = gtk_tree_view_get_model(tree_view); | 
 | 	char *name = NULL; | 
 | 	list_itr_t *itr = NULL; | 
 | 	sview_bb_info_t *sview_bb_info = NULL; | 
 |  | 
 | 	set_for_update(model, SORTID_UPDATED); | 
 |  | 
 | 	itr = list_iterator_create(info_list); | 
 | 	while ((sview_bb_info = list_next(itr))) { | 
 | 		/* This means the tree_store changed (added new column | 
 | 		 * or something). */ | 
 | 		if (last_model != model) | 
 | 			sview_bb_info->iter_set = false; | 
 |  | 
 | 		if (sview_bb_info->iter_set) { | 
 | 			gtk_tree_model_get(model, &sview_bb_info->iter_ptr, | 
 | 					   SORTID_NAME, &name, -1); | 
 | 			if (xstrcmp(name, sview_bb_info->bb_name)) { | 
 | 				/* Bad pointer */ | 
 | 				sview_bb_info->iter_set = false; | 
 | 				//g_print("bad resv iter pointer\n"); | 
 | 			} | 
 | 			g_free(name); | 
 | 		} | 
 | 		if (sview_bb_info->iter_set) { | 
 | 			_update_bb_record(sview_bb_info, | 
 | 					    GTK_TREE_STORE(model)); | 
 | 		} else { | 
 | 			_append_bb_record(sview_bb_info, | 
 | 					    GTK_TREE_STORE(model)); | 
 | 			sview_bb_info->iter_set = true; | 
 | 		} | 
 | 	} | 
 | 	list_iterator_destroy(itr); | 
 |  | 
 | 	/* remove all old bb */ | 
 | 	remove_old(model, SORTID_UPDATED); | 
 | 	last_model = model; | 
 | } | 
 |  | 
 | static list_t *_create_bb_info_list(burst_buffer_info_msg_t *bb_info_ptr) | 
 | { | 
 | 	static list_t *info_list = NULL; | 
 | 	list_t *last_list = NULL; | 
 | 	list_itr_t *last_list_itr = NULL; | 
 | 	int i, j, pos = 0; | 
 | 	static burst_buffer_info_msg_t *last_bb_info_ptr = NULL; | 
 | 	sview_bb_info_t *sview_bb_info_ptr = NULL; | 
 | 	burst_buffer_info_t *bb_ptr; | 
 | 	burst_buffer_resv_t *bb_resv_ptr = NULL; | 
 | 	char bb_name_id[32] = ""; | 
 |  | 
 | 	if (info_list && (bb_info_ptr == last_bb_info_ptr)) | 
 | 		return info_list; | 
 |  | 
 | 	last_bb_info_ptr = bb_info_ptr; | 
 | 	if (info_list) | 
 | 		last_list = info_list; | 
 | 	info_list = list_create(_bb_info_list_del); | 
 |  | 
 | 	for (i = 0, bb_ptr = bb_info_ptr->burst_buffer_array; | 
 | 	     i < bb_info_ptr->record_count; i++, bb_ptr++) { | 
 |  | 
 | 		for (j = 0, bb_resv_ptr = bb_ptr->burst_buffer_resv_ptr; | 
 | 		     j < bb_ptr->buffer_count; j++, bb_resv_ptr++) { | 
 |  | 
 | 			/* Find any existing record for this burst buffer */ | 
 | 			if (last_list) { | 
 | 				last_list_itr = list_iterator_create(last_list); | 
 | 				while ((sview_bb_info_ptr = | 
 | 					list_next(last_list_itr))) { | 
 | 					if (bb_resv_ptr->job_id && | 
 | 					    (bb_resv_ptr->job_id != | 
 | 					     sview_bb_info_ptr->bb_ptr->job_id)) | 
 | 						continue; | 
 | 					if (bb_resv_ptr->name && | 
 | 					    xstrcmp(sview_bb_info_ptr->bb_name, | 
 | 						    bb_resv_ptr->name)) | 
 | 						continue; | 
 | 					if (xstrcmp(sview_bb_info_ptr->plugin, | 
 | 						    bb_ptr->name)) | 
 | 						continue; | 
 | 					list_remove(last_list_itr); | 
 | 					_bb_info_free(sview_bb_info_ptr); | 
 | 					break; | 
 | 				} | 
 | 				list_iterator_destroy(last_list_itr); | 
 | 			} else { | 
 | 				sview_bb_info_ptr = NULL; | 
 | 			} | 
 |  | 
 | 			if (bb_resv_ptr->name) { | 
 | 				strlcpy(bb_name_id, bb_resv_ptr->name, | 
 | 					sizeof(bb_name_id)); | 
 | 			} else if (bb_resv_ptr->array_task_id == NO_VAL) { | 
 | 				convert_num_unit(bb_resv_ptr->job_id, | 
 | 						 bb_name_id, | 
 | 						 sizeof(bb_name_id), | 
 | 						 UNIT_NONE, NO_VAL, | 
 | 						 working_sview_config. | 
 | 						 convert_flags); | 
 | 			} else { | 
 | 				snprintf(bb_name_id, sizeof(bb_name_id), | 
 | 					 "%u_%u(%u)", | 
 | 					 bb_resv_ptr->array_job_id, | 
 | 					 bb_resv_ptr->array_task_id, | 
 | 					 bb_resv_ptr->job_id); | 
 | 			} | 
 |  | 
 | 			if (!sview_bb_info_ptr) {	/* Need new record */ | 
 | 				sview_bb_info_ptr = | 
 | 					xmalloc(sizeof(sview_bb_info_t)); | 
 | 			} | 
 | 			sview_bb_info_ptr->bb_ptr = bb_resv_ptr; | 
 | 			sview_bb_info_ptr->bb_name = xstrdup(bb_name_id); | 
 | 			bb_name_id[0] = '\0';	/* Clear bb_name_id */ | 
 | 			sview_bb_info_ptr->color_inx = pos % sview_colors_cnt; | 
 | 			sview_bb_info_ptr->plugin = xstrdup(bb_ptr->name); | 
 | 			sview_bb_info_ptr->pos = pos++; | 
 | 			list_append(info_list, sview_bb_info_ptr); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	FREE_NULL_LIST(last_list); | 
 | 	return info_list; | 
 | } | 
 |  | 
 | static void _display_info_bb(list_t *info_list, popup_info_t *popup_win) | 
 | { | 
 | 	specific_info_t *spec_info = popup_win->spec_info; | 
 | 	char *name = (char *)spec_info->search_info->gchar_data; | 
 | 	//int found = 0; | 
 | 	burst_buffer_resv_t *bb_ptr = NULL; | 
 | 	GtkTreeView *treeview = NULL; | 
 | 	list_itr_t *itr = NULL; | 
 | 	sview_bb_info_t *sview_bb_info = NULL; | 
 | 	int update = 0; | 
 | 	char bb_name_id[32]; | 
 |  | 
 | 	if (!spec_info->search_info->gchar_data) { | 
 | 		//info = xstrdup("No pointer given!"); | 
 | 		goto finished; | 
 | 	} | 
 |  | 
 | 	if (!spec_info->display_widget) { | 
 | 		treeview = create_treeview_2cols_attach_to_table( | 
 | 			popup_win->table); | 
 | 		spec_info->display_widget = | 
 | 			g_object_ref(GTK_WIDGET(treeview)); | 
 | 	} else { | 
 | 		treeview = GTK_TREE_VIEW(spec_info->display_widget); | 
 | 		update = 1; | 
 | 	} | 
 |  | 
 | 	itr = list_iterator_create(info_list); | 
 | 	while ((sview_bb_info = list_next(itr))) { | 
 | 		bb_ptr = sview_bb_info->bb_ptr; | 
 |  | 
 | 		if (bb_ptr->name) { | 
 | 			strlcpy(bb_name_id, bb_ptr->name, sizeof(bb_name_id)); | 
 | 		} else if (bb_ptr->array_task_id == NO_VAL) { | 
 | 			convert_num_unit(bb_ptr->job_id, | 
 | 					 bb_name_id, | 
 | 					 sizeof(bb_name_id), | 
 | 					 UNIT_NONE, NO_VAL, | 
 | 					 working_sview_config.convert_flags); | 
 | 		} else { | 
 | 			snprintf(bb_name_id, sizeof(bb_name_id), | 
 | 				 "%u_%u(%u)", | 
 | 				 bb_ptr->array_job_id, | 
 | 				 bb_ptr->array_task_id, | 
 | 				 bb_ptr->job_id); | 
 | 		} | 
 |  | 
 | 		if (!xstrcmp(bb_name_id, name)) { | 
 | 			_layout_bb_record(treeview, sview_bb_info, update); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	list_iterator_destroy(itr); | 
 | 	gtk_widget_show(spec_info->display_widget); | 
 |  | 
 | finished: | 
 |  | 
 | 	return; | 
 | } | 
 |  | 
 | /* extern GtkWidget *create_bb_entry(resv_desc_msg_t *resv_msg, */ | 
 | /*				    GtkTreeModel *model, GtkTreeIter *iter) */ | 
 | /* { */ | 
 | /* NOP */ | 
 | /* Function to add new burst buffer */ | 
 | /* Admin edit function */ | 
 | /* } */ | 
 |  | 
 | /* Fresh the Burst Buffer information */ | 
 | extern void refresh_bb(GtkAction *action, gpointer user_data) | 
 | { | 
 | 	popup_info_t *popup_win = (popup_info_t *)user_data; | 
 | 	xassert(popup_win); | 
 | 	xassert(popup_win->spec_info); | 
 | 	xassert(popup_win->spec_info->title); | 
 | 	popup_win->force_refresh = 1; | 
 | 	specific_info_bb(popup_win); | 
 | } | 
 |  | 
 | /* Get the Burst buffer information */ | 
 | extern int get_new_info_bb(burst_buffer_info_msg_t **info_ptr, int force) | 
 | { | 
 | 	static burst_buffer_info_msg_t *new_bb_ptr = NULL; | 
 | 	int error_code = SLURM_NO_CHANGE_IN_DATA; | 
 | 	time_t now = time(NULL); | 
 | 	static time_t last; | 
 | 	static bool changed = 0; | 
 |  | 
 | 	if (g_bb_info_ptr && !force | 
 | 	    && ((now - last) < working_sview_config.refresh_delay)) { | 
 | 		if (*info_ptr != g_bb_info_ptr) | 
 | 			error_code = SLURM_SUCCESS; | 
 | 		*info_ptr = g_bb_info_ptr; | 
 | 		if (changed) | 
 | 			error_code = SLURM_SUCCESS; | 
 | 		goto end_it; | 
 | 	} | 
 | 	last = now; | 
 | 	if (g_bb_info_ptr) { | 
 | 		error_code = slurm_load_burst_buffer_info(&new_bb_ptr); | 
 | 		if (error_code == SLURM_SUCCESS) { | 
 | 			slurm_free_burst_buffer_info_msg(g_bb_info_ptr); | 
 | 			changed = 1; | 
 | 		} else if (errno == SLURM_NO_CHANGE_IN_DATA) { | 
 | 			error_code = SLURM_NO_CHANGE_IN_DATA; | 
 | 			new_bb_ptr = g_bb_info_ptr; | 
 | 			changed = 0; | 
 | 		} | 
 | 	} else { | 
 | 		new_bb_ptr = NULL; | 
 | 		error_code = slurm_load_burst_buffer_info(&new_bb_ptr); | 
 | 		changed = 1; | 
 | 	} | 
 |  | 
 | 	g_bb_info_ptr = new_bb_ptr; | 
 |  | 
 | 	if (g_bb_info_ptr && (*info_ptr != g_bb_info_ptr)) | 
 | 		error_code = SLURM_SUCCESS; | 
 |  | 
 | 	*info_ptr = g_bb_info_ptr; | 
 | end_it: | 
 | 	return error_code; | 
 | } | 
 |  | 
 | /* Create the model with types with known values */ | 
 | extern GtkListStore *create_model_bb(int type) | 
 | { | 
 | /* Since none of the values can be edited this is left blank */ | 
 | /* NOP */ | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* If Burst buffer wants to be edited it goes here */ | 
 | extern void admin_edit_bb(GtkCellRendererText *cell, | 
 | 			    const char *path_string, | 
 | 			    const char *new_text, | 
 | 			    gpointer data) | 
 | { | 
 | 	/* NOP */ | 
 | } | 
 |  | 
 | extern void get_info_bb(GtkTable *table, display_data_t *display_data) | 
 | { | 
 | 	int error_code = SLURM_SUCCESS; | 
 | 	list_t *info_list = NULL; | 
 | 	static int view = -1; | 
 | 	static burst_buffer_info_msg_t *bb_info_ptr = NULL; | 
 | 	char error_char[100]; | 
 | 	GtkWidget *label = NULL; | 
 | 	GtkTreeView *tree_view = NULL; | 
 | 	static GtkWidget *display_widget = NULL; | 
 | 	GtkTreePath *path = NULL; | 
 | 	static bool set_opts = false; | 
 |  | 
 | 	if (!set_opts) { | 
 | 		set_page_opts(BB_PAGE, display_data_bb, | 
 | 			      SORTID_CNT, _initial_page_opts); | 
 | 	} | 
 | 	set_opts = true; | 
 |  | 
 | 	/* reset */ | 
 | 	if (!table && !display_data) { | 
 | 		if (display_widget) | 
 | 			gtk_widget_destroy(display_widget); | 
 | 		display_widget = NULL; | 
 | 		bb_info_ptr = NULL; | 
 | 		goto reset_curs; | 
 | 	} | 
 |  | 
 | 	if (display_data) | 
 | 		local_display_data = display_data; | 
 | 	if (!table) { | 
 | 		display_data_bb->set_menu = local_display_data->set_menu; | 
 | 		goto reset_curs; | 
 | 	} | 
 | 	if (cluster_flags & CLUSTER_FLAG_FED) { | 
 | 		view = ERROR_VIEW; | 
 | 		if (display_widget) | 
 | 			gtk_widget_destroy(display_widget); | 
 | 		label = gtk_label_new("Not available in a federated view"); | 
 | 		gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); | 
 | 		gtk_widget_show(label); | 
 | 		display_widget = g_object_ref(label); | 
 | 		goto end_it; | 
 | 	} | 
 |  | 
 | 	if (display_widget && toggled) { | 
 | 		gtk_widget_destroy(display_widget); | 
 | 		display_widget = NULL; | 
 | 		goto display_it; | 
 | 	} | 
 |  | 
 | 	error_code = get_new_info_bb(&bb_info_ptr, force_refresh); | 
 |  | 
 | 	if (error_code == SLURM_NO_CHANGE_IN_DATA) { | 
 | 	} else if (error_code != SLURM_SUCCESS) { | 
 | 		if (view == ERROR_VIEW) | 
 | 			goto end_it; | 
 | 		if (display_widget) | 
 | 			gtk_widget_destroy(display_widget); | 
 | 		view = ERROR_VIEW; | 
 | 		sprintf(error_char, "slurm_load_reservations: %s", | 
 | 			slurm_strerror(errno)); | 
 | 		label = gtk_label_new(error_char); | 
 | 		gtk_table_attach_defaults(table, label, 0, 1, 0, 1); | 
 | 		gtk_widget_show(label); | 
 | 		display_widget = g_object_ref(GTK_WIDGET(label)); | 
 | 		goto end_it; | 
 | 	} | 
 |  | 
 | display_it: | 
 | 	info_list = _create_bb_info_list(bb_info_ptr); | 
 |  | 
 | 	if (!info_list) { | 
 | 		goto reset_curs; | 
 | 	} | 
 |  | 
 | 	/* set up the grid */ | 
 | 	if (display_widget && GTK_IS_TREE_VIEW(display_widget) | 
 | 	    && gtk_tree_selection_count_selected_rows( | 
 | 		    gtk_tree_view_get_selection( | 
 | 			    GTK_TREE_VIEW(display_widget)))) { | 
 | 		GtkTreeViewColumn *focus_column = NULL; | 
 | 		/* highlight the correct nodes from the last selection */ | 
 | 		gtk_tree_view_get_cursor(GTK_TREE_VIEW(display_widget), | 
 | 					 &path, &focus_column); | 
 | 	} | 
 |  | 
 | 	change_grid_color(grid_button_list, -1, -1, | 
 | 			  MAKE_WHITE, true, 0); | 
 |  | 
 | 	if (view == ERROR_VIEW && display_widget) { | 
 | 		gtk_widget_destroy(display_widget); | 
 | 		display_widget = NULL; | 
 | 	} | 
 | 	if (!display_widget) { | 
 | 		tree_view = create_treeview(local_display_data, | 
 | 					    &grid_button_list); | 
 | 		gtk_tree_selection_set_mode( | 
 | 			gtk_tree_view_get_selection(tree_view), | 
 | 			GTK_SELECTION_MULTIPLE); | 
 | 		display_widget = g_object_ref(GTK_WIDGET(tree_view)); | 
 | 		gtk_table_attach_defaults(table, | 
 | 					  GTK_WIDGET(tree_view), | 
 | 					  0, 1, 0, 1); | 
 | 		/* since this function sets the model of the tree_view | 
 | 		   to the treestore we don't really care about | 
 | 		   the return value */ | 
 | 		create_treestore(tree_view, display_data_bb, | 
 | 				 SORTID_CNT, SORTID_NAME, SORTID_COLOR); | 
 |  | 
 | 	} | 
 |  | 
 | 	view = INFO_VIEW; | 
 | 	_update_info_bb(info_list, GTK_TREE_VIEW(display_widget)); | 
 | end_it: | 
 | 	toggled = false; | 
 | 	force_refresh = false; | 
 | reset_curs: | 
 | 	if (main_window && main_window->window) | 
 | 		gdk_window_set_cursor(main_window->window, NULL); | 
 |  | 
 | 	return; | 
 | } | 
 |  | 
 | /* Function for full information about a Burst Buffer */ | 
 | extern void specific_info_bb(popup_info_t *popup_win) | 
 | { | 
 | 	int bb_error_code = SLURM_SUCCESS; | 
 | 	static burst_buffer_info_msg_t *bb_info_ptr = NULL; | 
 | 	specific_info_t *spec_info = popup_win->spec_info; | 
 | 	char error_char[100]; | 
 | 	GtkWidget *label = NULL; | 
 | 	GtkTreeView *tree_view = NULL; | 
 | 	list_t *bb_list = NULL; | 
 | 	list_t *send_bb_list = NULL; | 
 | 	sview_bb_info_t *sview_bb_info_ptr = NULL; | 
 | 	list_itr_t *itr = NULL; | 
 |  | 
 | 	if (!spec_info->display_widget) { | 
 | 		setup_popup_info(popup_win, display_data_bb, SORTID_CNT); | 
 | 	} | 
 |  | 
 | 	if (spec_info->display_widget && popup_win->toggled) { | 
 | 		gtk_widget_destroy(spec_info->display_widget); | 
 | 		spec_info->display_widget = NULL; | 
 | 		goto display_it; | 
 | 	} | 
 |  | 
 | 	if ((bb_error_code = | 
 | 	     get_new_info_bb(&bb_info_ptr, popup_win->force_refresh)) | 
 | 	    == SLURM_NO_CHANGE_IN_DATA) { | 
 | 		if (!spec_info->display_widget || spec_info->view == ERROR_VIEW) | 
 | 			goto display_it; | 
 | 	} else if (bb_error_code != SLURM_SUCCESS) { | 
 | 		if (spec_info->view == ERROR_VIEW) | 
 | 			goto end_it; | 
 | 		spec_info->view = ERROR_VIEW; | 
 | 		if (spec_info->display_widget) | 
 | 			gtk_widget_destroy(spec_info->display_widget); | 
 | 		sprintf(error_char, "get_new_info_bb: %s", | 
 | 			slurm_strerror(errno)); | 
 | 		label = gtk_label_new(error_char); | 
 | 		gtk_table_attach_defaults(popup_win->table, | 
 | 					  label, | 
 | 					  0, 1, 0, 1); | 
 | 		gtk_widget_show(label); | 
 | 		spec_info->display_widget = g_object_ref(label); | 
 | 		goto end_it; | 
 | 	} | 
 |  | 
 | display_it: | 
 |  | 
 | 	bb_list = _create_bb_info_list(bb_info_ptr); | 
 | 	if (!bb_list) | 
 | 		return; | 
 |  | 
 | 	if ((spec_info->view == ERROR_VIEW) && spec_info->display_widget) { | 
 | 		gtk_widget_destroy(spec_info->display_widget); | 
 | 		spec_info->display_widget = NULL; | 
 | 	} | 
 | 	if (spec_info->type != INFO_PAGE && !spec_info->display_widget) { | 
 | 		tree_view = create_treeview(local_display_data, | 
 | 					    &popup_win->grid_button_list); | 
 | 		gtk_tree_selection_set_mode( | 
 | 			gtk_tree_view_get_selection(tree_view), | 
 | 			GTK_SELECTION_MULTIPLE); | 
 | 		spec_info->display_widget = | 
 | 			g_object_ref(GTK_WIDGET(tree_view)); | 
 | 		gtk_table_attach_defaults(popup_win->table, | 
 | 					  GTK_WIDGET(tree_view), | 
 | 					  0, 1, 0, 1); | 
 | 		/* | 
 | 		 * since this function sets the model of the tree_view | 
 | 		 * to the treestore we don't really care about | 
 | 		 * the return value | 
 | 		 */ | 
 | 		create_treestore(tree_view, popup_win->display_data, | 
 | 				 SORTID_CNT, SORTID_NAME, SORTID_COLOR); | 
 | 	} | 
 |  | 
 | 	setup_popup_grid_list(popup_win); | 
 |  | 
 | 	spec_info->view = INFO_VIEW; | 
 | 	if (spec_info->type == INFO_PAGE) { | 
 | 		_display_info_bb(bb_list, popup_win); | 
 | 		goto end_it; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * just linking to another list, don't free the inside, just the list | 
 | 	 */ | 
 | 	send_bb_list = list_create(NULL); | 
 | 	itr = list_iterator_create(bb_list); | 
 | 	/* | 
 | 	 * Set up additional menu options(ie the right click menu stuff) | 
 | 	 */ | 
 | 	while ((sview_bb_info_ptr = list_next(itr))) { | 
 | 		switch (spec_info->type) { | 
 | 		case BB_PAGE: | 
 | 			list_push(send_bb_list, sview_bb_info_ptr); | 
 | 			break; | 
 | 		case JOB_PAGE: | 
 | 		case NODE_PAGE: | 
 | 		case PART_PAGE: | 
 | 		case RESV_PAGE: | 
 | 		default: | 
 | 			/* | 
 | 			 * Since we will not use any of these pages we will | 
 | 			 * leave them blank | 
 | 			 */ | 
 | 			g_print("Unknown type %d\n", spec_info->type); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	list_iterator_destroy(itr); | 
 | 	post_setup_popup_grid_list(popup_win); | 
 |  | 
 | 	_update_info_bb(send_bb_list, | 
 | 			  GTK_TREE_VIEW(spec_info->display_widget)); | 
 | 	FREE_NULL_LIST(send_bb_list); | 
 | end_it: | 
 | 	popup_win->toggled = 0; | 
 | 	popup_win->force_refresh = 0; | 
 |  | 
 | 	return; | 
 | } | 
 |  | 
 | /* creates a popup window depending on what is clicked */ | 
 | extern void set_menus_bb(void *arg, void *arg2, GtkTreePath *path, int type) | 
 | { | 
 | 	GtkTreeView *tree_view = (GtkTreeView *)arg; | 
 | 	popup_info_t *popup_win = (popup_info_t *)arg; | 
 | 	GtkMenu *menu = (GtkMenu *)arg2; | 
 |  | 
 | 	switch (type) { | 
 | 	case TAB_CLICKED: | 
 | 		make_fields_menu(NULL, menu, display_data_bb, SORTID_CNT); | 
 | 		break; | 
 | 	case ROW_CLICKED: | 
 | 		make_options_menu(tree_view, path, menu, options_data_bb); | 
 | 		break; | 
 | 	case ROW_LEFT_CLICKED: | 
 | 		/* Highlights the node in th node grid */ | 
 | 		/* since we are not using this we will keep it empty */ | 
 | 		/* NOP */ | 
 | 		break; | 
 | 	case FULL_CLICKED: | 
 | 	{ | 
 | 		GtkTreeModel *model = gtk_tree_view_get_model(tree_view); | 
 | 		GtkTreeIter iter; | 
 | 		if (!gtk_tree_model_get_iter(model, &iter, path)) { | 
 | 			g_error("error getting iter from model\n"); | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		popup_all_bb(model, &iter, INFO_PAGE); | 
 |  | 
 | 		break; | 
 | 	} | 
 | 	case POPUP_CLICKED: | 
 | 		make_fields_menu(popup_win, menu, | 
 | 				 popup_win->display_data, SORTID_CNT); | 
 | 		break; | 
 | 	default: | 
 | 		g_error("UNKNOWN type %d given to set_fields\n", type); | 
 | 	} | 
 | } | 
 |  | 
 | /* Function to setup popup windows for Burst Buffer */ | 
 | extern void popup_all_bb(GtkTreeModel *model, GtkTreeIter *iter, int id) | 
 | { | 
 | 	char *name = NULL; | 
 | 	char title[100] = {0}; | 
 | 	list_itr_t *itr = NULL; | 
 | 	popup_info_t *popup_win = NULL; | 
 | 	GError *error = NULL; | 
 |  | 
 | 	gtk_tree_model_get(model, iter, SORTID_NAME, &name, -1); | 
 |  | 
 | 	switch (id) { | 
 | 	case INFO_PAGE: | 
 | 		snprintf(title, 100, "Full info for Burst Buffer %s", name); | 
 | 		break; | 
 | 	default: | 
 | 		g_print("Burst Buffer got %d\n", id); | 
 | 	} | 
 |  | 
 | 	itr = list_iterator_create(popup_list); | 
 | 	while ((popup_win = list_next(itr))) { | 
 | 		if (popup_win->spec_info) | 
 | 			if (!xstrcmp(popup_win->spec_info->title, title)) { | 
 | 				break; | 
 | 			} | 
 | 	} | 
 | 	list_iterator_destroy(itr); | 
 |  | 
 | 	if (!popup_win) { | 
 | 		if (id == INFO_PAGE) { | 
 | 			popup_win = create_popup_info(id, BB_PAGE, title); | 
 | 		} else { | 
 | 			popup_win = create_popup_info(BB_PAGE, id, title); | 
 | 		} | 
 | 	} else { | 
 | 		g_free(name); | 
 | 		gtk_window_present(GTK_WINDOW(popup_win->popup)); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* Pass the model and the structs from the iter so we can always get | 
 | 	   the current node_inx. | 
 | 	*/ | 
 | 	popup_win->model = model; | 
 | 	popup_win->iter = *iter; | 
 |  | 
 | 	/* Sets up right click information */ | 
 | 	switch (id) { | 
 | 	case JOB_PAGE: | 
 | 	case INFO_PAGE: | 
 | 		popup_win->spec_info->search_info->gchar_data = name; | 
 | 		specific_info_bb(popup_win); | 
 | 		break; | 
 | 	case NODE_PAGE: | 
 | 	case PART_PAGE: | 
 | 	case SUBMIT_PAGE: | 
 | 		break; | 
 | 	default: | 
 | 		g_print("Burst Buffer got unknown type %d\n", id); | 
 | 	} | 
 | 	if (!sview_thread_new((gpointer)popup_thr, popup_win, &error)) { | 
 | 		g_printerr ("Failed to create burst buffer popup thread: %s\n", | 
 | 			    error->message); | 
 | 		return; | 
 | 	} | 
 | } | 
 |  | 
 | /* static void _process_each_bb(GtkTreeModel *model, GtkTreePath *path, */ | 
 | /*			       GtkTreeIter*iter, gpointer userdata) */ | 
 | /* { */ | 
 | /* Function for admin edit */ | 
 | /* NOP */ | 
 | /* } */ | 
 |  | 
 | extern void select_admin_bb(GtkTreeModel *model, GtkTreeIter *iter, | 
 | 			      display_data_t *display_data, | 
 | 			      GtkTreeView *treeview) | 
 | { | 
 | /* NOP */ | 
 | /* Function for admin edit */ | 
 | } | 
 |  | 
 | /* static void _admin_bb(GtkTreeModel *model, GtkTreeIter *iter, char *type) */ | 
 | /* { */ | 
 | /* NOP */ | 
 | /* Function for admin edit */ | 
 | /* } */ | 
 |  | 
 | extern void cluster_change_bb(void) | 
 | { | 
 | 	get_info_bb(NULL, NULL); | 
 | } |