| /*****************************************************************************\ |
| * 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; |
| } |