blob: 3976ae9b81abe7a2aeea998653cc5575f6fc6a90 [file] [log] [blame]
/*****************************************************************************\
* slurmdb_helpers.c - data parsing slurmdb helpers
*****************************************************************************
* Copyright (C) SchedMD LLC.
*
* 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 "slurm/slurmdb.h"
#include "src/common/data.h"
#include "src/common/list.h"
#include "src/common/log.h"
#include "src/common/net.h"
#include "src/common/read_config.h"
#include "src/common/slurm_protocol_api.h"
#include "src/common/slurmdbd_defs.h"
#include "src/common/uid.h"
#include "src/common/xassert.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "alloc.h"
#include "events.h"
#include "parsing.h"
#include "slurmdb_helpers.h"
typedef struct {
char *pos;
char *str;
} list_to_str_args_t;
static int _slurmdb_query_failed(parse_op_t op, const parser_t *const parser,
args_t *args, int rc, const char *source,
const char *what, const char *func_name);
extern int db_query_list_funcname(parse_op_t op, data_parser_type_t type,
args_t *args, list_t **list,
db_list_query_func_t func, void *cond,
const char *func_name,
const char *func_caller_name)
{
int rc;
list_t *l;
xassert(!*list);
if (!args->db_conn)
return ESLURM_DB_CONNECTION;
errno = 0;
l = func(args->db_conn, cond);
if (errno) {
FREE_NULL_LIST(l);
rc = on_error(op, type, args, errno, func_name,
func_caller_name, "Slurmdb query failed");
} else if (!l) {
rc = on_error(op, type, args, ESLURM_REST_INVALID_QUERY,
func_name, func_caller_name,
"Slurmdbd query unexpectedly failed without a result");
} else if (!list_count(l)) {
FREE_NULL_LIST(l);
rc = on_error(op, type, args, ESLURM_REST_EMPTY_RESULT,
func_name, func_caller_name,
"Slurmdbd query returned with empty list");
} else {
rc = SLURM_SUCCESS;
}
if (!rc)
*list = l;
return rc;
}
extern int resolve_qos(parse_op_t op, const parser_t *const parser,
slurmdb_qos_rec_t **qos_ptr, data_t *src, args_t *args,
data_t *parent_path, const char *caller,
bool ignore_failure)
{
slurmdb_qos_rec_t *qos = NULL;
char *path = NULL;
int rc = SLURM_SUCCESS;
xassert(args->magic == MAGIC_ARGS);
xassert(data_get_type(src));
xassert(is_fast_mode(args) || data_get_type(parent_path));
xassert(!*qos_ptr);
/* find qos by name from global list */
if (!args->qos_list) {
rc = ESLURM_REST_EMPTY_RESULT;
if (!ignore_failure)
on_error(op, parser->type, args, rc,
set_source_path(&path, args, parent_path),
caller,
"Unable to resolve QOS when there are no QOS");
goto done;
}
if (data_get_type(src) == DATA_TYPE_NULL) {
/* nothing to resolve */
return SLURM_SUCCESS;
} else if (data_get_type(src) == DATA_TYPE_DICT) {
/* user may have provided entire QOS record */
const parser_t *const qos_parser =
find_parser_by_type(DATA_PARSER_QOS);
slurmdb_qos_rec_t *pqos = alloc_parser_obj(qos_parser);
xassert(xsize(pqos) == sizeof(*pqos));
if ((rc = parse(pqos, sizeof(*pqos), qos_parser, src, args,
parent_path))) {
if (!ignore_failure)
on_error(op, parser->type, args, rc,
set_source_path(&path, args,
parent_path), caller,
"Parsing dictionary into QOS failed");
slurmdb_destroy_qos_rec(pqos);
goto done;
}
xassert(!qos);
if (pqos->id > 0) {
if (!(qos = list_find_first(args->qos_list,
slurmdb_find_qos_in_list,
&pqos->id))) {
rc = ESLURM_REST_EMPTY_RESULT;
if (!ignore_failure)
on_error(op, parser->type, args, rc,
__func__,
set_source_path(&path, args,
parent_path),
"Unable to find QOS by given ID#%d",
pqos->id);
}
} else if (pqos->name) {
if (!(qos = list_find_first(
args->qos_list,
slurmdb_find_qos_in_list_by_name,
pqos->name))) {
rc = ESLURM_REST_EMPTY_RESULT;
if (!ignore_failure)
on_error(op, parser->type, args, rc,
set_source_path(&path, args,
parent_path),
__func__,
"Unable to find QOS by given name: %s",
pqos->name);
}
} else {
rc = ESLURM_REST_FAIL_PARSING;
if (!ignore_failure)
on_error(op, parser->type, args,
ESLURM_REST_FAIL_PARSING,
set_source_path(&path, args,
parent_path), caller,
"Unable to find QOS without ID# or name provided");
}
slurmdb_destroy_qos_rec(pqos);
goto done;
}
/* convert to best guessed type */
(void) data_convert_type(src, DATA_TYPE_NONE);
if (data_get_type(src) == DATA_TYPE_INT_64) {
uint64_t qos_id_full = data_get_int(src);
uint32_t qos_id = qos_id_full;
if (qos_id_full > INT32_MAX) {
rc = ESLURM_INVALID_QOS;
if (!ignore_failure)
on_error(op, parser->type, args, rc,
set_source_path(&path, args,
parent_path), caller,
"QOS id#%"PRIu64" too large",
qos_id_full);
goto done;
}
qos = list_find_first(args->qos_list, slurmdb_find_qos_in_list,
&qos_id);
} else if (data_convert_type(src, DATA_TYPE_STRING) ==
DATA_TYPE_STRING) {
const char *qos_name = data_get_string(src);
if (!qos_name || !qos_name[0])
return SLURM_SUCCESS;
qos = list_find_first(args->qos_list,
slurmdb_find_qos_in_list_by_name,
(void *) qos_name);
} else {
rc = ESLURM_REST_FAIL_PARSING;
if (ignore_failure)
on_error(op, parser->type, args, rc,
set_source_path(&path, args, parent_path),
caller,
"QOS resolution failed with unexpected QOS name/id formatted as data type:%s",
data_get_type_string(src));
goto done;
}
done:
xfree(path);
if (rc)
return rc;
if (!qos)
return ESLURM_REST_EMPTY_RESULT;
*qos_ptr = qos;
return SLURM_SUCCESS;
}
extern int resolve_assoc(parse_op_t op, const parser_t *const parser,
slurmdb_assoc_rec_t **assoc_ptr,
slurmdb_assoc_rec_t *key, args_t *args,
data_t *parent_path, const char *caller,
bool ignore_failure)
{
slurmdb_assoc_cond_t cond = {
.flags = ASSOC_COND_FLAG_WITH_DELETED,
};
slurmdb_qos_rec_t *qos = NULL;
list_t *assoc_list = NULL;
int rc;
assoc_list = list_create(slurmdb_destroy_assoc_rec);
cond.acct_list = list_create(xfree_ptr);
if (key->acct && key->acct[0])
list_append(cond.acct_list, xstrdup(key->acct));
else
list_append(cond.acct_list, xstrdup(""));
cond.cluster_list = list_create(xfree_ptr);
if (key->cluster && key->cluster[0])
list_append(cond.acct_list, xstrdup(key->cluster));
else
list_append(cond.acct_list, xstrdup(slurm_conf.cluster_name));
if ((key->id > 0) && (key->id != NO_VAL)) {
cond.id_list = list_create(xfree_ptr);
list_append(cond.id_list, xstrdup_printf("%u", key->id));
}
cond.partition_list = list_create(xfree_ptr);
if (key->partition && key->partition[0])
list_append(cond.partition_list, xstrdup(key->partition));
else
list_append(cond.partition_list, xstrdup(""));
cond.qos_list = list_create(xfree_ptr);
if (key->qos_list && !list_is_empty(key->qos_list)) {
list_append(cond.qos_list, xstrdup(list_peek(key->qos_list)));
} else if ((key->def_qos_id > 0) && (key->def_qos_id != NO_VAL) &&
args->qos_list &&
(qos = list_find_first(args->qos_list,
slurmdb_find_qos_in_list,
&key->def_qos_id))) {
list_append(cond.partition_list, xstrdup(qos->name));
} else {
list_append(cond.partition_list, xstrdup(""));
}
cond.user_list = list_create(xfree_ptr);
if (key->user && key->user[0])
list_append(cond.partition_list, xstrdup(key->user));
else
list_append(cond.partition_list, xstrdup(""));
if (!(rc = _db_query_list(QUERYING, parser->type, args, &assoc_list,
slurmdb_associations_get, &cond)) &&
!list_is_empty(assoc_list))
*assoc_ptr = list_pop(assoc_list);
FREE_NULL_LIST(assoc_list);
if (rc)
return _slurmdb_query_failed(op, parser, args, rc,
"slurmdb_associations_get",
"Associations", caller);
return rc;
}
static data_for_each_cmd_t _concat_data_to_str(data_t *data, void *arg)
{
list_to_str_args_t *args = arg;
char *flag_str = NULL;
if (args->str)
xstrcatat(args->str, &args->pos, ",");
if (data_get_string_converted(data, &flag_str))
error("%s: Could not convert data to string", __func__);
xstrcatat(args->str, &args->pos, flag_str);
xfree(flag_str);
return DATA_FOR_EACH_CONT;
}
static char *_data_list_to_str(data_t *data)
{
list_to_str_args_t args = { 0 };
xassert(data_get_type(data) == DATA_TYPE_LIST);
(void) data_list_for_each(data, _concat_data_to_str, &args);
return args.str;
}
static int _prereqs_placeholder(const parser_t *const parser,
args_t *args)
{
if (!args->tres_list && (parser->needs & NEED_TRES))
args->tres_list = list_create(NULL);
if (!args->qos_list && (parser->needs & NEED_QOS))
args->qos_list = list_create(NULL);
return SLURM_SUCCESS;
}
static char *_needs_to_string(need_t needs, args_t *args)
{
char *str = NULL;
data_t *d = data_new();
DUMP(NEED_PREREQS_FLAGS, needs, d, args);
str = _data_list_to_str(d);
FREE_NULL_DATA(d);
return str;
}
static int _slurmdb_query_failed(parse_op_t op, const parser_t *const parser,
args_t *args, int rc, const char *source,
const char *what, const char *func_name)
{
char *needs = _needs_to_string(parser->needs, args);
on_warn(op, parser->type, args, source, __func__,
"%s: Unable to query %s from Slurm accounting storage. Could not query the following [%s]: %s",
func_name, what, needs, slurm_strerror(rc));
xfree(needs);
return _prereqs_placeholder(parser, args);
}
extern int load_prereqs_funcname(parse_op_t op, const parser_t *const parser,
args_t *args, const char *func_name)
{
int rc = SLURM_SUCCESS;
check_parser(parser);
xassert(args->magic == MAGIC_ARGS);
xassert((op == PARSING) || (op == DUMPING) || (op == QUERYING));
if (parser->needs && !slurm_conf.accounting_storage_type) {
char *needs = _needs_to_string(parser->needs, args);
on_warn(op, parser->type, args, NULL, __func__,
"Slurm accounting storage is disabled. Could not query the following: [%s].",
needs);
xfree(needs);
return _prereqs_placeholder(parser, args);
}
if (parser->needs && !args->db_conn) {
errno = SLURM_ERROR;
if (!(args->db_conn = slurmdb_connection_get(NULL)))
return _slurmdb_query_failed(op, parser, args, errno,
"slurmdb_connection_get",
"connection", func_name);
args->close_db_conn = true;
}
if ((parser->needs & NEED_TRES) && !args->tres_list) {
slurmdb_tres_cond_t cond = {
.with_deleted = 1,
};
if ((rc = _db_query_list(QUERYING, parser->type, args,
&args->tres_list, slurmdb_tres_get,
&cond))) {
return _slurmdb_query_failed(op, parser, args, errno,
"slurmdb_tres_get", "TRES",
func_name);
}
log_flag(DATA, "loaded %u TRES for parser 0x%" PRIxPTR,
list_count(args->tres_list), (uintptr_t) args);
}
if ((parser->needs & NEED_QOS) && !args->qos_list) {
slurmdb_qos_cond_t cond = {
.flags = QOS_COND_FLAG_WITH_DELETED,
};
if ((rc = _db_query_list(QUERYING, parser->type, args,
&args->qos_list, slurmdb_qos_get,
&cond))) {
return _slurmdb_query_failed(op, parser, args, errno,
"slurmdb_qos_get", "QOS",
func_name);
}
log_flag(DATA, "loaded %u QOS for parser 0x%" PRIxPTR,
list_count(args->qos_list), (uintptr_t) args);
}
return SLURM_SUCCESS;
}
/* checks for mis-matches and rejects on the spot */
#define _match(field, x, y) \
do { \
/* both null */ \
if (!x->field && !y->field) \
continue; \
/* only 1 is null */ \
if (!x->field != !y->field) \
return 0; \
if (xstrcasecmp(x->field, y->field)) \
return 0; \
} while (0)
extern int compare_assoc(const slurmdb_assoc_rec_t *x,
const slurmdb_assoc_rec_t *y)
{
if ((y->id > 0) && (y->id == x->id)) {
/*
* Always match cluster because multiple clusters may have
* different associations with the same id.
*/
_match(cluster, x, y);
return 1;
}
_match(acct, x, y);
_match(cluster, x, y);
_match(partition, x, y);
_match(user, x, y);
return 1;
}
#undef _match
extern int fuzzy_match_tres(slurmdb_tres_rec_t *tres,
slurmdb_tres_rec_t *needle)
{
debug5("Comparing database tres(name:%s, type:%s, id:%u) with requested(name:%s, type:%s, id:%u).",
tres->name, tres->type, tres->id, needle->name, needle->type,
needle->id);
if ((needle->id > 0) &&
((needle->id == tres->id) &&
(!needle->type || !xstrcasecmp(needle->type, tres->type)) &&
(!needle->name || !xstrcasecmp(needle->name, tres->name))))
return 1;
if ((!needle->name || !needle->name[0]) &&
!xstrcasecmp(needle->type, tres->type))
return 1;
else if (!xstrcasecmp(needle->name, tres->name) &&
!xstrcasecmp(needle->type, tres->type))
return 1;
else
return 0;
}