|  | /*****************************************************************************\ | 
|  | *  node_info.c - Functions related to node display mode of sview. | 
|  | ***************************************************************************** | 
|  | *  Copyright (C) 2004-2007 The Regents of the University of California. | 
|  | *  Copyright (C) 2008-2010 Lawrence Livermore National Security. | 
|  | *  Copyright (C) SchedMD LLC. | 
|  | *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). | 
|  | *  Written by Danny Auble <da@llnl.gov> | 
|  | * | 
|  | *  CODE-OCEC-09-009. All rights reserved. | 
|  | * | 
|  | *  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/sview/sview.h" | 
|  |  | 
|  | #define _DEBUG 0 | 
|  | #define ECLIPSE_RT 0 | 
|  |  | 
|  | //static int _l_topo_color_ndx = MAKE_TOPO_1; | 
|  | //static int _l_sw_color_ndx = 0; | 
|  |  | 
|  | /* These need to be in alpha order (except POS and CNT) */ | 
|  | enum { | 
|  | SORTID_POS = POS_LOC, | 
|  | SORTID_ACTIVE_FEATURES, | 
|  | SORTID_ARCH, | 
|  | SORTID_AVAIL_FEATURES, | 
|  | SORTID_AVE_WATTS, | 
|  | SORTID_BOARDS, | 
|  | SORTID_BOOT_TIME, | 
|  | SORTID_CAP_WATTS, | 
|  | SORTID_CLUSTER_NAME, | 
|  | SORTID_COLOR, | 
|  | SORTID_COMMENT, | 
|  | SORTID_CPUS, | 
|  | SORTID_CPU_LOAD, | 
|  | SORTID_CORES, | 
|  | SORTID_CURRENT_WATTS, | 
|  | SORTID_ERR_CPUS, | 
|  | SORTID_EXTRA, | 
|  | SORTID_FREE_MEM, | 
|  | SORTID_GRES, | 
|  | SORTID_IDLE_CPUS, | 
|  | SORTID_MCS_LABEL, | 
|  | SORTID_NAME, | 
|  | SORTID_NODE_ADDR, | 
|  | SORTID_NODE_HOSTNAME, | 
|  | SORTID_OWNER, | 
|  | SORTID_PORT, | 
|  | SORTID_REAL_MEMORY, | 
|  | SORTID_REASON, | 
|  | SORTID_RES_CORES_PER_GPU, | 
|  | SORTID_RESV_NAME, | 
|  | SORTID_SLURMD_START_TIME, | 
|  | SORTID_SOCKETS, | 
|  | SORTID_STATE, | 
|  | SORTID_STATE_COMPLETE, | 
|  | SORTID_STATE_NUM, | 
|  | SORTID_THREADS, | 
|  | SORTID_TMP_DISK, | 
|  | SORTID_TRES_ALLOC, | 
|  | SORTID_TRES_CONFIG, | 
|  | SORTID_UPDATED, | 
|  | SORTID_USED_CPUS, | 
|  | SORTID_USED_MEMORY, | 
|  | SORTID_VERSION, | 
|  | SORTID_WEIGHT, | 
|  | SORTID_CNT | 
|  | }; | 
|  |  | 
|  | typedef struct { | 
|  | int node_col; | 
|  | char *nodelist; | 
|  | } process_node_t; | 
|  |  | 
|  | /*these are the settings to apply for the user | 
|  | * on the first startup after a fresh slurm install.*/ | 
|  | static char *_initial_page_opts = "Name,RackMidplane,State,CPU_Count," | 
|  | "Used_CPU_Count,Error_CPU_Count,CoresPerSocket,Sockets,ThreadsPerCore," | 
|  | "Real_Memory,Tmp_Disk"; | 
|  |  | 
|  | static display_data_t display_data_node[] = { | 
|  | {G_TYPE_INT, SORTID_POS, NULL, false, EDIT_NONE, refresh_node, | 
|  | create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_CLUSTER_NAME, "ClusterName", false, EDIT_NONE, | 
|  | refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_NAME, "Name", false, EDIT_NONE, refresh_node, | 
|  | create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_COLOR, NULL, true, EDIT_COLOR, refresh_node, | 
|  | create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_NODE_ADDR, "NodeAddr", false, EDIT_NONE, | 
|  | refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_NODE_HOSTNAME, "NodeHostName", false, EDIT_NONE, | 
|  | refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_OWNER, "Owner", false, EDIT_NONE, | 
|  | refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_MCS_LABEL, "MCS_Label", false, EDIT_NONE, | 
|  | refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_STATE, "State", false, EDIT_MODEL, refresh_node, | 
|  | create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_STATE_COMPLETE, "StateComplete", false, | 
|  | EDIT_MODEL, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_INT, SORTID_STATE_NUM, NULL, false, EDIT_NONE, refresh_node, | 
|  | create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_CPUS, "CPU Count", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_USED_CPUS, "Used CPU Count", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_ERR_CPUS, "Error CPU Count", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_EXTRA, "Extra", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_IDLE_CPUS, "Idle CPU Count", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_TRES_CONFIG, "Config TRES", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_TRES_ALLOC, "Alloc TRES", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_INT, SORTID_BOARDS, "Boards", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_INT, SORTID_SOCKETS, "Sockets", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_INT, SORTID_CORES, "CoresPerSocket", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_INT, SORTID_THREADS, "ThreadsPerCore", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_REAL_MEMORY, "Real Memory", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_USED_MEMORY, "Used Memory", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_FREE_MEM, "Free Memory", false, EDIT_NONE, | 
|  | refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_PORT, "Port", false, EDIT_NONE, | 
|  | refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_TMP_DISK, "Tmp Disk", false, EDIT_NONE, | 
|  | refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_ACTIVE_FEATURES, "Active Features", false, | 
|  | EDIT_TEXTBOX, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_ARCH, "Arch", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_AVAIL_FEATURES, "Available Features", false, | 
|  | EDIT_TEXTBOX, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_BOOT_TIME, "BootTime", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_CPU_LOAD, "CPU Load", false, EDIT_NONE, | 
|  | refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_COMMENT, "Comment", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_GRES, "Gres", false, | 
|  | EDIT_TEXTBOX, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_REASON, "Reason", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_INT, SORTID_RES_CORES_PER_GPU, | 
|  | "RestrictedCoresPerGPU", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_RESV_NAME, "ReservationName", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_SLURMD_START_TIME, "SlurmdStartTime", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_CURRENT_WATTS, "Current Watts", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_AVE_WATTS, "Average Watts", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_CAP_WATTS,"Cap Watts", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_STRING, SORTID_VERSION, "Version", false, | 
|  | EDIT_NONE, refresh_node, create_model_node, admin_edit_node}, | 
|  | {G_TYPE_INT, SORTID_WEIGHT,"Weight", false, EDIT_NONE, refresh_node, | 
|  | create_model_node, admin_edit_node}, | 
|  | {G_TYPE_INT, SORTID_UPDATED, NULL, false, EDIT_NONE, refresh_node, | 
|  | create_model_node, admin_edit_node}, | 
|  | {G_TYPE_NONE, -1, NULL, false, EDIT_NONE} | 
|  | }; | 
|  |  | 
|  | static display_data_t options_data_node[] = { | 
|  | {G_TYPE_INT, SORTID_POS, NULL, false, EDIT_NONE}, | 
|  | {G_TYPE_STRING, INFO_PAGE, "Full Info", true, NODE_PAGE}, | 
|  | {G_TYPE_STRING, NODE_PAGE, "Drain Node", true, ADMIN_PAGE}, | 
|  | {G_TYPE_STRING, NODE_PAGE, "Undrain Node", true, ADMIN_PAGE}, | 
|  | {G_TYPE_STRING, NODE_PAGE, "Resume Node", true, ADMIN_PAGE}, | 
|  | {G_TYPE_STRING, NODE_PAGE, "Set Node(s) Down", true, ADMIN_PAGE}, | 
|  | {G_TYPE_STRING, NODE_PAGE, "Make Node(s) Idle", true, ADMIN_PAGE}, | 
|  | {G_TYPE_STRING, NODE_PAGE, "Update Active Features", true, ADMIN_PAGE}, | 
|  | {G_TYPE_STRING, NODE_PAGE, "Update Available Features", true, ADMIN_PAGE}, | 
|  | {G_TYPE_STRING, NODE_PAGE, "Update Gres", true, ADMIN_PAGE}, | 
|  | {G_TYPE_STRING, JOB_PAGE,  "Jobs", true, NODE_PAGE}, | 
|  | {G_TYPE_STRING, PART_PAGE, "Partitions", true, NODE_PAGE}, | 
|  | {G_TYPE_STRING, RESV_PAGE, "Reservations", true, NODE_PAGE}, | 
|  | //{G_TYPE_STRING, SUBMIT_PAGE, "Job Submit", false, NODE_PAGE}, | 
|  | {G_TYPE_NONE, -1, NULL, false, EDIT_NONE} | 
|  | }; | 
|  |  | 
|  | static display_data_t *local_display_data = NULL; | 
|  | static GtkTreeModel *last_model = NULL; | 
|  |  | 
|  | static void _layout_node_record(GtkTreeView *treeview, | 
|  | sview_node_info_t *sview_node_info_ptr, | 
|  | int update) | 
|  | { | 
|  | char tmp_cnt[50]; | 
|  | char tmp_current_watts[50]; | 
|  | char tmp_ave_watts[50]; | 
|  | char tmp_owner[32]; | 
|  | char tmp_version[50]; | 
|  | char *upper = NULL, *lower = NULL; | 
|  | GtkTreeIter iter; | 
|  | uint16_t alloc_cpus = 0; | 
|  | node_info_t *node_ptr = sview_node_info_ptr->node_ptr; | 
|  | int idle_cpus = node_ptr->cpus_efctv; | 
|  | GtkTreeStore *treestore = | 
|  | GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); | 
|  | if (!treestore) | 
|  | return; | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_CLUSTER_NAME), | 
|  | node_ptr->cluster_name); | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_NAME), | 
|  | node_ptr->name); | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_NODE_ADDR), | 
|  | node_ptr->node_addr); | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_NODE_HOSTNAME), | 
|  | node_ptr->node_hostname); | 
|  |  | 
|  | if (node_ptr->owner == NO_VAL) { | 
|  | snprintf(tmp_owner, sizeof(tmp_owner), "N/A"); | 
|  | } else { | 
|  | char *user_name; | 
|  | user_name = uid_to_string((uid_t) node_ptr->owner); | 
|  | snprintf(tmp_owner, sizeof(tmp_owner), "%s(%u)", | 
|  | user_name, node_ptr->owner); | 
|  | xfree(user_name); | 
|  | } | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_OWNER), tmp_owner); | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_MCS_LABEL), | 
|  | (node_ptr->mcs_label == NULL) ? "N/A" : | 
|  | node_ptr->mcs_label), | 
|  |  | 
|  | convert_num_unit((float)node_ptr->cpus, tmp_cnt, sizeof(tmp_cnt), | 
|  | UNIT_NONE, NO_VAL, working_sview_config.convert_flags); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_CPUS), | 
|  | tmp_cnt); | 
|  |  | 
|  | snprintf(tmp_cnt, sizeof(tmp_cnt), "%.2f", | 
|  | (node_ptr->cpu_load / 100.0)); | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_CPU_LOAD), | 
|  | tmp_cnt); | 
|  |  | 
|  | if (node_ptr->free_mem == NO_VAL64) { | 
|  | snprintf(tmp_cnt, sizeof(tmp_cnt), "N/A"); | 
|  | } else { | 
|  | snprintf(tmp_cnt, sizeof(tmp_cnt), "%"PRIu64"M", | 
|  | node_ptr->free_mem); | 
|  | } | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_FREE_MEM), | 
|  | tmp_cnt); | 
|  |  | 
|  | alloc_cpus = node_ptr->alloc_cpus; | 
|  | idle_cpus -= alloc_cpus; | 
|  | convert_num_unit((float)alloc_cpus, tmp_cnt, | 
|  | sizeof(tmp_cnt), UNIT_NONE, NO_VAL, | 
|  | working_sview_config.convert_flags); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_USED_CPUS), | 
|  | tmp_cnt); | 
|  |  | 
|  | convert_num_unit((float)idle_cpus, tmp_cnt, sizeof(tmp_cnt), UNIT_NONE, | 
|  | NO_VAL, working_sview_config.convert_flags); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_IDLE_CPUS), | 
|  | tmp_cnt); | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_TRES_CONFIG), | 
|  | node_ptr->tres_fmt_str); | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_TRES_ALLOC), | 
|  | (node_ptr->alloc_tres_fmt_str ? | 
|  | node_ptr->alloc_tres_fmt_str : "")); | 
|  |  | 
|  | upper = node_state_string(node_ptr->node_state); | 
|  | lower = str_tolower(upper); | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_STATE), | 
|  | lower); | 
|  | xfree(lower); | 
|  |  | 
|  | lower = node_state_string_complete(node_ptr->node_state); | 
|  | xstrtolower(lower); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_STATE_COMPLETE), | 
|  | lower); | 
|  | xfree(lower); | 
|  |  | 
|  | convert_num_unit((float)node_ptr->boards, tmp_cnt, sizeof(tmp_cnt), | 
|  | UNIT_NONE, NO_VAL, working_sview_config.convert_flags); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_BOARDS), | 
|  | tmp_cnt); | 
|  |  | 
|  | convert_num_unit((float)node_ptr->sockets, tmp_cnt, sizeof(tmp_cnt), | 
|  | UNIT_NONE, NO_VAL, working_sview_config.convert_flags); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_SOCKETS), | 
|  | tmp_cnt); | 
|  |  | 
|  | convert_num_unit((float)node_ptr->cores, tmp_cnt, sizeof(tmp_cnt), | 
|  | UNIT_NONE, NO_VAL, working_sview_config.convert_flags); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_CORES), | 
|  | tmp_cnt); | 
|  |  | 
|  | convert_num_unit((float)node_ptr->port, tmp_cnt, sizeof(tmp_cnt), | 
|  | UNIT_NONE, NO_VAL, working_sview_config.convert_flags); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_PORT), | 
|  | tmp_cnt); | 
|  |  | 
|  | convert_num_unit((float)node_ptr->threads, tmp_cnt, sizeof(tmp_cnt), | 
|  | UNIT_NONE, NO_VAL, working_sview_config.convert_flags); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_THREADS), | 
|  | tmp_cnt); | 
|  |  | 
|  | convert_num_unit((float)node_ptr->real_memory, tmp_cnt, sizeof(tmp_cnt), | 
|  | UNIT_MEGA, NO_VAL, working_sview_config.convert_flags); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_REAL_MEMORY), | 
|  | tmp_cnt); | 
|  |  | 
|  | snprintf(tmp_cnt, sizeof(tmp_cnt), "%"PRIu64"M", | 
|  | node_ptr->alloc_memory); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_USED_MEMORY), | 
|  | tmp_cnt); | 
|  |  | 
|  | convert_num_unit((float)node_ptr->tmp_disk, tmp_cnt, sizeof(tmp_cnt), | 
|  | UNIT_MEGA, NO_VAL, working_sview_config.convert_flags); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_TMP_DISK), | 
|  | tmp_cnt); | 
|  | snprintf(tmp_cnt, sizeof(tmp_cnt), "%u", node_ptr->weight); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_WEIGHT), | 
|  | tmp_cnt); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_ARCH), | 
|  | node_ptr->arch); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_AVAIL_FEATURES), | 
|  | node_ptr->features); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_ACTIVE_FEATURES), | 
|  | node_ptr->features_act); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_GRES), | 
|  | node_ptr->gres); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_BOOT_TIME), | 
|  | sview_node_info_ptr->boot_time); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_SLURMD_START_TIME), | 
|  | sview_node_info_ptr->slurmd_start_time); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_REASON), | 
|  | sview_node_info_ptr->reason); | 
|  | convert_num_unit((float)node_ptr->res_cores_per_gpu, | 
|  | tmp_cnt, sizeof(tmp_cnt), | 
|  | UNIT_NONE, NO_VAL, working_sview_config.convert_flags); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_RES_CORES_PER_GPU), | 
|  | tmp_cnt); | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_RESV_NAME), | 
|  | node_ptr->resv_name); | 
|  |  | 
|  | if (node_ptr->energy->current_watts == NO_VAL) { | 
|  | snprintf(tmp_current_watts, sizeof(tmp_current_watts), | 
|  | "N/A"); | 
|  | snprintf(tmp_ave_watts, sizeof(tmp_ave_watts), | 
|  | "N/A"); | 
|  | } else { | 
|  | snprintf(tmp_current_watts, sizeof(tmp_current_watts), | 
|  | "%u", node_ptr->energy->current_watts); | 
|  | snprintf(tmp_ave_watts, sizeof(tmp_ave_watts), | 
|  | "%u", node_ptr->energy->ave_watts); | 
|  | } | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_CURRENT_WATTS), | 
|  | tmp_current_watts); | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_AVE_WATTS), | 
|  | tmp_ave_watts); | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_COMMENT), | 
|  | node_ptr->comment); | 
|  |  | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_EXTRA), | 
|  | node_ptr->extra); | 
|  |  | 
|  | if (node_ptr->version == NULL) { | 
|  | snprintf(tmp_version, sizeof(tmp_version), "N/A"); | 
|  | } else { | 
|  | snprintf(tmp_version, sizeof(tmp_version), "%s", | 
|  | node_ptr->version); | 
|  | } | 
|  | add_display_treestore_line(update, treestore, &iter, | 
|  | find_col_name(display_data_node, | 
|  | SORTID_VERSION), | 
|  | tmp_version); | 
|  | return; | 
|  | } | 
|  |  | 
|  | static void _update_node_record(sview_node_info_t *sview_node_info_ptr, | 
|  | GtkTreeStore *treestore) | 
|  | { | 
|  | uint16_t alloc_cpus = 0, idle_cpus; | 
|  | node_info_t *node_ptr = sview_node_info_ptr->node_ptr; | 
|  | char tmp_disk[20], tmp_cpus[20], tmp_idle_cpus[20]; | 
|  | char tmp_mem[20], tmp_used_memory[20]; | 
|  | char tmp_used_cpus[20], tmp_cpu_load[20], tmp_free_mem[20], tmp_owner[32]; | 
|  | char tmp_current_watts[50], tmp_ave_watts[50]; | 
|  | char tmp_version[50]; | 
|  | char *tmp_state_lower, *tmp_state_upper, *tmp_state_complete; | 
|  |  | 
|  | if (node_ptr->energy->current_watts == NO_VAL) { | 
|  | snprintf(tmp_current_watts, sizeof(tmp_current_watts), | 
|  | "N/A"); | 
|  | snprintf(tmp_ave_watts, sizeof(tmp_ave_watts), | 
|  | "N/A"); | 
|  | } else { | 
|  | snprintf(tmp_current_watts, sizeof(tmp_current_watts), | 
|  | "%u ", node_ptr->energy->current_watts); | 
|  | snprintf(tmp_ave_watts, sizeof(tmp_ave_watts), | 
|  | "%u", node_ptr->energy->ave_watts); | 
|  | } | 
|  |  | 
|  | snprintf(tmp_cpu_load, sizeof(tmp_cpu_load), "%.2f", | 
|  | (node_ptr->cpu_load / 100.0)); | 
|  |  | 
|  | if (node_ptr->free_mem == NO_VAL64) { | 
|  | strlcpy(tmp_free_mem, "N/A", sizeof(tmp_free_mem)); | 
|  | } else { | 
|  | snprintf(tmp_free_mem, sizeof(tmp_free_mem), | 
|  | "%"PRIu64"M", node_ptr->free_mem); | 
|  | } | 
|  |  | 
|  | convert_num_unit((float)node_ptr->cpus, tmp_cpus, | 
|  | sizeof(tmp_cpus), UNIT_NONE, NO_VAL, | 
|  | working_sview_config.convert_flags); | 
|  |  | 
|  | alloc_cpus = node_ptr->alloc_cpus; | 
|  | idle_cpus = node_ptr->cpus_efctv - alloc_cpus; | 
|  | convert_num_unit((float)alloc_cpus, tmp_used_cpus, | 
|  | sizeof(tmp_used_cpus), UNIT_NONE, NO_VAL, | 
|  | working_sview_config.convert_flags); | 
|  |  | 
|  | snprintf(tmp_used_memory, sizeof(tmp_used_memory), "%"PRIu64"M", | 
|  | node_ptr->alloc_memory); | 
|  |  | 
|  | convert_num_unit((float)alloc_cpus, tmp_used_cpus, | 
|  | sizeof(tmp_used_cpus), UNIT_NONE, NO_VAL, | 
|  | working_sview_config.convert_flags); | 
|  |  | 
|  | convert_num_unit((float)idle_cpus, tmp_idle_cpus, sizeof(tmp_idle_cpus), | 
|  | UNIT_NONE, NO_VAL, working_sview_config.convert_flags); | 
|  |  | 
|  | tmp_state_upper = node_state_string(node_ptr->node_state); | 
|  | tmp_state_lower = str_tolower(tmp_state_upper); | 
|  |  | 
|  | tmp_state_complete = node_state_string_complete(node_ptr->node_state); | 
|  | xstrtolower(tmp_state_complete); | 
|  |  | 
|  | convert_num_unit((float)node_ptr->real_memory, tmp_mem, sizeof(tmp_mem), | 
|  | UNIT_MEGA, NO_VAL, working_sview_config.convert_flags); | 
|  |  | 
|  | convert_num_unit((float)node_ptr->tmp_disk, tmp_disk, sizeof(tmp_disk), | 
|  | UNIT_MEGA, NO_VAL, working_sview_config.convert_flags); | 
|  |  | 
|  | if (node_ptr->version == NULL) { | 
|  | snprintf(tmp_version, sizeof(tmp_version), "N/A"); | 
|  | } else { | 
|  | snprintf(tmp_version, sizeof(tmp_version), "%s", | 
|  | node_ptr->version); | 
|  | } | 
|  |  | 
|  | if (node_ptr->owner == NO_VAL) { | 
|  | snprintf(tmp_owner, sizeof(tmp_owner), "N/A"); | 
|  | } else { | 
|  | char *user_name; | 
|  | user_name = uid_to_string((uid_t) node_ptr->owner); | 
|  | snprintf(tmp_owner, sizeof(tmp_owner), "%s(%u)", | 
|  | user_name, node_ptr->owner); | 
|  | xfree(user_name); | 
|  | } | 
|  |  | 
|  | /* Combining these records provides a slight performance improvement */ | 
|  | gtk_tree_store_set(treestore, &sview_node_info_ptr->iter_ptr, | 
|  | SORTID_ACTIVE_FEATURES, node_ptr->features_act, | 
|  | SORTID_ARCH,      node_ptr->arch, | 
|  | SORTID_AVAIL_FEATURES,  node_ptr->features, | 
|  | SORTID_AVE_WATTS, tmp_ave_watts, | 
|  | SORTID_BOARDS,    node_ptr->boards, | 
|  | SORTID_BOOT_TIME, sview_node_info_ptr->boot_time, | 
|  | SORTID_CLUSTER_NAME, node_ptr->cluster_name, | 
|  | SORTID_COLOR, | 
|  | sview_colors[sview_node_info_ptr->pos | 
|  | % sview_colors_cnt], | 
|  | SORTID_COMMENT,   node_ptr->comment, | 
|  | SORTID_CORES,     node_ptr->cores, | 
|  | SORTID_CPUS,      tmp_cpus, | 
|  | SORTID_CURRENT_WATTS, tmp_current_watts, | 
|  | SORTID_CPU_LOAD,  tmp_cpu_load, | 
|  | SORTID_EXTRA, node_ptr->extra, | 
|  | SORTID_FREE_MEM,  tmp_free_mem, | 
|  | SORTID_TMP_DISK,  tmp_disk, | 
|  | SORTID_IDLE_CPUS, tmp_idle_cpus, | 
|  | SORTID_GRES,      node_ptr->gres, | 
|  | SORTID_MCS_LABEL, (node_ptr->mcs_label == NULL) ? | 
|  | "N/A" : node_ptr->mcs_label, | 
|  | SORTID_REAL_MEMORY, tmp_mem, | 
|  | SORTID_NAME,      node_ptr->name, | 
|  | SORTID_NODE_ADDR, node_ptr->node_addr, | 
|  | SORTID_NODE_HOSTNAME, node_ptr->node_hostname, | 
|  | SORTID_OWNER,     tmp_owner, | 
|  | SORTID_REASON,    sview_node_info_ptr->reason, | 
|  | SORTID_RES_CORES_PER_GPU, | 
|  | node_ptr->res_cores_per_gpu, | 
|  | SORTID_RESV_NAME, node_ptr->resv_name, | 
|  | SORTID_SLURMD_START_TIME, | 
|  | sview_node_info_ptr->slurmd_start_time, | 
|  | SORTID_SOCKETS,   node_ptr->sockets, | 
|  | SORTID_STATE,     tmp_state_lower, | 
|  | SORTID_STATE_COMPLETE, tmp_state_complete, | 
|  | SORTID_STATE_NUM, node_ptr->node_state, | 
|  | SORTID_THREADS,   node_ptr->threads, | 
|  | SORTID_TRES_ALLOC, node_ptr->alloc_tres_fmt_str ? | 
|  | node_ptr->alloc_tres_fmt_str : "", | 
|  | SORTID_TRES_CONFIG, node_ptr->tres_fmt_str, | 
|  | SORTID_USED_CPUS, tmp_used_cpus, | 
|  | SORTID_USED_MEMORY, tmp_used_memory, | 
|  | SORTID_VERSION,   tmp_version, | 
|  | SORTID_WEIGHT,    node_ptr->weight, | 
|  | SORTID_UPDATED,   1, | 
|  | -1); | 
|  |  | 
|  | xfree(tmp_state_complete); | 
|  | xfree(tmp_state_lower); | 
|  | return; | 
|  | } | 
|  |  | 
|  | static void _append_node_record(sview_node_info_t *sview_node_info, | 
|  | GtkTreeStore *treestore) | 
|  | { | 
|  | gtk_tree_store_append(treestore, &sview_node_info->iter_ptr, NULL); | 
|  | gtk_tree_store_set(treestore, &sview_node_info->iter_ptr, SORTID_POS, | 
|  | sview_node_info->pos, -1); | 
|  | _update_node_record(sview_node_info, treestore); | 
|  | } | 
|  |  | 
|  | static void _update_info_node(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_node_info_t *sview_node_info = NULL; | 
|  |  | 
|  | set_for_update(model, SORTID_UPDATED); | 
|  |  | 
|  | itr = list_iterator_create(info_list); | 
|  | while ((sview_node_info = list_next(itr))) { | 
|  | /* This means the tree_store changed (added new column | 
|  | * or something). */ | 
|  | if (last_model != model) | 
|  | sview_node_info->iter_set = false; | 
|  |  | 
|  | if (sview_node_info->iter_set) { | 
|  | gtk_tree_model_get(model, &sview_node_info->iter_ptr, | 
|  | SORTID_NAME, &name, -1); | 
|  | if (xstrcmp(name, sview_node_info->node_name)) { | 
|  | /* Bad pointer */ | 
|  | sview_node_info->iter_set = false; | 
|  | //g_print("bad node iter pointer\n"); | 
|  | } | 
|  | g_free(name); | 
|  | } | 
|  | if (sview_node_info->iter_set) | 
|  | _update_node_record(sview_node_info, | 
|  | GTK_TREE_STORE(model)); | 
|  | else { | 
|  | _append_node_record(sview_node_info, | 
|  | GTK_TREE_STORE(model)); | 
|  | sview_node_info->iter_set = true; | 
|  | } | 
|  | } | 
|  | list_iterator_destroy(itr); | 
|  |  | 
|  | /* remove all old nodes */ | 
|  | remove_old(model, SORTID_UPDATED); | 
|  | last_model = model; | 
|  | } | 
|  |  | 
|  | static void _node_info_free(sview_node_info_t *sview_node_info) | 
|  | { | 
|  | if (sview_node_info) { | 
|  | xfree(sview_node_info->slurmd_start_time); | 
|  | xfree(sview_node_info->boot_time); | 
|  | xfree(sview_node_info->node_name); | 
|  | xfree(sview_node_info->rack_mp); | 
|  | xfree(sview_node_info->reason); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void _node_info_list_del(void *object) | 
|  | { | 
|  | sview_node_info_t *sview_node_info = (sview_node_info_t *)object; | 
|  |  | 
|  | if (sview_node_info) { | 
|  | _node_info_free(sview_node_info); | 
|  | xfree(sview_node_info); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void _display_info_node(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; | 
|  | node_info_t *node_ptr = NULL; | 
|  | GtkTreeView *treeview = NULL; | 
|  | int update = 0; | 
|  | list_itr_t *itr = NULL; | 
|  | sview_node_info_t *sview_node_info = NULL; | 
|  | int i = -1; | 
|  |  | 
|  | if (!spec_info->search_info->gchar_data) { | 
|  | goto finished; | 
|  | } | 
|  | need_refresh: | 
|  | 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_node_info = list_next(itr))) { | 
|  | node_ptr = sview_node_info->node_ptr; | 
|  | i++; | 
|  | if (!xstrcmp(node_ptr->name, name)) { | 
|  | change_grid_color(popup_win->grid_button_list, | 
|  | i, i, i, true, 0); | 
|  | _layout_node_record(treeview, sview_node_info, update); | 
|  | found = 1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | list_iterator_destroy(itr); | 
|  | if (!found) { | 
|  | if (!popup_win->not_found) { | 
|  | char *temp; | 
|  | GtkTreeIter iter; | 
|  | GtkTreeModel *model = NULL; | 
|  |  | 
|  | temp = "NODE NOT FOUND\n"; | 
|  | /* only time this will be run so no update */ | 
|  | model = gtk_tree_view_get_model(treeview); | 
|  | add_display_treestore_line(0, | 
|  | GTK_TREE_STORE(model), | 
|  | &iter, | 
|  | temp, ""); | 
|  | } | 
|  | popup_win->not_found = true; | 
|  | } else { | 
|  | if (popup_win->not_found) { | 
|  | popup_win->not_found = false; | 
|  | gtk_widget_destroy(spec_info->display_widget); | 
|  |  | 
|  | goto need_refresh; | 
|  | } | 
|  | } | 
|  | gtk_widget_show(spec_info->display_widget); | 
|  |  | 
|  | finished: | 
|  | return; | 
|  | } | 
|  |  | 
|  | static void _selected_page(GtkMenuItem *menuitem, | 
|  | display_data_t *display_data) | 
|  | { | 
|  | switch(display_data->extra) { | 
|  | case NODE_PAGE: | 
|  | popup_all_node_name(display_data->user_data, display_data->id, | 
|  | NULL); | 
|  | break; | 
|  | case ADMIN_PAGE: | 
|  | admin_node_name(display_data->user_data, | 
|  | NULL, display_data->name); | 
|  | break; | 
|  | default: | 
|  | g_print("node got %d %d\n", display_data->extra, | 
|  | display_data->id); | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | static void _process_each_node(GtkTreeModel *model, GtkTreePath *path, | 
|  | GtkTreeIter *iter, gpointer userdata) | 
|  | { | 
|  | char *name = NULL; | 
|  | process_node_t *process_node = userdata; | 
|  |  | 
|  | gtk_tree_model_get(model, iter, process_node->node_col, &name, -1); | 
|  | if (process_node->nodelist) | 
|  | xstrfmtcat(process_node->nodelist, ",%s", name); | 
|  | else | 
|  | process_node->nodelist = xstrdup(name); | 
|  | g_free(name); | 
|  | } | 
|  | /*process_each_node ^^^*/ | 
|  |  | 
|  |  | 
|  |  | 
|  | extern void refresh_node(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_node(popup_win); | 
|  | } | 
|  |  | 
|  | /* don't destroy the list from this function */ | 
|  | extern list_t *create_node_info_list(node_info_msg_t *node_info_ptr, | 
|  | bool by_partition) | 
|  | { | 
|  | static list_t *info_list = NULL; | 
|  | static node_info_msg_t *last_node_info_ptr = NULL; | 
|  | list_t *last_list = NULL; | 
|  | list_itr_t *last_list_itr = NULL; | 
|  | int i = 0; | 
|  | sview_node_info_t *sview_node_info_ptr = NULL; | 
|  | node_info_t *node_ptr = NULL; | 
|  | char time_str[256]; | 
|  |  | 
|  | if (!by_partition) { | 
|  | if (!node_info_ptr | 
|  | || (info_list && (node_info_ptr == last_node_info_ptr))) | 
|  | goto update_color; | 
|  | } | 
|  |  | 
|  | last_node_info_ptr = node_info_ptr; | 
|  |  | 
|  | if (info_list) | 
|  | last_list = info_list; | 
|  |  | 
|  | info_list = list_create(_node_info_list_del); | 
|  |  | 
|  | if (last_list) | 
|  | last_list_itr = list_iterator_create(last_list); | 
|  | for (i=0; i<node_info_ptr->record_count; i++) { | 
|  | node_ptr = &(node_info_ptr->node_array[i]); | 
|  |  | 
|  | if (!node_ptr->name || (node_ptr->name[0] == '\0')) | 
|  | continue; | 
|  |  | 
|  | sview_node_info_ptr = NULL; | 
|  |  | 
|  | if (last_list_itr) { | 
|  | while ((sview_node_info_ptr = | 
|  | list_next(last_list_itr))) { | 
|  | if (!xstrcmp(sview_node_info_ptr->node_name, | 
|  | node_ptr->name)) { | 
|  | list_remove(last_list_itr); | 
|  | _node_info_free(sview_node_info_ptr); | 
|  | break; | 
|  | } | 
|  | } | 
|  | list_iterator_reset(last_list_itr); | 
|  | } | 
|  |  | 
|  | /* constrain list to included partitions' nodes */ | 
|  | /* and there are excluded values to process */ | 
|  | /* and user has not requested to show hidden */ | 
|  | /* if (by_partition && apply_partition_check */ | 
|  | /*     && !working_sview_config.show_hidden */ | 
|  | /*     && !check_part_includes_node(i)) */ | 
|  | /* 	continue; */ | 
|  |  | 
|  | if (!sview_node_info_ptr) | 
|  | sview_node_info_ptr = | 
|  | xmalloc(sizeof(sview_node_info_t)); | 
|  | list_append(info_list, sview_node_info_ptr); | 
|  | sview_node_info_ptr->node_name = xstrdup(node_ptr->name); | 
|  | sview_node_info_ptr->node_ptr = node_ptr; | 
|  | sview_node_info_ptr->pos = i; | 
|  |  | 
|  | if (node_ptr->reason && | 
|  | (node_ptr->reason_uid != NO_VAL) && node_ptr->reason_time) { | 
|  | char *user = uid_to_string(node_ptr->reason_uid); | 
|  | slurm_make_time_str(&node_ptr->reason_time, | 
|  | time_str, sizeof(time_str)); | 
|  | sview_node_info_ptr->reason = xstrdup_printf( | 
|  | "%s [%s@%s]", node_ptr->reason, user, time_str); | 
|  | xfree(user); | 
|  | } else if (node_ptr->reason) | 
|  | sview_node_info_ptr->reason = xstrdup(node_ptr->reason); | 
|  |  | 
|  | if (node_ptr->boot_time) { | 
|  | slurm_make_time_str(&node_ptr->boot_time, | 
|  | time_str, sizeof(time_str)); | 
|  | sview_node_info_ptr->boot_time = xstrdup(time_str); | 
|  | } | 
|  | if (node_ptr->slurmd_start_time) { | 
|  | slurm_make_time_str(&node_ptr->slurmd_start_time, | 
|  | time_str, sizeof(time_str)); | 
|  | sview_node_info_ptr->slurmd_start_time = | 
|  | xstrdup(time_str); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (last_list) { | 
|  | list_iterator_destroy(last_list_itr); | 
|  | FREE_NULL_LIST(last_list); | 
|  | } | 
|  |  | 
|  | update_color: | 
|  |  | 
|  | return info_list; | 
|  | } | 
|  |  | 
|  | extern int get_new_info_node(node_info_msg_t **info_ptr, int force) | 
|  | { | 
|  | node_info_msg_t *new_node_ptr = NULL; | 
|  | uint16_t show_flags = SHOW_MIXED; | 
|  | int error_code = SLURM_NO_CHANGE_IN_DATA; | 
|  | time_t now = time(NULL), delay; | 
|  | static time_t last; | 
|  | static uint16_t last_flags = 0; | 
|  |  | 
|  | delay = now - last; | 
|  | if (delay < 2) { | 
|  | /* Avoid re-loading node information within 2 secs as the data | 
|  | * may still be in use. If we load new node data and free the | 
|  | * old data while it it still in use, the result is likely | 
|  | * invalid memory references. */ | 
|  | force = 0; | 
|  | /* FIXME: Add an "in use" flag, copy the data, or otherwise | 
|  | * permit the timely loading of new node information. */ | 
|  | } | 
|  |  | 
|  | if (g_node_info_ptr && !force && | 
|  | (delay < working_sview_config.refresh_delay)) { | 
|  | if (*info_ptr != g_node_info_ptr) | 
|  | error_code = SLURM_SUCCESS; | 
|  | *info_ptr = g_node_info_ptr; | 
|  | return error_code; | 
|  | } | 
|  | last = now; | 
|  |  | 
|  | if (cluster_flags & CLUSTER_FLAG_FED) | 
|  | show_flags |= SHOW_FEDERATION; | 
|  | //if (working_sview_config.show_hidden) | 
|  | show_flags |= SHOW_ALL; | 
|  | if (g_node_info_ptr) { | 
|  | if (show_flags != last_flags) | 
|  | g_node_info_ptr->last_update = 0; | 
|  | error_code = slurm_load_node(g_node_info_ptr->last_update, | 
|  | &new_node_ptr, show_flags); | 
|  | if (error_code == SLURM_SUCCESS) { | 
|  | slurm_free_node_info_msg(g_node_info_ptr); | 
|  | } else if (errno == SLURM_NO_CHANGE_IN_DATA) { | 
|  | error_code = SLURM_NO_CHANGE_IN_DATA; | 
|  | new_node_ptr = g_node_info_ptr; | 
|  | } | 
|  | } else { | 
|  | new_node_ptr = NULL; | 
|  | error_code = slurm_load_node((time_t) NULL, &new_node_ptr, | 
|  | show_flags); | 
|  | } | 
|  |  | 
|  | last_flags = show_flags; | 
|  | g_node_info_ptr = new_node_ptr; | 
|  |  | 
|  | if (g_node_info_ptr && (*info_ptr != g_node_info_ptr)) | 
|  | error_code = SLURM_SUCCESS; | 
|  |  | 
|  | *info_ptr = g_node_info_ptr; | 
|  |  | 
|  | if (!g_topo_info_msg_ptr && | 
|  | default_sview_config.grid_topological) { | 
|  | get_topo_conf(); /* pull in topology NOW */ | 
|  | } | 
|  |  | 
|  | return error_code; | 
|  | } | 
|  |  | 
|  | extern int update_active_features_node(GtkDialog *dialog, const char *nodelist, | 
|  | const char *old_features) | 
|  | { | 
|  | char tmp_char[100]; | 
|  | char *edit = NULL; | 
|  | GtkWidget *entry = NULL; | 
|  | GtkWidget *label = NULL; | 
|  | update_node_msg_t *node_msg = xmalloc(sizeof(update_node_msg_t)); | 
|  | int response = 0; | 
|  | int no_dialog = 0; | 
|  | int rc = SLURM_SUCCESS; | 
|  |  | 
|  |  | 
|  | if (_DEBUG) | 
|  | g_print("update_active_features_node:global_row_count: %d " | 
|  | "node_names %s\n", | 
|  | global_row_count, nodelist); | 
|  | if (!dialog) { | 
|  | snprintf(tmp_char, sizeof(tmp_char), | 
|  | "Update Active Features for Node(s) %s?", | 
|  | nodelist); | 
|  |  | 
|  | dialog = GTK_DIALOG( | 
|  | gtk_dialog_new_with_buttons( | 
|  | tmp_char, | 
|  | GTK_WINDOW(main_window), | 
|  | GTK_DIALOG_MODAL | 
|  | | GTK_DIALOG_DESTROY_WITH_PARENT, | 
|  | NULL)); | 
|  | no_dialog = 1; | 
|  | } | 
|  | label = gtk_dialog_add_button(dialog, | 
|  | GTK_STOCK_YES, GTK_RESPONSE_OK); | 
|  | gtk_window_set_type_hint(GTK_WINDOW(dialog), | 
|  | GDK_WINDOW_TYPE_HINT_NORMAL); | 
|  | gtk_window_set_default(GTK_WINDOW(dialog), label); | 
|  | gtk_dialog_add_button(dialog, | 
|  | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); | 
|  |  | 
|  | slurm_init_update_node_msg(node_msg); | 
|  | node_msg->node_names = xstrdup(nodelist); | 
|  |  | 
|  | snprintf(tmp_char, sizeof(tmp_char), | 
|  | "Active Features for Node(s) %s?", nodelist); | 
|  | label = gtk_label_new(tmp_char); | 
|  | gtk_box_pack_start(GTK_BOX(dialog->vbox), | 
|  | label, false, false, 0); | 
|  |  | 
|  | entry = create_entry(); | 
|  | if (!entry) | 
|  | goto end_it; | 
|  |  | 
|  | if (old_features) | 
|  | gtk_entry_set_text(GTK_ENTRY(entry), old_features); | 
|  |  | 
|  | gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, true, true, 0); | 
|  | gtk_widget_show_all(GTK_WIDGET(dialog)); | 
|  |  | 
|  | response = gtk_dialog_run(dialog); | 
|  | if (response == GTK_RESPONSE_OK) { | 
|  | node_msg->features_act = | 
|  | xstrdup(gtk_entry_get_text(GTK_ENTRY(entry))); | 
|  | if (!node_msg->features_act) { | 
|  | edit = g_strdup_printf("No features given."); | 
|  | display_edit_note(edit); | 
|  | g_free(edit); | 
|  | goto end_it; | 
|  | } | 
|  | if ((rc = slurm_update_node(node_msg)) == SLURM_SUCCESS) { | 
|  | edit = g_strdup_printf( | 
|  | "Node(s) %s updated successfully.", | 
|  | nodelist); | 
|  | display_edit_note(edit); | 
|  | g_free(edit); | 
|  |  | 
|  | } else { | 
|  | edit = g_strdup_printf( | 
|  | "Problem updating node(s) %s: %s", | 
|  | nodelist, slurm_strerror(rc)); | 
|  | display_edit_note(edit); | 
|  | g_free(edit); | 
|  | } | 
|  | } | 
|  |  | 
|  | end_it: | 
|  | slurm_free_update_node_msg(node_msg); | 
|  | if (no_dialog) | 
|  | gtk_widget_destroy(GTK_WIDGET(dialog)); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | extern int update_avail_features_node(GtkDialog *dialog, const char *nodelist, | 
|  | const char *old_features) | 
|  | { | 
|  | char tmp_char[100]; | 
|  | char *edit = NULL; | 
|  | GtkWidget *entry = NULL; | 
|  | GtkWidget *label = NULL; | 
|  | update_node_msg_t *node_msg = xmalloc(sizeof(update_node_msg_t)); | 
|  | int response = 0; | 
|  | int no_dialog = 0; | 
|  | int rc = SLURM_SUCCESS; | 
|  |  | 
|  |  | 
|  | if (_DEBUG) | 
|  | g_print("update_avail_features_node:global_row_count: %d " | 
|  | "node_names %s\n", | 
|  | global_row_count, nodelist); | 
|  | if (!dialog) { | 
|  | snprintf(tmp_char, sizeof(tmp_char), | 
|  | "Update Available Features for Node(s) %s?", | 
|  | nodelist); | 
|  |  | 
|  | dialog = GTK_DIALOG( | 
|  | gtk_dialog_new_with_buttons( | 
|  | tmp_char, | 
|  | GTK_WINDOW(main_window), | 
|  | GTK_DIALOG_MODAL | 
|  | | GTK_DIALOG_DESTROY_WITH_PARENT, | 
|  | NULL)); | 
|  | no_dialog = 1; | 
|  | } | 
|  | label = gtk_dialog_add_button(dialog, | 
|  | GTK_STOCK_YES, GTK_RESPONSE_OK); | 
|  | gtk_window_set_type_hint(GTK_WINDOW(dialog), | 
|  | GDK_WINDOW_TYPE_HINT_NORMAL); | 
|  | gtk_window_set_default(GTK_WINDOW(dialog), label); | 
|  | gtk_dialog_add_button(dialog, | 
|  | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); | 
|  |  | 
|  | slurm_init_update_node_msg(node_msg); | 
|  | node_msg->node_names = xstrdup(nodelist); | 
|  |  | 
|  | snprintf(tmp_char, sizeof(tmp_char), | 
|  | "Available Features for Node(s) %s?", nodelist); | 
|  | label = gtk_label_new(tmp_char); | 
|  | gtk_box_pack_start(GTK_BOX(dialog->vbox), | 
|  | label, false, false, 0); | 
|  |  | 
|  | entry = create_entry(); | 
|  | if (!entry) | 
|  | goto end_it; | 
|  |  | 
|  | if (old_features) | 
|  | gtk_entry_set_text(GTK_ENTRY(entry), old_features); | 
|  |  | 
|  | gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, true, true, 0); | 
|  | gtk_widget_show_all(GTK_WIDGET(dialog)); | 
|  |  | 
|  | response = gtk_dialog_run(dialog); | 
|  | if (response == GTK_RESPONSE_OK) { | 
|  | node_msg->features = | 
|  | xstrdup(gtk_entry_get_text(GTK_ENTRY(entry))); | 
|  | if (!node_msg->features) { | 
|  | edit = g_strdup_printf("No features given."); | 
|  | display_edit_note(edit); | 
|  | g_free(edit); | 
|  | goto end_it; | 
|  | } | 
|  | if ((rc = slurm_update_node(node_msg)) == SLURM_SUCCESS) { | 
|  | edit = g_strdup_printf( | 
|  | "Node(s) %s updated successfully.", | 
|  | nodelist); | 
|  | display_edit_note(edit); | 
|  | g_free(edit); | 
|  |  | 
|  | } else { | 
|  | edit = g_strdup_printf( | 
|  | "Problem updating node(s) %s: %s", | 
|  | nodelist, slurm_strerror(rc)); | 
|  | display_edit_note(edit); | 
|  | g_free(edit); | 
|  | } | 
|  | } | 
|  |  | 
|  | end_it: | 
|  | slurm_free_update_node_msg(node_msg); | 
|  | if (no_dialog) | 
|  | gtk_widget_destroy(GTK_WIDGET(dialog)); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | extern int update_gres_node(GtkDialog *dialog, const char *nodelist, | 
|  | const char *old_gres) | 
|  | { | 
|  | char tmp_char[100]; | 
|  | char *edit = NULL; | 
|  | GtkWidget *entry = NULL; | 
|  | GtkWidget *label = NULL; | 
|  | update_node_msg_t *node_msg = xmalloc(sizeof(update_node_msg_t)); | 
|  | int response = 0; | 
|  | int no_dialog = 0; | 
|  | int rc = SLURM_SUCCESS; | 
|  |  | 
|  | if (_DEBUG) | 
|  | g_print("update_gres_node:global_row_count:" | 
|  | " %d node_names %s\n", | 
|  | global_row_count, nodelist); | 
|  | if (!dialog) { | 
|  | snprintf(tmp_char, sizeof(tmp_char), | 
|  | "Update Gres for Node(s) %s?", | 
|  | nodelist); | 
|  |  | 
|  | dialog = GTK_DIALOG( | 
|  | gtk_dialog_new_with_buttons( | 
|  | tmp_char, | 
|  | GTK_WINDOW(main_window), | 
|  | GTK_DIALOG_MODAL | 
|  | | GTK_DIALOG_DESTROY_WITH_PARENT, | 
|  | NULL)); | 
|  | no_dialog = 1; | 
|  | } | 
|  | label = gtk_dialog_add_button(dialog, GTK_STOCK_YES, GTK_RESPONSE_OK); | 
|  | gtk_window_set_type_hint(GTK_WINDOW(dialog), | 
|  | GDK_WINDOW_TYPE_HINT_NORMAL); | 
|  | gtk_window_set_default(GTK_WINDOW(dialog), label); | 
|  | gtk_dialog_add_button(dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); | 
|  |  | 
|  | slurm_init_update_node_msg(node_msg); | 
|  | node_msg->node_names = xstrdup(nodelist); | 
|  |  | 
|  | snprintf(tmp_char, sizeof(tmp_char), "Gres for Node(s) %s?", nodelist); | 
|  |  | 
|  | label = gtk_label_new(tmp_char); | 
|  | gtk_box_pack_start(GTK_BOX(dialog->vbox), label, false, false, 0); | 
|  |  | 
|  | entry = create_entry(); | 
|  | if (!entry) | 
|  | goto end_it; | 
|  |  | 
|  | if (old_gres) | 
|  | gtk_entry_set_text(GTK_ENTRY(entry), old_gres); | 
|  |  | 
|  | gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, true, true, 0); | 
|  | gtk_widget_show_all(GTK_WIDGET(dialog)); | 
|  |  | 
|  | response = gtk_dialog_run(dialog); | 
|  | if (response == GTK_RESPONSE_OK) { | 
|  | node_msg->gres = xstrdup(gtk_entry_get_text(GTK_ENTRY(entry))); | 
|  | if (!node_msg->gres) { | 
|  | edit = g_strdup_printf("No gres given."); | 
|  | display_edit_note(edit); | 
|  | g_free(edit); | 
|  | goto end_it; | 
|  | } | 
|  | if ((rc = slurm_update_node(node_msg)) == SLURM_SUCCESS) { | 
|  | edit = g_strdup_printf( | 
|  | "Nodes %s updated successfully.", | 
|  | nodelist); | 
|  | display_edit_note(edit); | 
|  | g_free(edit); | 
|  | } else { | 
|  | edit = g_strdup_printf( | 
|  | "Problem updating nodes %s: %s", | 
|  | nodelist, slurm_strerror(rc)); | 
|  | display_edit_note(edit); | 
|  | g_free(edit); | 
|  | } | 
|  | } | 
|  |  | 
|  | end_it: | 
|  | slurm_free_update_node_msg(node_msg); | 
|  | if (no_dialog) | 
|  | gtk_widget_destroy(GTK_WIDGET(dialog)); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | extern int update_state_node(GtkDialog *dialog, | 
|  | const char *nodelist, const char *type) | 
|  | { | 
|  | uint16_t state = NO_VAL16; | 
|  | char *upper = NULL, *lower = NULL; | 
|  | int i = 0; | 
|  | int rc = SLURM_SUCCESS; | 
|  | char tmp_char[100]; | 
|  | update_node_msg_t *node_msg = xmalloc(sizeof(update_node_msg_t)); | 
|  | GtkWidget *label = NULL; | 
|  | GtkWidget *entry = NULL; | 
|  | int no_dialog = 0; | 
|  |  | 
|  | if (!dialog) { | 
|  | dialog = GTK_DIALOG( | 
|  | gtk_dialog_new_with_buttons( | 
|  | type, | 
|  | GTK_WINDOW(main_window), | 
|  | GTK_DIALOG_MODAL | 
|  | | GTK_DIALOG_DESTROY_WITH_PARENT, | 
|  | NULL)); | 
|  | no_dialog = 1; | 
|  | } | 
|  | label = gtk_dialog_add_button(dialog, GTK_STOCK_YES, GTK_RESPONSE_OK); | 
|  | gtk_window_set_type_hint(GTK_WINDOW(dialog), | 
|  | GDK_WINDOW_TYPE_HINT_NORMAL); | 
|  | gtk_window_set_default(GTK_WINDOW(dialog), label); | 
|  | gtk_dialog_add_button(dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); | 
|  |  | 
|  | slurm_init_update_node_msg(node_msg); | 
|  | node_msg->node_names = xstrdup(nodelist); | 
|  |  | 
|  | if (!xstrncasecmp("drain", type, 5)) { | 
|  | snprintf(tmp_char, sizeof(tmp_char), | 
|  | "Are you sure you want to drain node(s) %s?\n\n" | 
|  | "Please put reason.", | 
|  | nodelist); | 
|  | entry = create_entry(); | 
|  | label = gtk_label_new(tmp_char); | 
|  | state = NODE_STATE_DRAIN; | 
|  | } else if (!xstrncasecmp("resume", type, 5)) { | 
|  | snprintf(tmp_char, sizeof(tmp_char), | 
|  | "Are you sure you want to resume node(s) %s?", | 
|  | nodelist); | 
|  | label = gtk_label_new(tmp_char); | 
|  | state = NODE_RESUME; | 
|  | } else if (!xstrncasecmp("set", type, 3)) { | 
|  | snprintf(tmp_char, sizeof(tmp_char), | 
|  | "Are you sure you want to down node(s) %s?\n\n" | 
|  | "Please put reason.", | 
|  | nodelist); | 
|  | entry = create_entry(); | 
|  | label = gtk_label_new(tmp_char); | 
|  | state = NODE_STATE_DOWN; | 
|  | } else if (!xstrncasecmp("undrain", type, 5)) { | 
|  | snprintf(tmp_char, sizeof(tmp_char), | 
|  | "Are you sure you want to undrain node(s) %s?", | 
|  | nodelist); | 
|  | label = gtk_label_new(tmp_char); | 
|  | state = NODE_STATE_UNDRAIN; | 
|  | } else { | 
|  |  | 
|  | if (!xstrncasecmp("make", type, 4)) | 
|  | type = "idle"; | 
|  | for(i = 0; i < NODE_STATE_END; i++) { | 
|  | upper = node_state_string(i); | 
|  | lower = str_tolower(upper); | 
|  | if (!xstrcmp(lower, type)) { | 
|  | snprintf(tmp_char, sizeof(tmp_char), | 
|  | "Are you sure you want to set " | 
|  | "node(s) %s to %s?", | 
|  | nodelist, lower); | 
|  | label = gtk_label_new(tmp_char); | 
|  | state = i; | 
|  | xfree(lower); | 
|  | break; | 
|  | } | 
|  | xfree(lower); | 
|  | } | 
|  | } | 
|  | if (!label) | 
|  | goto end_it; | 
|  | node_msg->node_state = (uint16_t)state; | 
|  | gtk_box_pack_start(GTK_BOX(dialog->vbox), label, false, false, 0); | 
|  | if (entry) | 
|  | gtk_box_pack_start(GTK_BOX(dialog->vbox), entry, true, true, 0); | 
|  | gtk_widget_show_all(GTK_WIDGET(dialog)); | 
|  | i = gtk_dialog_run(dialog); | 
|  | if (i == GTK_RESPONSE_OK) { | 
|  | if (entry) { | 
|  | node_msg->reason = xstrdup( | 
|  | gtk_entry_get_text(GTK_ENTRY(entry))); | 
|  | if (!node_msg->reason || !strlen(node_msg->reason)) { | 
|  | lower = g_strdup_printf( | 
|  | "You need a reason to do that."); | 
|  | display_edit_note(lower); | 
|  | g_free(lower); | 
|  | goto end_it; | 
|  | } | 
|  | if (uid_from_string(getlogin(), | 
|  | &node_msg->reason_uid) != | 
|  | SLURM_SUCCESS) | 
|  | node_msg->reason_uid = getuid(); | 
|  |  | 
|  | } | 
|  | if ((rc = slurm_update_node(node_msg)) == SLURM_SUCCESS) { | 
|  | lower = g_strdup_printf( | 
|  | "Nodes %s updated successfully.", | 
|  | nodelist); | 
|  | display_edit_note(lower); | 
|  | g_free(lower); | 
|  | } else { | 
|  | lower = g_strdup_printf( | 
|  | "Problem updating nodes %s: %s", | 
|  | nodelist, slurm_strerror(rc)); | 
|  | display_edit_note(lower); | 
|  | g_free(lower); | 
|  | } | 
|  | } | 
|  | end_it: | 
|  | slurm_free_update_node_msg(node_msg); | 
|  | if (no_dialog) | 
|  | gtk_widget_destroy(GTK_WIDGET(dialog)); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | extern GtkListStore *create_model_node(int type) | 
|  | { | 
|  | GtkListStore *model = NULL; | 
|  | GtkTreeIter iter; | 
|  | char *upper = NULL, *lower = NULL; | 
|  | int i=0; | 
|  |  | 
|  | last_model = NULL;	/* Reformat display */ | 
|  | switch(type) { | 
|  | case SORTID_STATE: | 
|  | model = gtk_list_store_new(2, G_TYPE_STRING, | 
|  | G_TYPE_INT); | 
|  | gtk_list_store_append(model, &iter); | 
|  | gtk_list_store_set(model, &iter, | 
|  | 0, "drain", | 
|  | 1, i, | 
|  | -1); | 
|  | gtk_list_store_append(model, &iter); | 
|  | gtk_list_store_set(model, &iter, | 
|  | 0, "NoResp", | 
|  | 1, i, | 
|  | -1); | 
|  | gtk_list_store_append(model, &iter); | 
|  | gtk_list_store_set(model, &iter, | 
|  | 0, "resume", | 
|  | 1, i, | 
|  | -1); | 
|  | gtk_list_store_append(model, &iter); | 
|  | gtk_list_store_set(model, &iter, | 
|  | 0, "undrain", | 
|  | 1, i, | 
|  | -1); | 
|  | for(i = 0; i < NODE_STATE_END; i++) { | 
|  | upper = node_state_string(i); | 
|  | gtk_list_store_append(model, &iter); | 
|  | lower = str_tolower(upper); | 
|  | gtk_list_store_set(model, &iter, | 
|  | 0, lower, | 
|  | 1, i, | 
|  | -1); | 
|  | xfree(lower); | 
|  | } | 
|  |  | 
|  | break; | 
|  |  | 
|  | } | 
|  | return model; | 
|  | } | 
|  |  | 
|  | extern void admin_edit_node(GtkCellRendererText *cell, | 
|  | const char *path_string, | 
|  | const char *new_text, | 
|  | gpointer data) | 
|  | { | 
|  | GtkTreeStore *treestore = NULL; | 
|  | GtkTreePath *path = NULL; | 
|  | GtkTreeIter iter; | 
|  | char *nodelist = NULL; | 
|  | int column; | 
|  |  | 
|  | if (!new_text || !xstrcmp(new_text, "")) | 
|  | goto no_input; | 
|  |  | 
|  | if (cluster_flags & CLUSTER_FLAG_FED) { | 
|  | display_fed_disabled_popup(new_text); | 
|  | goto no_input; | 
|  | } | 
|  |  | 
|  | treestore = GTK_TREE_STORE(data); | 
|  | path = gtk_tree_path_new_from_string(path_string); | 
|  | gtk_tree_model_get_iter(GTK_TREE_MODEL(treestore), &iter, path); | 
|  |  | 
|  | column = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column")); | 
|  | switch(column) { | 
|  | case SORTID_STATE: | 
|  | gtk_tree_model_get(GTK_TREE_MODEL(treestore), &iter, | 
|  | SORTID_NAME, | 
|  | &nodelist, -1); | 
|  |  | 
|  | update_state_node(NULL, nodelist, new_text); | 
|  |  | 
|  | g_free(nodelist); | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | gtk_tree_path_free(path); | 
|  | no_input: | 
|  | g_mutex_unlock(sview_mutex); | 
|  | } | 
|  |  | 
|  | extern void get_info_node(GtkTable *table, display_data_t *display_data) | 
|  | { | 
|  | int error_code = SLURM_SUCCESS; | 
|  | static int view = -1; | 
|  | static node_info_msg_t *node_info_ptr = NULL; | 
|  | char error_char[100]; | 
|  | GtkWidget *label = NULL; | 
|  | GtkTreeView *tree_view = NULL; | 
|  | static GtkWidget *display_widget = NULL; | 
|  | list_t *info_list = NULL; | 
|  | int i = 0, sort_key; | 
|  | sview_node_info_t *sview_node_info_ptr = NULL; | 
|  | list_itr_t *itr = NULL; | 
|  | GtkTreePath *path = NULL; | 
|  | static bool set_opts = false; | 
|  |  | 
|  | if (!set_opts) | 
|  | set_page_opts(NODE_PAGE, display_data_node, | 
|  | SORTID_CNT, _initial_page_opts); | 
|  | set_opts = true; | 
|  |  | 
|  | /* reset */ | 
|  | if (!table && !display_data) { | 
|  | if (display_widget) | 
|  | gtk_widget_destroy(display_widget); | 
|  | display_widget = NULL; | 
|  | goto reset_curs; | 
|  | } | 
|  |  | 
|  | if (display_data) | 
|  | local_display_data = display_data; | 
|  | if (!table) { | 
|  | display_data_node->set_menu = local_display_data->set_menu; | 
|  | goto reset_curs; | 
|  | } | 
|  |  | 
|  | if (display_widget && toggled) { | 
|  | gtk_widget_destroy(display_widget); | 
|  | display_widget = NULL; | 
|  | /* Since the node_info_ptr could change out from under | 
|  | * us we always need to check if it is new or not. | 
|  | */ | 
|  | /* goto display_it; */ | 
|  | } | 
|  | if ((error_code = get_new_info_node(&node_info_ptr, force_refresh)) | 
|  | == SLURM_NO_CHANGE_IN_DATA) { | 
|  | if (!display_widget || view == ERROR_VIEW) | 
|  | goto display_it; | 
|  | } else if (error_code != SLURM_SUCCESS) { | 
|  | if (view == ERROR_VIEW) | 
|  | goto end_it; | 
|  | view = ERROR_VIEW; | 
|  | if (display_widget) | 
|  | gtk_widget_destroy(display_widget); | 
|  | sprintf(error_char, "slurm_load_node: %s", | 
|  | slurm_strerror(errno)); | 
|  | label = gtk_label_new(error_char); | 
|  | display_widget = g_object_ref(label); | 
|  | gtk_table_attach_defaults(table, label, 0, 1, 0, 1); | 
|  | gtk_widget_show(label); | 
|  | goto end_it; | 
|  | } | 
|  |  | 
|  | display_it: | 
|  | info_list = create_node_info_list(node_info_ptr, false); | 
|  | if (!info_list) | 
|  | goto reset_curs; | 
|  | i = 0; | 
|  | /* 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); | 
|  | } | 
|  | if (!path) { | 
|  | int array_size = node_info_ptr->record_count; | 
|  | int  *color_inx = xmalloc(sizeof(int) * array_size); | 
|  | bool *color_set_flag = xmalloc(sizeof(bool) * array_size); | 
|  | itr = list_iterator_create(info_list); | 
|  | while ((sview_node_info_ptr = list_next(itr))) { | 
|  | color_set_flag[i] = true; | 
|  | color_inx[i] = i; | 
|  | i++; | 
|  | } | 
|  | list_iterator_destroy(itr); | 
|  | change_grid_color_array(grid_button_list, array_size, | 
|  | color_inx, color_set_flag, true, 0); | 
|  | xfree(color_inx); | 
|  | xfree(color_set_flag); | 
|  | } else { | 
|  | highlight_grid(GTK_TREE_VIEW(display_widget), | 
|  | SORTID_POS, (int)NO_VAL, grid_button_list); | 
|  | gtk_tree_path_free(path); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | /*set multiple capability here*/ | 
|  | 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(GTK_TABLE(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 | 
|  | * On large clusters, sorting on the node name slows GTK down | 
|  | * by a large margin. */ | 
|  | if (node_info_ptr->record_count > 1000) | 
|  | sort_key = -1; | 
|  | else | 
|  | sort_key = SORTID_NAME; | 
|  | create_treestore(tree_view, display_data_node, | 
|  | SORTID_CNT, sort_key, SORTID_COLOR); | 
|  | } | 
|  |  | 
|  | view = INFO_VIEW; | 
|  | /* If the system has a large number of nodes then not all lines | 
|  | * will be displayed. You can try different values for the third | 
|  | * argument of gtk_widget_set_size_request() in an attempt to | 
|  | * maximumize the data displayed in your environment. These are my | 
|  | * results: Y=1000 good for 43 lines, Y=-1 good for 1151 lines, | 
|  | *  Y=64000 good for 2781 lines, Y=99000 good for 1453 lines */ | 
|  | /* gtk_widget_set_size_request(display_widget, -1, -1); */ | 
|  | _update_info_node(info_list, GTK_TREE_VIEW(display_widget)); | 
|  | end_it: | 
|  | toggled = false; | 
|  | force_refresh = 1; | 
|  | reset_curs: | 
|  | if (main_window && main_window->window) | 
|  | gdk_window_set_cursor(main_window->window, NULL); | 
|  | return; | 
|  |  | 
|  | } | 
|  |  | 
|  | extern void specific_info_node(popup_info_t *popup_win) | 
|  | { | 
|  | int error_code = SLURM_SUCCESS; | 
|  | static node_info_msg_t *node_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 *info_list = NULL; | 
|  | list_t *send_info_list = NULL; | 
|  | list_itr_t *itr = NULL; | 
|  | sview_node_info_t *sview_node_info_ptr = NULL; | 
|  | node_info_t *node_ptr = NULL; | 
|  | hostlist_t *hostlist = NULL; | 
|  | hostlist_iterator_t *host_itr = NULL; | 
|  | int i = -1, sort_key; | 
|  | sview_search_info_t *search_info = spec_info->search_info; | 
|  |  | 
|  | if (!spec_info->display_widget) | 
|  | setup_popup_info(popup_win, display_data_node, SORTID_CNT); | 
|  |  | 
|  | if (node_info_ptr && popup_win->toggled) { | 
|  | gtk_widget_destroy(spec_info->display_widget); | 
|  | spec_info->display_widget = NULL; | 
|  | /* Since the node_info_ptr could change out from under | 
|  | * us we always need to check if it is new or not. | 
|  | */ | 
|  | /* goto display_it; */ | 
|  | } | 
|  |  | 
|  | if ((error_code = get_new_info_node(&node_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 (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, "slurm_load_node: %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); | 
|  | return; | 
|  | } | 
|  | display_it: | 
|  |  | 
|  | info_list = create_node_info_list(node_info_ptr, false); | 
|  |  | 
|  | if (!info_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); | 
|  | /*set multiple capability here*/ | 
|  | 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 | 
|  | * On large clusters, sorting on the node name slows GTK down | 
|  | * by a large margin. */ | 
|  | if (node_info_ptr->record_count > 1000) | 
|  | sort_key = -1; | 
|  | else | 
|  | sort_key = SORTID_NAME; | 
|  | create_treestore(tree_view, popup_win->display_data, | 
|  | SORTID_CNT, sort_key, SORTID_COLOR); | 
|  | } | 
|  |  | 
|  | setup_popup_grid_list(popup_win); | 
|  |  | 
|  | spec_info->view = INFO_VIEW; | 
|  | if (spec_info->type == INFO_PAGE) { | 
|  | _display_info_node(info_list, popup_win); | 
|  | goto end_it; | 
|  | } | 
|  |  | 
|  | setup_popup_grid_list(popup_win); | 
|  |  | 
|  | /* just linking to another list, don't free the inside, just | 
|  | the list */ | 
|  | send_info_list = list_create(NULL); | 
|  | if (search_info->gchar_data) { | 
|  | hostlist = hostlist_create(search_info->gchar_data); | 
|  | host_itr = hostlist_iterator_create(hostlist); | 
|  | } | 
|  |  | 
|  | i = -1; | 
|  |  | 
|  | itr = list_iterator_create(info_list); | 
|  | while ((sview_node_info_ptr = list_next(itr))) { | 
|  | int found = 0; | 
|  | char *host = NULL; | 
|  | i++; | 
|  | node_ptr = sview_node_info_ptr->node_ptr; | 
|  |  | 
|  | switch(search_info->search_type) { | 
|  | case SEARCH_NODE_STATE: | 
|  | if (search_info->int_data == NO_VAL) | 
|  | continue; | 
|  | if ((node_ptr->node_state & NODE_STATE_BASE) == | 
|  | (search_info->int_data & NODE_STATE_BASE)) | 
|  | found = 1; | 
|  | if ((node_ptr->node_state & NODE_STATE_FLAGS) & | 
|  | (search_info->int_data & NODE_STATE_FLAGS)) | 
|  | found = 1; | 
|  | if (!found) | 
|  | continue; | 
|  | break; | 
|  | case SEARCH_NODE_NAME: | 
|  | default: | 
|  | if (!search_info->gchar_data) | 
|  | continue; | 
|  | while ((host = hostlist_next(host_itr))) { | 
|  | if (!xstrcmp(host, node_ptr->name)) { | 
|  | free(host); | 
|  | found = 1; | 
|  | break; | 
|  | } | 
|  | free(host); | 
|  | } | 
|  | hostlist_iterator_reset(host_itr); | 
|  |  | 
|  | if (!found) | 
|  | continue; | 
|  | break; | 
|  | } | 
|  | list_push(send_info_list, sview_node_info_ptr); | 
|  | change_grid_color(popup_win->grid_button_list, | 
|  | i, i, 0, true, 0); | 
|  | } | 
|  | list_iterator_destroy(itr); | 
|  | post_setup_popup_grid_list(popup_win); | 
|  |  | 
|  | if (search_info->gchar_data) { | 
|  | hostlist_iterator_destroy(host_itr); | 
|  | hostlist_destroy(hostlist); | 
|  | } | 
|  |  | 
|  | _update_info_node(send_info_list, | 
|  | GTK_TREE_VIEW(spec_info->display_widget)); | 
|  | FREE_NULL_LIST(send_info_list); | 
|  | end_it: | 
|  | popup_win->toggled = 0; | 
|  | popup_win->force_refresh = 0; | 
|  |  | 
|  | return; | 
|  |  | 
|  | } | 
|  |  | 
|  | extern void set_menus_node(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; | 
|  | list_t *button_list = arg2; | 
|  |  | 
|  | switch(type) { | 
|  | case TAB_CLICKED: | 
|  | make_fields_menu(NULL, menu, display_data_node, SORTID_CNT); | 
|  | break; | 
|  | case ROW_CLICKED: | 
|  | make_options_menu(tree_view, path, menu, options_data_node); | 
|  | break; | 
|  | case ROW_LEFT_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; | 
|  | } | 
|  | highlight_grid(tree_view, SORTID_POS, (int)NO_VAL, button_list); | 
|  | 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_node(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); | 
|  | } | 
|  | } | 
|  |  | 
|  | extern void popup_all_node(GtkTreeModel *model, GtkTreeIter *iter, int id) | 
|  | { | 
|  | char *name = NULL, *cluster_name = NULL; | 
|  |  | 
|  | gtk_tree_model_get(model, iter, SORTID_NAME, &name, -1); | 
|  | gtk_tree_model_get(model, iter, SORTID_CLUSTER_NAME, &cluster_name, -1); | 
|  | if (_DEBUG) | 
|  | g_print("popup_all_node: name = %s\n", name); | 
|  | popup_all_node_name(name, id, cluster_name); | 
|  | /* this name gets g_strdup'ed in the previous function */ | 
|  | g_free(name); | 
|  | g_free(cluster_name); | 
|  | } | 
|  |  | 
|  | extern void popup_all_node_name(char *name, int id, char *cluster_name) | 
|  | { | 
|  | char title[100] = {0}; | 
|  | list_itr_t *itr = NULL; | 
|  | popup_info_t *popup_win = NULL; | 
|  | GError *error = NULL; | 
|  |  | 
|  | switch(id) { | 
|  | case JOB_PAGE: | 
|  | snprintf(title, 100, "Job(s) with Node %s", name); | 
|  | break; | 
|  | case PART_PAGE: | 
|  | snprintf(title, 100, "Partition(s) with Node %s", name); | 
|  | break; | 
|  | case RESV_PAGE: | 
|  | snprintf(title, 100, "Reservation(s) with Node %s", name); | 
|  | break; | 
|  | case SUBMIT_PAGE: | 
|  | snprintf(title, 100, "Submit job on Node %s", name); | 
|  | break; | 
|  | case INFO_PAGE: | 
|  | snprintf(title, 100, "Full Info for Node %s", name); | 
|  | break; | 
|  | default: | 
|  | g_print("Node got %d\n", id); | 
|  | } | 
|  |  | 
|  | if (cluster_name && federation_name && | 
|  | (cluster_flags & CLUSTER_FLAG_FED)) { | 
|  | char *tmp_cname = | 
|  | xstrdup_printf(" (%s:%s)", | 
|  | federation_name, cluster_name); | 
|  | strncat(title, tmp_cname, sizeof(title) - strlen(title) - 1); | 
|  | xfree(tmp_cname); | 
|  | } | 
|  |  | 
|  | 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, NODE_PAGE, title); | 
|  | else | 
|  | popup_win = create_popup_info(NODE_PAGE, id, title); | 
|  | popup_win->spec_info->search_info->gchar_data = g_strdup(name); | 
|  |  | 
|  | if (cluster_flags & CLUSTER_FLAG_FED) | 
|  | popup_win->spec_info->search_info->cluster_name = | 
|  | g_strdup(cluster_name); | 
|  | if (!sview_thread_new((gpointer)popup_thr, popup_win, &error)) { | 
|  | g_printerr ("Failed to create node popup thread: " | 
|  | "%s\n", | 
|  | error->message); | 
|  | return; | 
|  | } | 
|  | } else | 
|  | gtk_window_present(GTK_WINDOW(popup_win->popup)); | 
|  | } | 
|  |  | 
|  | extern void admin_menu_node_name(char *name, GdkEventButton *event) | 
|  | { | 
|  | GtkMenu *menu = GTK_MENU(gtk_menu_new()); | 
|  | display_data_t *display_data = options_data_node; | 
|  | GtkWidget *menuitem; | 
|  |  | 
|  | while (display_data++) { | 
|  | if (display_data->id == -1) | 
|  | break; | 
|  | if (!display_data->name) | 
|  | continue; | 
|  |  | 
|  | display_data->user_data = name; | 
|  | menuitem = gtk_menu_item_new_with_label(display_data->name); | 
|  | g_signal_connect(menuitem, "activate", | 
|  | G_CALLBACK(_selected_page), | 
|  | display_data); | 
|  | gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); | 
|  | } | 
|  | gtk_widget_show_all(GTK_WIDGET(menu)); | 
|  | gtk_menu_popup(menu, NULL, NULL, NULL, NULL, | 
|  | event ? event->button : 0, | 
|  | gdk_event_get_time((GdkEvent*)event)); | 
|  | } | 
|  |  | 
|  | extern void select_admin_nodes(GtkTreeModel *model, | 
|  | GtkTreeIter *iter, | 
|  | display_data_t *display_data, | 
|  | uint32_t node_col, | 
|  | GtkTreeView *treeview) | 
|  | { | 
|  | if (treeview) { | 
|  | char *old_value = NULL; | 
|  | hostlist_t *hl = NULL; | 
|  | process_node_t process_node; | 
|  | memset(&process_node, 0, sizeof(process_node_t)); | 
|  | if (node_col == NO_VAL) | 
|  | process_node.node_col = SORTID_NAME; | 
|  | else | 
|  | process_node.node_col = node_col; | 
|  |  | 
|  | gtk_tree_selection_selected_foreach( | 
|  | gtk_tree_view_get_selection(treeview), | 
|  | _process_each_node, &process_node); | 
|  | hl = hostlist_create(process_node.nodelist); | 
|  | hostlist_uniq(hl); | 
|  | hostlist_sort(hl); | 
|  | xfree(process_node.nodelist); | 
|  | process_node.nodelist = hostlist_ranged_string_xmalloc(hl); | 
|  | hostlist_destroy(hl); | 
|  |  | 
|  | if (!xstrcasecmp("Update Features", display_data->name)) { | 
|  | /* get old features */ | 
|  | gtk_tree_model_get(model, iter, SORTID_AVAIL_FEATURES, | 
|  | &old_value, -1); | 
|  | } else if (!xstrcasecmp("Update Gres", display_data->name)) { | 
|  | /* get old gres */ | 
|  | gtk_tree_model_get(model, iter, SORTID_GRES, | 
|  | &old_value, -1); | 
|  | } | 
|  | admin_node_name(process_node.nodelist, old_value, | 
|  | display_data->name); | 
|  | xfree(process_node.nodelist); | 
|  | if (old_value) | 
|  | g_free(old_value); | 
|  |  | 
|  |  | 
|  | } | 
|  | } /*select_admin_nodes ^^^*/ | 
|  |  | 
|  | extern void admin_node_name(char *name, char *old_value, char *type) | 
|  | { | 
|  | GtkWidget *popup = NULL; | 
|  |  | 
|  | if (cluster_flags & CLUSTER_FLAG_FED) { | 
|  | display_fed_disabled_popup(type); | 
|  | return; | 
|  | } | 
|  |  | 
|  | popup = gtk_dialog_new_with_buttons( | 
|  | type, | 
|  | GTK_WINDOW(main_window), | 
|  | GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, | 
|  | NULL); | 
|  | gtk_window_set_type_hint(GTK_WINDOW(popup), | 
|  | GDK_WINDOW_TYPE_HINT_NORMAL); | 
|  | gtk_window_set_transient_for(GTK_WINDOW(popup), NULL); | 
|  |  | 
|  | if (!xstrcasecmp("Update Available Features", type) | 
|  | || !xstrcasecmp("Update Node Features", type) | 
|  | || !xstrcasecmp("Update Midplane Features", | 
|  | type)) { /* update features */ | 
|  | update_avail_features_node(GTK_DIALOG(popup), name, old_value); | 
|  | } else if (!xstrcasecmp("Update Active Features", type)) { | 
|  | update_active_features_node(GTK_DIALOG(popup), name, old_value); | 
|  | } else if (!xstrcasecmp("Update Gres", type)) { /* update gres */ | 
|  | update_gres_node(GTK_DIALOG(popup), name, old_value); | 
|  | } else /* something that has to deal with a node state change */ | 
|  | update_state_node(GTK_DIALOG(popup), name, type); | 
|  |  | 
|  | gtk_widget_destroy(popup); | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | extern void cluster_change_node(void) | 
|  | { | 
|  | display_data_t *display_data = display_data_node; | 
|  | while (display_data++) { | 
|  | if (display_data->id == -1) | 
|  | break; | 
|  | if (cluster_flags & CLUSTER_FLAG_FED) { | 
|  | switch(display_data->id) { | 
|  | case SORTID_CLUSTER_NAME: | 
|  | display_data->show = true; | 
|  | break; | 
|  | } | 
|  | } else { | 
|  | switch(display_data->id) { | 
|  | case SORTID_CLUSTER_NAME: | 
|  | display_data->show = false; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | get_info_node(NULL, NULL); | 
|  | } |