blob: 87951ca0763723a661e16f9250a9e5d641787cf3 [file] [log] [blame]
/*****************************************************************************\
* partition_info.c - get/print the partition state information of slurm
*****************************************************************************
* Copyright (C) 2002-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 Morris Jette <jette1@llnl.gov> et. al.
* 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.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "slurm/slurm.h"
#include "slurm/slurmdb.h"
#include "src/common/parse_time.h"
#include "src/common/read_config.h"
#include "src/common/slurm_protocol_api.h"
#include "src/common/slurm_resource_info.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/interfaces/select.h"
/* Data structures for pthreads used to gather partition information from
* multiple clusters in parallel */
typedef struct load_part_req_struct {
slurmdb_cluster_rec_t *cluster;
int cluster_inx;
slurm_msg_t *req_msg;
list_t *resp_msg_list;
uint16_t show_flags;
} load_part_req_struct_t;
typedef struct load_part_resp_struct {
int cluster_inx;
partition_info_msg_t *new_msg;
} load_part_resp_struct_t;
/*
* slurm_print_partition_info - output information about a specific Slurm
* partition based upon message as loaded using slurm_load_partitions
* IN out - file to write to
* IN part_ptr - an individual partition information record pointer
* IN one_liner - print as a single line if true
*/
void slurm_print_partition_info ( FILE* out, partition_info_t * part_ptr,
int one_liner )
{
char *print_this = slurm_sprint_partition_info(part_ptr, one_liner);
fprintf ( out, "%s", print_this);
xfree(print_this);
}
/*
* slurm_sprint_partition_info - output information about a specific Slurm
* partition based upon message as loaded using slurm_load_partitions
* IN part_ptr - an individual partition information record pointer
* IN one_liner - print as a single line if true
* RET out - char * containing formatted output (must be freed after call)
* NULL is returned on failure.
*/
char *slurm_sprint_partition_info ( partition_info_t * part_ptr,
int one_liner )
{
char *out = NULL;
char *allow_deny, *value;
uint16_t force, preempt_mode, val;
char *line_end = (one_liner) ? " " : "\n ";
bool power_save_on = false;
/*
* This is a good enough to determine if power_save is enabled.
* power_save_test() is better but makes more dependencies.
*/
if (slurm_conf.suspend_program && slurm_conf.resume_program)
power_save_on = true;
/****** Line 1 ******/
xstrfmtcat(out, "PartitionName=%s", part_ptr->name);
xstrcat(out, line_end);
/****** Line 2 ******/
if ((part_ptr->allow_groups == NULL) ||
(part_ptr->allow_groups[0] == '\0'))
xstrcat(out, "AllowGroups=ALL");
else {
xstrfmtcat(out, "AllowGroups=%s", part_ptr->allow_groups);
}
if (part_ptr->allow_accounts || !part_ptr->deny_accounts) {
allow_deny = "Allow";
if ((part_ptr->allow_accounts == NULL) ||
(part_ptr->allow_accounts[0] == '\0'))
value = "ALL";
else
value = part_ptr->allow_accounts;
} else {
allow_deny = "Deny";
value = part_ptr->deny_accounts;
}
xstrfmtcat(out, " %sAccounts=%s", allow_deny, value);
if (part_ptr->allow_qos || !part_ptr->deny_qos) {
allow_deny = "Allow";
if ((part_ptr->allow_qos == NULL) ||
(part_ptr->allow_qos[0] == '\0'))
value = "ALL";
else
value = part_ptr->allow_qos;
} else {
allow_deny = "Deny";
value = part_ptr->deny_qos;
}
xstrfmtcat(out, " %sQos=%s", allow_deny, value);
xstrcat(out, line_end);
/****** Line 3 ******/
if (part_ptr->allow_alloc_nodes == NULL)
xstrcat(out, "AllocNodes=ALL");
else
xstrfmtcat(out, "AllocNodes=%s", part_ptr->allow_alloc_nodes);
if (part_ptr->alternate != NULL) {
xstrfmtcat(out, " Alternate=%s", part_ptr->alternate);
}
if (part_ptr->flags & PART_FLAG_DEFAULT)
xstrcat(out, " Default=YES");
else
xstrcat(out, " Default=NO");
if (part_ptr->cpu_bind) {
char tmp_str[128];
slurm_sprint_cpu_bind_type(tmp_str, part_ptr->cpu_bind);
xstrfmtcat(out, " CpuBind=%s ", tmp_str);
}
if (part_ptr->qos_char)
xstrfmtcat(out, " QoS=%s", part_ptr->qos_char);
else
xstrcat(out, " QoS=N/A");
xstrcat(out, line_end);
/****** Line 5 ******/
if (part_ptr->default_time == INFINITE)
xstrcat(out, "DefaultTime=UNLIMITED");
else if (part_ptr->default_time == NO_VAL)
xstrcat(out, "DefaultTime=NONE");
else {
char time_line[32];
secs2time_str(part_ptr->default_time * 60, time_line,
sizeof(time_line));
xstrfmtcat(out, "DefaultTime=%s", time_line);
}
if (part_ptr->flags & PART_FLAG_NO_ROOT)
xstrcat(out, " DisableRootJobs=YES");
else
xstrcat(out, " DisableRootJobs=NO");
if (part_ptr->flags & PART_FLAG_EXCLUSIVE_USER)
xstrcat(out, " ExclusiveUser=YES");
else
xstrcat(out, " ExclusiveUser=NO");
if (part_ptr->flags & PART_FLAG_EXCLUSIVE_TOPO)
xstrcat(out, " ExclusiveTopo=YES");
else
xstrcat(out, " ExclusiveTopo=NO");
xstrfmtcat(out, " GraceTime=%u", part_ptr->grace_time);
if (part_ptr->flags & PART_FLAG_HIDDEN)
xstrcat(out, " Hidden=YES");
else
xstrcat(out, " Hidden=NO");
xstrcat(out, line_end);
/****** Line 6 ******/
if (part_ptr->max_nodes == INFINITE)
xstrcat(out, "MaxNodes=UNLIMITED");
else
xstrfmtcat(out, "MaxNodes=%u", part_ptr->max_nodes);
if (part_ptr->max_time == INFINITE)
xstrcat(out, " MaxTime=UNLIMITED");
else {
char time_line[32];
secs2time_str(part_ptr->max_time * 60, time_line,
sizeof(time_line));
xstrfmtcat(out, " MaxTime=%s", time_line);
}
xstrfmtcat(out, " MinNodes=%u", part_ptr->min_nodes);
if (part_ptr->flags & PART_FLAG_LLN)
xstrcat(out, " LLN=YES");
else
xstrcat(out, " LLN=NO");
if (part_ptr->max_cpus_per_node == INFINITE)
xstrcat(out, " MaxCPUsPerNode=UNLIMITED");
else {
xstrfmtcat(out, " MaxCPUsPerNode=%u",
part_ptr->max_cpus_per_node);
}
if (part_ptr->max_cpus_per_socket == INFINITE) {
xstrcat(out, " MaxCPUsPerSocket=UNLIMITED");
} else {
xstrfmtcat(out, " MaxCPUsPerSocket=%u",
part_ptr->max_cpus_per_socket);
}
xstrcat(out, line_end);
/****** Line ******/
if (part_ptr->nodesets) {
xstrfmtcat(out, "NodeSets=%s", part_ptr->nodesets);
xstrcat(out, line_end);
}
/****** Line ******/
xstrfmtcat(out, "Nodes=%s", part_ptr->nodes);
xstrcat(out, line_end);
/****** Line 7 ******/
xstrfmtcat(out, "PriorityJobFactor=%u", part_ptr->priority_job_factor);
xstrfmtcat(out, " PriorityTier=%u", part_ptr->priority_tier);
if (part_ptr->flags & PART_FLAG_ROOT_ONLY)
xstrcat(out, " RootOnly=YES");
else
xstrcat(out, " RootOnly=NO");
if (part_ptr->flags & PART_FLAG_REQ_RESV)
xstrcat(out, " ReqResv=YES");
else
xstrcat(out, " ReqResv=NO");
force = part_ptr->max_share & SHARED_FORCE;
val = part_ptr->max_share & (~SHARED_FORCE);
if (val == 0)
xstrcat(out, " OverSubscribe=EXCLUSIVE");
else if (force)
xstrfmtcat(out, " OverSubscribe=FORCE:%u", val);
else if (val == 1)
xstrcat(out, " OverSubscribe=NO");
else
xstrfmtcat(out, " OverSubscribe=YES:%u", val);
xstrcat(out, line_end);
/****** Line ******/
if (part_ptr->over_time_limit == NO_VAL16)
xstrfmtcat(out, "OverTimeLimit=NONE");
else if (part_ptr->over_time_limit == INFINITE16)
xstrfmtcat(out, "OverTimeLimit=UNLIMITED");
else
xstrfmtcat(out, "OverTimeLimit=%u", part_ptr->over_time_limit);
preempt_mode = part_ptr->preempt_mode;
if (preempt_mode == NO_VAL16)
preempt_mode = slurm_conf.preempt_mode; /* use cluster param */
xstrfmtcat(out, " PreemptMode=%s", preempt_mode_string(preempt_mode));
xstrcat(out, line_end);
/****** Line ******/
if (part_ptr->topology_name) {
xstrfmtcat(out, "Topology=%s", part_ptr->topology_name);
xstrcat(out, line_end);
}
/****** Line ******/
if (part_ptr->state_up == PARTITION_UP)
xstrcat(out, "State=UP");
else if (part_ptr->state_up == PARTITION_DOWN)
xstrcat(out, "State=DOWN");
else if (part_ptr->state_up == PARTITION_INACTIVE)
xstrcat(out, "State=INACTIVE");
else if (part_ptr->state_up == PARTITION_DRAIN)
xstrcat(out, "State=DRAIN");
else
xstrcat(out, "State=UNKNOWN");
xstrfmtcat(out, " TotalCPUs=%u", part_ptr->total_cpus);
xstrfmtcat(out, " TotalNodes=%u", part_ptr->total_nodes);
xstrfmtcat(out, " SelectTypeParameters=%s",
select_type_param_string(part_ptr->cr_type));
xstrcat(out, line_end);
/****** Line ******/
value = job_defaults_str(part_ptr->job_defaults_list);
xstrfmtcat(out, "JobDefaults=%s", value);
xfree(value);
xstrcat(out, line_end);
/****** Line ******/
if (part_ptr->def_mem_per_cpu & MEM_PER_CPU) {
if (part_ptr->def_mem_per_cpu == MEM_PER_CPU) {
xstrcat(out, "DefMemPerCPU=UNLIMITED");
} else {
xstrfmtcat(out, "DefMemPerCPU=%"PRIu64"",
part_ptr->def_mem_per_cpu & (~MEM_PER_CPU));
}
} else if (part_ptr->def_mem_per_cpu == 0) {
xstrcat(out, "DefMemPerNode=UNLIMITED");
} else {
xstrfmtcat(out, "DefMemPerNode=%"PRIu64"",
part_ptr->def_mem_per_cpu);
}
if (part_ptr->max_mem_per_cpu & MEM_PER_CPU) {
if (part_ptr->max_mem_per_cpu == MEM_PER_CPU) {
xstrcat(out, " MaxMemPerCPU=UNLIMITED");
} else {
xstrfmtcat(out, " MaxMemPerCPU=%"PRIu64"",
part_ptr->max_mem_per_cpu & (~MEM_PER_CPU));
}
} else if (part_ptr->max_mem_per_cpu == 0) {
xstrcat(out, " MaxMemPerNode=UNLIMITED");
} else {
xstrfmtcat(out, " MaxMemPerNode=%"PRIu64"",
part_ptr->max_mem_per_cpu);
}
/****** Line ******/
xstrcat(out, line_end);
xstrfmtcat(out, "TRES=%s", part_ptr->tres_fmt_str);
/****** Line 10 ******/
if (part_ptr->billing_weights_str) {
xstrcat(out, line_end);
xstrfmtcat(out, "TRESBillingWeights=%s",
part_ptr->billing_weights_str);
}
/****** Line ******/
if (power_save_on) {
xstrcat(out, line_end);
if (part_ptr->resume_timeout == NO_VAL16)
xstrcat(out, "ResumeTimeout=GLOBAL");
else if (part_ptr->resume_timeout == INFINITE16)
xstrcat(out, "ResumeTimeout=INFINITE");
else
xstrfmtcat(out, "ResumeTimeout=%d",
part_ptr->resume_timeout);
if (part_ptr->suspend_timeout == NO_VAL16)
xstrcat(out, " SuspendTimeout=GLOBAL");
else if (part_ptr->suspend_timeout == INFINITE16)
xstrcat(out, " SuspendTimeout=INFINITE");
else
xstrfmtcat(out, " SuspendTimeout=%d",
part_ptr->suspend_timeout);
if (part_ptr->suspend_time == NO_VAL)
xstrcat(out, " SuspendTime=GLOBAL");
else if (part_ptr->suspend_time == INFINITE)
xstrcat(out, " SuspendTime=INFINITE");
else
xstrfmtcat(out, " SuspendTime=%d",
part_ptr->suspend_time);
if (part_ptr->flags & PART_FLAG_PDOI)
xstrcat(out, " PowerDownOnIdle=YES");
else
xstrcat(out, " PowerDownOnIdle=NO");
}
if (one_liner)
xstrcat(out, "\n");
else
xstrcat(out, "\n\n");
return out;
}
static int _load_cluster_parts(slurm_msg_t *req_msg,
partition_info_msg_t **resp,
slurmdb_cluster_rec_t *cluster)
{
slurm_msg_t resp_msg;
int rc;
slurm_msg_t_init(&resp_msg);
if (slurm_send_recv_controller_msg(req_msg, &resp_msg, cluster) < 0)
return SLURM_ERROR;
switch (resp_msg.msg_type) {
case RESPONSE_PARTITION_INFO:
*resp = (partition_info_msg_t *) resp_msg.data;
break;
case RESPONSE_SLURM_RC:
rc = ((return_code_msg_t *) resp_msg.data)->return_code;
slurm_free_return_code_msg(resp_msg.data);
if (rc)
slurm_seterrno_ret(rc);
*resp = NULL;
break;
default:
slurm_seterrno_ret(SLURM_UNEXPECTED_MSG_ERROR);
break;
}
return SLURM_SUCCESS;
}
/* Maintain a consistent ordering of records */
static int _sort_by_cluster_inx(void *x, void *y)
{
load_part_resp_struct_t *resp_x = *(load_part_resp_struct_t **) x;
load_part_resp_struct_t *resp_y = *(load_part_resp_struct_t **) y;
if (resp_x->cluster_inx > resp_y->cluster_inx)
return -1;
if (resp_x->cluster_inx < resp_y->cluster_inx)
return 1;
return 0;
}
/* Thread to read partition information from some cluster */
static void *_load_part_thread(void *args)
{
load_part_req_struct_t *load_args = (load_part_req_struct_t *) args;
slurmdb_cluster_rec_t *cluster = load_args->cluster;
partition_info_msg_t *new_msg = NULL;
int i, rc;
if ((rc = _load_cluster_parts(load_args->req_msg, &new_msg, cluster)) ||
!new_msg) {
verbose("Error reading partition information from cluster %s: %s",
cluster->name, slurm_strerror(rc));
} else {
load_part_resp_struct_t *part_resp;
for (i = 0; i < new_msg->record_count; i++) {
if (!new_msg->partition_array[i].cluster_name) {
new_msg->partition_array[i].cluster_name =
xstrdup(cluster->name);
}
}
part_resp = xmalloc(sizeof(load_part_resp_struct_t));
part_resp->cluster_inx = load_args->cluster_inx;
part_resp->new_msg = new_msg;
list_append(load_args->resp_msg_list, part_resp);
}
xfree(args);
return NULL;
}
static int _load_fed_parts(slurm_msg_t *req_msg,
partition_info_msg_t **part_info_msg_pptr,
uint16_t show_flags, char *cluster_name,
slurmdb_federation_rec_t *fed)
{
int cluster_inx = 0, i;
load_part_resp_struct_t *part_resp;
partition_info_msg_t *orig_msg = NULL, *new_msg = NULL;
uint32_t new_rec_cnt;
slurmdb_cluster_rec_t *cluster;
list_itr_t *iter;
int pthread_count = 0;
pthread_t *load_thread = 0;
load_part_req_struct_t *load_args;
list_t *resp_msg_list;
*part_info_msg_pptr = NULL;
/* Spawn one pthread per cluster to collect partition information */
resp_msg_list = list_create(NULL);
load_thread = xmalloc(sizeof(pthread_t) *
list_count(fed->cluster_list));
iter = list_iterator_create(fed->cluster_list);
while ((cluster = (slurmdb_cluster_rec_t *) list_next(iter))) {
if ((cluster->control_host == NULL) ||
(cluster->control_host[0] == '\0'))
continue; /* Cluster down */
load_args = xmalloc(sizeof(load_part_req_struct_t));
load_args->cluster = cluster;
load_args->cluster_inx = cluster_inx++;
load_args->req_msg = req_msg;
load_args->resp_msg_list = resp_msg_list;
load_args->show_flags = show_flags;
slurm_thread_create(&load_thread[pthread_count],
_load_part_thread, load_args);
pthread_count++;
}
list_iterator_destroy(iter);
/* Wait for all pthreads to complete */
for (i = 0; i < pthread_count; i++)
slurm_thread_join(load_thread[i]);
xfree(load_thread);
/* Maintain a consistent cluster/node ordering */
list_sort(resp_msg_list, _sort_by_cluster_inx);
/* Merge the responses into a single response message */
iter = list_iterator_create(resp_msg_list);
while ((part_resp = (load_part_resp_struct_t *) list_next(iter))) {
new_msg = part_resp->new_msg;
if (!orig_msg) {
orig_msg = new_msg;
*part_info_msg_pptr = orig_msg;
} else {
/* Merge the node records */
orig_msg->last_update = MIN(orig_msg->last_update,
new_msg->last_update);
new_rec_cnt = orig_msg->record_count +
new_msg->record_count;
if (new_msg->record_count) {
orig_msg->partition_array =
xrealloc(orig_msg->partition_array,
sizeof(partition_info_t) *
new_rec_cnt);
(void) memcpy(orig_msg->partition_array +
orig_msg->record_count,
new_msg->partition_array,
sizeof(partition_info_t) *
new_msg->record_count);
orig_msg->record_count = new_rec_cnt;
}
xfree(new_msg->partition_array);
xfree(new_msg);
}
xfree(part_resp);
}
list_iterator_destroy(iter);
FREE_NULL_LIST(resp_msg_list);
if (!orig_msg)
slurm_seterrno_ret(SLURM_ERROR);
return SLURM_SUCCESS;
}
/*
* slurm_load_partitions - issue RPC to get slurm all partition configuration
* information if changed since update_time
* IN update_time - time of current configuration data
* IN partition_info_msg_pptr - place to store a partition configuration
* pointer
* IN show_flags - partition filtering options
* RET 0 or a slurm error code
* NOTE: free the response using slurm_free_partition_info_msg
*/
extern int slurm_load_partitions(time_t update_time,
partition_info_msg_t **resp,
uint16_t show_flags)
{
slurm_msg_t req_msg;
part_info_request_msg_t req;
char *cluster_name = NULL;
void *ptr = NULL;
slurmdb_federation_rec_t *fed;
int rc;
if (working_cluster_rec)
cluster_name = working_cluster_rec->name;
else
cluster_name = slurm_conf.cluster_name;
if ((show_flags & SHOW_FEDERATION) && !(show_flags & SHOW_LOCAL) &&
(slurm_load_federation(&ptr) == SLURM_SUCCESS) &&
cluster_in_federation(ptr, cluster_name)) {
/* In federation. Need full info from all clusters */
update_time = (time_t) 0;
show_flags &= (~SHOW_LOCAL);
} else {
/* Report local cluster info only */
show_flags |= SHOW_LOCAL;
show_flags &= (~SHOW_FEDERATION);
}
slurm_msg_t_init(&req_msg);
memset(&req, 0, sizeof(req));
req.last_update = update_time;
req.show_flags = show_flags;
req_msg.msg_type = REQUEST_PARTITION_INFO;
req_msg.data = &req;
if ((show_flags & SHOW_FEDERATION) && ptr) { /* "ptr" check for CLANG */
fed = (slurmdb_federation_rec_t *) ptr;
rc = _load_fed_parts(&req_msg, resp, show_flags, cluster_name,
fed);
} else {
rc = _load_cluster_parts(&req_msg, resp, working_cluster_rec);
}
if (ptr)
slurm_destroy_federation_rec(ptr);
return rc;
}
/*
* slurm_load_partitions2 - equivalent to slurm_load_partitions() with addition
* of cluster record for communications in a federation
*/
extern int slurm_load_partitions2(time_t update_time,
partition_info_msg_t **resp,
uint16_t show_flags,
slurmdb_cluster_rec_t *cluster)
{
slurm_msg_t req_msg;
part_info_request_msg_t req;
slurm_msg_t_init(&req_msg);
memset(&req, 0, sizeof(req));
req.last_update = update_time;
req.show_flags = show_flags;
req_msg.msg_type = REQUEST_PARTITION_INFO;
req_msg.data = &req;
return _load_cluster_parts(&req_msg, resp, cluster);
}