blob: 08b61afa4b534b70d523a4c466c3c343fa3c793c [file] [log] [blame]
/*****************************************************************************\
* as_mysql_assoc.c - functions dealing with associations.
*****************************************************************************
* Copyright (C) 2004-2007 The Regents of the University of California.
* Copyright (C) 2008-2010 Lawrence Livermore National Security.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Danny Auble <da@llnl.gov>
*
* 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 "as_mysql_assoc.h"
#include "as_mysql_usage.h"
#include "as_mysql_user.h"
#define ADD_ASSOC_FLAG_STR_ERR SLURM_BIT(0)
#define ADD_ASSOC_FLAG_ADDED SLURM_BIT(1)
typedef struct {
slurmdb_assoc_rec_t *alloc_assoc;
slurmdb_add_assoc_cond_t *add_assoc;
bool added_defaults;
bool assoc_mgr_locked;
char *base_lineage;
char *cols;
list_t *coord_users;
char *extra;
uint32_t flags;
bool is_coord;
mysql_conn_t *mysql_conn;
int rc;
char *ret_str;
char *ret_str_pos;
char *txn_query;
char *txn_query_pos;
uint32_t uid;
char *user_name;
char *vals;
} add_assoc_cond_t;
typedef struct {
uint32_t check_qos;
char *ret_str;
char *ret_str_pos;
} mod_def_qos_t;
typedef struct {
slurmdb_assoc_flags_t flags;
slurmdb_user_rec_t *user_rec;
} coord_parent_flag_t;
/* if this changes you will need to edit the corresponding enum */
char *assoc_req_inx[] = {
"id_assoc",
"user",
"acct",
"`partition`",
"comment",
"shares",
"grp_tres_mins",
"grp_tres_run_mins",
"grp_tres",
"grp_jobs",
"grp_jobs_accrue",
"grp_submit_jobs",
"grp_wall",
"max_tres_mins_pj",
"max_tres_run_mins",
"max_tres_pj",
"max_tres_pn",
"max_jobs",
"max_jobs_accrue",
"min_prio_thresh",
"max_submit_jobs",
"max_wall_pj",
"parent_acct",
"priority",
"def_qos_id",
"qos",
"delta_qos",
"is_def",
"deleted",
"id_parent",
"lineage",
"flags",
};
enum {
ASSOC_REQ_ID,
ASSOC_REQ_USER,
ASSOC_REQ_ACCT,
ASSOC_REQ_PART,
ASSOC_REQ_COMMENT,
ASSOC_REQ_FS,
ASSOC_REQ_GTM,
ASSOC_REQ_GTRM,
ASSOC_REQ_GT,
ASSOC_REQ_GJ,
ASSOC_REQ_GJA,
ASSOC_REQ_GSJ,
ASSOC_REQ_GW,
ASSOC_REQ_MTMPJ,
ASSOC_REQ_MTRM,
ASSOC_REQ_MTPJ,
ASSOC_REQ_MTPN,
ASSOC_REQ_MJ,
ASSOC_REQ_MJA,
ASSOC_REQ_MPT,
ASSOC_REQ_MSJ,
ASSOC_REQ_MWPJ,
ASSOC_REQ_PARENT,
ASSOC_REQ_PRIO,
ASSOC_REQ_DEF_QOS,
ASSOC_REQ_QOS,
ASSOC_REQ_DELTA_QOS,
ASSOC_REQ_DEFAULT,
ASSOC_REQ_DELETED,
ASSOC_REQ_ID_PAR,
ASSOC_REQ_LINEAGE,
ASSOC_REQ_FLAGS,
ASSOC_REQ_COUNT
};
enum {
ASSOC2_REQ_MJ,
ASSOC2_REQ_MJA,
ASSOC2_REQ_MPT,
ASSOC2_REQ_MSJ,
ASSOC2_REQ_MWPJ,
ASSOC2_REQ_MTPJ,
ASSOC2_REQ_MTPN,
ASSOC2_REQ_MTMPJ,
ASSOC2_REQ_MTRM,
ASSOC2_REQ_DEF_QOS,
ASSOC2_REQ_QOS,
ASSOC2_REQ_DELTA_QOS,
ASSOC2_REQ_PRIO,
};
static char *massoc_req_inx[] = {
"id_assoc",
"acct",
"parent_acct",
"user",
"`partition`",
"qos",
"grp_tres_mins",
"grp_tres_run_mins",
"grp_tres",
"max_tres_mins_pj",
"max_tres_run_mins",
"max_tres_pj",
"max_tres_pn",
"lineage",
"flags",
};
enum {
MASSOC_ID,
MASSOC_ACCT,
MASSOC_PACCT,
MASSOC_USER,
MASSOC_PART,
MASSOC_QOS,
MASSOC_GTM,
MASSOC_GTRM,
MASSOC_GT,
MASSOC_MTMPJ,
MASSOC_MTRM,
MASSOC_MTPJ,
MASSOC_MTPN,
MASSOC_LINEAGE,
MASSOC_FLAGS,
MASSOC_COUNT
};
/* if this changes you will need to edit the corresponding
* enum below also t1 is step_table */
static char *rassoc_req_inx[] = {
"id_assoc",
"id_parent",
"acct",
"parent_acct",
"user",
"`partition`"
};
enum {
RASSOC_ID,
RASSOC_ID_PAR,
RASSOC_ACCT,
RASSOC_PACCT,
RASSOC_USER,
RASSOC_PART,
RASSOC_COUNT
};
static int _assoc_sort_cluster(void *r1, void *r2)
{
slurmdb_assoc_rec_t *rec_a = *(slurmdb_assoc_rec_t **)r1;
slurmdb_assoc_rec_t *rec_b = *(slurmdb_assoc_rec_t **)r2;
int diff;
diff = xstrcmp(rec_a->cluster, rec_b->cluster);
if (diff < 0)
return -1;
else if (diff > 0)
return 1;
return 0;
}
/* Caller responsible for freeing query being sent in as it could be
* changed while running the function.
*/
static int _reset_default_assoc(mysql_conn_t *mysql_conn,
slurmdb_assoc_rec_t *assoc,
char **query,
bool add_to_update)
{
time_t now = time(NULL);
int rc = SLURM_SUCCESS;
char *reset_query = NULL;
char **use_query = NULL;
bool run_update = false;
if ((assoc->is_def != 1) || !assoc->cluster
|| !assoc->acct || !assoc->user)
return SLURM_ERROR;
if (add_to_update) {
char *sel_query = NULL;
MYSQL_RES *result = NULL;
MYSQL_ROW row;
/* If moved parent all the associations will be sent
so no need to do this extra step. Else, this has
to be done one at a time so we can send
the updated assocs back to the slurmctlds
*/
xstrfmtcat(sel_query, "select id_assoc from \"%s_%s\" "
"where (user='%s' && acct!='%s' && is_def=1);",
assoc->cluster, assoc_table,
assoc->user, assoc->acct);
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", sel_query);
if (!(result = mysql_db_query_ret(
mysql_conn, sel_query, 1))) {
xfree(sel_query);
rc = SLURM_ERROR;
goto end_it;
}
xfree(sel_query);
while ((row = mysql_fetch_row(result))) {
slurmdb_assoc_rec_t *mod_assoc = xmalloc(
sizeof(slurmdb_assoc_rec_t));
slurmdb_init_assoc_rec(mod_assoc, 0);
mod_assoc->cluster = xstrdup(assoc->cluster);
mod_assoc->id = slurm_atoul(row[0]);
mod_assoc->is_def = 0;
if (addto_update_list(mysql_conn->update_list,
SLURMDB_MODIFY_ASSOC,
mod_assoc)
!= SLURM_SUCCESS) {
slurmdb_destroy_assoc_rec(mod_assoc);
error("couldn't add to the update list");
rc = SLURM_ERROR;
break;
}
run_update = true;
}
mysql_free_result(result);
} else
run_update = true;
if (run_update) {
use_query = query ? query : &reset_query;
xstrfmtcat(*use_query,
"update \"%s_%s\" set is_def=0, mod_time=%ld where (user='%s' && acct!='%s' && is_def=1);",
assoc->cluster, assoc_table, (long)now,
assoc->user, assoc->acct);
if (reset_query) {
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", reset_query);
if ((rc = mysql_db_query(mysql_conn, reset_query)) !=
SLURM_SUCCESS)
error("Couldn't reset default assocs");
xfree(reset_query);
}
}
end_it:
return rc;
}
static int _make_sure_user_has_default_internal(
mysql_conn_t *mysql_conn, char *user, char *cluster)
{
MYSQL_RES *result = NULL;
MYSQL_ROW row;
char *query = NULL;
int rc = SLURM_SUCCESS;
slurmdb_assoc_rec_t *mod_assoc;
if (slurmdbd_conf->flags & DBD_CONF_FLAG_ALLOW_NO_DEF_ACCT)
return rc;
query = xstrdup_printf(
"select distinct is_def, acct from "
"\"%s_%s\" where user='%s' and deleted!=1 "
"ORDER BY is_def desc, creation_time desc "
"LIMIT 1;",
cluster, assoc_table, user);
debug4("%d(%s:%d) query\n%s",
mysql_conn->conn, THIS_FILE, __LINE__, query);
if (!(result = mysql_db_query_ret(
mysql_conn, query, 0))) {
xfree(query);
error("couldn't query the database");
return SLURM_ERROR;
}
xfree(query);
/* Check to see if the user is even added to
the cluster.
*/
if (!mysql_num_rows(result)) {
mysql_free_result(result);
return SLURM_SUCCESS;
}
/* check if the row is default */
row = mysql_fetch_row(result);
if (row[0][0] == '1') {
/* default found, continue */
mysql_free_result(result);
return SLURM_SUCCESS;
}
/* if we made it here, there is no default */
query = xstrdup_printf(
"update \"%s_%s\" set is_def=1 where "
"user='%s' and acct='%s';",
cluster, assoc_table, user, row[1]);
mysql_free_result(result);
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s",
query);
rc = mysql_db_query(mysql_conn, query);
xfree(query);
if (rc != SLURM_SUCCESS) {
error("problem with update query");
return SLURM_ERROR;
}
/*
* Now we need to add this association as the default to
* the update_list.
*/
query = xstrdup_printf(
"select id_assoc from \"%s_%s\" where user='%s' and is_def=1 and deleted!=1 LIMIT 1;",
cluster, assoc_table, user);
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s",
query);
if (!(result = mysql_db_query_ret(
mysql_conn, query, 0))) {
xfree(query);
error("couldn't query the database");
return SLURM_ERROR;
}
xfree(query);
/* check if the row is default */
row = mysql_fetch_row(result);
if (!row || !row[0]) {
error("User '%s' doesn't have a default like you would expect on cluster '%s'.",
user, cluster);
/* default found, continue */
mysql_free_result(result);
return SLURM_SUCCESS;
}
mod_assoc = xmalloc(sizeof(*mod_assoc));
slurmdb_init_assoc_rec(mod_assoc, 0);
mod_assoc->cluster = xstrdup(cluster);
mod_assoc->id = slurm_atoul(row[0]);
mod_assoc->is_def = 1;
mysql_free_result(result);
if (addto_update_list(mysql_conn->update_list,
SLURMDB_MODIFY_ASSOC,
mod_assoc) != SLURM_SUCCESS) {
slurmdb_destroy_assoc_rec(mod_assoc);
error("couldn't add to the update list");
return SLURM_ERROR;
}
return SLURM_SUCCESS;
}
static int _get_parent_id(
mysql_conn_t *mysql_conn, char *parent, char *cluster,
uint32_t *parent_id, char **lineage)
{
MYSQL_RES *result = NULL;
MYSQL_ROW row;
char *query = NULL;
int rc = SLURM_SUCCESS;
xassert(parent);
xassert(cluster);
query = xstrdup_printf("select id_assoc, lineage from \"%s_%s\" where user='' "
"and deleted!=1 and acct='%s';",
cluster, assoc_table, parent);
debug4("%d(%s:%d) query\n%s",
mysql_conn->conn, THIS_FILE, __LINE__, query);
if (!(result = mysql_db_query_ret(mysql_conn, query, 1))) {
xfree(query);
return SLURM_ERROR;
}
xfree(query);
if ((row = mysql_fetch_row(result))) {
if (row[0])
*parent_id = slurm_atoul(row[0]);
if (lineage && row[1])
*lineage = xstrdup(row[1]);
} else {
error("no association for parent %s on cluster %s",
parent, cluster);
rc = ESLURM_INVALID_PARENT_ACCOUNT;
}
mysql_free_result(result);
return rc;
}
static int _set_assoc_limits_for_add(
mysql_conn_t *mysql_conn, slurmdb_assoc_rec_t *assoc)
{
MYSQL_RES *result = NULL;
MYSQL_ROW row;
char *query = NULL;
char *parent = NULL;
char *qos_delta = NULL;
uint32_t tres_str_flags = TRES_STR_FLAG_REMOVE;
xassert(assoc);
if (assoc->parent_acct)
parent = assoc->parent_acct;
else if (assoc->user)
parent = assoc->acct;
else
return SLURM_SUCCESS;
query = xstrdup_printf("call get_parent_limits('%s', "
"'%s', '%s', %u);",
assoc_table, parent, assoc->cluster, 0);
debug4("%d(%s:%d) query\n%s",
mysql_conn->conn, THIS_FILE, __LINE__, query);
if (!(result = mysql_db_query_ret(mysql_conn, query, 1))) {
xfree(query);
return SLURM_ERROR;
}
xfree(query);
if (!(row = mysql_fetch_row(result)))
goto end_it;
if (row[ASSOC2_REQ_DEF_QOS] && assoc->def_qos_id == INFINITE)
assoc->def_qos_id = slurm_atoul(row[ASSOC2_REQ_DEF_QOS]);
else if (assoc->def_qos_id == INFINITE)
assoc->def_qos_id = 0;
if (row[ASSOC2_REQ_MJ] && assoc->max_jobs == INFINITE)
assoc->max_jobs = slurm_atoul(row[ASSOC2_REQ_MJ]);
if (row[ASSOC2_REQ_MJA] && assoc->max_jobs_accrue == INFINITE)
assoc->max_jobs_accrue = slurm_atoul(row[ASSOC2_REQ_MJA]);
if (row[ASSOC2_REQ_MPT] && assoc->min_prio_thresh == INFINITE)
assoc->min_prio_thresh = slurm_atoul(row[ASSOC2_REQ_MPT]);
if (row[ASSOC2_REQ_MSJ] && assoc->max_submit_jobs == INFINITE)
assoc->max_submit_jobs = slurm_atoul(row[ASSOC2_REQ_MSJ]);
if (row[ASSOC2_REQ_MWPJ] && assoc->max_wall_pj == INFINITE)
assoc->max_wall_pj = slurm_atoul(row[ASSOC2_REQ_MWPJ]);
if (row[ASSOC2_REQ_PRIO] && assoc->priority == INFINITE)
assoc->priority = slurm_atoul(row[ASSOC2_REQ_PRIO]);
/* For the tres limits we just concatted the limits going up
* the hierarchy slurmdb_tres_list_from_string will just skip
* over any reoccuring limit to give us the first one per
* TRES.
*/
slurmdb_combine_tres_strings(
&assoc->max_tres_pj, row[ASSOC2_REQ_MTPJ],
tres_str_flags);
slurmdb_combine_tres_strings(
&assoc->max_tres_pn, row[ASSOC2_REQ_MTPN],
tres_str_flags);
slurmdb_combine_tres_strings(
&assoc->max_tres_mins_pj, row[ASSOC2_REQ_MTMPJ],
tres_str_flags);
slurmdb_combine_tres_strings(
&assoc->max_tres_run_mins, row[ASSOC2_REQ_MTRM],
tres_str_flags);
if (assoc->qos_list) {
int set = 0;
char *tmp_char = NULL;
list_itr_t *qos_itr = list_iterator_create(assoc->qos_list);
while ((tmp_char = list_next(qos_itr))) {
/* we don't want to include blank names */
if (!tmp_char[0])
continue;
if (!set) {
if (tmp_char[0] != '+' && tmp_char[0] != '-')
break;
set = 1;
}
xstrfmtcat(qos_delta, ",%s", tmp_char);
}
list_iterator_destroy(qos_itr);
if (tmp_char) {
/* we have the qos here nothing from parents
needed */
goto end_it;
}
list_flush(assoc->qos_list);
} else
assoc->qos_list = list_create(xfree_ptr);
if (row[ASSOC2_REQ_QOS][0])
slurm_addto_char_list(assoc->qos_list, row[ASSOC2_REQ_QOS]+1);
if (row[ASSOC2_REQ_DELTA_QOS][0])
slurm_addto_char_list(assoc->qos_list,
row[ASSOC2_REQ_DELTA_QOS]+1);
if (qos_delta) {
slurm_addto_char_list(assoc->qos_list, qos_delta+1);
xfree(qos_delta);
}
end_it:
mysql_free_result(result);
return SLURM_SUCCESS;
}
static int _set_lineage(mysql_conn_t *mysql_conn, slurmdb_assoc_rec_t *assoc,
char *parent_acct, char *acct, char *user, char *part)
{
int rc;
char *query = NULL, *query_pos = NULL;
xassert(assoc);
xassert(assoc->cluster);
xassert(parent_acct);
xassert(acct);
rc = _get_parent_id(mysql_conn,
parent_acct,
assoc->cluster,
&assoc->parent_id,
&assoc->lineage);
if (rc != SLURM_SUCCESS)
return rc;
if (user && user[0]) {
xstrfmtcat(assoc->lineage, "0-%s/", user);
if (part && part[0])
xstrfmtcat(assoc->lineage, "%s/", part);
} else
xstrfmtcat(assoc->lineage, "%s/", acct);
//info("%u parent's is %s(%u) '%s' '%s'", assoc->id, parent_acct, assoc->parent_id, assoc->parent_acct, assoc->lineage);
/*
* This has to be updated immediately so others can grab this right
* afterward.
*/
xstrfmtcatat(query, &query_pos,
"update \"%s_%s\" set lineage='%s', id_parent=%u",
assoc->cluster, assoc_table,
assoc->lineage, assoc->parent_id);
if (!user || !user[0])
xstrfmtcatat(query, &query_pos,
", parent_acct='%s'", parent_acct);
xstrfmtcatat(query, &query_pos, " where id_assoc=%u", assoc->id);
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", query);
rc = mysql_db_query(mysql_conn, query);
xfree(query);
return rc;
}
/*
* Used to get all the associations in a lineage. This is just
* to send the assoc_mgr all the associations that are being modified from
* a previous change to it's parent.
*/
static int _modify_child_assocs(mysql_conn_t *mysql_conn,
slurmdb_assoc_rec_t *assoc,
char *acct,
char *lineage,
list_t *ret_list, int moved_parent,
char *old_parent, char *new_parent)
{
MYSQL_RES *result = NULL;
MYSQL_ROW row;
char *query = NULL, *query_pos = NULL, *object = NULL;
int i, rc = SLURM_SUCCESS;
uint32_t tres_str_flags = TRES_STR_FLAG_REMOVE | TRES_STR_FLAG_NO_NULL;
char *assoc_inx[] = {
"id_assoc",
"user",
"acct",
"parent_acct",
"`partition`",
"max_jobs",
"max_jobs_accrue",
"min_prio_thresh",
"max_submit_jobs",
"max_tres_pj",
"max_tres_pn",
"max_wall_pj",
"max_tres_mins_pj",
"max_tres_run_mins",
"priority",
"def_qos_id",
"qos",
"delta_qos",
"lineage",
};
enum {
ASSOC_ID,
ASSOC_USER,
ASSOC_ACCT,
ASSOC_PACCT,
ASSOC_PART,
ASSOC_MJ,
ASSOC_MJA,
ASSOC_MPT,
ASSOC_MSJ,
ASSOC_MTPJ,
ASSOC_MTPN,
ASSOC_MWPJ,
ASSOC_MTMPJ,
ASSOC_MTRM,
ASSOC_PRIO,
ASSOC_DEF_QOS,
ASSOC_QOS,
ASSOC_DELTA_QOS,
ASSOC_LINEAGE,
ASSOC_COUNT
};
xassert(assoc);
xassert(assoc->cluster);
if (!ret_list || !lineage)
return SLURM_ERROR;
xstrcat(object, assoc_inx[0]);
for (i=1; i<ASSOC_COUNT; i++)
xstrfmtcat(object, ", %s", assoc_inx[i]);
/* We want all direct sub accounts and user accounts */
xstrfmtcatat(query, &query_pos,
"select distinct %s from \"%s_%s\" where deleted!=1 && id_assoc!=%u && lineage like '%s%%' && ((user = '' && parent_acct = '%s') || (user != '' && acct = '%s')) order by lineage;",
object, assoc->cluster, assoc_table,
assoc->id, lineage, acct, acct);
xfree(object);
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", query);
if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
xfree(query);
return SLURM_ERROR;
}
xfree(query);
while ((row = mysql_fetch_row(result))) {
slurmdb_assoc_rec_t *mod_assoc = NULL;
int modified = 0;
char *tmp_char = NULL;
mod_assoc = xmalloc(sizeof(slurmdb_assoc_rec_t));
slurmdb_init_assoc_rec(mod_assoc, 0);
mod_assoc->id = slurm_atoul(row[ASSOC_ID]);
mod_assoc->cluster = xstrdup(assoc->cluster);
/*
* DON'T DO FLAGS HERE UNLESS A CHILD NEEDS THE PARENT'S FLAGS
* IN THE FUTURE.
*/
if (!row[ASSOC_DEF_QOS] && assoc->def_qos_id != NO_VAL) {
mod_assoc->def_qos_id = assoc->def_qos_id;
modified = 1;
}
if (!row[ASSOC_MJ] && assoc->max_jobs != NO_VAL) {
mod_assoc->max_jobs = assoc->max_jobs;
modified = 1;
}
if (!row[ASSOC_MJA] && assoc->max_jobs_accrue != NO_VAL) {
mod_assoc->max_jobs_accrue = assoc->max_jobs_accrue;
modified = 1;
}
if (!row[ASSOC_MPT] && assoc->min_prio_thresh != NO_VAL) {
mod_assoc->min_prio_thresh = assoc->min_prio_thresh;
modified = 1;
}
if (!row[ASSOC_MSJ] && assoc->max_submit_jobs != NO_VAL) {
mod_assoc->max_submit_jobs = assoc->max_submit_jobs;
modified = 1;
}
if (!row[ASSOC_MWPJ] && assoc->max_wall_pj != NO_VAL) {
mod_assoc->max_wall_pj = assoc->max_wall_pj;
modified = 1;
}
if (!row[ASSOC_PRIO] && assoc->priority != NO_VAL) {
mod_assoc->priority = assoc->priority;
modified = 1;
}
if (assoc->max_tres_pj) {
tmp_char = xstrdup(row[ASSOC_MTPJ]);
slurmdb_combine_tres_strings(
&tmp_char, assoc->max_tres_pj,
tres_str_flags);
mod_assoc->max_tres_pj = tmp_char;
tmp_char = NULL;
modified = 1;
}
if (assoc->max_tres_pn) {
tmp_char = xstrdup(row[ASSOC_MTPN]);
slurmdb_combine_tres_strings(
&tmp_char, assoc->max_tres_pn,
tres_str_flags);
mod_assoc->max_tres_pn = tmp_char;
tmp_char = NULL;
modified = 1;
}
if (assoc->max_tres_mins_pj) {
tmp_char = xstrdup(row[ASSOC_MTMPJ]);
slurmdb_combine_tres_strings(
&tmp_char, assoc->max_tres_mins_pj,
tres_str_flags);
mod_assoc->max_tres_mins_pj = tmp_char;
tmp_char = NULL;
modified = 1;
}
if (assoc->max_tres_run_mins) {
tmp_char = xstrdup(row[ASSOC_MTRM]);
slurmdb_combine_tres_strings(
&tmp_char, assoc->max_tres_run_mins,
tres_str_flags);
mod_assoc->max_tres_run_mins = tmp_char;
tmp_char = NULL;
modified = 1;
}
if (!row[ASSOC_QOS][0] && assoc->qos_list) {
list_t *delta_qos_list = NULL;
char *qos_char = NULL, *delta_char = NULL;
list_itr_t *delta_itr = NULL;
list_itr_t *qos_itr =
list_iterator_create(assoc->qos_list);
if (row[ASSOC_DELTA_QOS][0]) {
delta_qos_list = list_create(xfree_ptr);
slurm_addto_char_list(delta_qos_list,
row[ASSOC_DELTA_QOS]+1);
delta_itr =
list_iterator_create(delta_qos_list);
}
mod_assoc->qos_list = list_create(xfree_ptr);
/* here we are making sure a child does not
have the qos added or removed before we add
it to the parent.
*/
while ((qos_char = list_next(qos_itr))) {
if (delta_itr && qos_char[0] != '=') {
while ((delta_char =
list_next(delta_itr))) {
if ((qos_char[0]
!= delta_char[0])
&& (!xstrcmp(qos_char+1,
delta_char+1)))
break;
}
list_iterator_reset(delta_itr);
if (delta_char)
continue;
}
list_append(mod_assoc->qos_list,
xstrdup(qos_char));
}
list_iterator_destroy(qos_itr);
if (delta_itr)
list_iterator_destroy(delta_itr);
FREE_NULL_LIST(delta_qos_list);
if (list_count(mod_assoc->qos_list)
|| !list_count(assoc->qos_list))
modified = 1;
else {
FREE_NULL_LIST(mod_assoc->qos_list);
mod_assoc->qos_list = NULL;
}
}
if (moved_parent) {
char *use_parent;
if (row[ASSOC_USER][0])
use_parent = row[ASSOC_ACCT];
else if (!xstrcmp(row[ASSOC_ACCT], new_parent))
use_parent = old_parent;
else
use_parent = row[ASSOC_PACCT];
/*
* Now set lineage on all of the associations related
* set_lineage() sets mod_time as well.
*/
rc = _set_lineage(mysql_conn, mod_assoc, use_parent,
row[ASSOC_ACCT], row[ASSOC_USER],
row[ASSOC_PART]);
if (rc != SLURM_SUCCESS) {
slurmdb_destroy_assoc_rec(mod_assoc);
break;
}
modified = 1;
}
/* We only want to add those that are modified here */
if (modified) {
char *object_pos = NULL;
xstrfmtcatat(object, &object_pos,
"C = %-10s A = %-20s",
assoc->cluster, row[ASSOC_ACCT]);
if (row[ASSOC_USER][0]) {
/* Only send modified user associations */
mod_assoc->shares_raw = NO_VAL;
xstrfmtcatat(object, &object_pos,
" U = %-9s",
row[ASSOC_USER]);
if (row[ASSOC_PART][0])
xstrfmtcatat(object, &object_pos,
" P = %s",
row[ASSOC_PART]);
}
list_append(ret_list, object);
object = NULL;
if (row[ASSOC_PACCT][0]) {
/*
* This is a sub account so run it
* through as if it is a parent.
* We have already handled the parent change, so
* send in the current parent as the last parent
* as well.
*/
_modify_child_assocs(mysql_conn,
mod_assoc,
row[ASSOC_ACCT],
row[ASSOC_LINEAGE],
ret_list,
moved_parent,
row[ASSOC_PACCT],
row[ASSOC_PACCT]);
}
if (addto_update_list(mysql_conn->update_list,
SLURMDB_MODIFY_ASSOC,
mod_assoc) !=
SLURM_SUCCESS) {
error("couldn't add to the update list");
} else
mod_assoc = NULL;
}
slurmdb_destroy_assoc_rec(mod_assoc);
}
mysql_free_result(result);
return rc;
}
static char *_setup_assoc_table_query(char *cluster_name, char *fields,
char *filters, char *end)
{
return xstrdup_printf("select distinct %s from \"%s_%s\" as t1 where%s%s",
fields, cluster_name, assoc_table, filters, end);
}
/* When doing a select on this all the select should have a prefix of t1. */
static int _setup_assoc_cond_limits(slurmdb_assoc_cond_t *assoc_cond,
const char *prefix, char **extra)
{
int set = 0;
list_itr_t *itr = NULL;
char *object = NULL;
if (!assoc_cond) {
xstrfmtcat(*extra, " TRUE");
return 0;
}
/*
* Don't use prefix here, always use t1 or we could get extra "deleted"
* entries we don't want.
*/
if (assoc_cond->flags & ASSOC_COND_FLAG_WITH_DELETED)
xstrfmtcat(*extra, " (t1.deleted=0 || t1.deleted=1)");
else
xstrfmtcat(*extra, " t1.deleted=0");
if (assoc_cond->flags & ASSOC_COND_FLAG_ONLY_DEFS) {
set = 1;
xstrfmtcat(*extra, " && (%s.is_def=1)", prefix);
}
if (assoc_cond->acct_list && list_count(assoc_cond->acct_list)) {
set = 0;
xstrcat(*extra, " && (");
itr = list_iterator_create(assoc_cond->acct_list);
while ((object = list_next(itr))) {
if (set)
xstrcat(*extra, " || ");
if (assoc_cond->flags & ASSOC_COND_FLAG_SUB_ACCTS) {
xstrfmtcat(*extra,
"%s.lineage like '%%/%s/%%'",
prefix, object);
} else {
xstrfmtcat(*extra, "%s.acct='%s'",
prefix, object);
}
set = 1;
}
list_iterator_destroy(itr);
xstrcat(*extra, ")");
}
if (assoc_cond->def_qos_id_list
&& list_count(assoc_cond->def_qos_id_list)) {
set = 0;
xstrcat(*extra, " && (");
itr = list_iterator_create(assoc_cond->def_qos_id_list);
while ((object = list_next(itr))) {
if (set)
xstrcat(*extra, " || ");
xstrfmtcat(*extra, "%s.def_qos_id='%s'",
prefix, object);
set = 1;
}
list_iterator_destroy(itr);
xstrcat(*extra, ")");
}
if (assoc_cond->user_list && list_count(assoc_cond->user_list)) {
set = 0;
xstrcat(*extra, " && (");
itr = list_iterator_create(assoc_cond->user_list);
while ((object = list_next(itr))) {
if (set)
xstrcat(*extra, " || ");
xstrfmtcat(*extra, "%s.user='%s'", prefix, object);
set = 1;
}
list_iterator_destroy(itr);
xstrcat(*extra, ")");
} else if (assoc_cond->user_list) {
/* we want all the users, but no non-user associations */
set = 1;
xstrfmtcat(*extra, " && (%s.user!='')", prefix);
}
if (assoc_cond->partition_list
&& list_count(assoc_cond->partition_list)) {
set = 0;
xstrcat(*extra, " && (");
itr = list_iterator_create(assoc_cond->partition_list);
while ((object = list_next(itr))) {
if (set)
xstrcat(*extra, " || ");
xstrfmtcat(*extra, "%s.partition='%s'",
prefix, object);
set = 1;
}
list_iterator_destroy(itr);
xstrcat(*extra, ")");
}
if (assoc_cond->id_list && list_count(assoc_cond->id_list)) {
set = 0;
xstrcat(*extra, " && (");
itr = list_iterator_create(assoc_cond->id_list);
while ((object = list_next(itr))) {
if (set)
xstrcat(*extra, " || ");
xstrfmtcat(*extra, "%s.id_assoc=%s", prefix, object);
set = 1;
}
list_iterator_destroy(itr);
xstrcat(*extra, ")");
}
if (assoc_cond->parent_acct_list
&& list_count(assoc_cond->parent_acct_list)) {
set = 0;
xstrcat(*extra, " && (");
itr = list_iterator_create(assoc_cond->parent_acct_list);
while ((object = list_next(itr))) {
if (set)
xstrcat(*extra, " || ");
xstrfmtcat(*extra, "%s.parent_acct='%s'",
prefix, object);
set = 1;
}
list_iterator_destroy(itr);
xstrcat(*extra, ")");
}
return set;
}
/*
* Use this on returned assocs from the db to validate if you have access to the
* QOS or not.
*
* Use the assoc_mgr to verify this assoc is viable based off of the QOS. It's
* faster to do it after the fact instead of a complicated join in SQL because
* of the hierarchy.
*/
static bool _assoc_id_has_qos(mysql_conn_t *mysql_conn, char *cluster,
uint32_t assoc_id, bitstr_t *wanted_qos)
{
if (wanted_qos) {
slurmdb_assoc_rec_t *assoc_ptr = NULL;
slurmdb_assoc_rec_t assoc_req = {
.cluster = cluster,
.id = assoc_id,
};
xassert(verify_assoc_lock(ASSOC_LOCK, READ_LOCK));
/*
* Assoc mgr maintains the inherited qos for an assoc. Using its
* version avoids an expensive sql query to get it.
*/
assoc_mgr_fill_in_assoc(mysql_conn, &assoc_req,
ACCOUNTING_ENFORCE_ASSOCS, &assoc_ptr,
true);
if (!assoc_ptr ||
!assoc_ptr->usage ||
!assoc_ptr->usage->valid_qos ||
!bit_overlap(assoc_ptr->usage->valid_qos, wanted_qos))
return false;
}
return true;
}
/*
* If assoc_mgr_locked then the following READ_LOCKs need to be already owned:
* ASSOC_LOCK, USER_LOCK, QOS_LOCK, TRES_LOCK
*/
static int _process_modify_assoc_results(mysql_conn_t *mysql_conn,
MYSQL_RES *result,
slurmdb_assoc_rec_t *assoc,
slurmdb_user_rec_t *user,
char *cluster_name, char *sent_vals,
bool is_admin, bool same_user,
list_t *ret_list,
slurmdb_assoc_cond_t *qos_assoc_cond,
bitstr_t *wanted_qos,
bool assoc_mgr_locked)
{
MYSQL_ROW row;
int added = 0;
int rc = SLURM_SUCCESS;
int set_qos_vals = 0;
int moved_parent = 0;
char *query = NULL, *vals = NULL, *object = NULL, *name_char = NULL;
char *reset_query = NULL;
char *str = NULL;
time_t now = time(NULL);
bool is_coord = false;
bool disable_coord_dbd = false;
bool checked_new_parent = false;
bool checked_parent_is_not_child = false;
xassert(result);
if (!mysql_num_rows(result))
return SLURM_SUCCESS;
vals = xstrdup(sent_vals);
disable_coord_dbd = slurmdbd_conf->flags &
DBD_CONF_FLAG_DISABLE_COORD_DBD;
while ((row = mysql_fetch_row(result))) {
MYSQL_RES *result2 = NULL;
slurmdb_assoc_rec_t *mod_assoc = NULL, alt_assoc;
int account_type=0;
/* If parent changes these also could change
so we need to keep track of the latest
ones.
*/
uint32_t id = slurm_atoul(row[MASSOC_ID]);
char *orig_acct, *account;
if (!_assoc_id_has_qos(mysql_conn, cluster_name, id,
wanted_qos))
continue;
orig_acct = account = row[MASSOC_ACCT];
slurmdb_init_assoc_rec(&alt_assoc, 0);
/* Here we want to see if the person
* is a coord of the parent account
* since we don't want him to be able
* to alter the limits of the account
* he is directly coord of. They
* should be able to alter the
* sub-accounts though. If no parent account
* that means we are talking about a user
* association so account is really the parent
* of the user a coord can change that all day long.
*/
if (row[MASSOC_PACCT][0])
account = row[MASSOC_PACCT];
/* If this is the same user all has been done
previously to make sure the user is only changing
things they are allowed to change.
*/
if (!is_admin && !same_user) {
char *orig_account = account;
if (disable_coord_dbd) {
error("Coordinator privilege revoked with DisableCoordDBD, only admins can modify accounts.");
rc = ESLURM_ACCESS_DENIED;
goto end_it;
}
if (!user->coord_accts) { // This should never
// happen
error("We are here with no coord accts.");
rc = ESLURM_ACCESS_DENIED;
goto end_it;
}
check_again:
if (!list_find_first(user->coord_accts,
assoc_mgr_find_coord_in_user,
account)) {
if (row[MASSOC_PACCT][0])
error("User %s(%d) can not modify "
"account (%s) because they "
"are not coordinators of "
"parent account '%s'.",
user->name, user->uid,
row[MASSOC_ACCT],
row[MASSOC_PACCT]);
else
error("User %s(%d) does not have the "
"ability to modify the account "
"(%s).",
user->name, user->uid,
row[MASSOC_ACCT]);
rc = ESLURM_ACCESS_DENIED;
goto end_it;
} else if (!assoc_mgr_check_coord_qos(
cluster_name, account, user->name,
assoc->qos_list, assoc_mgr_locked)) {
/* Caller may have already locked the qos */
assoc_mgr_lock_t locks = {
.qos = READ_LOCK,
};
char *requested_qos;
if (!assoc_mgr_locked)
assoc_mgr_lock(&locks);
xassert(verify_assoc_lock(QOS_LOCK, locks.qos));
requested_qos = get_qos_complete_str(
assoc_mgr_qos_list, assoc->qos_list);
if (!assoc_mgr_locked)
assoc_mgr_unlock(&locks);
error("Coordinator %s(%d) does not have the "
"access to all the qos requested (%s), "
"so they can't modify account "
"%s with it.",
user->name, user->uid, requested_qos,
account);
xfree(requested_qos);
rc = ESLURM_ACCESS_DENIED;
goto end_it;
}
/*
* Now check to see if they are also a coord of the new
* parent.
*/
if (!checked_new_parent &&
assoc->parent_acct &&
row[MASSOC_PACCT][0]) {
account = assoc->parent_acct;
checked_new_parent = true;
goto check_again;
} else
account = orig_account;
is_coord = true;
}
if (assoc->parent_acct && row[MASSOC_PACCT][0]) {
account = assoc->parent_acct;
moved_parent = 1;
if (!checked_parent_is_not_child) {
slurmdb_assoc_rec_t par_assoc = {
.acct = assoc->parent_acct,
.cluster = cluster_name,
.uid = NO_VAL,
};
slurmdb_assoc_rec_t *par_assoc_ptr = NULL;
if (assoc_mgr_fill_in_assoc(
mysql_conn, &par_assoc,
ACCOUNTING_ENFORCE_ASSOCS,
&par_assoc_ptr, assoc_mgr_locked) !=
SLURM_SUCCESS) {
object = xstrdup_printf(
"Parent account %s doesn't exist on cluster %s",
assoc->parent_acct,
cluster_name);
list_append(ret_list, object);
object = NULL;
/* We want SLURM_SUCCESS here */
// rc = ESLURM_INVALID_PARENT_ACCOUNT;
break;
}
while (par_assoc_ptr) {
if (id == par_assoc_ptr->id)
break;
par_assoc_ptr =
par_assoc_ptr->usage->
parent_assoc_ptr;
}
if (par_assoc_ptr) {
object = xstrdup_printf(
"Requested parent account %s is a child of %s on cluster %s. Please re-parent %s before using it as a parent.",
assoc->parent_acct,
row[MASSOC_ACCT],
cluster_name,
assoc->parent_acct);
list_append(ret_list, object);
object = NULL;
/* We want SLURM_SUCCESS here */
// rc = ESLURM_INVALID_PARENT_ACCOUNT;
break;
}
checked_parent_is_not_child = true;
}
}
/* Only do this when not dealing with the root association. */
if (xstrcmp(orig_acct, "root") || row[MASSOC_USER][0]) {
MYSQL_ROW row2;
/* If there is a variable cleared here we need to make
sure we get the parent's information, if any. */
query = xstrdup_printf(
"call get_parent_limits('%s', "
"'%s', '%s', %u);",
assoc_table, account,
cluster_name, 0);
debug4("%d(%s:%d) query\n%s",
mysql_conn->conn, THIS_FILE, __LINE__, query);
if (!(result2 = mysql_db_query_ret(
mysql_conn, query, 1))) {
xfree(query);
break;
}
xfree(query);
if ((row2 = mysql_fetch_row(result2))) {
if (assoc->def_qos_id == INFINITE
&& row2[ASSOC2_REQ_DEF_QOS])
alt_assoc.def_qos_id = slurm_atoul(
row2[ASSOC2_REQ_DEF_QOS]);
if ((assoc->max_jobs == INFINITE)
&& row2[ASSOC2_REQ_MJ])
alt_assoc.max_jobs = slurm_atoul(
row2[ASSOC2_REQ_MJ]);
if ((assoc->max_jobs_accrue == INFINITE)
&& row2[ASSOC2_REQ_MJA])
alt_assoc.max_jobs_accrue = slurm_atoul(
row2[ASSOC2_REQ_MJA]);
if ((assoc->min_prio_thresh == INFINITE)
&& row2[ASSOC2_REQ_MPT])
alt_assoc.min_prio_thresh = slurm_atoul(
row2[ASSOC2_REQ_MPT]);
if ((assoc->max_submit_jobs == INFINITE)
&& row2[ASSOC2_REQ_MSJ])
alt_assoc.max_submit_jobs = slurm_atoul(
row2[ASSOC2_REQ_MSJ]);
if ((assoc->max_wall_pj == INFINITE)
&& row2[ASSOC2_REQ_MWPJ])
alt_assoc.max_wall_pj = slurm_atoul(
row2[ASSOC2_REQ_MWPJ]);
if ((assoc->priority == INFINITE)
&& row2[ASSOC2_REQ_PRIO])
alt_assoc.priority = slurm_atoul(
row2[ASSOC2_REQ_PRIO]);
/* We don't have to copy these strings
* or check for their existence,
* slurmdb_combine_tres_strings will
* do this for us below.
*/
if (row2[ASSOC2_REQ_MTPJ][0])
alt_assoc.max_tres_pj =
row2[ASSOC2_REQ_MTPJ];
if (row2[ASSOC2_REQ_MTPN][0])
alt_assoc.max_tres_pn =
row2[ASSOC2_REQ_MTPN];
if (row2[ASSOC2_REQ_MTMPJ][0])
alt_assoc.max_tres_mins_pj =
row2[ASSOC2_REQ_MTMPJ];
if (row2[ASSOC2_REQ_MTRM][0])
alt_assoc.max_tres_run_mins =
row2[ASSOC2_REQ_MTRM];
}
}
mod_assoc = xmalloc(sizeof(slurmdb_assoc_rec_t));
slurmdb_init_assoc_rec(mod_assoc, 0);
mod_assoc->id = id;
mod_assoc->flags = slurm_atoul(row[MASSOC_FLAGS]);
mod_assoc->cluster = xstrdup(cluster_name);
if (moved_parent) {
/*
* Now set the lineage with the new parent. This needs
* to be done here and it's children will be moved later
* below in _modify_child_assocs().
*/
mod_assoc->parent_acct = xstrdup(assoc->parent_acct);
rc = _set_lineage(mysql_conn, mod_assoc,
mod_assoc->parent_acct,
row[MASSOC_ACCT], NULL, NULL);
if (rc != SLURM_SUCCESS) {
slurmdb_destroy_assoc_rec(mod_assoc);
object = xstrdup_printf("Parent account %s doesn't exist on cluster %s", assoc->parent_acct, cluster_name);
list_append(ret_list, object);
object = NULL;
break;
}
}
if (row[MASSOC_PART][0]) {
// see if there is a partition name
object = xstrdup_printf(
"C = %-10s A = %-20s U = %-9s P = %s",
cluster_name, row[MASSOC_ACCT],
row[MASSOC_USER], row[MASSOC_PART]);
} else if (row[MASSOC_USER][0]){
object = xstrdup_printf(
"C = %-10s A = %-20s U = %-9s",
cluster_name, row[MASSOC_ACCT],
row[MASSOC_USER]);
} else {
if (assoc->parent_acct) {
if (!xstrcasecmp(row[MASSOC_ACCT],
assoc->parent_acct)) {
error("You can't make an account be a "
"child of it's self");
continue;
} else if (!xstrcasecmp(row[MASSOC_PACCT],
assoc->parent_acct)) {
DB_DEBUG(DB_ASSOC, mysql_conn->conn,
"Trying to move association to the same parent? Nothing to do.");
continue;
}
}
if (row[MASSOC_PACCT][0]) {
object = xstrdup_printf(
"C = %-10s A = %s of %s",
cluster_name, row[MASSOC_ACCT],
row[MASSOC_PACCT]);
} else {
object = xstrdup_printf(
"C = %-10s A = %s",
cluster_name, row[MASSOC_ACCT]);
}
account_type = 1;
}
list_append(ret_list, object);
object = NULL;
added++;
if (name_char)
xstrfmtcat(name_char, " || id_assoc=%s",
row[MASSOC_ID]);
else
xstrfmtcat(name_char, "(id_assoc=%s", row[MASSOC_ID]);
if (alt_assoc.def_qos_id != NO_VAL)
mod_assoc->def_qos_id = alt_assoc.def_qos_id;
else
mod_assoc->def_qos_id = assoc->def_qos_id;
mod_assoc->comment = xstrdup(assoc->comment);
mod_assoc->flags |= assoc->flags;
mod_assoc->is_def = assoc->is_def;
mod_assoc->shares_raw = assoc->shares_raw;
mod_tres_str(&mod_assoc->grp_tres,
assoc->grp_tres, row[MASSOC_GT],
NULL, "grp_tres", &vals, mod_assoc->id, 1);
mod_tres_str(&mod_assoc->grp_tres_mins,
assoc->grp_tres_mins, row[MASSOC_GTM],
NULL, "grp_tres_mins", &vals, mod_assoc->id, 1);
mod_tres_str(&mod_assoc->grp_tres_run_mins,
assoc->grp_tres_run_mins, row[MASSOC_GTRM],
NULL, "grp_tres_run_mins", &vals,
mod_assoc->id, 1);
mod_assoc->grp_jobs = assoc->grp_jobs;
mod_assoc->grp_jobs_accrue = assoc->grp_jobs_accrue;
mod_assoc->grp_submit_jobs = assoc->grp_submit_jobs;
mod_assoc->grp_wall = assoc->grp_wall;
mod_tres_str(&mod_assoc->max_tres_pj,
assoc->max_tres_pj, row[MASSOC_MTPJ],
alt_assoc.max_tres_pj, "max_tres_pj",
&vals, mod_assoc->id, 1);
mod_tres_str(&mod_assoc->max_tres_pn,
assoc->max_tres_pn, row[MASSOC_MTPN],
alt_assoc.max_tres_pn, "max_tres_pn",
&vals, mod_assoc->id, 1);
mod_tres_str(&mod_assoc->max_tres_mins_pj,
assoc->max_tres_mins_pj, row[MASSOC_MTMPJ],
alt_assoc.max_tres_mins_pj, "max_tres_mins_pj",
&vals, mod_assoc->id, 1);
mod_tres_str(&mod_assoc->max_tres_run_mins,
assoc->max_tres_run_mins, row[MASSOC_MTRM],
alt_assoc.max_tres_run_mins, "max_tres_run_mins",
&vals, mod_assoc->id, 1);
if (result2)
mysql_free_result(result2);
if (alt_assoc.max_jobs != NO_VAL)
mod_assoc->max_jobs = alt_assoc.max_jobs;
else
mod_assoc->max_jobs = assoc->max_jobs;
if (alt_assoc.max_jobs_accrue != NO_VAL)
mod_assoc->max_jobs_accrue = alt_assoc.max_jobs_accrue;
else
mod_assoc->max_jobs_accrue = assoc->max_jobs_accrue;
if (alt_assoc.min_prio_thresh != NO_VAL)
mod_assoc->min_prio_thresh = alt_assoc.min_prio_thresh;
else
mod_assoc->min_prio_thresh = assoc->min_prio_thresh;
if (alt_assoc.max_submit_jobs != NO_VAL)
mod_assoc->max_submit_jobs = alt_assoc.max_submit_jobs;
else
mod_assoc->max_submit_jobs = assoc->max_submit_jobs;
if (alt_assoc.max_wall_pj != NO_VAL)
mod_assoc->max_wall_pj = alt_assoc.max_wall_pj;
else
mod_assoc->max_wall_pj = assoc->max_wall_pj;
if (alt_assoc.priority != NO_VAL)
mod_assoc->priority = alt_assoc.priority;
else
mod_assoc->priority = assoc->priority;
if (is_coord &&
assoc_mgr_check_assoc_lim_incr(mod_assoc, &str,
assoc_mgr_locked)) {
error("Coordinators can not increase %s above the parent limit",
str);
xfree(str);
slurmdb_destroy_assoc_rec(mod_assoc);
xfree(reset_query);
rc = ESLURM_COORD_NO_INCREASE_JOB_LIMIT;
goto end_it;
}
if (assoc->qos_list && list_count(assoc->qos_list)) {
list_itr_t *new_qos_itr =
list_iterator_create(assoc->qos_list);
char *new_qos = NULL, *tmp_qos = NULL;
bool adding_straight = 0;
mod_assoc->qos_list = list_create(xfree_ptr);
while ((new_qos = list_next(new_qos_itr))) {
if (new_qos[0] == '-' || new_qos[0] == '+') {
list_append(mod_assoc->qos_list,
xstrdup(new_qos));
} else if (new_qos[0]) {
list_append(mod_assoc->qos_list,
xstrdup_printf("=%s",
new_qos));
}
if (set_qos_vals)
continue;
/* Now we can set up the values and
make sure we aren't over writing
things that are really from the
parent
*/
if (new_qos[0] == '-') {
xstrfmtcat(vals,
", qos=if (qos='', '', "
"replace(replace("
"qos, ',%s,', ','), "
"',,', ','))"
", qos=if (qos=',', '', qos)"
", delta_qos=if (qos='', "
"replace(concat(replace("
"replace("
"delta_qos, ',+%s,', ','), "
"',-%s,', ','), "
"',%s,'), ',,', ','), '')",
new_qos+1, new_qos+1,
new_qos+1, new_qos);
} else if (new_qos[0] == '+') {
xstrfmtcat(vals,
", qos=if (qos='', '', "
"replace(concat("
"replace(qos, ',%s,', ','), "
"',%s,'), ',,', ',')), "
"delta_qos=if ("
"qos='', replace(concat("
"replace(replace("
"delta_qos, ',+%s,', ','), "
"',-%s,', ','), "
"',%s,'), ',,', ','), '')",
new_qos+1, new_qos+1,
new_qos+1, new_qos+1,
new_qos);
} else if (new_qos[0]) {
xstrfmtcat(tmp_qos, ",%s", new_qos);
adding_straight = 1;
} else
xstrcat(tmp_qos, "");
}
list_iterator_destroy(new_qos_itr);
if (!set_qos_vals && tmp_qos) {
xstrfmtcat(vals, ", qos='%s%s', delta_qos=''",
tmp_qos, adding_straight ? "," : "");
}
xfree(tmp_qos);
set_qos_vals = 1;
}
if ((assoc->qos_list ||
(assoc->def_qos_id && (assoc->def_qos_id != NO_VAL)))) {
if (!qos_assoc_cond->acct_list)
qos_assoc_cond->acct_list =
list_create(xfree_ptr);
slurm_addto_char_list(qos_assoc_cond->acct_list,
row[MASSOC_ACCT]);
if (row[MASSOC_USER][0]) {
if (!qos_assoc_cond->user_list)
qos_assoc_cond->user_list =
list_create(xfree_ptr);
slurm_addto_char_list(qos_assoc_cond->user_list,
row[MASSOC_USER]);
}
}
if (account_type) {
_modify_child_assocs(mysql_conn,
mod_assoc,
row[MASSOC_ACCT],
row[MASSOC_LINEAGE],
ret_list,
moved_parent,
row[MASSOC_PACCT],
assoc->parent_acct);
} else if ((assoc->is_def == 1) && row[MASSOC_USER][0]) {
/* Use fresh one here so we don't have to
worry about dealing with bad values.
*/
slurmdb_assoc_rec_t tmp_assoc;
slurmdb_init_assoc_rec(&tmp_assoc, 0);
tmp_assoc.is_def = 1;
tmp_assoc.cluster = cluster_name;
tmp_assoc.acct = row[MASSOC_ACCT];
tmp_assoc.user = row[MASSOC_USER];
if ((rc = _reset_default_assoc(
mysql_conn, &tmp_assoc, &reset_query,
moved_parent ? 0 : 1))
!= SLURM_SUCCESS) {
slurmdb_destroy_assoc_rec(mod_assoc);
xfree(reset_query);
goto end_it;
}
}
if (!moved_parent && (!vals || !vals[0]))
slurmdb_destroy_assoc_rec(mod_assoc);
else if (addto_update_list(mysql_conn->update_list,
SLURMDB_MODIFY_ASSOC,
mod_assoc) != SLURM_SUCCESS) {
error("couldn't add to the update list");
slurmdb_destroy_assoc_rec(mod_assoc);
}
}
/*
* If we were only moving associations to where they already are then we
* can get here
*/
if (!name_char)
goto end_it;
xstrcat(name_char, ")");
if (assoc->parent_acct) {
if (((rc == ESLURM_INVALID_PARENT_ACCOUNT)
|| (rc == ESLURM_SAME_PARENT_ACCOUNT))
&& added)
rc = SLURM_SUCCESS;
}
if (rc != SLURM_SUCCESS)
goto end_it;
if (vals && vals[0]) {
char *user_name = uid_to_string((uid_t) user->uid);
rc = modify_common(mysql_conn, DBD_MODIFY_ASSOCS, now,
user_name, assoc_table, name_char, vals,
cluster_name);
xfree(user_name);
if (rc == SLURM_ERROR) {
error("Couldn't modify associations");
goto end_it;
}
}
if (reset_query) {
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", reset_query);
if ((rc = mysql_db_query(mysql_conn, reset_query))
!= SLURM_SUCCESS)
error("Couldn't update defaults");
}
end_it:
xfree(reset_query);
xfree(name_char);
xfree(vals);
return rc;
}
static int _foreach_coord_parent_flag(void *x, void *arg)
{
slurmdb_assoc_rec_t *assoc_ptr = x;
coord_parent_flag_t *coord_parent_flag = arg;
xassert(coord_parent_flag->user_rec);
as_mysql_user_handle_user_coord_flag(
coord_parent_flag->user_rec, coord_parent_flag->flags,
assoc_ptr->acct);
if (assoc_ptr->usage->children_list)
return list_for_each(assoc_ptr->usage->children_list,
_foreach_coord_parent_flag,
coord_parent_flag);
return 0;
}
/*
* This will set up user_rec->coord_accts to be correct for update to the
* assoc_mgr.
*/
static int _handle_coord_parent_flag(add_assoc_cond_t *add_assoc_cond,
slurmdb_assoc_rec_t *assoc,
slurmdb_assoc_flags_t flags)
{
slurmdb_assoc_rec_t par_assoc = {
.id = assoc->parent_id,
.cluster = assoc->cluster,
.uid = NO_VAL,
};
slurmdb_assoc_rec_t *par_assoc_ptr = NULL;
coord_parent_flag_t coord_parent_flag = {
.flags = flags,
};
assoc_mgr_lock_t locks = {
.assoc = READ_LOCK,
.user = READ_LOCK,
};
int rc = SLURM_SUCCESS;
/* This is a new cluster, tree might not be set up yet correctly */
if (assoc->flags & ASSOC_FLAG_BLOCK_ADD)
return rc;
if (!add_assoc_cond->assoc_mgr_locked)
assoc_mgr_lock(&locks);
xassert(assoc->user);
xassert(verify_assoc_lock(ASSOC_LOCK, READ_LOCK));
xassert(verify_assoc_lock(USER_LOCK, READ_LOCK));
xassert((flags & ASSOC_FLAG_USER_COORD_NO) ||
(flags & ASSOC_FLAG_USER_COORD));
/* Find the parent assoc */
if (assoc_mgr_fill_in_assoc(add_assoc_cond->mysql_conn,
&par_assoc,
ACCOUNTING_ENFORCE_ASSOCS,
&par_assoc_ptr, true) != SLURM_SUCCESS) {
error("We can't find assoc %u on cluster %s",
assoc->parent_id, assoc->cluster);
rc = SLURM_ERROR;
goto end_it;
}
/* If the flag isn't set just return */
if (!assoc_mgr_tree_has_user_coord(par_assoc_ptr, true)) {
rc = SLURM_SUCCESS;
goto end_it;
}
/* Otherwise set this user up to be a coord of this account */
coord_parent_flag.user_rec = as_mysql_user_add_coord_update(
add_assoc_cond->mysql_conn,
&add_assoc_cond->coord_users,
assoc->user,
true);
if (!coord_parent_flag.user_rec) {
rc = SLURM_ERROR;
goto end_it;
}
(void) _foreach_coord_parent_flag(par_assoc_ptr, &coord_parent_flag);
end_it:
if (!add_assoc_cond->assoc_mgr_locked)
assoc_mgr_unlock(&locks);
return rc;
}
static int _process_remove_assoc_results(remove_common_args_t *args,
MYSQL_RES *result,
slurmdb_user_rec_t *user,
bool is_admin,
add_assoc_cond_t *add_assoc_cond)
{
list_itr_t *itr = NULL;
MYSQL_ROW row;
int rc = SLURM_SUCCESS;
char *object = NULL;
bool disable_coord_dbd = false;
xassert(result);
if (args->jobs_running || args->default_account) {
goto skip_process;
}
disable_coord_dbd = slurmdbd_conf->flags &
DBD_CONF_FLAG_DISABLE_COORD_DBD;
while ((row = mysql_fetch_row(result))) {
slurmdb_assoc_rec_t *rem_assoc = NULL;
if (!is_admin) {
slurmdb_coord_rec_t *coord = NULL;
if (disable_coord_dbd) {
error("Coordinator privilege revoked with DisableCoordDBD, only admins/operators can modify accounts.");
rc = ESLURM_ACCESS_DENIED;
goto end_it;
}
if (!user->coord_accts) { // This should never
// happen
error("We are here with no coord accts");
rc = ESLURM_ACCESS_DENIED;
goto end_it;
}
itr = list_iterator_create(user->coord_accts);
while ((coord = list_next(itr))) {
if (!xstrcasecmp(coord->name,
row[RASSOC_ACCT]))
break;
}
list_iterator_destroy(itr);
if (!coord) {
error("User %s(%d) does not have the "
"ability to change this account (%s)",
user->name, user->uid, row[RASSOC_ACCT]);
rc = ESLURM_ACCESS_DENIED;
goto end_it;
}
}
if (row[RASSOC_PART][0]) {
// see if there is a partition name
object = xstrdup_printf(
"C = %-10s A = %-10s U = %-9s P = %s",
args->cluster_name, row[RASSOC_ACCT],
row[RASSOC_USER], row[RASSOC_PART]);
} else if (row[RASSOC_USER][0]){
object = xstrdup_printf(
"C = %-10s A = %-10s U = %-9s",
args->cluster_name, row[RASSOC_ACCT],
row[RASSOC_USER]);
} else {
if (row[RASSOC_PACCT][0]) {
object = xstrdup_printf(
"C = %-10s A = %s of %s",
args->cluster_name, row[RASSOC_ACCT],
row[RASSOC_PACCT]);
} else {
object = xstrdup_printf(
"C = %-10s A = %s",
args->cluster_name, row[RASSOC_ACCT]);
}
}
list_append(args->ret_list, object);
if (args->assoc_char)
xstrfmtcat(args->assoc_char, " || id_assoc=%s",
row[RASSOC_ID]);
else
xstrfmtcat(args->assoc_char, "id_assoc=%s",
row[RASSOC_ID]);
rem_assoc = xmalloc(sizeof(slurmdb_assoc_rec_t));
slurmdb_init_assoc_rec(rem_assoc, 0);
rem_assoc->flags |= ASSOC_FLAG_DELETED;
rem_assoc->id = slurm_atoul(row[RASSOC_ID]);
rem_assoc->cluster = xstrdup(args->cluster_name);
if (addto_update_list(args->mysql_conn->update_list,
SLURMDB_REMOVE_ASSOC,
rem_assoc) != SLURM_SUCCESS) {
slurmdb_destroy_assoc_rec(rem_assoc);
error("couldn't add to the update list");
}
/* Remove potential flag coord */
if (row[RASSOC_USER][0]) {
rem_assoc->user = row[RASSOC_USER];
rem_assoc->parent_id = slurm_atoul(row[RASSOC_ID_PAR]);
_handle_coord_parent_flag(
add_assoc_cond,
rem_assoc,
ASSOC_FLAG_USER_COORD_NO);
rem_assoc->user = NULL;
}
}
skip_process:
rc = remove_common(args);
end_it:
xfree(args->assoc_char);
return rc;
}
static int _cluster_get_assocs(mysql_conn_t *mysql_conn,
slurmdb_user_rec_t *user,
slurmdb_assoc_cond_t *assoc_cond,
char *cluster_name,
char *fields, char *sent_extra,
bool is_admin, list_t *sent_list)
{
list_t *assoc_list;
list_t *delta_qos_list = NULL;
list_itr_t *itr = NULL;
MYSQL_RES *result = NULL;
MYSQL_ROW row;
uint32_t parent_def_qos_id = 0;
uint32_t parent_mj = INFINITE;
uint32_t parent_mja = INFINITE;
uint32_t parent_mpt = INFINITE;
uint32_t parent_msj = INFINITE;
uint32_t parent_mwpj = INFINITE;
uint32_t parent_prio = INFINITE;
char *parent_mtpj = NULL;
char *parent_mtpn = NULL;
char *parent_mtmpj = NULL;
char *parent_mtrm = NULL;
char *parent_acct = NULL;
char *parent_qos = NULL;
char *parent_delta_qos = NULL;
char *last_acct = NULL;
char *last_cluster = NULL;
char *query = NULL;
char *extra = xstrdup(sent_extra);
/* needed if we don't have an assoc_cond */
uint16_t without_parent_info = 0;
uint16_t without_parent_limits = 0;
uint16_t with_usage = 0;
uint16_t with_raw_qos = 0;
bitstr_t *wanted_qos = NULL;
assoc_mgr_lock_t assoc_locks = {
.assoc = READ_LOCK,
};
if (assoc_cond) {
with_raw_qos = assoc_cond->flags & ASSOC_COND_FLAG_RAW_QOS;
with_usage = assoc_cond->flags &
(ASSOC_COND_FLAG_WITH_USAGE |
ASSOC_COND_FLAG_WITH_NG_USAGE);
without_parent_limits =
assoc_cond->flags & ASSOC_COND_FLAG_WOPL;
without_parent_info = assoc_cond->flags & ASSOC_COND_FLAG_WOPI;
}
/* this is here to make sure we are looking at only this user
* if this flag is set. We also include any accounts they may be
* coordinator of.
*/
if (!is_admin &&
((slurm_conf.private_data & PRIVATE_DATA_USERS) ||
(with_usage && (slurm_conf.private_data & PRIVATE_DATA_USAGE)))) {
int set = 0;
query = xstrdup_printf("select lineage from \"%s_%s\" where user='%s'",
cluster_name, assoc_table, user->name);
if (user->coord_accts && list_count(user->coord_accts)) {
slurmdb_coord_rec_t *coord = NULL;
bool added = false;
xstrcat(query, " || (user='' && (");
itr = list_iterator_create(user->coord_accts);
while ((coord = list_next(itr))) {
xstrfmtcat(query, "%sacct='%s'",
added ? " || " : "", coord->name);
added = true;
}
list_iterator_destroy(itr);
xstrcat(query, "))");
}
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", query);
if (!(result = mysql_db_query_ret(
mysql_conn, query, 0))) {
xfree(extra);
xfree(query);
return SLURM_ERROR;
}
xfree(query);
set = 0;
while ((row = mysql_fetch_row(result))) {
if (set) {
xstrfmtcat(extra,
" || (t1.lineage like '%s%%')",
row[0]);
} else {
set = 1;
xstrfmtcat(extra,
" && ((t1.lineage like '%s%%')",
row[0]);
}
}
mysql_free_result(result);
if (set)
xstrcat(extra, ")");
else {
xfree(extra);
debug("User %s has no associations, and is not admin, "
"so not returning any.", user->name);
/* This user has no valid associations, so
* end. */
return SLURM_SUCCESS;
}
}
//START_TIMER;
query = _setup_assoc_table_query(cluster_name, fields, extra,
" order by lineage;");
xfree(extra);
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", query);
if (!(result = mysql_db_query_ret(
mysql_conn, query, 0))) {
xfree(query);
if (mysql_errno(mysql_conn->db_conn) == ER_NO_SUCH_TABLE)
return SLURM_SUCCESS;
else
return SLURM_ERROR;
}
xfree(query);
if (!mysql_num_rows(result)) {
mysql_free_result(result);
return SLURM_SUCCESS;
}
/* If asking for QOS usage do not filter here. */
if (assoc_cond &&
!(assoc_cond->flags & ASSOC_COND_FLAG_QOS_USAGE) &&
assoc_cond->qos_list &&
list_count(assoc_cond->qos_list)) {
wanted_qos = bit_alloc(g_qos_count);
set_qos_bitstr_from_list(wanted_qos, assoc_cond->qos_list);
assoc_mgr_lock(&assoc_locks);
}
assoc_list = list_create(slurmdb_destroy_assoc_rec);
delta_qos_list = list_create(xfree_ptr);
while ((row = mysql_fetch_row(result))) {
slurmdb_assoc_rec_t *assoc = NULL;
MYSQL_RES *result2 = NULL;
MYSQL_ROW row2;
uint32_t id = slurm_atoul(row[ASSOC_REQ_ID]);
if (!_assoc_id_has_qos(mysql_conn, cluster_name, id,
wanted_qos))
continue;
assoc = xmalloc(sizeof(slurmdb_assoc_rec_t));
list_append(assoc_list, assoc);
assoc->id = id;
assoc->is_def = slurm_atoul(row[ASSOC_REQ_DEFAULT]);
assoc->comment = xstrdup(row[ASSOC_REQ_COMMENT]);
assoc->flags = slurm_atoul(row[ASSOC_REQ_FLAGS]);
if (slurm_atoul(row[ASSOC_REQ_DELETED]))
assoc->flags |= ASSOC_FLAG_DELETED;
if (row[ASSOC_REQ_USER][0])
assoc->user = xstrdup(row[ASSOC_REQ_USER]);
assoc->acct = xstrdup(row[ASSOC_REQ_ACCT]);
assoc->cluster = xstrdup(cluster_name);
if (row[ASSOC_REQ_GJ])
assoc->grp_jobs = slurm_atoul(row[ASSOC_REQ_GJ]);
else
assoc->grp_jobs = INFINITE;
if (row[ASSOC_REQ_GJA])
assoc->grp_jobs_accrue =
slurm_atoul(row[ASSOC_REQ_GJA]);
else
assoc->grp_jobs_accrue = INFINITE;
if (row[ASSOC_REQ_GSJ])
assoc->grp_submit_jobs =
slurm_atoul(row[ASSOC_REQ_GSJ]);
else
assoc->grp_submit_jobs = INFINITE;
if (row[ASSOC_REQ_GW])
assoc->grp_wall = slurm_atoul(row[ASSOC_REQ_GW]);
else
assoc->grp_wall = INFINITE;
if (row[ASSOC_REQ_GT][0])
assoc->grp_tres = xstrdup(row[ASSOC_REQ_GT]);
if (row[ASSOC_REQ_GTM][0])
assoc->grp_tres_mins = xstrdup(row[ASSOC_REQ_GTM]);
if (row[ASSOC_REQ_GTRM][0])
assoc->grp_tres_run_mins = xstrdup(row[ASSOC_REQ_GTRM]);
assoc->parent_id = slurm_atoul(row[ASSOC_REQ_ID_PAR]);
assoc->lineage = xstrdup(row[ASSOC_REQ_LINEAGE]);
parent_acct = row[ASSOC_REQ_ACCT];
if (!without_parent_info
&& row[ASSOC_REQ_PARENT][0]) {
assoc->parent_acct = xstrdup(row[ASSOC_REQ_PARENT]);
parent_acct = row[ASSOC_REQ_PARENT];
} else if (!assoc->user) {
/* This is the root association so we have no
parent id */
parent_acct = NULL;
}
if (row[ASSOC_REQ_PART][0])
assoc->partition = xstrdup(row[ASSOC_REQ_PART]);
if (row[ASSOC_REQ_FS])
assoc->shares_raw = slurm_atoul(row[ASSOC_REQ_FS]);
else
assoc->shares_raw = 1;
if (!without_parent_info && parent_acct &&
(!last_acct || !last_cluster
|| xstrcmp(parent_acct, last_acct)
|| xstrcmp(cluster_name, last_cluster))) {
query = xstrdup_printf(
"call get_parent_limits('%s', "
"'%s', '%s', %u);",
assoc_table, parent_acct,
cluster_name,
without_parent_limits);
debug4("%d(%s:%d) query\n%s",
mysql_conn->conn, THIS_FILE, __LINE__, query);
if (!(result2 = mysql_db_query_ret(
mysql_conn, query, 1))) {
xfree(query);
break;
}
xfree(query);
if (!(row2 = mysql_fetch_row(result2))) {
goto no_parent_limits;
}
if (!without_parent_limits) {
if (row2[ASSOC2_REQ_DEF_QOS])
parent_def_qos_id = slurm_atoul(
row2[ASSOC2_REQ_DEF_QOS]);
else
parent_def_qos_id = 0;
if (row2[ASSOC2_REQ_MJ])
parent_mj = slurm_atoul(
row2[ASSOC2_REQ_MJ]);
else
parent_mj = INFINITE;
if (row2[ASSOC2_REQ_MJA])
parent_mja = slurm_atoul(
row2[ASSOC2_REQ_MJA]);
else
parent_mja = INFINITE;
if (row2[ASSOC2_REQ_MPT])
parent_mpt = slurm_atoul(
row2[ASSOC2_REQ_MPT]);
else
parent_mpt = INFINITE;
if (row2[ASSOC2_REQ_MSJ])
parent_msj = slurm_atoul(
row2[ASSOC2_REQ_MSJ]);
else
parent_msj = INFINITE;
if (row2[ASSOC2_REQ_MWPJ])
parent_mwpj = slurm_atoul(
row2[ASSOC2_REQ_MWPJ]);
else
parent_mwpj = INFINITE;
if (row2[ASSOC2_REQ_PRIO])
parent_prio = slurm_atoul(
row2[ASSOC2_REQ_PRIO]);
else
parent_prio = INFINITE;
xfree(parent_mtpj);
if (row2[ASSOC2_REQ_MTPJ][0])
parent_mtpj = xstrdup(
row2[ASSOC2_REQ_MTPJ]);
xfree(parent_mtpn);
if (row2[ASSOC2_REQ_MTPN][0])
parent_mtpn = xstrdup(
row2[ASSOC2_REQ_MTPN]);
xfree(parent_mtmpj);
if (row2[ASSOC2_REQ_MTMPJ][0])
parent_mtmpj = xstrdup(
row2[ASSOC2_REQ_MTMPJ]);
xfree(parent_mtrm);
if (row2[ASSOC2_REQ_MTRM][0])
parent_mtrm = xstrdup(
row2[ASSOC2_REQ_MTRM]);
xfree(parent_qos);
if (row2[ASSOC2_REQ_QOS][0])
parent_qos =
xstrdup(row2[ASSOC2_REQ_QOS]);
xfree(parent_delta_qos);
if (row2[ASSOC2_REQ_DELTA_QOS][0])
parent_delta_qos = xstrdup(
row2[ASSOC2_REQ_DELTA_QOS]);
}
last_acct = parent_acct;
last_cluster = cluster_name;
no_parent_limits:
mysql_free_result(result2);
}
if (row[ASSOC_REQ_DEF_QOS])
assoc->def_qos_id = slurm_atoul(row[ASSOC_REQ_DEF_QOS]);
else
assoc->def_qos_id = parent_def_qos_id;
if (row[ASSOC_REQ_MJ])
assoc->max_jobs = slurm_atoul(row[ASSOC_REQ_MJ]);
else
assoc->max_jobs = parent_mj;
if (row[ASSOC_REQ_MJA])
assoc->max_jobs_accrue =
slurm_atoul(row[ASSOC_REQ_MJA]);
else
assoc->max_jobs_accrue = parent_mja;
if (row[ASSOC_REQ_MPT])
assoc->min_prio_thresh = slurm_atoul(
row[ASSOC_REQ_MPT]);
else
assoc->min_prio_thresh = parent_mpt;
if (row[ASSOC_REQ_MSJ])
assoc->max_submit_jobs = slurm_atoul(
row[ASSOC_REQ_MSJ]);
else
assoc->max_submit_jobs = parent_msj;
if (row[ASSOC_REQ_MWPJ])
assoc->max_wall_pj = slurm_atoul(row[ASSOC_REQ_MWPJ]);
else
assoc->max_wall_pj = parent_mwpj;
if (row[ASSOC_REQ_PRIO])
assoc->priority = slurm_atoul(row[ASSOC_REQ_PRIO]);
else
assoc->priority = parent_prio;
if (row[ASSOC_REQ_MTPJ][0])
assoc->max_tres_pj = xstrdup(row[ASSOC_REQ_MTPJ]);
if (row[ASSOC_REQ_MTPN][0])
assoc->max_tres_pn = xstrdup(row[ASSOC_REQ_MTPN]);
if (row[ASSOC_REQ_MTMPJ][0])
assoc->max_tres_mins_pj = xstrdup(row[ASSOC_REQ_MTMPJ]);
if (row[ASSOC_REQ_MTRM][0])
assoc->max_tres_run_mins = xstrdup(row[ASSOC_REQ_MTRM]);
/* For the tres limits we just concatted the limits going up
* the hierarchy slurmdb_tres_list_from_string will just skip
* over any reoccuring limit to give us the first one per
* TRES.
*/
slurmdb_combine_tres_strings(
&assoc->max_tres_pj, parent_mtpj,
TRES_STR_FLAG_SORT_ID);
slurmdb_combine_tres_strings(
&assoc->max_tres_pn, parent_mtpn,
TRES_STR_FLAG_SORT_ID);
slurmdb_combine_tres_strings(
&assoc->max_tres_mins_pj, parent_mtmpj,
TRES_STR_FLAG_SORT_ID);
slurmdb_combine_tres_strings(
&assoc->max_tres_run_mins, parent_mtrm,
TRES_STR_FLAG_SORT_ID);
assoc->qos_list = list_create(xfree_ptr);
/* do a plus 1 since a comma is the first thing there
* in the list. Also you can never have both a qos
* and a delta qos so if you have a qos don't worry
* about the delta.
*/
if (row[ASSOC_REQ_QOS][0])
slurm_addto_char_list(assoc->qos_list,
row[ASSOC_REQ_QOS]+1);
else {
/* if qos is set on the association itself do
not worry about the deltas
*/
/* add the parents first */
if (parent_qos)
slurm_addto_char_list(assoc->qos_list,
parent_qos+1);
/* then add the parents delta */
if (parent_delta_qos)
slurm_addto_char_list(delta_qos_list,
parent_delta_qos+1);
/* now add the associations */
if (row[ASSOC_REQ_DELTA_QOS][0])
slurm_addto_char_list(
delta_qos_list,
row[ASSOC_REQ_DELTA_QOS]+1);
}
/* Sometimes we want to see exactly what is here in
the database instead of a complete list. This will
give it to us.
*/
if (with_raw_qos && list_count(delta_qos_list)) {
list_transfer(assoc->qos_list, delta_qos_list);
list_flush(delta_qos_list);
} else if (list_count(delta_qos_list)) {
list_itr_t *curr_qos_itr =
list_iterator_create(assoc->qos_list);
list_itr_t *new_qos_itr =
list_iterator_create(delta_qos_list);
char *new_qos = NULL, *curr_qos = NULL;
while ((new_qos = list_next(new_qos_itr))) {
if (new_qos[0] == '-') {
while ((curr_qos =
list_next(curr_qos_itr))) {
if (!xstrcmp(curr_qos,
new_qos+1)) {
list_delete_item(
curr_qos_itr);
break;
}
}
list_iterator_reset(curr_qos_itr);
} else if (new_qos[0] == '+') {
while ((curr_qos =
list_next(curr_qos_itr))) {
if (!xstrcmp(curr_qos,
new_qos+1)) {
break;
}
}
if (!curr_qos) {
list_append(assoc->qos_list,
xstrdup(new_qos+1));
}
list_iterator_reset(curr_qos_itr);
}
}
list_iterator_destroy(new_qos_itr);
list_iterator_destroy(curr_qos_itr);
list_flush(delta_qos_list);
}
//info("parent id is %d", assoc->parent_id);
//log_assoc_rec(assoc);
}
if (wanted_qos)
assoc_mgr_unlock(&assoc_locks);
FREE_NULL_BITMAP(wanted_qos);
xfree(parent_mtpj);
xfree(parent_mtpn);
xfree(parent_mtmpj);
xfree(parent_mtrm);
mysql_free_result(result);
FREE_NULL_LIST(delta_qos_list);
xfree(parent_delta_qos);
xfree(parent_qos);
if (with_usage && assoc_list && list_count(assoc_list)) {
if (assoc_cond->flags & ASSOC_COND_FLAG_QOS_USAGE) {
usage_qos_query_t qos_usage = {
.assoc_list = assoc_list,
.qos_list = assoc_cond->qos_list,
};
get_usage_for_list(mysql_conn, DBD_GET_QOS_USAGE,
&qos_usage, cluster_name,
assoc_cond->usage_start,
assoc_cond->usage_end);
} else if (assoc_cond->flags & ASSOC_COND_FLAG_WITH_NG_USAGE) {
get_usage_for_list(mysql_conn, DBD_GET_ASSOC_NG_USAGE,
assoc_list, cluster_name,
assoc_cond->usage_start,
assoc_cond->usage_end);
} else {
get_usage_for_list(mysql_conn, DBD_GET_ASSOC_USAGE,
assoc_list, cluster_name,
assoc_cond->usage_start,
assoc_cond->usage_end);
}
}
list_transfer(sent_list, assoc_list);
FREE_NULL_LIST(assoc_list);
return SLURM_SUCCESS;
}
static int _check_defaults(void *x, void *arg)
{
add_assoc_cond_t *add_assoc_cond = arg;
int rc = _make_sure_user_has_default_internal(
add_assoc_cond->mysql_conn,
x,
add_assoc_cond->add_assoc->assoc.cluster);
if (rc != SLURM_SUCCESS)
return -1;
return 0;
}
static void _post_add_assoc_cond_cluster(add_assoc_cond_t *add_assoc_cond)
{
if (add_assoc_cond->add_assoc->user_list)
if (list_for_each_ro(add_assoc_cond->add_assoc->user_list,
_check_defaults,
add_assoc_cond) < 0)
return;
return;
}
static int _add_assoc_internal(add_assoc_cond_t *add_assoc_cond)
{
slurmdb_assoc_rec_t *assoc = add_assoc_cond->alloc_assoc;
slurmdb_assoc_rec_t *assoc_in = assoc ?
assoc : &add_assoc_cond->add_assoc->assoc;
bool is_coord = add_assoc_cond->is_coord;
mysql_conn_t *mysql_conn = add_assoc_cond->mysql_conn;
char *user_name = add_assoc_cond->user_name;
int rc;
uint32_t assoc_id = 0;
char *parent = NULL;
char *cols = NULL, *vals = NULL, *tmp_extra = NULL;
char *extra = NULL, *query = NULL, *update = NULL;
time_t now = time(NULL);
bool is_def = false;
if (!assoc_in->cluster || !assoc_in->cluster[0] ||
!assoc_in->acct || !assoc_in->acct[0]) {
error("We need an association, cluster and acct to add one.");
return SLURM_ERROR;
}
/*
* When adding if this isn't a default might as well
* force it to be 0 to avoid confusion since
* uninitialized it is NO_VAL.
*/
if (assoc_in->is_def == 1)
is_def = 1;
else
is_def = 0;
/*
* If the user issuing the command is a coordinator,
* do not allow changing the default account
*/
if (is_coord && (assoc_in->is_def == 1)) {
MYSQL_RES *result = NULL;
char *query = NULL;
int has_def_acct = 0;
/* Check if there is already a default account. */
query = xstrdup_printf("select id_assoc from \"%s_%s\" where user='%s' && acct!='%s' && is_def=1 && deleted!=1;",
assoc_in->cluster, assoc_table,
assoc_in->user, assoc_in->acct);
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", query);
result = mysql_db_query_ret(mysql_conn, query, 1);
xfree(query);
if (!result)
return SLURM_ERROR;
has_def_acct = mysql_num_rows(result);
mysql_free_result(result);
if (has_def_acct) {
debug("Coordinator %s(%u) tried to change the default account of user %s to account %s. This is only allowed on initial user creation. Ignoring default account.",
user_name, add_assoc_cond->uid,
assoc_in->user, assoc_in->acct);
is_def = 0;
}
}
xstrcat(cols, "creation_time, mod_time, acct");
xstrfmtcat(vals, "%ld, %ld, '%s'", now, now, assoc_in->acct);
xstrfmtcat(update, "where acct='%s'", assoc_in->acct);
xstrfmtcat(extra, ", mod_time=%ld, acct='%s'", now, assoc_in->acct);
if (!assoc) {
/* Copy the assoc_in to the actual association we want to add */
assoc = xmalloc(sizeof(*assoc));
memcpy(assoc, assoc_in, sizeof(*assoc));
assoc->acct = xstrdup(assoc_in->acct);
assoc->cluster = xstrdup(assoc_in->cluster);
assoc->comment = xstrdup(assoc_in->comment);
assoc->flags = assoc_in->flags & ~ASSOC_FLAG_BASE;
assoc->grp_tres = xstrdup(assoc_in->grp_tres);
assoc->grp_tres_mins = xstrdup(assoc_in->grp_tres_mins);
assoc->grp_tres_run_mins = xstrdup(assoc_in->grp_tres_run_mins);
assoc->is_def = is_def;
/*
* This will change on the next, so no reason to copy, just
* transfer
*/
assoc->lineage = assoc_in->lineage;
assoc_in->lineage = NULL;
assoc->max_tres_mins_pj = xstrdup(assoc_in->max_tres_mins_pj);
assoc->max_tres_run_mins = xstrdup(assoc_in->max_tres_run_mins);
assoc->max_tres_pj = xstrdup(assoc_in->max_tres_pj);
assoc->max_tres_pn = xstrdup(assoc_in->max_tres_pn);
assoc->parent_acct = xstrdup(assoc_in->parent_acct);
assoc->partition = xstrdup(assoc_in->partition);
assoc->qos_list = slurm_copy_char_list(assoc_in->qos_list);
assoc->user = xstrdup(assoc_in->user);
/**************************************************************/
/* If we have a assoc this has already been added to the mix */
xstrcat(cols, ", is_def");
xstrfmtcat(vals, ", %d", assoc->is_def);
xstrfmtcat(extra, ", is_def=%d", assoc->is_def);
} else
assoc->is_def = is_def;
if (assoc->parent_acct) {
parent = assoc->parent_acct;
} else if (assoc->user) {
parent = assoc->acct;
} else {
parent = "root";
}
if (!assoc->user) {
xstrcat(cols, ", parent_acct");
xstrfmtcat(vals, ", '%s'", parent);
xstrfmtcat(extra, ", parent_acct='%s', user=''",
parent);
xstrfmtcat(update, " && user=''");
} else {
char *part = assoc->partition;
xstrcat(cols, ", user");
xstrfmtcat(vals, ", '%s'", assoc->user);
xstrfmtcat(update, " && user='%s'", assoc->user);
xstrfmtcat(extra, ", user='%s'", assoc->user);
/*
* We need to give a partition whether it be '' or the actual
* partition name given.
*/
if (!part)
part = "";
xstrcat(cols, ", `partition`");
xstrfmtcat(vals, ", '%s'", part);
xstrfmtcat(update, " && `partition`='%s'", part);
xstrfmtcat(extra, ", `partition`='%s'", part);
}
xassert(assoc->parent_id);
xassert(assoc->lineage);
xstrcat(cols, ", id_parent, lineage");
xstrfmtcat(vals, ", %d, '%s'", assoc->parent_id, assoc->lineage);
xstrfmtcat(extra, ", id_parent='%d', lineage='%s'",
assoc->parent_id, assoc->lineage);
if (add_assoc_cond->extra)
xstrcat(extra, add_assoc_cond->extra);
assoc_id = 0;
xstrfmtcat(query,
"insert into \"%s_%s\" (%s%s) values (%s%s) on duplicate key update deleted=0%s;",
assoc->cluster, assoc_table,
cols,
add_assoc_cond->cols ? add_assoc_cond->cols : "",
vals,
add_assoc_cond->vals ? add_assoc_cond->vals : "",
extra);
xfree(cols);
xfree(vals);
xfree(update);
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", query);
rc = mysql_db_query(mysql_conn, query);
xfree(query);
if (rc != SLURM_SUCCESS) {
error("Couldn't add assoc");
xfree(extra);
slurmdb_destroy_assoc_rec(assoc);
return rc;
}
/* see if this was an insert or update. On an update
* the assoc_id will already be set
*/
if (!assoc_id) {
(void) last_affected_rows(mysql_conn);
assoc_id = mysql_insert_id(mysql_conn->db_conn);
//info("last id was %d", assoc_id);
}
assoc->id = assoc_id;
/*
* If we have a alloc_assoc it means we are using the old method that
* should already have this info, don't send it back.
*/
if (!add_assoc_cond->alloc_assoc) {
if (!add_assoc_cond->ret_str)
xstrcatat(add_assoc_cond->ret_str,
&add_assoc_cond->ret_str_pos,
" Associations =\n");
xstrfmtcatat(add_assoc_cond->ret_str,
&add_assoc_cond->ret_str_pos,
" C = %-10s A = %-20s",
assoc->cluster, assoc->acct);
if (assoc->user) {
xstrfmtcatat(add_assoc_cond->ret_str,
&add_assoc_cond->ret_str_pos,
" U = %-9s",
assoc->user);
if (assoc->partition)
xstrfmtcatat(add_assoc_cond->ret_str,
&add_assoc_cond->ret_str_pos,
" P = %s",
assoc->partition);
}
xstrcatat(add_assoc_cond->ret_str,
&add_assoc_cond->ret_str_pos, "\n");
}
add_assoc_cond->flags |= ADD_ASSOC_FLAG_ADDED;
_set_assoc_limits_for_add(mysql_conn, assoc);
if (assoc->user &&
assoc->def_qos_id &&
(assoc->def_qos_id != INFINITE) &&
assoc->qos_list) {
bitstr_t *valid_qos = bit_alloc(g_qos_count);
bool access_def = false;
set_qos_bitstr_from_list(valid_qos, assoc->qos_list);
access_def = bit_test(valid_qos, assoc->def_qos_id);
FREE_NULL_BITMAP(valid_qos);
if (!access_def) {
xfree(extra);
slurmdb_destroy_assoc_rec(assoc);
xfree(add_assoc_cond->ret_str);
rc = ESLURM_NO_REMOVE_DEFAULT_QOS;
return rc;
}
}
if (assoc->is_def && assoc->user &&
(rc = _reset_default_assoc(
mysql_conn, assoc, NULL, true)) != SLURM_SUCCESS) {
slurmdb_destroy_assoc_rec(assoc);
xfree(extra);
return -1;
}
/* we always have a ', ' as the first 2 chars */
tmp_extra = slurm_add_slash_to_quotes(extra+2);
if (add_assoc_cond->txn_query)
xstrfmtcatat(add_assoc_cond->txn_query,
&add_assoc_cond->txn_query_pos,
", (%ld, %d, 'id_assoc=%d', '%s', '%s', '%s')",
now, DBD_ADD_ASSOCS, assoc_id,
user_name,
tmp_extra, assoc->cluster);
else
xstrfmtcatat(add_assoc_cond->txn_query,
&add_assoc_cond->txn_query_pos,
"insert into %s (timestamp, action, name, actor, info, cluster) values (%ld, %d, 'id_assoc=%d', '%s', '%s', '%s')",
txn_table,
now, DBD_ADD_ASSOCS, assoc_id,
user_name,
tmp_extra, assoc->cluster);
xfree(tmp_extra);
xfree(extra);
if (assoc->flags & ASSOC_FLAG_NO_UPDATE)
slurmdb_destroy_assoc_rec(assoc);
else if (addto_update_list(mysql_conn->update_list,
SLURMDB_ADD_ASSOC, assoc) != SLURM_SUCCESS) {
slurmdb_destroy_assoc_rec(assoc);
error("couldn't add to the update list");
rc = SLURM_ERROR;
}
return rc;
}
static void _add_assoc_cond_user_internal(add_assoc_cond_t *add_assoc_cond)
{
slurmdb_assoc_rec_t user_assoc;
int rc;
memset(&user_assoc, 0, sizeof(slurmdb_assoc_rec_t));
user_assoc.cluster = add_assoc_cond->add_assoc->assoc.cluster;
user_assoc.acct = add_assoc_cond->add_assoc->assoc.acct;
user_assoc.user = add_assoc_cond->add_assoc->assoc.user;
user_assoc.uid = add_assoc_cond->add_assoc->assoc.uid;
rc = assoc_mgr_fill_in_assoc(
add_assoc_cond->mysql_conn,
&user_assoc,
ACCOUNTING_ENFORCE_ASSOCS, NULL, true);
if (rc == SLURM_SUCCESS)
debug2("Association %s/%s/%s is already here, not adding again.",
user_assoc.cluster, user_assoc.acct,
user_assoc.user);
else {
add_assoc_cond->add_assoc->assoc.lineage =
xstrdup_printf(
"%s0-%s/", add_assoc_cond->base_lineage,
add_assoc_cond->add_assoc->assoc.user);
add_assoc_cond->rc =
_add_assoc_internal(add_assoc_cond);
xfree(add_assoc_cond->add_assoc->assoc.lineage);
}
}
static int _add_assoc_cond_partition(void *x, void *arg)
{
add_assoc_cond_t *add_assoc_cond = arg;
char *partition = x;
slurmdb_assoc_rec_t user_assoc;
int rc;
/*
* For some reason we have a empty partition name, handle as if it were
* a non-partition association.
*/
if (!partition || !partition[0]) {
_add_assoc_cond_user_internal(add_assoc_cond);
goto endit;
}
add_assoc_cond->add_assoc->assoc.partition = partition;
memset(&user_assoc, 0, sizeof(slurmdb_assoc_rec_t));
user_assoc.cluster = add_assoc_cond->add_assoc->assoc.cluster;
user_assoc.acct = add_assoc_cond->add_assoc->assoc.acct;
user_assoc.user = add_assoc_cond->add_assoc->assoc.user;
user_assoc.uid = add_assoc_cond->add_assoc->assoc.uid;
user_assoc.partition = add_assoc_cond->add_assoc->assoc.partition;
/*
* We want to look for this exact assoc, not the non-partition version
*/
user_assoc.flags |= ASSOC_FLAG_EXACT;
rc = assoc_mgr_fill_in_assoc(add_assoc_cond->mysql_conn,
&user_assoc,
ACCOUNTING_ENFORCE_ASSOCS, NULL, true);
if (rc == SLURM_SUCCESS)
debug2("Association %s/%s/%s/%s is already here, not adding again.",
user_assoc.cluster, user_assoc.acct,
user_assoc.user, user_assoc.partition);
else {
add_assoc_cond->add_assoc->assoc.lineage = xstrdup_printf(
"%s0-%s/%s/", add_assoc_cond->base_lineage,
add_assoc_cond->add_assoc->assoc.user,
add_assoc_cond->add_assoc->assoc.partition);
add_assoc_cond->rc = _add_assoc_internal(add_assoc_cond);
xfree(add_assoc_cond->add_assoc->assoc.lineage);
/* We only want one of these as default */
add_assoc_cond->add_assoc->assoc.is_def = 0;
}
add_assoc_cond->add_assoc->assoc.partition = NULL;
endit:
if (add_assoc_cond->rc != SLURM_SUCCESS)
return -1;
else
return 0;
}
static int _add_assoc_cond_user(void *x, void *arg)
{
add_assoc_cond_t *add_assoc_cond = arg;
uid_t pw_uid;
int rc = SLURM_SUCCESS;
bool set_def = false;
add_assoc_cond->add_assoc->assoc.user = x;
if (uid_from_string(add_assoc_cond->add_assoc->assoc.user, &pw_uid) !=
SLURM_SUCCESS)
add_assoc_cond->add_assoc->assoc.uid = NO_VAL;
else
add_assoc_cond->add_assoc->assoc.uid = pw_uid;
xassert(add_assoc_cond->base_lineage);
if (!add_assoc_cond->add_assoc->default_acct &&
!add_assoc_cond->add_assoc->assoc.is_def &&
!add_assoc_cond->added_defaults) {
slurmdb_user_rec_t check_object;
/*
* Check to see if it is already in the assoc_mgr. If it isn't
* use this first account as the default.
*/
memset(&check_object, 0, sizeof(check_object));
check_object.name = add_assoc_cond->add_assoc->assoc.user;
/*
* We have to use uid = NO_VAL here to avoid potential issues
* where a user is added to the system after it was originally
* added to the Slurm database and the assoc_mgr hasn't updated
* the uid yet.
*/
check_object.uid = NO_VAL;
rc = assoc_mgr_fill_in_user(add_assoc_cond->mysql_conn,
&check_object,
ACCOUNTING_ENFORCE_ASSOCS,
NULL, true);
if (rc != SLURM_SUCCESS) {
add_assoc_cond->add_assoc->assoc.is_def = 1;
set_def = true;
DB_DEBUG(DB_ASSOC, add_assoc_cond->mysql_conn->conn,
"No default account given for user User %s. Using %s.",
add_assoc_cond->add_assoc->assoc.user,
add_assoc_cond->add_assoc->assoc.acct);
}
}
_handle_coord_parent_flag(add_assoc_cond,
&add_assoc_cond->add_assoc->assoc,
add_assoc_cond->flags);
if (add_assoc_cond->add_assoc->partition_list)
(void) list_for_each_ro(
add_assoc_cond->add_assoc->partition_list,
_add_assoc_cond_partition,
add_assoc_cond);
else
_add_assoc_cond_user_internal(add_assoc_cond);
if (set_def)
add_assoc_cond->add_assoc->assoc.is_def = 0;
add_assoc_cond->add_assoc->assoc.user = NULL;
add_assoc_cond->add_assoc->assoc.uid = NO_VAL;
if (add_assoc_cond->rc != SLURM_SUCCESS)
return -1;
else
return 0;
}
static int _add_assoc_cond_acct(void *x, void *arg)
{
add_assoc_cond_t *add_assoc_cond = arg;
slurmdb_assoc_rec_t acct_assoc;
int rc;
add_assoc_cond->add_assoc->assoc.acct = x;
memset(&acct_assoc, 0, sizeof(slurmdb_assoc_rec_t));
acct_assoc.cluster = add_assoc_cond->add_assoc->assoc.cluster;
acct_assoc.acct = add_assoc_cond->add_assoc->assoc.acct;
acct_assoc.uid = NO_VAL;
if (add_assoc_cond->is_coord &&
!assoc_mgr_check_coord_qos(
acct_assoc.cluster,
acct_assoc.acct,
add_assoc_cond->user_name,
add_assoc_cond->add_assoc->assoc.qos_list, false)) {
assoc_mgr_lock_t locks = {
.qos = READ_LOCK,
};
char *requested_qos;
assoc_mgr_lock(&locks);
requested_qos = get_qos_complete_str(
assoc_mgr_qos_list, add_assoc_cond->add_assoc->assoc.qos_list);
assoc_mgr_unlock(&locks);
error("Coordinator %s(%u) does not have the access to all the qos requested (%s), so they can't add to account %s with it.",
add_assoc_cond->user_name, add_assoc_cond->uid,
requested_qos, acct_assoc.acct);
xfree(requested_qos);
add_assoc_cond->rc = ESLURM_ACCESS_DENIED;
goto end_it;
}
rc = assoc_mgr_fill_in_assoc(add_assoc_cond->mysql_conn,
&acct_assoc,
ACCOUNTING_ENFORCE_ASSOCS,
NULL, true);
if (add_assoc_cond->add_assoc->user_list) {
if (rc != SLURM_SUCCESS) {
char *tmp_str = xstrdup_printf(
"No account %s on cluster %s, skipping.",
acct_assoc.acct, acct_assoc.cluster);
debug("%s", tmp_str);
xstrfmtcatat(add_assoc_cond->ret_str,
&add_assoc_cond->ret_str_pos,
"%s\n", tmp_str);
xfree(tmp_str);
goto end_it;
}
if (add_assoc_cond->add_assoc->default_acct &&
!xstrcasecmp(acct_assoc.acct,
add_assoc_cond->add_assoc->default_acct))
add_assoc_cond->add_assoc->assoc.is_def = 1;
else
add_assoc_cond->add_assoc->assoc.is_def = 0;
add_assoc_cond->add_assoc->assoc.parent_id = acct_assoc.id;
add_assoc_cond->base_lineage = acct_assoc.lineage;
(void) list_for_each_ro(add_assoc_cond->add_assoc->user_list,
_add_assoc_cond_user,
add_assoc_cond);
add_assoc_cond->added_defaults = true;
goto end_it;
}
/* Add account (non-user associations) */
if (rc == SLURM_SUCCESS) {
char *tmp_str = xstrdup_printf(
"Already existing account %s on cluster %s",
acct_assoc.acct, acct_assoc.cluster);
debug2("%s", tmp_str);
xstrfmtcatat(add_assoc_cond->ret_str,
&add_assoc_cond->ret_str_pos,
"%s\n", tmp_str);
xfree(tmp_str);
goto end_it;
}
add_assoc_cond->add_assoc->assoc.lineage = xstrdup_printf(
"%s%s/", add_assoc_cond->base_lineage,
add_assoc_cond->add_assoc->assoc.acct);
add_assoc_cond->rc = _add_assoc_internal(add_assoc_cond);
end_it:
xfree(add_assoc_cond->add_assoc->assoc.lineage);
add_assoc_cond->add_assoc->assoc.acct = NULL;
if (add_assoc_cond->rc != SLURM_SUCCESS)
return -1;
else
return 0;
}
static int _add_assoc_cond_cluster(void *x, void *arg)
{
add_assoc_cond_t *add_assoc_cond = arg;
add_assoc_cond->add_assoc->assoc.cluster = x;
add_assoc_cond->add_assoc->assoc.parent_id = 0;
add_assoc_cond->added_defaults = 0;
add_assoc_cond->base_lineage = NULL;
if (!add_assoc_cond->add_assoc->user_list) {
slurmdb_assoc_rec_t acct_assoc;
int rc;
memset(&acct_assoc, 0, sizeof(slurmdb_assoc_rec_t));
acct_assoc.cluster = add_assoc_cond->add_assoc->assoc.cluster;
acct_assoc.acct = add_assoc_cond->add_assoc->assoc.parent_acct;
acct_assoc.uid = NO_VAL;
rc = assoc_mgr_fill_in_assoc(add_assoc_cond->mysql_conn,
&acct_assoc,
ACCOUNTING_ENFORCE_ASSOCS,
NULL, true);
if (rc != SLURM_SUCCESS) {
xfree(add_assoc_cond->ret_str);
add_assoc_cond->flags |= ADD_ASSOC_FLAG_STR_ERR;
if (!xstrcmp(acct_assoc.acct, "root")) {
add_assoc_cond->rc =
ESLURM_INVALID_CLUSTER_NAME;
add_assoc_cond->ret_str = xstrdup_printf(
"Cluster '%s' has not been added yet, please contact your admin before adding accounts to it",
acct_assoc.cluster);
} else {
add_assoc_cond->rc =
ESLURM_INVALID_PARENT_ACCOUNT;
add_assoc_cond->ret_str = xstrdup_printf(
"No parent account '%s' on cluster '%s'",
acct_assoc.acct, acct_assoc.cluster);
}
debug("%s", add_assoc_cond->ret_str);
goto end_it;
}
add_assoc_cond->add_assoc->assoc.parent_id = acct_assoc.id;
add_assoc_cond->base_lineage = acct_assoc.lineage;
}
if (list_for_each_ro(add_assoc_cond->add_assoc->acct_list,
_add_assoc_cond_acct,
add_assoc_cond) < 0)
goto end_it;
_post_add_assoc_cond_cluster(add_assoc_cond);
end_it:
add_assoc_cond->add_assoc->assoc.cluster = NULL;
if (add_assoc_cond->rc != SLURM_SUCCESS)
return -1;
else
return 0;
}
static int _foreach_is_coord(void *x, void *arg)
{
if (!assoc_mgr_is_user_acct_coord_user_rec(arg, x))
return -1;
return 0;
}
static int _find_qos_id(void *x, void *arg)
{
char *qos = x;
mod_def_qos_t *mod_def_qos = arg;
if (qos[0] == '-')
return 0;
else if (qos[0] == '+')
qos++;
/* info("looking for %u ?= %s", mod_def_qos->check_qos, qos); */
if (mod_def_qos->check_qos == slurm_atoul(qos))
return 1;
return 0;
}
static int _foreach_check_default_qos(void *x, void *arg)
{
slurmdb_assoc_rec_t *assoc = x;
mod_def_qos_t *mod_def_qos = arg;
bool found = false;
/*
* If def_qos_id is 0 (give me the first one on this list) or
* NO_VAL (not changed) just return.
*/
if (!assoc->def_qos_id || (assoc->def_qos_id == NO_VAL))
return 0;
if (assoc->qos_list) {
mod_def_qos->check_qos = assoc->def_qos_id;
if (list_find_first(assoc->qos_list, _find_qos_id, mod_def_qos))
found = true;
}
if (!found) {
char *name = slurmdb_qos_str(assoc_mgr_qos_list,
assoc->def_qos_id);
if (!mod_def_qos->ret_str)
xstrcatat(mod_def_qos->ret_str,
&mod_def_qos->ret_str_pos,
"\n These associations don't have access to their default qos.\n Please give them access before they the default can be set to this.\n");
xstrfmtcatat(mod_def_qos->ret_str,
&mod_def_qos->ret_str_pos,
" DefQOS = %-10s C = %-10s A = %-20s",
name, assoc->cluster, assoc->acct);
if (assoc->user) {
xstrfmtcatat(mod_def_qos->ret_str,
&mod_def_qos->ret_str_pos,
" U = %-9s",
assoc->user);
if (assoc->partition)
xstrfmtcatat(mod_def_qos->ret_str,
&mod_def_qos->ret_str_pos,
" P = %s",
assoc->partition);
}
xstrcatat(mod_def_qos->ret_str,
&mod_def_qos->ret_str_pos,
"\n");
}
return 0;
}
extern int as_mysql_add_assocs(mysql_conn_t *mysql_conn, uint32_t uid,
list_t *assoc_list)
{
list_itr_t *itr = NULL;
int rc = SLURM_SUCCESS;
slurmdb_assoc_rec_t *object = NULL;
char *parent = NULL;
char *my_par_lineage = NULL;
uint32_t my_par_id = 0;
char *last_parent = NULL, *last_cluster = NULL;
add_assoc_cond_t add_assoc_cond;
slurmdb_add_assoc_cond_t add_assoc;
if (check_connection(mysql_conn) != SLURM_SUCCESS)
return ESLURM_DB_CONNECTION;
if (!assoc_list || !list_count(assoc_list)) {
error("%s: Trying to add empty assoc list", __func__);
return ESLURM_EMPTY_LIST;
}
memset(&add_assoc_cond, 0, sizeof(add_assoc_cond));
memset(&add_assoc, 0, sizeof(add_assoc));
add_assoc_cond.add_assoc = &add_assoc;
add_assoc_cond.mysql_conn = mysql_conn;
if (!is_user_min_admin_level(mysql_conn, uid,
SLURMDB_ADMIN_OPERATOR)) {
list_itr_t *itr2 = NULL;
slurmdb_user_rec_t user;
slurmdb_coord_rec_t *coord = NULL;
slurmdb_assoc_rec_t *object = NULL;
if (slurmdbd_conf->flags & DBD_CONF_FLAG_DISABLE_COORD_DBD) {
error("Coordinator privilege revoked with DisableCoordDBD, only admins/operators can add associations.");
return ESLURM_ACCESS_DENIED;
}
memset(&user, 0, sizeof(slurmdb_user_rec_t));
user.uid = uid;
if (!is_user_any_coord(mysql_conn, &user)) {
error("Only admins/operators/coordinators "
"can add associations");
return ESLURM_ACCESS_DENIED;
}
itr = list_iterator_create(assoc_list);
itr2 = list_iterator_create(user.coord_accts);
while ((object = list_next(itr))) {
char *account = "root";
if (object->user)
account = object->acct;
else if (object->parent_acct)
account = object->parent_acct;
list_iterator_reset(itr2);
while ((coord = list_next(itr2))) {
if (!xstrcasecmp(coord->name, account))
break;
}
if (!coord)
break;
}
list_iterator_destroy(itr2);
list_iterator_destroy(itr);
if (!coord) {
error("Coordinator %s(%d) tried to add associations "
"where they were not allowed",
user.name, user.uid);
return ESLURM_ACCESS_DENIED;
}
add_assoc_cond.is_coord = true;
}
add_assoc_cond.uid = uid;
add_assoc_cond.user_name = uid_to_string((uid_t) uid);
/* these need to be in a specific order */
list_sort(assoc_list, (ListCmpF)_assoc_sort_cluster);
itr = list_iterator_create(assoc_list);
while ((object = list_next(itr))) {
if (!object->cluster || !object->cluster[0]
|| !object->acct || !object->acct[0]) {
error("We need an association, cluster and acct to add one.");
rc = SLURM_ERROR;
continue;
}
if (add_assoc_cond.is_coord &&
!assoc_mgr_check_coord_qos(object->cluster, object->acct,
add_assoc_cond.user_name,
object->qos_list, false)) {
assoc_mgr_lock_t locks = {
NO_LOCK, NO_LOCK, READ_LOCK, NO_LOCK,
NO_LOCK, NO_LOCK, NO_LOCK };
char *requested_qos;
assoc_mgr_lock(&locks);
requested_qos = get_qos_complete_str(
assoc_mgr_qos_list, object->qos_list);
assoc_mgr_unlock(&locks);
error("Coordinator %s(%d) does not have the "
"access to all the qos requested (%s), "
"so they can't add to account "
"%s with it.",
add_assoc_cond.user_name, uid, requested_qos,
object->acct);
xfree(requested_qos);
rc = ESLURM_ACCESS_DENIED;
break;
}
if (xstrcmp(object->cluster, last_cluster)) {
if (last_cluster) {
_post_add_assoc_cond_cluster(
&add_assoc_cond);
xfree(add_assoc.assoc.cluster);
FREE_NULL_LIST(add_assoc.user_list);
if (add_assoc_cond.rc != SLURM_SUCCESS) {
rc = add_assoc_cond.rc;
break;
}
}
/*
* We can't just add the pointer here, we have to copy
* the string or we could hit invalid reads afterwards
* when we call _post_add_assoc_cond_cluster() ->
* _check_defaults().
*/
add_assoc.assoc.cluster = xstrdup(object->cluster);
}
if (object->user) {
/*
* We can't just add the pointer here, we have to copy
* the string or we could hit invalid reads afterwards
* when we call _post_add_assoc_cond_cluster() ->
* _check_defaults().
*/
if (!add_assoc.user_list)
add_assoc.user_list = list_create(xfree_ptr);
if (!list_find_first(add_assoc.user_list,
slurm_find_char_in_list,
object->user))
list_append(add_assoc.user_list,
xstrdup(object->user));
}
if (object->parent_acct) {
parent = object->parent_acct;
} else if (object->user) {
parent = object->acct;
} else {
parent = "root";
}
if ((!last_parent || !last_cluster ||
xstrcmp(parent, last_parent) ||
xstrcmp(object->cluster, last_cluster))) {
xfree(my_par_lineage);
if ((rc = _get_parent_id(mysql_conn,
parent,
object->cluster,
&my_par_id,
&my_par_lineage)) !=
SLURM_SUCCESS) {
rc = ESLURM_INVALID_PARENT_ACCOUNT;
break;
}
xfree(last_parent);
xfree(last_cluster);
last_parent = xstrdup(parent);
last_cluster = xstrdup(object->cluster);
}
object->parent_id = my_par_id;
xfree(object->lineage);
if (object->user) {
object->lineage = xstrdup_printf(
"%s0-%s/", my_par_lineage, object->user);
if (object->partition)
xstrfmtcat(object->lineage, "%s/",
object->partition);
_handle_coord_parent_flag(&add_assoc_cond, object,
ASSOC_FLAG_USER_COORD);
} else {
object->lineage = xstrdup_printf(
"%s%s/", my_par_lineage, object->acct);
}
if ((rc = setup_assoc_limits(object,
&add_assoc_cond.cols,
&add_assoc_cond.vals,
&add_assoc_cond.extra,
QOS_LEVEL_NONE, 1))) {
error("%s: Failed, setup_assoc_limits functions returned error",
__func__);
break;
}
add_assoc_cond.add_assoc = &add_assoc;
add_assoc_cond.alloc_assoc = object;
list_remove(itr);
add_assoc_cond.mysql_conn = mysql_conn;
rc = _add_assoc_internal(&add_assoc_cond);
xfree(add_assoc_cond.cols);
xfree(add_assoc_cond.extra);
/* The caller can't receive this, so don't send it */
xfree(add_assoc_cond.ret_str);
xfree(add_assoc_cond.vals);
}
list_iterator_destroy(itr);
xfree(my_par_lineage);
if (rc != SLURM_SUCCESS)
goto end_it;
if (last_cluster) {
_post_add_assoc_cond_cluster(&add_assoc_cond);
xfree(add_assoc.assoc.cluster);
FREE_NULL_LIST(add_assoc.user_list);
if (add_assoc_cond.rc != SLURM_SUCCESS) {
goto end_it;
}
}
if (add_assoc_cond.txn_query) {
xstrcat(add_assoc_cond.txn_query, ";");
debug4("%d(%s:%d) query\n%s",
mysql_conn->conn, THIS_FILE,
__LINE__, add_assoc_cond.txn_query);
rc = mysql_db_query(mysql_conn,
add_assoc_cond.txn_query);
if (rc != SLURM_SUCCESS) {
error("Couldn't add txn");
goto end_it;
}
}
end_it:
if (add_assoc_cond.rc != SLURM_SUCCESS) {
reset_mysql_conn(mysql_conn);
} else {
if ((add_assoc_cond.rc == SLURM_SUCCESS) &&
add_assoc_cond.txn_query) {
xstrcat(add_assoc_cond.txn_query, ";");
debug4("%d(%s:%d) query\n%s",
mysql_conn->conn, THIS_FILE,
__LINE__, add_assoc_cond.txn_query);
rc = mysql_db_query(mysql_conn,
add_assoc_cond.txn_query);
if (rc != SLURM_SUCCESS) {
error("Couldn't add txn");
}
}
}
xfree(add_assoc.assoc.cluster);
FREE_NULL_LIST(add_assoc.user_list);
xfree(add_assoc_cond.cols);
FREE_NULL_LIST(add_assoc_cond.coord_users);
xfree(add_assoc_cond.extra);
xfree(add_assoc_cond.ret_str);
xfree(add_assoc_cond.txn_query);
xfree(add_assoc_cond.user_name);
xfree(add_assoc_cond.vals);
xfree(last_parent);
xfree(last_cluster);
return add_assoc_cond.rc;
}
extern char *as_mysql_add_assocs_cond(mysql_conn_t *mysql_conn, uint32_t uid,
slurmdb_add_assoc_cond_t *add_assoc)
{
int rc = SLURM_SUCCESS;
list_t *use_cluster_list = NULL;
add_assoc_cond_t add_assoc_cond;
assoc_mgr_lock_t locks = {
.assoc = READ_LOCK,
.user = READ_LOCK,
.qos = READ_LOCK,
};
if (!add_assoc) {
error("we need something to change");
return NULL;
}
if (check_connection(mysql_conn) != SLURM_SUCCESS)
return NULL;
memset(&add_assoc_cond, 0, sizeof(add_assoc_cond));
if (!add_assoc->user_list && !add_assoc->assoc.parent_acct)
add_assoc->assoc.parent_acct = xstrdup("root");
assoc_mgr_lock(&locks);
add_assoc_cond.assoc_mgr_locked = true;
add_assoc_cond.flags = ASSOC_FLAG_USER_COORD;
if (!is_user_min_admin_level_locked(mysql_conn, uid,
SLURMDB_ADMIN_OPERATOR)) {
slurmdb_user_rec_t user;
if (slurmdbd_conf->flags & DBD_CONF_FLAG_DISABLE_COORD_DBD) {
error("Coordinator privilege revoked with DisableCoordDBD, only admins/operators can add associations.");
assoc_mgr_unlock(&locks);
errno = ESLURM_ACCESS_DENIED;
return NULL;
}
memset(&user, 0, sizeof(slurmdb_user_rec_t));
user.uid = uid;
if (!is_user_any_coord_locked(mysql_conn, &user)) {
error("Only admins/operators/coordinators can add associations");
assoc_mgr_unlock(&locks);
errno = ESLURM_ACCESS_DENIED;
return NULL;
}
if (add_assoc->user_list)
rc = list_for_each_ro(add_assoc->acct_list,
_foreach_is_coord,
&user);
else
rc = _foreach_is_coord(add_assoc->assoc.parent_acct,
&user);
if (rc < 0) {
error("Coordinator %s(%d) tried to add associations where they were not allowed",
user.name, user.uid);
assoc_mgr_unlock(&locks);
errno = ESLURM_ACCESS_DENIED;
return NULL;
}
add_assoc_cond.is_coord = true;
}
if ((rc = setup_assoc_limits_locked(&add_assoc->assoc,
&add_assoc_cond.cols,
&add_assoc_cond.vals,
&add_assoc_cond.extra,
QOS_LEVEL_NONE,
true))) {
xfree(add_assoc_cond.cols);
xfree(add_assoc_cond.extra);
xfree(add_assoc_cond.vals);
errno = rc;
error("%s: Failed, setup_assoc_limits functions returned error",
__func__);
assoc_mgr_unlock(&locks);
return NULL;
}
if (add_assoc->cluster_list && list_count(add_assoc->cluster_list))
use_cluster_list = add_assoc->cluster_list;
else
/*
* No need to do a shallow copy here as we are doing a
* list_for_each_ro() which will handle the locks for us.
*/
use_cluster_list = as_mysql_cluster_list;
add_assoc_cond.add_assoc = add_assoc;
add_assoc_cond.mysql_conn = mysql_conn;
add_assoc_cond.uid = uid;
add_assoc_cond.user_name = uid_to_string((uid_t) uid);
(void) list_for_each_ro(use_cluster_list, _add_assoc_cond_cluster,
&add_assoc_cond);
assoc_mgr_unlock(&locks);
xfree(add_assoc_cond.cols);
xfree(add_assoc_cond.extra);
FREE_NULL_LIST(add_assoc_cond.coord_users);
if (add_assoc_cond.rc != SLURM_SUCCESS) {
reset_mysql_conn(mysql_conn);
if (!(add_assoc_cond.flags & ADD_ASSOC_FLAG_STR_ERR))
xfree(add_assoc_cond.ret_str);
errno = add_assoc_cond.rc;
} else if (!(add_assoc_cond.flags & ADD_ASSOC_FLAG_ADDED)) {
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "didn't affect anything");
errno = SLURM_NO_CHANGE_IN_DATA;
} else {
if ((add_assoc_cond.rc == SLURM_SUCCESS) &&
add_assoc_cond.txn_query) {
xstrcat(add_assoc_cond.txn_query, ";");
debug4("%d(%s:%d) query\n%s",
mysql_conn->conn, THIS_FILE,
__LINE__, add_assoc_cond.txn_query);
rc = mysql_db_query(mysql_conn,
add_assoc_cond.txn_query);
if (rc != SLURM_SUCCESS) {
error("Couldn't add txn");
rc = SLURM_SUCCESS;
}
}
errno = SLURM_SUCCESS;
}
xfree(add_assoc_cond.txn_query);
xfree(add_assoc_cond.user_name);
xfree(add_assoc_cond.vals);
return add_assoc_cond.ret_str;
}
extern list_t *as_mysql_modify_assocs(mysql_conn_t *mysql_conn, uint32_t uid,
slurmdb_assoc_cond_t *assoc_cond,
slurmdb_assoc_rec_t *assoc)
{
list_itr_t *itr = NULL;
list_t *ret_list = NULL;
int rc = SLURM_SUCCESS;
char *object = NULL;
char *vals = NULL, *extra = NULL, *query = NULL;
int i = 0;
bool is_admin=0, same_user=0;
MYSQL_RES *result = NULL;
slurmdb_user_rec_t user;
char *tmp_char1=NULL, *tmp_char2=NULL;
char *cluster_name = NULL;
char *prefix = "t1";
list_t *use_cluster_list = NULL;
bool locked = false;
slurmdb_assoc_cond_t qos_assoc_cond;
bitstr_t *wanted_qos = NULL;
/*
* ASSOC_LOCK required by _assoc_id_has_qos/assoc_mgr_check_coord_qos
* QOS_LOCK required by _process_modify_assoc_results
* TRES_LOCK requerd by assoc_mgr_check_assoc_lim_incr
* USER_LOCK required by assoc_mgr_check_coord_qos
*/
assoc_mgr_lock_t assoc_locks = {
.assoc = READ_LOCK,
.qos = READ_LOCK,
.tres = READ_LOCK,
.user = READ_LOCK,
};
bool assoc_mgr_locked = false;
if (!assoc_cond || !assoc) {
error("we need something to change");
return NULL;
}
if (check_connection(mysql_conn) != SLURM_SUCCESS)
return NULL;
memset(&user, 0, sizeof(slurmdb_user_rec_t));
user.uid = uid;
if (!(is_admin = is_user_min_admin_level(
mysql_conn, uid, SLURMDB_ADMIN_OPERATOR))) {
if (is_user_any_coord(mysql_conn, &user)) {
if (slurmdbd_conf->flags &
DBD_CONF_FLAG_DISABLE_COORD_DBD) {
error("Coordinator privilege revoked with DisableCoordDBD, only admins/operators can modify associations.");
errno = ESLURM_ACCESS_DENIED;
return NULL;
}
if (assoc->parent_acct) {
rc = _foreach_is_coord(assoc->parent_acct,
&user);
if (rc < 0) {
error("Coordinator %s(%d) tried to add associations where they were not allowed",
user.name, user.uid);
errno = ESLURM_ACCESS_DENIED;
return NULL;
}
}
goto is_same_user;
} else if (assoc_cond->user_list
&& (list_count(assoc_cond->user_list) == 1)) {
uid_t pw_uid;
char *name;
name = list_peek(assoc_cond->user_list);
if ((uid_from_string(name, &pw_uid) == SLURM_SUCCESS) &&
(pw_uid == uid)) {
uint16_t is_def = assoc->is_def;
uint32_t def_qos_id = assoc->def_qos_id;
/* Make sure they aren't trying to
change something they aren't
allowed to. Currently they are
only allowed to change the default
account, and default QOS.
*/
slurmdb_init_assoc_rec(assoc, 1);
assoc->is_def = is_def;
assoc->def_qos_id = def_qos_id;
same_user = 1;
goto is_same_user;
}
}
error("Only admins/coordinators can modify associations");
errno = ESLURM_ACCESS_DENIED;
return NULL;
}
is_same_user:
(void) _setup_assoc_cond_limits(assoc_cond, prefix, &extra);
/* This needs to be here to make sure we only modify the
correct set of assocs The first clause was already
taken care of above. */
if (assoc_cond->user_list && !list_count(assoc_cond->user_list)) {
debug4("no user specified looking at users");
xstrcat(extra, " && user != '' ");
} else if (!assoc_cond->user_list) {
debug4("no user specified looking at accounts");
xstrcat(extra, " && user = '' ");
}
if ((rc = setup_assoc_limits(assoc, &tmp_char1, &tmp_char2,
&vals, QOS_LEVEL_MODIFY, 0))) {
xfree(tmp_char1);
xfree(tmp_char2);
xfree(vals);
xfree(extra);
errno = rc;
error("%s: Failed, setup_assoc_limits functions returned error",
__func__);
return NULL;
}
xfree(tmp_char1);
xfree(tmp_char2);
if (!extra || (!vals && !assoc->parent_acct)) {
xfree(vals);
xfree(extra);
errno = SLURM_NO_CHANGE_IN_DATA;
error("Nothing to change");
return NULL;
}
xstrfmtcat(object, "t1.%s", massoc_req_inx[0]);
for(i=1; i<MASSOC_COUNT; i++)
xstrfmtcat(object, ", t1.%s", massoc_req_inx[i]);
ret_list = list_create(xfree_ptr);
if (assoc_cond->cluster_list && list_count(assoc_cond->cluster_list))
use_cluster_list = assoc_cond->cluster_list;
else {
slurm_rwlock_rdlock(&as_mysql_cluster_list_lock);
use_cluster_list = list_shallow_copy(as_mysql_cluster_list);
locked = true;
}
if (assoc_cond->qos_list && list_count(assoc_cond->qos_list)) {
wanted_qos = bit_alloc(g_qos_count);
set_qos_bitstr_from_list(wanted_qos, assoc_cond->qos_list);
assoc_mgr_lock(&assoc_locks);
assoc_mgr_locked = true;
} else if (assoc->parent_acct) {
assoc_mgr_lock(&assoc_locks);
assoc_mgr_locked = true;
}
memset(&qos_assoc_cond, 0, sizeof(qos_assoc_cond));
itr = list_iterator_create(use_cluster_list);
while ((cluster_name = list_next(itr))) {
query = _setup_assoc_table_query(cluster_name, object, extra,
" ORDER BY lineage FOR UPDATE;");
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", query);
if (!(result = mysql_db_query_ret(
mysql_conn, query, 0))) {
xfree(query);
if (mysql_errno(mysql_conn->db_conn)
!= ER_NO_SUCH_TABLE) {
FREE_NULL_LIST(ret_list);
ret_list = NULL;
}
break;
}
xfree(query);
rc = _process_modify_assoc_results(mysql_conn, result, assoc,
&user, cluster_name, vals,
is_admin, same_user,
ret_list, &qos_assoc_cond,
wanted_qos,
assoc_mgr_locked);
mysql_free_result(result);
if ((rc == ESLURM_INVALID_PARENT_ACCOUNT)
|| (rc == ESLURM_SAME_PARENT_ACCOUNT)) {
continue;
} else if (rc != SLURM_SUCCESS) {
FREE_NULL_LIST(ret_list);
ret_list = NULL;
break;
}
if (qos_assoc_cond.acct_list) {
if (!qos_assoc_cond.cluster_list)
qos_assoc_cond.cluster_list =
list_create(NULL);
list_append(qos_assoc_cond.cluster_list,
cluster_name);
}
}
list_iterator_destroy(itr);
if (wanted_qos || assoc->parent_acct) {
assoc_mgr_unlock(&assoc_locks);
assoc_mgr_locked = false;
}
FREE_NULL_BITMAP(wanted_qos);
if (ret_list && qos_assoc_cond.cluster_list) {
list_t *local_assoc_list = as_mysql_get_assocs(
mysql_conn, uid, &qos_assoc_cond);
if (local_assoc_list) {
mod_def_qos_t mod_def_qos;
memset(&mod_def_qos, 0, sizeof(mod_def_qos));
list_for_each(local_assoc_list,
_foreach_check_default_qos,
&mod_def_qos);
FREE_NULL_LIST(local_assoc_list);
if (mod_def_qos.ret_str) {
list_flush(ret_list);
list_append(ret_list, mod_def_qos.ret_str);
mod_def_qos.ret_str = NULL;
rc = ESLURM_NO_REMOVE_DEFAULT_QOS;
reset_mysql_conn(mysql_conn);
}
}
}
FREE_NULL_LIST(qos_assoc_cond.cluster_list);
FREE_NULL_LIST(qos_assoc_cond.acct_list);
FREE_NULL_LIST(qos_assoc_cond.user_list);
if (locked) {
FREE_NULL_LIST(use_cluster_list);
slurm_rwlock_unlock(&as_mysql_cluster_list_lock);
}
xfree(vals);
xfree(object);
xfree(extra);
if (!ret_list) {
reset_mysql_conn(mysql_conn);
errno = rc;
return NULL;
} else if (!list_count(ret_list)) {
reset_mysql_conn(mysql_conn);
errno = SLURM_NO_CHANGE_IN_DATA;
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "didn't affect anything");
return ret_list;
}
errno = rc;
return ret_list;
}
extern list_t *as_mysql_remove_assocs(mysql_conn_t *mysql_conn, uint32_t uid,
slurmdb_assoc_cond_t *assoc_cond)
{
list_itr_t *itr = NULL;
int rc = SLURM_SUCCESS;
char *object = NULL;
char *extra = NULL, *query = NULL;
int i = 0, is_admin = 0;
MYSQL_RES *result = NULL;
MYSQL_ROW row;
slurmdb_user_rec_t user;
char *prefix = "t1";
bool locked = false;
add_assoc_cond_t add_assoc_cond = {
.mysql_conn = mysql_conn,
};
bitstr_t *wanted_qos = NULL;
/*
* ASSOC_LOCK needed by _assoc_id_has_qos and _handle_coord_parent_flag
* QOS_LOCK needed by _check_jobs_before_remove
* USER_LOCK needed by _handle_coord_parent_flag
* WCKEY_LOCK needed by _check_jobs_before_remove
*/
assoc_mgr_lock_t assoc_locks = {
.assoc = READ_LOCK,
.qos = READ_LOCK,
.user = READ_LOCK,
.wckey = READ_LOCK,
};
remove_common_args_t args = {
.default_account = false,
.jobs_running = false,
.mysql_conn = mysql_conn,
.table = assoc_table,
.type = DBD_REMOVE_ASSOCS,
};
if (!assoc_cond) {
error("we need something to change");
return NULL;
}
if (check_connection(mysql_conn) != SLURM_SUCCESS)
return NULL;
memset(&user, 0, sizeof(slurmdb_user_rec_t));
user.uid = uid;
if (!(is_admin = is_user_min_admin_level(
mysql_conn, uid, SLURMDB_ADMIN_OPERATOR))) {
if (slurmdbd_conf->flags & DBD_CONF_FLAG_DISABLE_COORD_DBD) {
error("Coordinator privilege revoked with DisableCoordDBD, only admins/operators can remove associations.");
errno = ESLURM_ACCESS_DENIED;
return NULL;
}
if (!is_user_any_coord(mysql_conn, &user)) {
error("Only admins/coordinators can "
"remove associations");
errno = ESLURM_ACCESS_DENIED;
return NULL;
}
}
(void)_setup_assoc_cond_limits(assoc_cond, prefix, &extra);
xstrcat(object, rassoc_req_inx[0]);
for(i=1; i<RASSOC_COUNT; i++)
xstrfmtcat(object, ", %s", rassoc_req_inx[i]);
args.ret_list = list_create(xfree_ptr);
args.user_name = uid_to_string((uid_t) user.uid);
args.now = time(NULL);
if (assoc_cond->cluster_list && list_count(assoc_cond->cluster_list))
args.use_cluster_list = assoc_cond->cluster_list;
else {
slurm_rwlock_rdlock(&as_mysql_cluster_list_lock);
args.use_cluster_list =
list_shallow_copy(as_mysql_cluster_list);
locked = true;
}
if (assoc_cond->qos_list && list_count(assoc_cond->qos_list)) {
wanted_qos = bit_alloc(g_qos_count);
set_qos_bitstr_from_list(wanted_qos, assoc_cond->qos_list);
assoc_mgr_lock(&assoc_locks);
add_assoc_cond.assoc_mgr_locked = true;
args.qos_wckey_locked = true;
}
itr = list_iterator_create(args.use_cluster_list);
while ((args.cluster_name = list_next(itr))) {
query = _setup_assoc_table_query(args.cluster_name,
"t1.id_assoc, t1.lineage",
extra, " ORDER BY lineage;");
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", query);
if (!(result = mysql_db_query_ret(
mysql_conn, query, 0))) {
xfree(query);
if (mysql_errno(mysql_conn->db_conn)
!= ER_NO_SUCH_TABLE) {
FREE_NULL_LIST(args.ret_list);
}
break;
}
xfree(query);
if (!mysql_num_rows(result)) {
mysql_free_result(result);
continue;
}
while ((row = mysql_fetch_row(result))) {
/*
* Filter assoc recs by qos here rather than performing
* an expensive sql query.
*/
if (wanted_qos) {
uint32_t id = slurm_atoul(row[0]);
if (!_assoc_id_has_qos(mysql_conn,
args.cluster_name, id,
wanted_qos))
continue;
}
xstrfmtcat(args.name_char,
"%slineage='%s'",
args.name_char ? " || " : "", row[1]);
}
mysql_free_result(result);
query = xstrdup_printf("select distinct %s "
"from \"%s_%s\" where (%s) "
"and deleted = 0 order by lineage;",
object,
args.cluster_name, assoc_table,
args.name_char);
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", query);
if (!(result = mysql_db_query_ret(
mysql_conn, query, 0))) {
xfree(query);
xfree(args.name_char);
FREE_NULL_LIST(args.ret_list);
break;
}
xfree(query);
rc = _process_remove_assoc_results(&args, result,
&user,
is_admin,
&add_assoc_cond);
xfree(args.name_char);
mysql_free_result(result);
if (rc != SLURM_SUCCESS) {
FREE_NULL_LIST(args.ret_list);
break;
}
}
if (wanted_qos) {
assoc_mgr_unlock(&assoc_locks);
add_assoc_cond.assoc_mgr_locked = false;
args.qos_wckey_locked = false;
}
FREE_NULL_LIST(add_assoc_cond.coord_users);
FREE_NULL_BITMAP(wanted_qos);
list_iterator_destroy(itr);
if (locked) {
FREE_NULL_LIST(args.use_cluster_list);
slurm_rwlock_unlock(&as_mysql_cluster_list_lock);
}
xfree(args.user_name);
xfree(object);
xfree(extra);
if (!args.ret_list) {
reset_mysql_conn(mysql_conn);
return NULL;
} else if (!list_count(args.ret_list)) {
reset_mysql_conn(mysql_conn);
errno = SLURM_NO_CHANGE_IN_DATA;
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "didn't affect anything");
return args.ret_list;
}
if (args.default_account)
errno = ESLURM_NO_REMOVE_DEFAULT_ACCOUNT;
else if (args.jobs_running)
errno = ESLURM_JOBS_RUNNING_ON_ASSOC;
else
errno = SLURM_SUCCESS;
return args.ret_list;
}
extern list_t *as_mysql_get_assocs(mysql_conn_t *mysql_conn, uid_t uid,
slurmdb_assoc_cond_t *assoc_cond)
{
//DEF_TIMERS;
char *extra = NULL;
char *tmp = NULL;
list_t *assoc_list = NULL;
list_itr_t *itr = NULL;
int i=0, is_admin=1;
slurmdb_user_rec_t user;
char *prefix = "t1";
list_t *use_cluster_list = NULL;
char *cluster_name = NULL;
bool locked = false;
if (!assoc_cond) {
xstrcat(extra, " where deleted=0");
goto empty;
}
if (check_connection(mysql_conn) != SLURM_SUCCESS)
return NULL;
memset(&user, 0, sizeof(slurmdb_user_rec_t));
user.uid = uid;
if ((slurm_conf.private_data & PRIVATE_DATA_USERS) ||
((assoc_cond->flags & ASSOC_COND_FLAG_WITH_USAGE) &&
(slurm_conf.private_data & PRIVATE_DATA_USAGE))) {
if (!(is_admin = is_user_min_admin_level(
mysql_conn, uid, SLURMDB_ADMIN_OPERATOR))) {
/* Fill in the user with any accounts they may
be coordinator of, which is checked inside
_cluster_get_assocs.
*/
assoc_mgr_fill_in_user(
mysql_conn, &user, 1, NULL, false);
}
if (!is_admin && !user.name) {
debug("User %u has no associations, and is not admin, "
"so not returning any.", user.uid);
return NULL;
}
}
(void) _setup_assoc_cond_limits(assoc_cond, prefix, &extra);
empty:
xfree(tmp);
xstrfmtcat(tmp, "t1.%s", assoc_req_inx[i]);
for(i=1; i<ASSOC_REQ_COUNT; i++) {
xstrfmtcat(tmp, ", t1.%s", assoc_req_inx[i]);
}
assoc_list = list_create(slurmdb_destroy_assoc_rec);
if (assoc_cond && assoc_cond->cluster_list &&
list_count(assoc_cond->cluster_list)) {
use_cluster_list = assoc_cond->cluster_list;
} else {
slurm_rwlock_rdlock(&as_mysql_cluster_list_lock);
use_cluster_list = list_shallow_copy(as_mysql_cluster_list);
locked = true;
}
itr = list_iterator_create(use_cluster_list);
while ((cluster_name = list_next(itr))) {
int rc;
if ((rc = _cluster_get_assocs(mysql_conn, &user, assoc_cond,
cluster_name, tmp, extra,
is_admin, assoc_list))
!= SLURM_SUCCESS) {
FREE_NULL_LIST(assoc_list);
assoc_list = NULL;
break;
}
}
list_iterator_destroy(itr);
if (locked) {
FREE_NULL_LIST(use_cluster_list);
slurm_rwlock_unlock(&as_mysql_cluster_list_lock);
}
xfree(tmp);
xfree(extra);
//END_TIMER2("get_assocs");
return assoc_list;
}
extern int as_mysql_assoc_remove_default(mysql_conn_t *mysql_conn,
list_t *user_list, list_t *cluster_list)
{
char *query = NULL;
list_t *use_cluster_list = NULL;
list_itr_t *itr, *itr2;
slurmdb_assoc_rec_t assoc;
bool locked = false;
int rc = SLURM_SUCCESS;
xassert(user_list);
if (!(slurmdbd_conf->flags & DBD_CONF_FLAG_ALLOW_NO_DEF_ACCT))
return ESLURM_NO_REMOVE_DEFAULT_ACCOUNT;
slurmdb_init_assoc_rec(&assoc, 0);
assoc.acct = "";
assoc.is_def = 1;
if (cluster_list && list_count(cluster_list))
use_cluster_list = cluster_list;
else {
slurm_rwlock_rdlock(&as_mysql_cluster_list_lock);
use_cluster_list = list_shallow_copy(as_mysql_cluster_list);
locked = true;
}
itr = list_iterator_create(use_cluster_list);
itr2 = list_iterator_create(user_list);
while ((assoc.cluster = list_next(itr))) {
list_iterator_reset(itr2);
while ((assoc.user = list_next(itr2))) {
rc = _reset_default_assoc(
mysql_conn, &assoc, &query, true);
if (rc != SLURM_SUCCESS)
break;
}
if (rc != SLURM_SUCCESS)
break;
}
list_iterator_destroy(itr2);
list_iterator_destroy(itr);
if (locked) {
FREE_NULL_LIST(use_cluster_list);
slurm_rwlock_unlock(&as_mysql_cluster_list_lock);
}
if (rc != SLURM_SUCCESS)
xfree(query);
if (query) {
DB_DEBUG(DB_ASSOC, mysql_conn->conn, "query\n%s", query);
rc = mysql_db_query(mysql_conn, query);
xfree(query);
if (rc != SLURM_SUCCESS)
error("Couldn't remove default assocs");
}
return rc;
}