blob: d119e6aea6c81685996755721c5ca06cd86e1c59 [file] [log] [blame] [edit]
/*****************************************************************************\
* options.c - option functions for sacct
*****************************************************************************
* Copyright (C) 2006-2007 The Regents of the University of California.
* Copyright (C) 2008-2009 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.
*
* 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 "src/common/data.h"
#include "src/common/parse_time.h"
#include "src/common/proc_args.h"
#include "src/common/read_config.h"
#include "src/common/ref.h"
#include "src/common/slurm_time.h"
#include "src/common/xstring.h"
#include "src/interfaces/data_parser.h"
#include "src/interfaces/serializer.h"
#include "sacct.h"
#include <time.h>
/* getopt_long options, integers but not characters */
#define OPT_LONG_DELIMITER 0x100
#define OPT_LONG_LOCAL 0x101
#define OPT_LONG_NAME 0x102
#define OPT_LONG_NOCONVERT 0x103
#define OPT_LONG_UNITS 0x104
#define OPT_LONG_FEDR 0x105
#define OPT_LONG_WHETJOB 0x106
#define OPT_LONG_LOCAL_UID 0x107
#define OPT_LONG_ENV 0x108
#define OPT_LONG_JSON 0x109
#define OPT_LONG_YAML 0x110
#define OPT_LONG_AUTOCOMP 0x111
#define OPT_LONG_ARRAY 0x112
#define OPT_LONG_HELPSTATE 0x113
#define OPT_LONG_HELPREASON 0x114
#define OPT_LONG_EXPAND_PATTERNS 0x115
#define JOB_HASH_SIZE 1000
static void _help_fields_msg(void);
static void _help_msg(void);
static void _init_params(void);
static void _usage(void);
decl_static_data(help_txt);
list_t *selected_parts = NULL;
list_t *selected_steps = NULL;
void *acct_db_conn = NULL;
list_t *print_fields_list = NULL;
list_itr_t *print_fields_itr = NULL;
int field_count = 0;
list_t *g_qos_list = NULL;
list_t *g_tres_list = NULL;
static list_t *_build_cluster_list(slurmdb_federation_rec_t *fed)
{
slurmdb_cluster_rec_t *cluster;
list_itr_t *iter;
list_t *cluster_list;
cluster_list = list_create(xfree_ptr);
iter = list_iterator_create(fed->cluster_list);
while ((cluster = list_next(iter)))
(void) slurm_addto_char_list(cluster_list, cluster->name);
list_iterator_destroy(iter);
return cluster_list;
}
static void _help_fields_msg(void)
{
int i;
for (i = 0; fields[i].name; i++) {
if (i & 3)
printf(" ");
else if (i)
printf("\n");
printf("%-19s", fields[i].name);
}
printf("\n");
}
static void _help_job_state_msg(void)
{
for (int idx = 0; idx < JOB_END; idx++) {
if (idx & 3)
printf(" ");
else if (idx)
printf("\n");
printf("%-19s", job_state_string(idx));
}
printf("\n");
}
static void _help_job_reason_msg(void)
{
for (int idx = 0; idx < REASON_END; idx++) {
if (idx & 1)
printf(" ");
else if (idx)
printf("\n");
printf("%-39s", job_state_reason_string(idx));
}
printf("\n");
}
/* returns number of objects added to list */
static int _addto_reason_char_list_internal(list_t *char_list, char *name,
void *x)
{
uint32_t c;
char *tmp_name = NULL;
c = job_state_reason_num(name);
if (c == NO_VAL)
fatal("unrecognized job reason value '%s'", name);
tmp_name = xstrdup_printf("%u", c);
if (!list_find_first(char_list, slurm_find_char_in_list, tmp_name)) {
list_append(char_list, tmp_name);
return 1;
} else {
xfree(tmp_name);
return 0;
}
}
/* returns number of objects added to list */
static int _addto_reason_char_list(list_t *char_list, char *names)
{
if (!char_list) {
error("No list was given to fill in");
return 0;
}
return slurm_parse_char_list(char_list, names, NULL,
_addto_reason_char_list_internal);
}
static bool _supported_state(uint32_t state_num)
{
/* Not all state and state flags are accounted */
switch(state_num) {
case JOB_PENDING:
case JOB_RUNNING:
case JOB_SUSPENDED:
case JOB_COMPLETE:
case JOB_CANCELLED:
case JOB_FAILED:
case JOB_TIMEOUT:
case JOB_NODE_FAIL:
case JOB_PREEMPTED:
case JOB_BOOT_FAIL:
case JOB_DEADLINE:
case JOB_OOM:
case JOB_REQUEUE:
case JOB_RESIZING:
case JOB_REVOKED:
return true;
break;
default:
return false;
break;
}
}
static int _addto_state_char_list_internal(list_t *char_list, char *name,
void *x)
{
uint32_t c;
char *tmp_name = NULL;
c = job_state_num(name);
if (c == NO_VAL)
fatal("unrecognized job state value '%s'", name);
if (!_supported_state(c))
fatal("job state %s is not supported / accounted", name);
tmp_name = xstrdup_printf("%d", c);
if (!list_find_first(char_list, slurm_find_char_in_list, tmp_name)) {
list_append(char_list, tmp_name);
return 1;
} else {
xfree(tmp_name);
return 0;
}
}
/* returns number of objects added to list */
/* also checks if states are supported by sacct and fatals if not */
static int _addto_state_char_list(list_t *char_list, char *names)
{
if (!char_list) {
error("No list was given to fill in");
return 0;
}
return slurm_parse_char_list(char_list, names, NULL,
_addto_state_char_list_internal);
}
static void _help_msg(void)
{
char *txt;
static_ref_to_cstring(txt, help_txt);
printf("%s", txt);
xfree(txt);
}
static void _usage(void)
{
printf("Usage: sacct [options]\n\tUse --help for help\n");
}
static void _init_params(void)
{
memset(&params, 0, sizeof(sacct_parameters_t));
params.job_cond = xmalloc(sizeof(slurmdb_job_cond_t));
params.job_cond->db_flags = SLURMDB_JOB_FLAG_NOTSET;
params.job_cond->flags |= JOBCOND_FLAG_NO_TRUNC;
params.convert_flags = CONVERT_NUM_UNIT_EXACT;
params.units = NO_VAL;
}
static int _sort_desc_submit_time(void *x, void *y)
{
slurmdb_job_rec_t *j1 = *(slurmdb_job_rec_t **)x;
slurmdb_job_rec_t *j2 = *(slurmdb_job_rec_t **)y;
if (j1->submit < j2->submit)
return -1;
else if (j1->submit > j2->submit)
return 1;
if (j1->array_job_id < j2->array_job_id)
return -1;
else if (j1->array_job_id > j2->array_job_id)
return 1;
if (j1->array_task_id < j2->array_task_id)
return -1;
else if (j1->array_task_id > j2->array_task_id)
return 1;
if (j1->jobid < j2->jobid)
return -1;
else if (j1->jobid > j2->jobid)
return 1;
return 0;
}
static int _sort_asc_submit_time(void *x, void *y)
{
slurmdb_job_rec_t *j1 = *(slurmdb_job_rec_t **)x;
slurmdb_job_rec_t *j2 = *(slurmdb_job_rec_t **)y;
if (j1->submit < j2->submit)
return 1;
else if (j1->submit > j2->submit)
return -1;
return 0;
}
static void _remove_duplicate_fed_jobs(list_t *jobs)
{
int i, j;
uint32_t hash_inx;
bool found = false;
uint32_t *hash_tbl_size = NULL;
slurmdb_job_rec_t ***hash_job = NULL;
slurmdb_job_rec_t *job = NULL;
list_itr_t *itr = NULL;
xassert(jobs);
hash_tbl_size = xmalloc(sizeof(uint32_t) * JOB_HASH_SIZE);
hash_job = xmalloc(sizeof(slurmdb_job_rec_t **) * JOB_HASH_SIZE);
for (i = 0; i < JOB_HASH_SIZE; i++) {
hash_tbl_size[i] = 100;
hash_job[i] = xmalloc(sizeof(slurmdb_job_rec_t *) *
hash_tbl_size[i]);
}
/* Put newest jobs at the front so that the later jobs can be removed
* easily */
list_sort(jobs, _sort_asc_submit_time);
itr = list_iterator_create(jobs);
while ((job = list_next(itr))) {
found = false;
hash_inx = job->jobid % JOB_HASH_SIZE;
for (j = 0; (j < hash_tbl_size[hash_inx] &&
hash_job[hash_inx][j]); j++) {
if (job->jobid == hash_job[hash_inx][j]->jobid) {
found = true;
break;
}
}
if (found) {
/* Show sibling jobs that are related. e.g. when a
* pending sibling job is cancelled all siblings have
* the state as cancelled. Since jobids won't roll in a
* day -- unless the system is amazing -- just remove
* jobs that are older than a day. */
if (hash_job[hash_inx][j]->submit > (job->submit +
86400))
list_delete_item(itr);
} else {
if (j >= hash_tbl_size[hash_inx]) {
hash_tbl_size[hash_inx] *= 2;
xrealloc(hash_job[hash_inx],
sizeof(slurmdb_job_rec_t *) *
hash_tbl_size[hash_inx]);
}
hash_job[hash_inx][j] = job;
}
}
list_iterator_destroy(itr);
/* Put jobs back in desc order */
list_sort(jobs, _sort_desc_submit_time);
for (i = 0; i < JOB_HASH_SIZE; i++)
xfree(hash_job[i]);
xfree(hash_tbl_size);
xfree(hash_job);
}
extern int get_data(void)
{
slurmdb_job_rec_t *job = NULL;
slurmdb_step_rec_t *step = NULL;
list_itr_t *itr = NULL;
list_itr_t *itr_step = NULL;
slurmdb_job_cond_t *job_cond = params.job_cond;
int cnt;
if (params.opt_completion) {
jobs = slurmdb_jobcomp_jobs_get(job_cond);
return SLURM_SUCCESS;
} else {
jobs = slurmdb_jobs_get(acct_db_conn, job_cond);
}
if (!jobs)
return SLURM_ERROR;
/*
* Remove duplicate federated jobs. The db will remove duplicates for
* one cluster but not when jobs for multiple clusters are requested.
* Remove the current job if there were jobs with the same id submitted
* in the future.
* Else sort the jobs to order the jobs so the last task of arrays don't
* appear to run before any of the other tasks.
*/
if (params.cluster_name && !(job_cond->flags & JOBCOND_FLAG_DUP))
_remove_duplicate_fed_jobs(jobs);
else
list_sort(jobs, _sort_desc_submit_time);
itr = list_iterator_create(jobs);
while ((job = list_next(itr))) {
if (!job->steps || !(cnt = list_count(job->steps)))
continue;
itr_step = list_iterator_create(job->steps);
while ((step = list_next(itr_step))) {
/* now aggregate the aggregatable */
if (step->state < JOB_COMPLETE)
continue;
job->tot_cpu_sec += step->tot_cpu_sec;
job->tot_cpu_usec += step->tot_cpu_usec;
job->user_cpu_sec +=
step->user_cpu_sec;
job->user_cpu_usec +=
step->user_cpu_usec;
job->sys_cpu_sec +=
step->sys_cpu_sec;
job->sys_cpu_usec +=
step->sys_cpu_usec;
}
list_iterator_destroy(itr_step);
}
list_iterator_destroy(itr);
return SLURM_SUCCESS;
}
extern void parse_command_line(int argc, char **argv)
{
extern int optind;
int c, i, option_index = 0;
char *end = NULL, *start = NULL;
slurm_selected_step_t *selected_step = NULL;
list_itr_t *itr = NULL;
struct stat stat_buf;
char *dot = NULL;
char *env_val = NULL;
bool brief_output = false, long_output = false;
bool all_users = false;
bool all_clusters = false;
char *qos_names = NULL;
slurmdb_job_cond_t *job_cond = params.job_cond;
log_options_t opts = LOG_OPTS_STDERR_ONLY ;
int verbosity; /* count of -v options */
bool set;
static struct option long_options[] = {
{"autocomplete", required_argument, 0, OPT_LONG_AUTOCOMP},
{"allusers", no_argument, 0, 'a'},
{"accounts", required_argument, 0, 'A'},
{"allocations", no_argument, 0, 'X'},
{"array", no_argument, 0, OPT_LONG_ARRAY},
{"brief", no_argument, 0, 'b'},
{"batch-script", no_argument, 0, 'B'},
{"completion", no_argument, 0, 'c'},
{"constraints", required_argument, 0, 'C'},
{"delimiter", required_argument, 0, OPT_LONG_DELIMITER},
{"duplicates", no_argument, 0, 'D'},
{"federation", no_argument, 0, OPT_LONG_FEDR},
{"helpformat", no_argument, 0, 'e'},
{"help-fields", no_argument, 0, 'e'},
{"helpreason", no_argument, 0, OPT_LONG_HELPREASON},
{"helpstate", no_argument, 0, OPT_LONG_HELPSTATE},
{"endtime", required_argument, 0, 'E'},
{"env-vars", no_argument, 0, OPT_LONG_ENV},
{"expand-patterns",no_argument, 0, OPT_LONG_EXPAND_PATTERNS},
{"file", required_argument, 0, 'f'},
{"flags", required_argument, 0, 'F'},
{"gid", required_argument, 0, 'g'},
{"group", required_argument, 0, 'g'},
{"help", no_argument, 0, 'h'},
{"local", no_argument, 0, OPT_LONG_LOCAL},
{"name", required_argument, 0, OPT_LONG_NAME},
{"nnodes", required_argument, 0, 'i'},
{"ncpus", required_argument, 0, 'I'},
{"jobs", required_argument, 0, 'j'},
{"timelimit-min", required_argument, 0, 'k'},
{"timelimit-max", required_argument, 0, 'K'},
{"long", no_argument, 0, 'l'},
{"allclusters", no_argument, 0, 'L'},
{"cluster", required_argument, 0, 'M'},
{"clusters", required_argument, 0, 'M'},
{"nodelist", required_argument, 0, 'N'},
{"noconvert", no_argument, 0, OPT_LONG_NOCONVERT},
{"units", required_argument, 0, OPT_LONG_UNITS},
{"noheader", no_argument, 0, 'n'},
{"fields", required_argument, 0, 'o'},
{"format", required_argument, 0, 'o'},
{"parsable", no_argument, 0, 'p'},
{"parsable2", no_argument, 0, 'P'},
{"qos", required_argument, 0, 'q'},
{"partition", required_argument, 0, 'r'},
{"reason", required_argument, 0, 'R'},
{"state", required_argument, 0, 's'},
{"starttime", required_argument, 0, 'S'},
{"truncate", no_argument, 0, 'T'},
{"uid", required_argument, 0, 'u'},
{"use-local-uid", no_argument, 0, OPT_LONG_LOCAL_UID},
{"usage", no_argument, 0, 'U'},
{"user", required_argument, 0, 'u'},
{"verbose", no_argument, 0, 'v'},
{"version", no_argument, 0, 'V'},
{"wckeys", required_argument, 0, 'W'},
{"whole-hetjob", optional_argument, 0, OPT_LONG_WHETJOB},
{"associations", required_argument, 0, 'x'},
{"json", optional_argument, 0, OPT_LONG_JSON},
{"yaml", optional_argument, 0, OPT_LONG_YAML},
{0, 0, 0, 0}};
params.opt_uid = getuid();
params.opt_gid = getgid();
verbosity = 0;
log_init("sacct", opts, SYSLOG_FACILITY_DAEMON, NULL);
opterr = 1; /* Let getopt report problems to the user */
if (xstrstr(slurm_conf.fed_params, "fed_display"))
params.opt_federation = true;
if (getenv("SACCT_FEDERATION"))
params.opt_federation = true;
if (getenv("SACCT_LOCAL"))
params.opt_local = true;
while (1) { /* now cycle through the command line */
c = getopt_long(argc, argv,
"aA:bBcC:DeE:f:F:g:hi:I:j:k:K:lLM:nN:o:pPq:r:s:S:Ttu:UvVW:x:X",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'a':
all_users = true;
break;
case 'A':
if (!job_cond->acct_list)
job_cond->acct_list = list_create(xfree_ptr);
slurm_addto_char_list(job_cond->acct_list, optarg);
break;
case OPT_LONG_ARRAY:
params.opt_array = true;
break;
case 'b':
brief_output = true;
break;
case 'B':
job_cond->flags |= JOBCOND_FLAG_SCRIPT;
job_cond->flags |= JOBCOND_FLAG_NO_STEP;
break;
case 'c':
params.opt_completion = 1;
break;
case OPT_LONG_DELIMITER:
fields_delimiter = optarg;
break;
case 'C':
if (!job_cond->constraint_list)
job_cond->constraint_list =
list_create(xfree_ptr);
slurm_addto_char_list(job_cond->constraint_list,
optarg);
break;
case 'M':
if (!xstrcasecmp(optarg, "all") ||
!xstrcasecmp(optarg, "-1")) { /* vestigial */
all_clusters = true;
break;
}
all_clusters = false;
params.opt_local = true;
if (!job_cond->cluster_list)
job_cond->cluster_list = list_create(xfree_ptr);
slurm_addto_char_list(job_cond->cluster_list, optarg);
break;
case 'D':
job_cond->flags |= JOBCOND_FLAG_DUP;
break;
case 'e':
_help_fields_msg();
exit(0);
break;
case 'E':
job_cond->usage_end = parse_time(optarg, 1);
if (errno == ESLURM_INVALID_TIME_VALUE)
exit(1);
break;
case OPT_LONG_ENV:
job_cond->flags |= JOBCOND_FLAG_ENV;
job_cond->flags |= JOBCOND_FLAG_NO_STEP;
break;
case OPT_LONG_EXPAND_PATTERNS:
params.expand_patterns = true;
break;
case 'f':
xfree(slurm_conf.job_comp_loc);
if ((stat(optarg, &stat_buf) != 0) ||
(!S_ISREG(stat_buf.st_mode))) {
fprintf(stderr, "%s is not a valid file\n",
optarg);
exit(1);
}
slurm_conf.job_comp_loc = xstrdup(optarg);
params.opt_completion = 1;
break;
case 'F':
job_cond->db_flags = str_2_job_flags(optarg);
if (job_cond->db_flags == SLURMDB_JOB_FLAG_NOTSET)
exit(1);
break;
case 'g':
if (!job_cond->groupid_list)
job_cond->groupid_list = list_create(xfree_ptr);
if (slurm_addto_id_char_list(job_cond->groupid_list,
optarg, 1) < 1)
exit(1);
break;
case 'h':
_help_msg();
exit(0);
break;
case 'i':
set = get_resource_arg_range(
optarg,
"requested node range",
(int *)&job_cond->nodes_min,
(int *)&job_cond->nodes_max,
true);
if (set == false) {
error("invalid node range -i '%s'",
optarg);
exit(1);
}
break;
case 'I':
set = get_resource_arg_range(
optarg,
"requested cpu range",
(int *)&job_cond->cpus_min,
(int *)&job_cond->cpus_max,
true);
if (set == false) {
error("invalid cpu range -i '%s'",
optarg);
exit(1);
}
break;
case 'j':
if (!job_cond->step_list)
job_cond->step_list = list_create(
slurm_destroy_selected_step);
slurm_addto_step_list(job_cond->step_list, optarg);
if (!list_count(job_cond->step_list))
FREE_NULL_LIST(job_cond->step_list);
break;
case 'k':
job_cond->timelimit_min = time_str2mins(optarg);
if (((int32_t)job_cond->timelimit_min <= 0)
&& (job_cond->timelimit_min != INFINITE))
fatal("Invalid time limit specification");
break;
case 'K':
job_cond->timelimit_max = time_str2mins(optarg);
if (((int32_t)job_cond->timelimit_max <= 0)
&& (job_cond->timelimit_max != INFINITE))
fatal("Invalid time limit specification");
break;
case 'L':
all_clusters = true;
break;
case 'l':
long_output = true;
break;
case OPT_LONG_FEDR:
params.opt_federation = true;
all_clusters = false;
break;
case OPT_LONG_LOCAL:
params.opt_local = true;
all_clusters = false;
break;
case OPT_LONG_NOCONVERT:
params.convert_flags |= CONVERT_NUM_UNIT_NO;
break;
case OPT_LONG_UNITS:
{
int type = get_unit_type(*optarg);
if (type == SLURM_ERROR)
fatal("Invalid unit type");
params.units = type;
}
break;
case 'n':
print_fields_have_header = 0;
break;
case 'N':
if (job_cond->used_nodes) {
error("Already asked for nodes '%s'",
job_cond->used_nodes);
break;
}
job_cond->used_nodes = xstrdup(optarg);
break;
case OPT_LONG_NAME:
if (!job_cond->jobname_list)
job_cond->jobname_list = list_create(xfree_ptr);
slurm_addto_char_list(job_cond->jobname_list, optarg);
break;
case 'o':
xstrfmtcat(params.opt_field_list, "%s,", optarg);
break;
case 'p':
print_fields_parsable_print =
PRINT_FIELDS_PARSABLE_ENDING;
break;
case 'P':
print_fields_parsable_print =
PRINT_FIELDS_PARSABLE_NO_ENDING;
break;
case 'q':
qos_names = xstrdup(optarg);
break;
case 'r':
if (!job_cond->partition_list)
job_cond->partition_list =
list_create(xfree_ptr);
slurm_addto_char_list(job_cond->partition_list,
optarg);
break;
case 'R':
if (!job_cond->reason_list)
job_cond->reason_list = list_create(xfree_ptr);
_addto_reason_char_list(job_cond->reason_list, optarg);
break;
case 's':
if (!job_cond->state_list)
job_cond->state_list = list_create(xfree_ptr);
_addto_state_char_list(job_cond->state_list, optarg);
break;
case 'S':
job_cond->usage_start = parse_time(optarg, 1);
if (errno == ESLURM_INVALID_TIME_VALUE)
exit(1);
break;
case 'T':
job_cond->flags &= ~JOBCOND_FLAG_NO_TRUNC;
break;
case 'U':
_usage();
exit(0);
break;
case 'u':
if (!xstrcmp(optarg, "-1")) {
all_users = true;
break;
}
all_users = false;
if (!job_cond->userid_list)
job_cond->userid_list = list_create(xfree_ptr);
if (slurm_addto_id_char_list(job_cond->userid_list,
optarg, 0) < 1)
exit(1);
break;
case OPT_LONG_LOCAL_UID:
params.use_local_uid = true;
break;
case 'v':
/* Handle -vvv thusly...
*/
verbosity++;
break;
case 'W':
if (!job_cond->wckey_list)
job_cond->wckey_list = list_create(xfree_ptr);
slurm_addto_char_list(job_cond->wckey_list, optarg);
break;
case OPT_LONG_WHETJOB:
if (!optarg || !xstrcasecmp(optarg, "yes") ||
!xstrcasecmp(optarg, "y"))
job_cond->flags |= JOBCOND_FLAG_WHOLE_HETJOB;
else if (!xstrcasecmp(optarg, "no") ||
!xstrcasecmp(optarg, "n"))
job_cond->flags |= JOBCOND_FLAG_NO_WHOLE_HETJOB;
else if (optarg) {
error("Invalid --whole-hetjob value \"%s\"."
" Valid values: [yes|no].", optarg);
exit(1);
}
break;
case 'V':
print_slurm_version();
exit(0);
case 'x':
if (!job_cond->associd_list)
job_cond->associd_list = list_create(xfree_ptr);
slurm_addto_char_list(job_cond->associd_list, optarg);
break;
case 't':
/* 't' is deprecated and was replaced with 'X'. */
case 'X':
job_cond->flags |= JOBCOND_FLAG_NO_STEP;
break;
case OPT_LONG_JSON:
params.mimetype = MIME_TYPE_JSON;
params.data_parser = optarg;
serializer_required(MIME_TYPE_JSON);
break;
case OPT_LONG_YAML:
params.mimetype = MIME_TYPE_YAML;
params.data_parser = optarg;
serializer_required(MIME_TYPE_YAML);
break;
case OPT_LONG_AUTOCOMP:
suggest_completion(long_options, optarg);
exit(0);
break;
case OPT_LONG_HELPSTATE:
_help_job_state_msg();
exit(0);
break;
case OPT_LONG_HELPREASON:
_help_job_reason_msg();
exit(0);
break;
case ':':
case '?': /* getopt() has explained it */
exit(1);
}
}
if (!job_cond->step_list || !list_count(job_cond->step_list)) {
char *reason = NULL;
if (job_cond->flags & JOBCOND_FLAG_SCRIPT)
reason = "job scripts";
if (job_cond->flags & JOBCOND_FLAG_ENV)
reason = "job environment";
if (reason)
fatal("When requesting %s you must also request specific jobs with the '-j' option.", reason);
}
if ((job_cond->flags & JOBCOND_FLAG_SCRIPT) &&
(job_cond->flags & JOBCOND_FLAG_ENV))
fatal("Options --batch-script and --env-vars are mutually exclusive");
if (long_output && params.opt_field_list)
fatal("Options -o(--format) and -l(--long) are mutually exclusive. Please remove one and retry.");
if (verbosity) {
opts.stderr_level += verbosity;
opts.prefix_level = 1;
log_alter(opts, 0, NULL);
}
slurmdb_job_cond_def_start_end(job_cond);
if (job_cond->usage_end &&
(job_cond->usage_start > job_cond->usage_end)) {
char start_str[256], end_str[256];
slurm_make_time_str(&job_cond->usage_start, start_str,
sizeof(start_str));
slurm_make_time_str(&job_cond->usage_end, end_str,
sizeof(end_str));
error("Start time (%s) requested is after end time (%s).",
start_str, end_str);
exit(1);
}
if (verbosity > 0) {
char start_char[25], end_char[25];
char *verbosity_states = NULL;
if (job_cond->state_list && list_count(job_cond->state_list)) {
char *state;
list_itr_t *itr = list_iterator_create(
job_cond->state_list);
while ((state = list_next(itr))) {
if (verbosity_states)
xstrcat(verbosity_states, ",");
xstrfmtcat(verbosity_states, "%s",
job_state_string_complete(
atol(state)));
}
list_iterator_destroy(itr);
} else
verbosity_states = xstrdup("Eligible");
if (!job_cond->usage_start)
strlcpy(start_char, "Epoch 0", sizeof(start_char));
else
slurm_ctime2_r(&job_cond->usage_start, start_char);
slurm_ctime2_r(&job_cond->usage_end, end_char);
if (xstrcmp(start_char, end_char))
info("Jobs %s in the time window from %s to %s",
verbosity_states, start_char, end_char);
else
info("Jobs %s at the time instant %s",
verbosity_states, start_char);
xfree(verbosity_states);
}
debug("Options selected:\n"
"\topt_completion=%s\n"
"\topt_dup=%s\n"
"\topt_field_list=%s\n"
"\topt_no_steps=%s\n"
"\topt_whole_hetjob=%s",
params.opt_completion ? "yes" : "no",
(job_cond->flags & JOBCOND_FLAG_DUP) ? "yes" : "no",
params.opt_field_list,
(job_cond->flags & JOBCOND_FLAG_NO_STEP) ? "yes" : "no",
(job_cond->flags & JOBCOND_FLAG_WHOLE_HETJOB) ? "yes" :
(job_cond->flags & JOBCOND_FLAG_NO_WHOLE_HETJOB ? "no" : 0));
if (params.opt_completion) {
if (!slurm_conf.job_comp_type) {
fprintf(stderr, "Slurm job completion is disabled\n");
exit(1);
}
if (slurmdb_jobcomp_init() != SLURM_SUCCESS) {
fprintf(stderr,
"Slurm unable to initialize jobcomp plugin\n");
exit(1);
}
} else {
if (!slurm_conf.accounting_storage_type) {
fprintf(stderr,
"Slurm accounting storage is disabled\n");
exit(1);
}
if (acct_storage_g_init() != SLURM_SUCCESS) {
fprintf(stderr,
"Slurm unable to initialize storage plugin\n");
exit(1);
}
acct_db_conn = slurmdb_connection_get(NULL);
if (errno != SLURM_SUCCESS) {
error("Problem talking to the database: %m");
exit(1);
}
}
if (qos_names) {
if (!g_qos_list) {
slurmdb_qos_cond_t qos_cond = {
.flags = QOS_COND_FLAG_WITH_DELETED,
};
g_qos_list = slurmdb_qos_get(
acct_db_conn, &qos_cond);
}
if (!job_cond->qos_list)
job_cond->qos_list = list_create(xfree_ptr);
if (slurmdb_addto_qos_char_list(job_cond->qos_list,
g_qos_list, qos_names, 0) < 1)
fatal("problem processing qos list");
xfree(qos_names);
}
/* specific clusters requested? */
if (params.opt_federation && !all_clusters && !job_cond->cluster_list &&
!params.opt_local && !params.opt_completion) {
/* Test if in federated cluster and if so, get information from
* all clusters in that federation */
slurmdb_federation_rec_t *fed = NULL;
slurmdb_federation_cond_t fed_cond;
list_t *fed_list = NULL;
list_t *cluster_list = list_create(NULL);
params.cluster_name = xstrdup(slurm_conf.cluster_name);
list_append(cluster_list, params.cluster_name);
slurmdb_init_federation_cond(&fed_cond, 0);
fed_cond.cluster_list = cluster_list;
if ((fed_list = slurmdb_federations_get(
acct_db_conn, &fed_cond)) &&
list_count(fed_list) == 1) {
fed = list_peek(fed_list);
job_cond->cluster_list = _build_cluster_list(fed);
/* Leave cluster_name to identify remote only jobs */
// xfree(params.cluster_name);
} else
xfree(params.cluster_name);
FREE_NULL_LIST(cluster_list);
FREE_NULL_LIST(fed_list);
}
if (all_clusters) {
if (job_cond->cluster_list
&& list_count(job_cond->cluster_list)) {
FREE_NULL_LIST(job_cond->cluster_list);
}
debug2("Clusters requested:\tall");
} else if (job_cond->cluster_list
&& list_count(job_cond->cluster_list)) {
debug2( "Clusters requested:");
itr = list_iterator_create(job_cond->cluster_list);
while ((start = list_next(itr)))
debug2("\t: %s", start);
list_iterator_destroy(itr);
} else if (!job_cond->cluster_list
|| !list_count(job_cond->cluster_list)) {
if (!job_cond->cluster_list)
job_cond->cluster_list = list_create(xfree_ptr);
if ((start = xstrdup(slurm_conf.cluster_name))) {
list_append(job_cond->cluster_list, start);
debug2("Clusters requested:\t%s", start);
}
}
/* if any jobs or nodes are specified set to look for all users if none
are set */
if (!job_cond->userid_list || !list_count(job_cond->userid_list))
if ((job_cond->step_list && list_count(job_cond->step_list))
|| job_cond->used_nodes)
all_users = true;
/* set all_users for user root if not requesting any */
if (!job_cond->userid_list && !params.opt_uid)
all_users = true;
if (all_users) {
if (job_cond->userid_list &&
list_count(job_cond->userid_list)) {
FREE_NULL_LIST(job_cond->userid_list);
}
debug2("Userids requested:\tall");
} else if (job_cond->userid_list && list_count(job_cond->userid_list)) {
debug2("Userids requested:");
itr = list_iterator_create(job_cond->userid_list);
while ((start = list_next(itr)))
debug2("\t: %s", start);
list_iterator_destroy(itr);
} else if (!job_cond->userid_list
|| !list_count(job_cond->userid_list)) {
if (!job_cond->userid_list)
job_cond->userid_list = list_create(xfree_ptr);
start = xstrdup_printf("%u", params.opt_uid);
list_append(job_cond->userid_list, start);
debug2("Userid requested\t: %s", start);
}
if (job_cond->groupid_list && list_count(job_cond->groupid_list)) {
debug2("Groupids requested:");
itr = list_iterator_create(job_cond->groupid_list);
while ((start = list_next(itr)))
debug2("\t: %s", start);
list_iterator_destroy(itr);
}
/* specific partitions requested? */
if (job_cond->partition_list && list_count(job_cond->partition_list)) {
debug2("Partitions requested:");
itr = list_iterator_create(job_cond->partition_list);
while ((start = list_next(itr)))
debug2("\t: %s", start);
list_iterator_destroy(itr);
}
/* specific qos' requested? */
if (job_cond->qos_list && list_count(job_cond->qos_list)) {
start = get_qos_complete_str(g_qos_list, job_cond->qos_list);
debug2("QOS requested\t: %s\n", start);
xfree(start);
}
/* specific jobs requested? */
if (job_cond->step_list && list_count(job_cond->step_list)) {
debug2("Jobs requested:");
itr = list_iterator_create(job_cond->step_list);
while ((selected_step = list_next(itr))) {
char id[FORMAT_STRING_SIZE];
debug2("\t: %s", slurm_get_selected_step_id(
id, sizeof(id), selected_step));
}
list_iterator_destroy(itr);
}
/* specific states (completion state) requested? */
if (job_cond->state_list && list_count(job_cond->state_list)) {
debug2("States requested:");
itr = list_iterator_create(job_cond->state_list);
while ((start = list_next(itr))) {
debug2("\t: %s",
job_state_string(atoi(start)));
}
list_iterator_destroy(itr);
}
if (job_cond->wckey_list && list_count(job_cond->wckey_list)) {
debug2("Wckeys requested:");
itr = list_iterator_create(job_cond->wckey_list);
while ((start = list_next(itr)))
debug2("\t: %s\n", start);
list_iterator_destroy(itr);
}
if (job_cond->timelimit_min) {
char time_str[128], tmp1[32], tmp2[32];
mins2time_str(job_cond->timelimit_min, tmp1, sizeof(tmp1));
sprintf(time_str, "%s", tmp1);
if (job_cond->timelimit_max) {
int len = strlen(tmp1);
mins2time_str(job_cond->timelimit_max,
tmp2, sizeof(tmp2));
sprintf(time_str+len, " - %s", tmp2);
}
debug2("Timelimit requested\t: %s", time_str);
}
/* specific jobnames requested? */
if (job_cond->jobname_list && list_count(job_cond->jobname_list)) {
debug2("Jobnames requested:");
itr = list_iterator_create(job_cond->jobname_list);
while ((start = list_next(itr))) {
debug2("\t: %s", start);
}
list_iterator_destroy(itr);
}
/* select the output fields */
if (brief_output) {
if (params.opt_completion)
dot = BRIEF_COMP_FIELDS;
else
dot = BRIEF_FIELDS;
xstrfmtcat(params.opt_field_list, "%s,", dot);
}
if (long_output) {
if (params.opt_completion)
dot = LONG_COMP_FIELDS;
else
dot = LONG_FIELDS;
xstrfmtcat(params.opt_field_list, "%s,", dot);
}
if (params.opt_field_list == NULL) {
if (params.opt_completion)
dot = DEFAULT_COMP_FIELDS;
else if ( ( env_val = getenv("SACCT_FORMAT") ) )
dot = xstrdup(env_val);
else
dot = DEFAULT_FIELDS;
xstrfmtcat(params.opt_field_list, "%s,", dot);
}
start = params.opt_field_list;
while ((end = strstr(start, ","))) {
char *tmp_char = NULL;
int command_len = 0;
int newlen = 0;
bool newlen_set = false;
*end = 0;
while (isspace(*start))
start++; /* discard whitespace */
if (!(int)*start)
continue;
if ((tmp_char = strstr(start, "\%"))) {
newlen_set = true;
newlen = atoi(tmp_char+1);
tmp_char[0] = '\0';
}
command_len = strlen(start);
if (!xstrncasecmp("ALL", start, command_len)) {
for (i = 0; fields[i].name; i++) {
if (newlen_set)
fields[i].len = newlen;
list_append(print_fields_list, &fields[i]);
start = end + 1;
}
start = end + 1;
continue;
}
for (i = 0; fields[i].name; i++) {
if (!xstrncasecmp(fields[i].name, start, command_len))
goto foundfield;
}
if (!xstrcasecmp("AllocGRES", start)) {
fatal("AllocGRES has been removed, please use AllocTRES");
}
if (!xstrcasecmp("ReqGRES", start)) {
fatal("ReqGRES has been removed, please use ReqTRES");
}
error("Invalid field requested: \"%s\"", start);
exit(1);
foundfield:
if (newlen_set)
fields[i].len = newlen;
list_append(print_fields_list, &fields[i]);
start = end + 1;
}
field_count = list_count(print_fields_list);
if (optind < argc) {
error("Unknown arguments:");
for (i=optind; i<argc; i++)
error(" %s", argv[i]);
exit(1);
}
}
/* Return true if the specified job id is local to a cluster
* (not a federated job) */
static bool _test_local_job(uint32_t job_id)
{
if ((job_id & (~MAX_JOB_ID)) == 0)
return true;
return false;
}
static void _print_script(slurmdb_job_rec_t *job)
{
if (print_fields_have_header) {
char *id = slurmdb_get_job_id_str(job);
printf("Batch Script for %s\n"
"--------------------------------------------------------------------------------\n",
id);
xfree(id);
}
printf("%s", job->script ? job->script : "NONE\n");
}
static void _print_env(slurmdb_job_rec_t *job)
{
if (print_fields_have_header) {
char *id = slurmdb_get_job_id_str(job);
printf("Environment used for %s (must be batch to display)\n"
"--------------------------------------------------------------------------------\n",
id);
xfree(id);
}
printf("%s", job->env ? job->env : "NONE\n");
}
/* do_list() -- List the assembled data
*
* In: Nothing explicit.
* Out: void.
*
* At this point, we have already selected the desired data,
* so we just need to print it for the user.
*/
extern void do_list(int argc, char **argv)
{
list_itr_t *itr = NULL;
list_itr_t *itr_step = NULL;
slurmdb_job_rec_t *job = NULL;
slurmdb_step_rec_t *step = NULL;
slurmdb_job_cond_t *job_cond = params.job_cond;
if (params.mimetype) {
DATA_DUMP_CLI_SINGLE(OPENAPI_SLURMDBD_JOBS_RESP, jobs, argc,
argv, acct_db_conn, params.mimetype,
params.data_parser, errno);
return;
}
if (!jobs)
return;
itr = list_iterator_create(jobs);
while ((job = list_next(itr))) {
if ((params.cluster_name) &&
_test_local_job(job->jobid) &&
xstrcmp(params.cluster_name, job->cluster))
continue;
if (job_cond->flags & JOBCOND_FLAG_SCRIPT) {
_print_script(job);
continue;
} else if (job_cond->flags & JOBCOND_FLAG_ENV) {
_print_env(job);
continue;
}
if (job->show_full)
print_fields(JOB, job);
if (!(job_cond->flags & JOBCOND_FLAG_NO_STEP)) {
itr_step = list_iterator_create(job->steps);
while ((step = list_next(itr_step))) {
if (step->end == 0)
step->end = job->end;
print_fields(JOBSTEP, step);
}
list_iterator_destroy(itr_step);
}
}
list_iterator_destroy(itr);
}
/* do_list_completion() -- List the assembled data
*
* In: Nothing explicit.
* Out: void.
*
* NOTE: This data is from the job completion data and not federation compliant.
* At this point, we have already selected the desired data,
* so we just need to print it for the user.
*/
extern void do_list_completion(void)
{
list_itr_t *itr = NULL;
jobcomp_job_rec_t *job = NULL;
if (!jobs)
return;
itr = list_iterator_create(jobs);
while ((job = list_next(itr))) {
print_fields(JOBCOMP, job);
}
list_iterator_destroy(itr);
}
extern void sacct_init(void)
{
_init_params();
print_fields_list = list_create(NULL);
print_fields_itr = list_iterator_create(print_fields_list);
}
extern void sacct_fini(void)
{
if (print_fields_itr)
list_iterator_destroy(print_fields_itr);
FREE_NULL_LIST(print_fields_list);
FREE_NULL_LIST(jobs);
FREE_NULL_LIST(g_qos_list);
FREE_NULL_LIST(g_tres_list);
if (params.opt_completion)
slurmdb_jobcomp_fini();
else {
slurmdb_connection_close(&acct_db_conn);
acct_storage_g_fini();
}
xfree(params.opt_field_list);
slurmdb_destroy_job_cond(params.job_cond);
}