| /*****************************************************************************\ |
| * reservation.c - resource reservation management |
| ***************************************************************************** |
| * Copyright (C) 2009-2010 Lawrence Livermore National Security. |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). |
| * Written by Morris Jette <jette1@llnl.gov> et. al. |
| * CODE-OCEC-09-009. All rights reserved. |
| * |
| * This file is part of SLURM, a resource management program. |
| * For details, see <https://computing.llnl.gov/linux/slurm/>. |
| * 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. |
| \*****************************************************************************/ |
| |
| #ifdef HAVE_CONFIG_H |
| # include "config.h" |
| #endif |
| |
| #ifdef WITH_PTHREADS |
| # include <pthread.h> |
| #endif /* WITH_PTHREADS */ |
| |
| #include <fcntl.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <time.h> |
| #include <unistd.h> |
| #include <slurm/slurm.h> |
| #include <slurm/slurm_errno.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| #include "src/common/assoc_mgr.h" |
| #include "src/common/bitstring.h" |
| #include "src/common/hostlist.h" |
| #include "src/common/list.h" |
| #include "src/common/log.h" |
| #include "src/common/macros.h" |
| #include "src/common/node_select.h" |
| #include "src/common/pack.h" |
| #include "src/common/parse_time.h" |
| #include "src/common/slurm_accounting_storage.h" |
| #include "src/common/uid.h" |
| #include "src/common/xassert.h" |
| #include "src/common/xmalloc.h" |
| #include "src/common/xstring.h" |
| |
| #include "src/slurmctld/licenses.h" |
| #include "src/slurmctld/locks.h" |
| #include "src/slurmctld/slurmctld.h" |
| #include "src/slurmctld/state_save.h" |
| |
| #define ONE_YEAR (365 * 24 * 60 * 60) |
| #define RESV_MAGIC 0x3b82 |
| |
| /* Change RESV_STATE_VERSION value when changing the state save format |
| * Add logic to permit reading of the previous version's state in order |
| * to avoid losing reservations between releases major SLURM updates. */ |
| #define RESV_STATE_VERSION "VER003" |
| |
| time_t last_resv_update = (time_t) 0; |
| List resv_list = (List) NULL; |
| uint32_t resv_over_run; |
| uint32_t top_suffix = 0; |
| #ifdef HAVE_BG |
| uint32_t cnodes_per_bp = 0; |
| #endif |
| |
| static void _advance_resv_time(slurmctld_resv_t *resv_ptr); |
| static void _advance_time(time_t *res_time, int day_cnt); |
| static int _build_account_list(char *accounts, int *account_cnt, |
| char ***account_list); |
| static int _build_uid_list(char *users, int *user_cnt, uid_t **user_list); |
| static void _clear_job_resv(slurmctld_resv_t *resv_ptr); |
| static slurmctld_resv_t *_copy_resv(slurmctld_resv_t *resv_orig_ptr); |
| static void _del_resv_rec(void *x); |
| static void _dump_resv_req(resv_desc_msg_t *resv_ptr, char *mode); |
| static int _find_resv_id(void *x, void *key); |
| static int _find_resv_name(void *x, void *key); |
| static void _generate_resv_id(void); |
| static void _generate_resv_name(resv_desc_msg_t *resv_ptr); |
| static uint32_t _get_job_duration(struct job_record *job_ptr); |
| static bool _is_account_valid(char *account); |
| static bool _is_resv_used(slurmctld_resv_t *resv_ptr); |
| static bool _job_overlap(time_t start_time, uint16_t flags, |
| bitstr_t *node_bitmap); |
| static List _list_dup(List license_list); |
| static int _open_resv_state_file(char **state_file); |
| static void _pack_resv(slurmctld_resv_t *resv_ptr, Buf buffer, |
| bool internal); |
| static int _post_resv_create(slurmctld_resv_t *resv_ptr); |
| static int _post_resv_delete(slurmctld_resv_t *resv_ptr); |
| static int _post_resv_update(slurmctld_resv_t *resv_ptr, |
| slurmctld_resv_t *old_resv_ptr); |
| static bitstr_t *_pick_idle_nodes(bitstr_t *avail_nodes, |
| resv_desc_msg_t *resv_desc_ptr); |
| static bitstr_t *_pick_idle_nodes2(bitstr_t *avail_nodes, |
| resv_desc_msg_t *resv_desc_ptr); |
| static int _resize_resv(slurmctld_resv_t *resv_ptr, uint32_t node_cnt); |
| static bool _resv_overlap(time_t start_time, time_t end_time, |
| uint16_t flags, bitstr_t *node_bitmap, |
| slurmctld_resv_t *this_resv_ptr); |
| static int _select_nodes(resv_desc_msg_t *resv_desc_ptr, |
| struct part_record **part_ptr, |
| bitstr_t **resv_bitmap); |
| static int _set_assoc_list(slurmctld_resv_t *resv_ptr); |
| static void _set_cpu_cnt(slurmctld_resv_t *resv_ptr); |
| static void _set_nodes_maint(slurmctld_resv_t *resv_ptr, time_t now); |
| static void _swap_resv(slurmctld_resv_t *resv_backup, |
| slurmctld_resv_t *resv_ptr); |
| static int _update_account_list(slurmctld_resv_t *resv_ptr, |
| char *accounts); |
| static int _update_uid_list(slurmctld_resv_t *resv_ptr, char *users); |
| static void _validate_all_reservations(void); |
| static int _valid_job_access_resv(struct job_record *job_ptr, |
| slurmctld_resv_t *resv_ptr); |
| static bool _validate_one_reservation(slurmctld_resv_t *resv_ptr); |
| static void _validate_node_choice(slurmctld_resv_t *resv_ptr); |
| |
| /* Advance res_time by the specified day count, |
| * account for daylight savings time */ |
| static void _advance_time(time_t *res_time, int day_cnt) |
| { |
| time_t save_time = *res_time; |
| struct tm time_tm; |
| |
| localtime_r(res_time, &time_tm); |
| time_tm.tm_isdst = -1; |
| time_tm.tm_mday += day_cnt; |
| *res_time = mktime(&time_tm); |
| if (*res_time == (time_t)(-1)) { |
| error("Could not compute reservation time %lu", |
| (long unsigned int) save_time); |
| *res_time = save_time + (24 * 60 * 60); |
| } |
| } |
| |
| static List _list_dup(List license_list) |
| { |
| ListIterator iter; |
| licenses_t *license_src, *license_dest; |
| List lic_list = (List) NULL; |
| |
| if (!license_list) |
| return lic_list; |
| |
| lic_list = list_create(license_free_rec); |
| if (lic_list == NULL) |
| fatal("list_create malloc failure"); |
| iter = list_iterator_create(license_list); |
| if (iter == NULL) |
| fatal("list_interator_create malloc failure"); |
| while ((license_src = (licenses_t *) list_next(iter))) { |
| license_dest = xmalloc(sizeof(licenses_t)); |
| license_dest->name = xstrdup(license_src->name); |
| license_dest->used = license_src->used; |
| list_push(lic_list, license_dest); |
| } |
| list_iterator_destroy(iter); |
| return lic_list; |
| } |
| |
| static slurmctld_resv_t *_copy_resv(slurmctld_resv_t *resv_orig_ptr) |
| { |
| slurmctld_resv_t *resv_copy_ptr; |
| int i; |
| |
| xassert(resv_orig_ptr->magic == RESV_MAGIC); |
| resv_copy_ptr = xmalloc(sizeof(slurmctld_resv_t)); |
| resv_copy_ptr->accounts = xstrdup(resv_orig_ptr->accounts); |
| resv_copy_ptr->account_cnt = resv_orig_ptr->account_cnt; |
| resv_copy_ptr->account_list = xmalloc(sizeof(char *) * |
| resv_orig_ptr->account_cnt); |
| for (i=0; i<resv_copy_ptr->account_cnt; i++) { |
| resv_copy_ptr->account_list[i] = |
| xstrdup(resv_orig_ptr->account_list[i]); |
| } |
| resv_copy_ptr->assoc_list = xstrdup(resv_orig_ptr->assoc_list); |
| resv_copy_ptr->cpu_cnt = resv_orig_ptr->cpu_cnt; |
| resv_copy_ptr->end_time = resv_orig_ptr->end_time; |
| resv_copy_ptr->features = xstrdup(resv_orig_ptr->features); |
| resv_copy_ptr->flags = resv_orig_ptr->flags; |
| resv_copy_ptr->job_pend_cnt = resv_orig_ptr->job_pend_cnt; |
| resv_copy_ptr->job_run_cnt = resv_orig_ptr->job_run_cnt; |
| resv_copy_ptr->licenses = xstrdup(resv_orig_ptr->licenses); |
| resv_copy_ptr->license_list = _list_dup(resv_orig_ptr-> |
| license_list); |
| resv_copy_ptr->magic = resv_orig_ptr->magic; |
| resv_copy_ptr->name = xstrdup(resv_orig_ptr->name); |
| resv_copy_ptr->node_bitmap = bit_copy(resv_orig_ptr->node_bitmap); |
| resv_copy_ptr->node_cnt = resv_orig_ptr->node_cnt; |
| resv_copy_ptr->node_list = xstrdup(resv_orig_ptr->node_list); |
| resv_copy_ptr->partition = xstrdup(resv_orig_ptr->partition); |
| resv_copy_ptr->part_ptr = resv_orig_ptr->part_ptr; |
| resv_copy_ptr->resv_id = resv_orig_ptr->resv_id; |
| resv_copy_ptr->start_time = resv_orig_ptr->start_time; |
| resv_copy_ptr->start_time_first = resv_orig_ptr->start_time_first; |
| resv_copy_ptr->start_time_prev = resv_orig_ptr->start_time_prev; |
| resv_copy_ptr->users = xstrdup(resv_orig_ptr->users); |
| resv_copy_ptr->user_cnt = resv_orig_ptr->user_cnt; |
| resv_copy_ptr->user_list = xmalloc(sizeof(uid_t) * |
| resv_orig_ptr->user_cnt); |
| for (i=0; i<resv_copy_ptr->user_cnt; i++) |
| resv_copy_ptr->user_list[i] = resv_orig_ptr->user_list[i]; |
| |
| return resv_copy_ptr; |
| } |
| |
| /* Swaping the contents of two reservation records */ |
| static void _swap_resv(slurmctld_resv_t *resv_backup, |
| slurmctld_resv_t *resv_ptr) |
| { |
| resv_desc_msg_t *resv_copy_ptr; |
| |
| xassert(resv_backup->magic == RESV_MAGIC); |
| xassert(resv_ptr->magic == RESV_MAGIC); |
| resv_copy_ptr = xmalloc(sizeof(slurmctld_resv_t)); |
| memcpy(resv_copy_ptr, resv_backup, sizeof(slurmctld_resv_t)); |
| memcpy(resv_backup, resv_ptr, sizeof(slurmctld_resv_t)); |
| memcpy(resv_ptr, resv_copy_ptr, sizeof(slurmctld_resv_t)); |
| xfree(resv_copy_ptr); |
| } |
| |
| static void _del_resv_rec(void *x) |
| { |
| int i; |
| slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) x; |
| |
| if (resv_ptr) { |
| xassert(resv_ptr->magic == RESV_MAGIC); |
| resv_ptr->magic = 0; |
| xfree(resv_ptr->accounts); |
| for (i=0; i<resv_ptr->account_cnt; i++) |
| xfree(resv_ptr->account_list[i]); |
| xfree(resv_ptr->account_list); |
| xfree(resv_ptr->assoc_list); |
| xfree(resv_ptr->features); |
| if (resv_ptr->license_list) |
| list_destroy(resv_ptr->license_list); |
| xfree(resv_ptr->licenses); |
| xfree(resv_ptr->name); |
| FREE_NULL_BITMAP(resv_ptr->node_bitmap); |
| xfree(resv_ptr->node_list); |
| xfree(resv_ptr->partition); |
| xfree(resv_ptr->users); |
| xfree(resv_ptr->user_list); |
| xfree(resv_ptr); |
| } |
| } |
| |
| static int _find_resv_id(void *x, void *key) |
| { |
| slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) x; |
| uint32_t *resv_id = (uint32_t *) key; |
| |
| xassert(resv_ptr->magic == RESV_MAGIC); |
| |
| if (resv_ptr->resv_id != *resv_id) |
| return 0; |
| else |
| return 1; /* match */ |
| } |
| |
| static int _find_resv_name(void *x, void *key) |
| { |
| slurmctld_resv_t *resv_ptr = (slurmctld_resv_t *) x; |
| |
| xassert(resv_ptr->magic == RESV_MAGIC); |
| |
| if (strcmp(resv_ptr->name, (char *) key)) |
| return 0; |
| else |
| return 1; /* match */ |
| } |
| |
| static void _dump_resv_req(resv_desc_msg_t *resv_ptr, char *mode) |
| { |
| |
| char start_str[32] = "-1", end_str[32] = "-1", *flag_str = NULL; |
| int duration; |
| |
| if (!(slurm_get_debug_flags() & DEBUG_FLAG_RESERVATION)) |
| return; |
| |
| if (resv_ptr->start_time != (time_t) NO_VAL) { |
| slurm_make_time_str(&resv_ptr->start_time, |
| start_str, sizeof(start_str)); |
| } |
| if (resv_ptr->end_time != (time_t) NO_VAL) { |
| slurm_make_time_str(&resv_ptr->end_time, |
| end_str, sizeof(end_str)); |
| } |
| if (resv_ptr->flags != (uint16_t) NO_VAL) |
| flag_str = reservation_flags_string(resv_ptr->flags); |
| |
| if (resv_ptr->duration == NO_VAL) |
| duration = -1; |
| else |
| duration = resv_ptr->duration; |
| |
| info("%s: Name=%s StartTime=%s EndTime=%s Duration=%d " |
| "Flags=%s NodeCnt=%d NodeList=%s Features=%s " |
| "PartitionName=%s Users=%s Accounts=%s Licenses=%s", |
| mode, resv_ptr->name, start_str, end_str, duration, |
| flag_str, resv_ptr->node_cnt, resv_ptr->node_list, |
| resv_ptr->features, resv_ptr->partition, |
| resv_ptr->users, resv_ptr->accounts, resv_ptr->licenses); |
| |
| xfree(flag_str); |
| } |
| |
| static void _generate_resv_id(void) |
| { |
| while (1) { |
| if (top_suffix >= 9999) |
| top_suffix = 1; /* wrap around */ |
| else |
| top_suffix++; |
| if (!list_find_first(resv_list, _find_resv_id, &top_suffix)) |
| break; |
| } |
| } |
| |
| static void _generate_resv_name(resv_desc_msg_t *resv_ptr) |
| { |
| char *key, *name, *sep; |
| int len; |
| |
| /* Generate name prefix, based upon the first account |
| * name if provided otherwise first user name */ |
| if (resv_ptr->accounts && resv_ptr->accounts[0]) |
| key = resv_ptr->accounts; |
| else |
| key = resv_ptr->users; |
| sep = strchr(key, ','); |
| if (sep) |
| len = sep - key; |
| else |
| len = strlen(key); |
| name = xmalloc(len + 16); |
| strncpy(name, key, len); |
| |
| xstrfmtcat(name, "_%d", top_suffix); |
| len++; |
| |
| resv_ptr->name = name; |
| } |
| |
| /* Validate an account name */ |
| static bool _is_account_valid(char *account) |
| { |
| slurmdb_association_rec_t assoc_rec, *assoc_ptr; |
| |
| if (!(accounting_enforce & ACCOUNTING_ENFORCE_ASSOCS)) |
| return true; /* don't worry about account validity */ |
| |
| memset(&assoc_rec, 0, sizeof(slurmdb_association_rec_t)); |
| assoc_rec.uid = NO_VAL; |
| assoc_rec.acct = account; |
| |
| if (assoc_mgr_fill_in_assoc(acct_db_conn, &assoc_rec, |
| accounting_enforce, &assoc_ptr)) { |
| return false; |
| } |
| return true; |
| } |
| |
| static int _append_assoc_list(List assoc_list, slurmdb_association_rec_t *assoc) |
| { |
| int rc = ESLURM_INVALID_ACCOUNT; |
| slurmdb_association_rec_t *assoc_ptr = NULL; |
| if (assoc_mgr_fill_in_assoc( |
| acct_db_conn, assoc, |
| accounting_enforce, |
| &assoc_ptr)) { |
| if(accounting_enforce & ACCOUNTING_ENFORCE_ASSOCS) { |
| error("No association for user %u and account %s", |
| assoc->uid, assoc->acct); |
| } else { |
| verbose("No association for user %u and account %s", |
| assoc->uid, assoc->acct); |
| rc = SLURM_SUCCESS; |
| } |
| |
| } |
| if (assoc_ptr) { |
| list_append(assoc_list, assoc_ptr); |
| rc = SLURM_SUCCESS; |
| } |
| |
| return rc; |
| } |
| /* Set a association list based upon accounts and users */ |
| static int _set_assoc_list(slurmctld_resv_t *resv_ptr) |
| { |
| int rc = SLURM_SUCCESS, i = 0, j = 0; |
| List assoc_list = NULL; |
| slurmdb_association_rec_t assoc, *assoc_ptr = NULL; |
| |
| /* no need to do this if we can't ;) */ |
| if (!association_based_accounting) |
| return rc; |
| |
| assoc_list = list_create(NULL); |
| |
| memset(&assoc, 0, sizeof(slurmdb_association_rec_t)); |
| xfree(resv_ptr->assoc_list); |
| |
| if (resv_ptr->user_cnt) { |
| for (i=0; i < resv_ptr->user_cnt; i++) { |
| if (resv_ptr->account_cnt) { |
| for (j=0; j < resv_ptr->account_cnt; j++) { |
| memset(&assoc, 0, |
| sizeof(slurmdb_association_rec_t)); |
| assoc.uid = resv_ptr->user_list[i]; |
| assoc.acct = resv_ptr->account_list[j]; |
| rc = _append_assoc_list(assoc_list, |
| &assoc); |
| if (rc != SLURM_SUCCESS) |
| goto end_it; |
| } |
| } else { |
| memset(&assoc, 0, |
| sizeof(slurmdb_association_rec_t)); |
| assoc.uid = resv_ptr->user_list[i]; |
| rc = assoc_mgr_get_user_assocs( |
| acct_db_conn, &assoc, |
| accounting_enforce, assoc_list); |
| if (rc != SLURM_SUCCESS) { |
| rc = ESLURM_INVALID_ACCOUNT; |
| goto end_it; |
| } |
| } |
| } |
| } else if (resv_ptr->account_cnt) { |
| for (i=0; i < resv_ptr->account_cnt; i++) { |
| memset(&assoc, 0, |
| sizeof(slurmdb_association_rec_t)); |
| assoc.uid = (uint32_t)NO_VAL; |
| assoc.acct = resv_ptr->account_list[i]; |
| if ((rc = _append_assoc_list(assoc_list, &assoc)) |
| != SLURM_SUCCESS) { |
| goto end_it; |
| } |
| } |
| } else if(accounting_enforce & ACCOUNTING_ENFORCE_ASSOCS) { |
| error("We need at least 1 user or 1 account to " |
| "create a reservtion."); |
| rc = SLURM_ERROR; |
| } |
| |
| if(list_count(assoc_list)) { |
| ListIterator itr = list_iterator_create(assoc_list); |
| xfree(resv_ptr->assoc_list); /* clear for modify */ |
| while((assoc_ptr = list_next(itr))) { |
| if(resv_ptr->assoc_list) |
| xstrfmtcat(resv_ptr->assoc_list, "%u,", |
| assoc_ptr->id); |
| else |
| xstrfmtcat(resv_ptr->assoc_list, ",%u,", |
| assoc_ptr->id); |
| } |
| list_iterator_destroy(itr); |
| } |
| //info("list is '%s'", resv_ptr->assoc_list); |
| |
| end_it: |
| list_destroy(assoc_list); |
| return rc; |
| } |
| |
| /* Post reservation create */ |
| static int _post_resv_create(slurmctld_resv_t *resv_ptr) |
| { |
| int rc = SLURM_SUCCESS; |
| slurmdb_reservation_rec_t resv; |
| char temp_bit[BUF_SIZE]; |
| |
| memset(&resv, 0, sizeof(slurmdb_reservation_rec_t)); |
| |
| resv.assocs = resv_ptr->assoc_list; |
| resv.cluster = slurmctld_cluster_name; |
| resv.cpus = resv_ptr->cpu_cnt; |
| resv.flags = resv_ptr->flags; |
| resv.id = resv_ptr->resv_id; |
| resv.name = resv_ptr->name; |
| resv.nodes = resv_ptr->node_list; |
| if (resv_ptr->node_bitmap) { |
| resv.node_inx = bit_fmt(temp_bit, sizeof(temp_bit), |
| resv_ptr->node_bitmap); |
| } |
| |
| resv.time_end = resv_ptr->end_time; |
| resv.time_start = resv_ptr->start_time; |
| |
| rc = acct_storage_g_add_reservation(acct_db_conn, &resv); |
| |
| return rc; |
| } |
| |
| /* Note that a reservation has been deleted */ |
| static int _post_resv_delete(slurmctld_resv_t *resv_ptr) |
| { |
| int rc = SLURM_SUCCESS; |
| slurmdb_reservation_rec_t resv; |
| memset(&resv, 0, sizeof(slurmdb_reservation_rec_t)); |
| |
| resv.cluster = slurmctld_cluster_name; |
| resv.id = resv_ptr->resv_id; |
| resv.name = resv_ptr->name; |
| resv.time_start = resv_ptr->start_time; |
| /* This is just a time stamp here to delete if the reservation |
| * hasn't started yet so we don't get trash records in the |
| * database if said database isn't up right now */ |
| resv.time_start_prev = time(NULL); |
| rc = acct_storage_g_remove_reservation(acct_db_conn, &resv); |
| |
| return rc; |
| } |
| |
| /* Note that a reservation has been updated */ |
| static int _post_resv_update(slurmctld_resv_t *resv_ptr, |
| slurmctld_resv_t *old_resv_ptr) |
| { |
| int rc = SLURM_SUCCESS; |
| slurmdb_reservation_rec_t resv; |
| char temp_bit[BUF_SIZE]; |
| |
| memset(&resv, 0, sizeof(slurmdb_reservation_rec_t)); |
| |
| resv.cluster = slurmctld_cluster_name; |
| resv.id = resv_ptr->resv_id; |
| resv.time_end = resv_ptr->end_time; |
| |
| if(!old_resv_ptr) { |
| resv.assocs = resv_ptr->assoc_list; |
| resv.cpus = resv_ptr->cpu_cnt; |
| resv.flags = resv_ptr->flags; |
| resv.nodes = resv_ptr->node_list; |
| } else { |
| time_t now = time(NULL); |
| |
| if(old_resv_ptr->assoc_list && resv_ptr->assoc_list) { |
| if(strcmp(old_resv_ptr->assoc_list, |
| resv_ptr->assoc_list)) |
| resv.assocs = resv_ptr->assoc_list; |
| } else if(resv_ptr->assoc_list) |
| resv.assocs = resv_ptr->assoc_list; |
| |
| if(old_resv_ptr->cpu_cnt != resv_ptr->cpu_cnt) |
| resv.cpus = resv_ptr->cpu_cnt; |
| else |
| resv.cpus = (uint32_t)NO_VAL; |
| |
| if(old_resv_ptr->flags != resv_ptr->flags) |
| resv.flags = resv_ptr->flags; |
| else |
| resv.flags = (uint16_t)NO_VAL; |
| |
| if(old_resv_ptr->node_list && resv_ptr->node_list) { |
| if(strcmp(old_resv_ptr->node_list, |
| resv_ptr->node_list)) |
| resv.nodes = resv_ptr->node_list; |
| } else if(resv_ptr->node_list) |
| resv.nodes = resv_ptr->node_list; |
| |
| /* Here if the reservation has started already we need |
| * to mark a new start time for it if certain |
| * variables are needed in accounting. Right now if |
| * the assocs, nodes, flags or cpu count changes we need a |
| * new start time of now. */ |
| if((resv_ptr->start_time < now) |
| && (resv.assocs |
| || resv.nodes |
| || (resv.flags != (uint16_t)NO_VAL) |
| || (resv.cpus != (uint32_t)NO_VAL))) { |
| resv_ptr->start_time_prev = resv_ptr->start_time; |
| resv_ptr->start_time = now; |
| } |
| } |
| /* now set the (maybe new) start_times */ |
| resv.time_start = resv_ptr->start_time; |
| resv.time_start_prev = resv_ptr->start_time_prev; |
| |
| if (resv.nodes && resv_ptr->node_bitmap) { |
| resv.node_inx = bit_fmt(temp_bit, sizeof(temp_bit), |
| resv_ptr->node_bitmap); |
| } |
| |
| rc = acct_storage_g_modify_reservation(acct_db_conn, &resv); |
| |
| return rc; |
| } |
| |
| /* |
| * Validate a comma delimited list of account names and build an array of |
| * them |
| * IN account - a list of account names |
| * OUT account_cnt - number of accounts in the list |
| * OUT account_list - list of the account names, |
| * CALLER MUST XFREE this plus each individual record |
| * RETURN 0 on success |
| */ |
| static int _build_account_list(char *accounts, int *account_cnt, |
| char ***account_list) |
| { |
| char *last = NULL, *tmp, *tok; |
| int ac_cnt = 0, i; |
| char **ac_list; |
| |
| *account_cnt = 0; |
| *account_list = (char **) NULL; |
| |
| if (!accounts) |
| return ESLURM_INVALID_ACCOUNT; |
| |
| i = strlen(accounts); |
| ac_list = xmalloc(sizeof(char *) * (i + 2)); |
| tmp = xstrdup(accounts); |
| tok = strtok_r(tmp, ",", &last); |
| while (tok) { |
| if (!_is_account_valid(tok)) { |
| info("Reservation request has invalid account %s", |
| tok); |
| goto inval; |
| } |
| ac_list[ac_cnt++] = xstrdup(tok); |
| tok = strtok_r(NULL, ",", &last); |
| } |
| *account_cnt = ac_cnt; |
| *account_list = ac_list; |
| xfree(tmp); |
| return SLURM_SUCCESS; |
| |
| inval: for (i=0; i<ac_cnt; i++) |
| xfree(ac_list[i]); |
| xfree(ac_list); |
| xfree(tmp); |
| return ESLURM_INVALID_ACCOUNT; |
| } |
| |
| /* |
| * Update a account list for an existing reservation based upon an |
| * update comma delimited specification of accounts to add (+name), |
| * remove (-name), or set value of |
| * IN/OUT resv_ptr - pointer to reservation structure being updated |
| * IN accounts - a list of account names, to set, add, or remove |
| * RETURN 0 on success |
| */ |
| static int _update_account_list(slurmctld_resv_t *resv_ptr, |
| char *accounts) |
| { |
| char *last = NULL, *ac_cpy, *tok; |
| int ac_cnt = 0, i, j, k; |
| int *ac_type, minus_account = 0, plus_account = 0; |
| char **ac_list; |
| bool found_it; |
| |
| if (!accounts) |
| return ESLURM_INVALID_ACCOUNT; |
| |
| i = strlen(accounts); |
| ac_list = xmalloc(sizeof(char *) * (i + 2)); |
| ac_type = xmalloc(sizeof(int) * (i + 2)); |
| ac_cpy = xstrdup(accounts); |
| tok = strtok_r(ac_cpy, ",", &last); |
| while (tok) { |
| if (tok[0] == '-') { |
| ac_type[ac_cnt] = 1; /* minus */ |
| minus_account = 1; |
| tok++; |
| } else if (tok[0] == '+') { |
| ac_type[ac_cnt] = 2; /* plus */ |
| plus_account = 1; |
| tok++; |
| } else if (tok[0] == '\0') { |
| continue; |
| } else if (plus_account || minus_account) { |
| info("Reservation account expression invalid %s", |
| accounts); |
| goto inval; |
| } else |
| ac_type[ac_cnt] = 3; /* set */ |
| if (!_is_account_valid(tok)) { |
| info("Reservation request has invalid account %s", |
| tok); |
| goto inval; |
| } |
| ac_list[ac_cnt++] = xstrdup(tok); |
| tok = strtok_r(NULL, ",", &last); |
| } |
| |
| if ((plus_account == 0) && (minus_account == 0)) { |
| /* Just a reset of account list */ |
| xfree(resv_ptr->accounts); |
| if (accounts[0] != '\0') |
| resv_ptr->accounts = xstrdup(accounts); |
| xfree(resv_ptr->account_list); |
| resv_ptr->account_list = ac_list; |
| resv_ptr->account_cnt = ac_cnt; |
| xfree(ac_cpy); |
| xfree(ac_type); |
| return SLURM_SUCCESS; |
| } |
| |
| /* Modification of existing account list */ |
| if (minus_account) { |
| if (resv_ptr->account_cnt == 0) |
| goto inval; |
| for (i=0; i<ac_cnt; i++) { |
| if (ac_type[i] != 1) |
| continue; |
| found_it = false; |
| for (j=0; j<resv_ptr->account_cnt; j++) { |
| if (strcmp(resv_ptr->account_list[j], |
| ac_list[i])) { |
| continue; |
| } |
| found_it = true; |
| xfree(resv_ptr->account_list[j]); |
| resv_ptr->account_cnt--; |
| for (k=j; k<resv_ptr->account_cnt; k++) { |
| resv_ptr->account_list[k] = |
| resv_ptr->account_list[k+1]; |
| } |
| break; |
| } |
| if (!found_it) |
| goto inval; |
| } |
| xfree(resv_ptr->accounts); |
| for (i=0; i<resv_ptr->account_cnt; i++) { |
| if (i == 0) { |
| resv_ptr->accounts = xstrdup(resv_ptr-> |
| account_list[i]); |
| } else { |
| xstrcat(resv_ptr->accounts, ","); |
| xstrcat(resv_ptr->accounts, |
| resv_ptr->account_list[i]); |
| } |
| } |
| } |
| |
| if (plus_account) { |
| for (i=0; i<ac_cnt; i++) { |
| if (ac_type[i] != 2) |
| continue; |
| found_it = false; |
| for (j=0; j<resv_ptr->account_cnt; j++) { |
| if (strcmp(resv_ptr->account_list[j], |
| ac_list[i])) { |
| continue; |
| } |
| found_it = true; |
| break; |
| } |
| if (found_it) |
| continue; /* duplicate entry */ |
| xrealloc(resv_ptr->account_list, |
| sizeof(char *) * (resv_ptr->account_cnt + 1)); |
| resv_ptr->account_list[resv_ptr->account_cnt++] = |
| xstrdup(ac_list[i]); |
| } |
| xfree(resv_ptr->accounts); |
| for (i=0; i<resv_ptr->account_cnt; i++) { |
| if (i == 0) { |
| resv_ptr->accounts = xstrdup(resv_ptr-> |
| account_list[i]); |
| } else { |
| xstrcat(resv_ptr->accounts, ","); |
| xstrcat(resv_ptr->accounts, |
| resv_ptr->account_list[i]); |
| } |
| } |
| } |
| |
| for (i=0; i<ac_cnt; i++) |
| xfree(ac_list[i]); |
| xfree(ac_list); |
| xfree(ac_type); |
| xfree(ac_cpy); |
| return SLURM_SUCCESS; |
| |
| inval: for (i=0; i<ac_cnt; i++) |
| xfree(ac_list[i]); |
| xfree(ac_list); |
| xfree(ac_type); |
| xfree(ac_cpy); |
| return ESLURM_INVALID_ACCOUNT; |
| } |
| |
| /* |
| * Validate a comma delimited list of user names and build an array of |
| * their UIDs |
| * IN users - a list of user names |
| * OUT user_cnt - number of UIDs in the list |
| * OUT user_list - list of the user's uid, CALLER MUST XFREE; |
| * RETURN 0 on success |
| */ |
| static int _build_uid_list(char *users, int *user_cnt, uid_t **user_list) |
| { |
| char *last = NULL, *tmp = NULL, *tok; |
| int u_cnt = 0, i; |
| uid_t *u_list, u_tmp; |
| |
| *user_cnt = 0; |
| *user_list = (uid_t *) NULL; |
| |
| if (!users) |
| return ESLURM_USER_ID_MISSING; |
| |
| i = strlen(users); |
| u_list = xmalloc(sizeof(uid_t) * (i + 2)); |
| tmp = xstrdup(users); |
| tok = strtok_r(tmp, ",", &last); |
| while (tok) { |
| if (uid_from_string (tok, &u_tmp) < 0) { |
| info("Reservation request has invalid user %s", tok); |
| goto inval; |
| } |
| u_list[u_cnt++] = u_tmp; |
| tok = strtok_r(NULL, ",", &last); |
| } |
| *user_cnt = u_cnt; |
| *user_list = u_list; |
| xfree(tmp); |
| return SLURM_SUCCESS; |
| |
| inval: xfree(tmp); |
| xfree(u_list); |
| return ESLURM_USER_ID_MISSING; |
| } |
| |
| /* |
| * Update a user/uid list for an existing reservation based upon an |
| * update comma delimited specification of users to add (+name), |
| * remove (-name), or set value of |
| * IN/OUT resv_ptr - pointer to reservation structure being updated |
| * IN users - a list of user names, to set, add, or remove |
| * RETURN 0 on success |
| */ |
| static int _update_uid_list(slurmctld_resv_t *resv_ptr, char *users) |
| { |
| char *last = NULL, *u_cpy = NULL, *tmp = NULL, *tok; |
| int u_cnt = 0, i, j, k; |
| uid_t *u_list, u_tmp; |
| int *u_type, minus_user = 0, plus_user = 0; |
| char **u_name; |
| bool found_it; |
| |
| if (!users) |
| return ESLURM_USER_ID_MISSING; |
| |
| /* Parse the incoming user expression */ |
| i = strlen(users); |
| u_list = xmalloc(sizeof(uid_t) * (i + 2)); |
| u_name = xmalloc(sizeof(char *) * (i + 2)); |
| u_type = xmalloc(sizeof(int) * (i + 2)); |
| u_cpy = xstrdup(users); |
| tok = strtok_r(u_cpy, ",", &last); |
| while (tok) { |
| if (tok[0] == '-') { |
| u_type[u_cnt] = 1; /* minus */ |
| minus_user = 1; |
| tok++; |
| } else if (tok[0] == '+') { |
| u_type[u_cnt] = 2; /* plus */ |
| plus_user = 1; |
| tok++; |
| } else if (tok[0] == '\0') { |
| continue; |
| } else if (plus_user || minus_user) { |
| info("Reservation user expression invalid %s", users); |
| goto inval; |
| } else |
| u_type[u_cnt] = 3; /* set */ |
| |
| if (uid_from_string (tok, &u_tmp) < 0) { |
| info("Reservation request has invalid user %s", tok); |
| goto inval; |
| } |
| |
| u_name[u_cnt] = tok; |
| u_list[u_cnt++] = u_tmp; |
| tok = strtok_r(NULL, ",", &last); |
| } |
| |
| if ((plus_user == 0) && (minus_user == 0)) { |
| /* Just a reset of user list */ |
| xfree(resv_ptr->users); |
| xfree(resv_ptr->user_list); |
| if (users[0] != '\0') |
| resv_ptr->users = xstrdup(users); |
| resv_ptr->user_cnt = u_cnt; |
| resv_ptr->user_list = u_list; |
| xfree(u_cpy); |
| xfree(u_name); |
| xfree(u_type); |
| return SLURM_SUCCESS; |
| } |
| |
| /* Modification of existing user list */ |
| if (minus_user) { |
| for (i=0; i<u_cnt; i++) { |
| if (u_type[i] != 1) |
| continue; |
| found_it = false; |
| for (j=0; j<resv_ptr->user_cnt; j++) { |
| if (resv_ptr->user_list[j] != u_list[i]) |
| continue; |
| found_it = true; |
| resv_ptr->user_cnt--; |
| for (k=j; k<resv_ptr->user_cnt; k++) { |
| resv_ptr->user_list[k] = |
| resv_ptr->user_list[k+1]; |
| } |
| break; |
| } |
| if (!found_it) |
| goto inval; |
| /* Now we need to remove from users string */ |
| k = strlen(u_name[i]); |
| tmp = resv_ptr->users; |
| while ((tok = strstr(tmp, u_name[i]))) { |
| if (((tok != resv_ptr->users) && |
| (tok[-1] != ',')) || |
| ((tok[k] != '\0') && (tok[k] != ','))) { |
| tmp = tok + 1; |
| continue; |
| } |
| if (tok[-1] == ',') { |
| tok--; |
| k++; |
| } else if (tok[k] == ',') |
| k++; |
| for (j=0; ; j++) { |
| tok[j] = tok[j+k]; |
| if (tok[j] == '\0') |
| break; |
| } |
| } |
| } |
| } |
| |
| if (plus_user) { |
| for (i=0; i<u_cnt; i++) { |
| if (u_type[i] != 2) |
| continue; |
| found_it = false; |
| for (j=0; j<resv_ptr->user_cnt; j++) { |
| if (resv_ptr->user_list[j] != u_list[i]) |
| continue; |
| found_it = true; |
| break; |
| } |
| if (found_it) |
| continue; /* duplicate entry */ |
| if (resv_ptr->users && resv_ptr->users[0]) |
| xstrcat(resv_ptr->users, ","); |
| xstrcat(resv_ptr->users, u_name[i]); |
| xrealloc(resv_ptr->user_list, |
| sizeof(uid_t) * (resv_ptr->user_cnt + 1)); |
| resv_ptr->user_list[resv_ptr->user_cnt++] = |
| u_list[i]; |
| } |
| } |
| xfree(u_cpy); |
| xfree(u_list); |
| xfree(u_name); |
| xfree(u_type); |
| return SLURM_SUCCESS; |
| |
| inval: xfree(u_cpy); |
| xfree(u_list); |
| xfree(u_name); |
| xfree(u_type); |
| return ESLURM_USER_ID_MISSING; |
| } |
| |
| /* |
| * _pack_resv - dump configuration information about a specific reservation |
| * in machine independent form (for network transmission or state save) |
| * IN resv_ptr - pointer to reservation for which information is requested |
| * IN/OUT buffer - buffer in which data is placed, pointers automatically |
| * updated |
| * IN internal - true if for internal save state, false for xmit to users |
| * NOTE: if you make any changes here be sure to make the corresponding |
| * to _unpack_reserve_info_members() in common/slurm_protocol_pack.c |
| * plus load_all_resv_state() below. |
| */ |
| static void _pack_resv(slurmctld_resv_t *resv_ptr, Buf buffer, |
| bool internal) |
| { |
| #ifdef HAVE_BG |
| uint32_t cnode_cnt; |
| #endif |
| |
| packstr(resv_ptr->accounts, buffer); |
| pack_time(resv_ptr->end_time, buffer); |
| packstr(resv_ptr->features, buffer); |
| packstr(resv_ptr->licenses, buffer); |
| packstr(resv_ptr->name, buffer); |
| #ifdef HAVE_BG |
| if (!cnodes_per_bp) { |
| select_g_alter_node_cnt(SELECT_GET_NODE_SCALING, |
| &cnodes_per_bp); |
| } |
| cnode_cnt = resv_ptr->node_cnt; |
| if (cnodes_per_bp && !internal) |
| cnode_cnt *= cnodes_per_bp; |
| pack32(cnode_cnt, buffer); |
| #else |
| pack32(resv_ptr->node_cnt, buffer); |
| #endif |
| packstr(resv_ptr->node_list, buffer); |
| packstr(resv_ptr->partition, buffer); |
| pack_time(resv_ptr->start_time_first, buffer); |
| pack16(resv_ptr->flags, buffer); |
| packstr(resv_ptr->users, buffer); |
| |
| if (internal) { |
| packstr(resv_ptr->assoc_list, buffer); |
| pack32(resv_ptr->cpu_cnt, buffer); |
| pack32(resv_ptr->resv_id, buffer); |
| pack_time(resv_ptr->start_time_prev, buffer); |
| pack_time(resv_ptr->start_time, buffer); |
| pack32(resv_ptr->duration, buffer); |
| } else { |
| pack_bit_fmt(resv_ptr->node_bitmap, buffer); |
| } |
| } |
| |
| /* |
| * Test if a new/updated reservation request will overlap running jobs |
| * RET true if overlap |
| */ |
| static bool _job_overlap(time_t start_time, uint16_t flags, |
| bitstr_t *node_bitmap) |
| { |
| ListIterator job_iterator; |
| struct job_record *job_ptr; |
| bool overlap = false; |
| |
| if (flags & RESERVE_FLAG_IGN_JOBS) /* ignore job overlap */ |
| return overlap; |
| |
| job_iterator = list_iterator_create(job_list); |
| while ((job_ptr = (struct job_record *) list_next(job_iterator))) { |
| if (IS_JOB_RUNNING(job_ptr) && |
| (job_ptr->end_time > start_time) && |
| (bit_overlap(job_ptr->node_bitmap, node_bitmap) > 0)) { |
| overlap = true; |
| break; |
| } |
| } |
| list_iterator_destroy(job_iterator); |
| |
| return overlap; |
| } |
| |
| /* |
| * Test if a new/updated reservation request overlaps an existing |
| * reservation |
| * RET true if overlap |
| */ |
| static bool _resv_overlap(time_t start_time, time_t end_time, |
| uint16_t flags, bitstr_t *node_bitmap, |
| slurmctld_resv_t *this_resv_ptr) |
| { |
| ListIterator iter; |
| slurmctld_resv_t *resv_ptr; |
| bool rc = false; |
| int i, j; |
| time_t s_time1, s_time2, e_time1, e_time2; |
| |
| if ((flags & RESERVE_FLAG_MAINT) || |
| (flags & RESERVE_FLAG_OVERLAP) || |
| (!node_bitmap)) |
| return rc; |
| |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| |
| while ((resv_ptr = (slurmctld_resv_t *) list_next(iter))) { |
| if (resv_ptr == this_resv_ptr) |
| continue; /* skip self */ |
| if (resv_ptr->node_bitmap == NULL) |
| continue; /* no specific nodes in reservation */ |
| if (!bit_overlap(resv_ptr->node_bitmap, node_bitmap)) |
| continue; /* no overlap */ |
| |
| for (i=0; ((i<7) && (!rc)); i++) { /* look forward one week */ |
| s_time1 = start_time; |
| e_time1 = end_time; |
| _advance_time(&s_time1, i); |
| _advance_time(&e_time1, i); |
| for (j=0; ((j<7) && (!rc)); j++) { |
| s_time2 = resv_ptr->start_time; |
| e_time2 = resv_ptr->end_time; |
| _advance_time(&s_time2, j); |
| _advance_time(&e_time2, j); |
| if ((s_time1 < e_time2) && |
| (e_time1 > s_time2)) { |
| verbose("Reservation overlap with %s", |
| resv_ptr->name); |
| rc = true; |
| break; |
| } |
| if (!(resv_ptr->flags & RESERVE_FLAG_DAILY)) |
| break; |
| } |
| if ((flags & RESERVE_FLAG_DAILY) == 0) |
| break; |
| } |
| } |
| list_iterator_destroy(iter); |
| |
| return rc; |
| } |
| |
| /* Set a reservation's CPU count. Requires that the reservation's |
| * node_bitmap be set. */ |
| static void _set_cpu_cnt(slurmctld_resv_t *resv_ptr) |
| { |
| int i; |
| uint32_t cpu_cnt = 0; |
| struct node_record *node_ptr = node_record_table_ptr; |
| |
| if (!resv_ptr->node_bitmap) |
| return; |
| |
| for (i=0; i<node_record_count; i++, node_ptr++) { |
| if (!bit_test(resv_ptr->node_bitmap, i)) |
| continue; |
| if (slurmctld_conf.fast_schedule) |
| cpu_cnt += node_ptr->config_ptr->cpus; |
| else |
| cpu_cnt += node_ptr->cpus; |
| } |
| resv_ptr->cpu_cnt = cpu_cnt; |
| } |
| |
| /* Create a resource reservation */ |
| extern int create_resv(resv_desc_msg_t *resv_desc_ptr) |
| { |
| int i, rc = SLURM_SUCCESS; |
| time_t now = time(NULL); |
| struct part_record *part_ptr = NULL; |
| bitstr_t *node_bitmap = NULL; |
| slurmctld_resv_t *resv_ptr; |
| int account_cnt = 0, user_cnt = 0; |
| char **account_list = NULL; |
| uid_t *user_list = NULL; |
| char start_time[32], end_time[32]; |
| List license_list = (List) NULL; |
| char *name1, *name2, *val1, *val2; |
| |
| if (!resv_list) |
| resv_list = list_create(_del_resv_rec); |
| _dump_resv_req(resv_desc_ptr, "create_resv"); |
| |
| /* Validate the request */ |
| if (resv_desc_ptr->start_time != (time_t) NO_VAL) { |
| if (resv_desc_ptr->start_time < (now - 60)) { |
| info("Reservation request has invalid start time"); |
| rc = ESLURM_INVALID_TIME_VALUE; |
| goto bad_parse; |
| } |
| } else |
| resv_desc_ptr->start_time = now; |
| |
| if (resv_desc_ptr->end_time != (time_t) NO_VAL) { |
| if (resv_desc_ptr->end_time < (now - 60)) { |
| info("Reservation request has invalid end time"); |
| rc = ESLURM_INVALID_TIME_VALUE; |
| goto bad_parse; |
| } |
| } else if (resv_desc_ptr->duration == INFINITE) { |
| resv_desc_ptr->end_time = resv_desc_ptr->start_time + ONE_YEAR; |
| } else if (resv_desc_ptr->duration) { |
| resv_desc_ptr->end_time = resv_desc_ptr->start_time + |
| (resv_desc_ptr->duration * 60); |
| } else |
| resv_desc_ptr->end_time = INFINITE; |
| if (resv_desc_ptr->flags == (uint16_t) NO_VAL) |
| resv_desc_ptr->flags = 0; |
| else { |
| resv_desc_ptr->flags &= RESERVE_FLAG_MAINT | |
| RESERVE_FLAG_OVERLAP | |
| RESERVE_FLAG_IGN_JOBS | |
| RESERVE_FLAG_DAILY | |
| RESERVE_FLAG_WEEKLY; |
| } |
| if (resv_desc_ptr->partition) { |
| part_ptr = find_part_record(resv_desc_ptr->partition); |
| if (!part_ptr) { |
| info("Reservation request has invalid partition %s", |
| resv_desc_ptr->partition); |
| rc = ESLURM_INVALID_PARTITION_NAME; |
| goto bad_parse; |
| } |
| } |
| if ((resv_desc_ptr->accounts == NULL) && |
| (resv_desc_ptr->users == NULL)) { |
| info("Reservation request lacks users or accounts"); |
| rc = ESLURM_INVALID_ACCOUNT; |
| goto bad_parse; |
| } |
| if (resv_desc_ptr->accounts) { |
| rc = _build_account_list(resv_desc_ptr->accounts, |
| &account_cnt, &account_list); |
| if (rc) |
| goto bad_parse; |
| } |
| if (resv_desc_ptr->users) { |
| rc = _build_uid_list(resv_desc_ptr->users, |
| &user_cnt, &user_list); |
| if (rc) |
| goto bad_parse; |
| } |
| if (resv_desc_ptr->licenses) { |
| bool valid; |
| license_list = license_validate(resv_desc_ptr->licenses, |
| &valid); |
| if (!valid) { |
| info("Reservation request has invalid licenses %s", |
| resv_desc_ptr->licenses); |
| rc = ESLURM_INVALID_LICENSES; |
| goto bad_parse; |
| } |
| if ((resv_desc_ptr->node_cnt == NO_VAL) && |
| (resv_desc_ptr->node_list == NULL)) |
| resv_desc_ptr->node_cnt = 0; |
| } |
| |
| #ifdef HAVE_BG |
| if (!cnodes_per_bp) { |
| select_g_alter_node_cnt(SELECT_GET_NODE_SCALING, |
| &cnodes_per_bp); |
| } |
| if ((resv_desc_ptr->node_cnt != NO_VAL) && cnodes_per_bp) { |
| /* Convert c-node count to midplane count */ |
| resv_desc_ptr->node_cnt = (resv_desc_ptr->node_cnt + |
| cnodes_per_bp - 1) / cnodes_per_bp; |
| } |
| #endif |
| |
| if (resv_desc_ptr->node_list) { |
| resv_desc_ptr->flags |= RESERVE_FLAG_SPEC_NODES; |
| if (strcasecmp(resv_desc_ptr->node_list, "ALL") == 0) { |
| node_bitmap = bit_alloc(node_record_count); |
| bit_nset(node_bitmap, 0, (node_record_count - 1)); |
| xfree(resv_desc_ptr->node_list); |
| resv_desc_ptr->node_list = |
| bitmap2node_name(node_bitmap); |
| } else if (node_name2bitmap(resv_desc_ptr->node_list, |
| false, &node_bitmap)) { |
| rc = ESLURM_INVALID_NODE_NAME; |
| goto bad_parse; |
| } |
| if (resv_desc_ptr->node_cnt == NO_VAL) |
| resv_desc_ptr->node_cnt = 0; |
| if (!(resv_desc_ptr->flags & RESERVE_FLAG_OVERLAP) && |
| _resv_overlap(resv_desc_ptr->start_time, |
| resv_desc_ptr->end_time, |
| resv_desc_ptr->flags, node_bitmap, |
| NULL)) { |
| info("Reservation request overlaps another"); |
| rc = ESLURM_RESERVATION_OVERLAP; |
| goto bad_parse; |
| } |
| resv_desc_ptr->node_cnt = bit_set_count(node_bitmap); |
| if (!(resv_desc_ptr->flags & RESERVE_FLAG_IGN_JOBS) && |
| _job_overlap(resv_desc_ptr->start_time, |
| resv_desc_ptr->flags, node_bitmap)) { |
| info("Reservation request overlaps jobs"); |
| rc = ESLURM_NODES_BUSY; |
| goto bad_parse; |
| } |
| } else if (resv_desc_ptr->node_cnt == NO_VAL) { |
| info("Reservation request lacks node specification"); |
| rc = ESLURM_INVALID_NODE_NAME; |
| goto bad_parse; |
| } else if ((rc = _select_nodes(resv_desc_ptr, &part_ptr, &node_bitmap)) |
| != SLURM_SUCCESS) { |
| goto bad_parse; |
| } |
| |
| _generate_resv_id(); |
| if (resv_desc_ptr->name) { |
| resv_ptr = (slurmctld_resv_t *) list_find_first (resv_list, |
| _find_resv_name, resv_desc_ptr->name); |
| if (resv_ptr) { |
| info("Reservation request name duplication (%s)", |
| resv_desc_ptr->name); |
| rc = ESLURM_RESERVATION_INVALID; |
| goto bad_parse; |
| } |
| } else { |
| while (1) { |
| _generate_resv_name(resv_desc_ptr); |
| resv_ptr = (slurmctld_resv_t *) |
| list_find_first (resv_list, |
| _find_resv_name, resv_desc_ptr->name); |
| if (!resv_ptr) |
| break; |
| _generate_resv_id(); /* makes new suffix */ |
| /* Same as previously created name, retry */ |
| } |
| } |
| |
| /* Create a new reservation record */ |
| resv_ptr = xmalloc(sizeof(slurmctld_resv_t)); |
| resv_ptr->accounts = resv_desc_ptr->accounts; |
| resv_desc_ptr->accounts = NULL; /* Nothing left to free */ |
| resv_ptr->account_cnt = account_cnt; |
| resv_ptr->account_list = account_list; |
| resv_ptr->duration = resv_desc_ptr->duration; |
| resv_ptr->end_time = resv_desc_ptr->end_time; |
| resv_ptr->features = resv_desc_ptr->features; |
| resv_desc_ptr->features = NULL; /* Nothing left to free */ |
| resv_ptr->licenses = resv_desc_ptr->licenses; |
| resv_desc_ptr->licenses = NULL; /* Nothing left to free */ |
| resv_ptr->license_list = license_list; |
| resv_ptr->resv_id = top_suffix; |
| xassert(resv_ptr->magic = RESV_MAGIC); /* Sets value */ |
| resv_ptr->name = xstrdup(resv_desc_ptr->name); |
| resv_ptr->node_cnt = resv_desc_ptr->node_cnt; |
| resv_ptr->node_list = resv_desc_ptr->node_list; |
| resv_desc_ptr->node_list = NULL; /* Nothing left to free */ |
| resv_ptr->node_bitmap = node_bitmap; /* May be unset */ |
| resv_ptr->partition = resv_desc_ptr->partition; |
| resv_desc_ptr->partition = NULL; /* Nothing left to free */ |
| resv_ptr->part_ptr = part_ptr; |
| resv_ptr->start_time = resv_desc_ptr->start_time; |
| resv_ptr->start_time_first = resv_ptr->start_time; |
| resv_ptr->start_time_prev = resv_ptr->start_time; |
| resv_ptr->flags = resv_desc_ptr->flags; |
| resv_ptr->users = resv_desc_ptr->users; |
| resv_ptr->user_cnt = user_cnt; |
| resv_ptr->user_list = user_list; |
| resv_desc_ptr->users = NULL; /* Nothing left to free */ |
| _set_cpu_cnt(resv_ptr); |
| if((rc = _set_assoc_list(resv_ptr)) != SLURM_SUCCESS) |
| goto bad_parse; |
| |
| /* This needs to be done after all other setup is done. */ |
| _post_resv_create(resv_ptr); |
| |
| slurm_make_time_str(&resv_ptr->start_time, start_time, |
| sizeof(start_time)); |
| slurm_make_time_str(&resv_ptr->end_time, end_time, sizeof(end_time)); |
| if (resv_ptr->accounts) { |
| name1 = " accounts="; |
| val1 = resv_ptr->accounts; |
| } else |
| name1 = val1 = ""; |
| if (resv_ptr->users) { |
| name2 = " users="; |
| val2 = resv_ptr->users; |
| } else |
| name2 = val2 = ""; |
| info("sched: Created reservation %s%s%s%s%s nodes=%s start=%s end=%s", |
| resv_ptr->name, name1, val1, name2, val2, |
| resv_ptr->node_list, start_time, end_time); |
| list_append(resv_list, resv_ptr); |
| last_resv_update = now; |
| schedule_resv_save(); |
| |
| return SLURM_SUCCESS; |
| |
| bad_parse: |
| for (i=0; i<account_cnt; i++) |
| xfree(account_list[i]); |
| xfree(account_list); |
| if (license_list) |
| list_destroy(license_list); |
| FREE_NULL_BITMAP(node_bitmap); |
| xfree(user_list); |
| return rc; |
| } |
| |
| /* Purge all reservation data structures */ |
| extern void resv_fini(void) |
| { |
| if (resv_list) { |
| list_destroy(resv_list); |
| resv_list = (List) NULL; |
| } |
| } |
| |
| /* Update an exiting resource reservation */ |
| extern int update_resv(resv_desc_msg_t *resv_desc_ptr) |
| { |
| time_t now = time(NULL); |
| slurmctld_resv_t *resv_backup, *resv_ptr; |
| int error_code = SLURM_SUCCESS, rc; |
| char start_time[32], end_time[32]; |
| char *name1, *name2, *val1, *val2; |
| |
| if (!resv_list) |
| resv_list = list_create(_del_resv_rec); |
| _dump_resv_req(resv_desc_ptr, "update_resv"); |
| |
| /* Find the specified reservation */ |
| if ((resv_desc_ptr->name == NULL)) |
| return ESLURM_RESERVATION_INVALID; |
| resv_ptr = (slurmctld_resv_t *) list_find_first (resv_list, |
| _find_resv_name, resv_desc_ptr->name); |
| if (!resv_ptr) |
| return ESLURM_RESERVATION_INVALID; |
| |
| /* Make backup to restore state in case of failure */ |
| resv_backup = _copy_resv(resv_ptr); |
| |
| /* Process the request */ |
| if (resv_desc_ptr->flags != (uint16_t) NO_VAL) { |
| if (resv_desc_ptr->flags & RESERVE_FLAG_MAINT) |
| resv_ptr->flags |= RESERVE_FLAG_MAINT; |
| if (resv_desc_ptr->flags & RESERVE_FLAG_NO_MAINT) |
| resv_ptr->flags &= (~RESERVE_FLAG_MAINT); |
| if (resv_desc_ptr->flags & RESERVE_FLAG_OVERLAP) |
| resv_ptr->flags |= RESERVE_FLAG_OVERLAP; |
| if (resv_desc_ptr->flags & RESERVE_FLAG_IGN_JOBS) |
| resv_ptr->flags |= RESERVE_FLAG_IGN_JOBS; |
| if (resv_desc_ptr->flags & RESERVE_FLAG_NO_IGN_JOB) |
| resv_ptr->flags &= (~RESERVE_FLAG_IGN_JOBS); |
| if (resv_desc_ptr->flags & RESERVE_FLAG_DAILY) |
| resv_ptr->flags |= RESERVE_FLAG_DAILY; |
| if (resv_desc_ptr->flags & RESERVE_FLAG_NO_DAILY) |
| resv_ptr->flags &= (~RESERVE_FLAG_DAILY); |
| if (resv_desc_ptr->flags & RESERVE_FLAG_WEEKLY) |
| resv_ptr->flags |= RESERVE_FLAG_WEEKLY; |
| if (resv_desc_ptr->flags & RESERVE_FLAG_NO_WEEKLY) |
| resv_ptr->flags &= (~RESERVE_FLAG_WEEKLY); |
| } |
| if (resv_desc_ptr->partition && (resv_desc_ptr->partition[0] == '\0')){ |
| /* Clear the partition */ |
| xfree(resv_desc_ptr->partition); |
| xfree(resv_ptr->partition); |
| resv_ptr->part_ptr = NULL; |
| } |
| if (resv_desc_ptr->partition) { |
| struct part_record *part_ptr = NULL; |
| part_ptr = find_part_record(resv_desc_ptr->partition); |
| if (!part_ptr) { |
| info("Reservation request has invalid partition (%s)", |
| resv_desc_ptr->partition); |
| error_code = ESLURM_INVALID_PARTITION_NAME; |
| goto update_failure; |
| } |
| xfree(resv_ptr->partition); |
| resv_ptr->partition = resv_desc_ptr->partition; |
| resv_desc_ptr->partition = NULL; /* Nothing left to free */ |
| resv_ptr->part_ptr = part_ptr; |
| } |
| if (resv_desc_ptr->accounts) { |
| rc = _update_account_list(resv_ptr, resv_desc_ptr->accounts); |
| if (rc) { |
| error_code = rc; |
| goto update_failure; |
| } |
| } |
| if (resv_desc_ptr->licenses && (resv_desc_ptr->licenses[0] == '\0')) { |
| if ((resv_desc_ptr->node_cnt == 0) || |
| ((resv_desc_ptr->node_cnt == NO_VAL) && |
| (resv_ptr->node_cnt == 0))) { |
| info("Reservation attempt to clear licenses with " |
| "NodeCount=0"); |
| rc = ESLURM_INVALID_LICENSES; |
| goto update_failure; |
| } |
| xfree(resv_desc_ptr->licenses); /* clear licenses */ |
| xfree(resv_ptr->licenses); |
| if (resv_ptr->license_list) |
| list_destroy(resv_ptr->license_list); |
| } |
| |
| if (resv_desc_ptr->licenses) { |
| bool valid = true; |
| List license_list; |
| license_list = license_validate(resv_desc_ptr->licenses, |
| &valid); |
| if (!valid) { |
| info("Reservation invalid license update (%s)", |
| resv_desc_ptr->licenses); |
| error_code = ESLURM_INVALID_LICENSES; |
| goto update_failure; |
| } |
| xfree(resv_ptr->licenses); |
| resv_ptr->licenses = resv_desc_ptr->licenses; |
| resv_desc_ptr->licenses = NULL; /* Nothing left to free */ |
| if (resv_ptr->license_list) |
| list_destroy(resv_ptr->license_list); |
| resv_ptr->license_list = license_list; |
| } |
| if (resv_desc_ptr->features && (resv_desc_ptr->features[0] == '\0')) { |
| xfree(resv_desc_ptr->features); /* clear features */ |
| xfree(resv_ptr->features); |
| } |
| if (resv_desc_ptr->features) { |
| /* To support in the future, the reservation resources would |
| * need to be selected again. For now, administrator can |
| * delete this reservation and create a new one. */ |
| info("Attempt to change features of reservation %s", |
| resv_desc_ptr->name); |
| error_code = ESLURM_NOT_SUPPORTED; |
| goto update_failure; |
| } |
| if (resv_desc_ptr->users) { |
| rc = _update_uid_list(resv_ptr, resv_desc_ptr->users); |
| if (rc) { |
| error_code = rc; |
| goto update_failure; |
| } |
| } |
| if ((resv_ptr->users == NULL) && (resv_ptr->accounts == NULL)) { |
| info("Reservation request lacks users or accounts"); |
| error_code = ESLURM_INVALID_ACCOUNT; |
| goto update_failure; |
| } |
| |
| if (resv_desc_ptr->start_time != (time_t) NO_VAL) { |
| if (resv_desc_ptr->start_time < (now - 60)) { |
| info("Reservation request has invalid start time"); |
| error_code = ESLURM_INVALID_TIME_VALUE; |
| goto update_failure; |
| } |
| resv_ptr->start_time_prev = resv_ptr->start_time; |
| resv_ptr->start_time = resv_desc_ptr->start_time; |
| resv_ptr->start_time_first = resv_desc_ptr->start_time; |
| if(resv_ptr->duration) { |
| resv_ptr->end_time = resv_ptr->start_time_first + |
| (resv_ptr->duration * 60); |
| } |
| } |
| if (resv_desc_ptr->end_time != (time_t) NO_VAL) { |
| if (resv_desc_ptr->end_time < (now - 60)) { |
| info("Reservation request has invalid end time"); |
| error_code = ESLURM_INVALID_TIME_VALUE; |
| goto update_failure; |
| } |
| resv_ptr->end_time = resv_desc_ptr->end_time; |
| resv_ptr->duration = 0; |
| } |
| if (resv_desc_ptr->duration != NO_VAL) { |
| resv_ptr->duration = resv_desc_ptr->duration; |
| resv_ptr->end_time = resv_ptr->start_time_first + |
| (resv_desc_ptr->duration * 60); |
| } |
| |
| if (resv_ptr->start_time >= resv_ptr->end_time) { |
| error_code = ESLURM_INVALID_TIME_VALUE; |
| goto update_failure; |
| } |
| if (resv_desc_ptr->node_list && |
| (resv_desc_ptr->node_list[0] == '\0')) { /* Clear bitmap */ |
| resv_ptr->flags &= (~RESERVE_FLAG_SPEC_NODES); |
| xfree(resv_desc_ptr->node_list); |
| xfree(resv_ptr->node_list); |
| FREE_NULL_BITMAP(resv_ptr->node_bitmap); |
| resv_ptr->node_bitmap = bit_alloc(node_record_count); |
| if (resv_desc_ptr->node_cnt == NO_VAL) |
| resv_desc_ptr->node_cnt = resv_ptr->node_cnt; |
| resv_ptr->node_cnt = 0; |
| } |
| if (resv_desc_ptr->node_list) { /* Change bitmap last */ |
| bitstr_t *node_bitmap; |
| resv_ptr->flags |= RESERVE_FLAG_SPEC_NODES; |
| if (strcasecmp(resv_desc_ptr->node_list, "ALL") == 0) { |
| node_bitmap = bit_alloc(node_record_count); |
| bit_nset(node_bitmap, 0, (node_record_count - 1)); |
| } else if (node_name2bitmap(resv_desc_ptr->node_list, |
| false, &node_bitmap)) { |
| error_code = ESLURM_INVALID_NODE_NAME; |
| goto update_failure; |
| } |
| xfree(resv_ptr->node_list); |
| resv_ptr->node_list = resv_desc_ptr->node_list; |
| resv_desc_ptr->node_list = NULL; /* Nothing left to free */ |
| FREE_NULL_BITMAP(resv_ptr->node_bitmap); |
| resv_ptr->node_bitmap = node_bitmap; |
| resv_ptr->node_cnt = bit_set_count(resv_ptr->node_bitmap); |
| } |
| if (resv_desc_ptr->node_cnt != NO_VAL) { |
| #ifdef HAVE_BG |
| if (!cnodes_per_bp) { |
| select_g_alter_node_cnt(SELECT_GET_NODE_SCALING, |
| &cnodes_per_bp); |
| } |
| if (cnodes_per_bp) { |
| /* Convert c-node count to midplane count */ |
| resv_desc_ptr->node_cnt = (resv_desc_ptr->node_cnt + |
| cnodes_per_bp - 1) / |
| cnodes_per_bp; |
| } |
| #endif |
| rc = _resize_resv(resv_ptr, resv_desc_ptr->node_cnt); |
| if (rc) { |
| error_code = rc; |
| goto update_failure; |
| } |
| resv_ptr->node_cnt = bit_set_count(resv_ptr->node_bitmap); |
| } |
| if (_resv_overlap(resv_ptr->start_time, resv_ptr->end_time, |
| resv_ptr->flags, resv_ptr->node_bitmap, resv_ptr)) { |
| info("Reservation request overlaps another"); |
| error_code = ESLURM_RESERVATION_OVERLAP; |
| goto update_failure; |
| } |
| if (_job_overlap(resv_ptr->start_time, resv_ptr->flags, |
| resv_ptr->node_bitmap)) { |
| info("Reservation request overlaps jobs"); |
| error_code = ESLURM_NODES_BUSY; |
| goto update_failure; |
| } |
| _set_cpu_cnt(resv_ptr); |
| |
| /* This needs to be after checks for both account and user |
| changes */ |
| if ((error_code = _set_assoc_list(resv_ptr)) != SLURM_SUCCESS) |
| goto update_failure; |
| |
| slurm_make_time_str(&resv_ptr->start_time, start_time, |
| sizeof(start_time)); |
| slurm_make_time_str(&resv_ptr->end_time, end_time, sizeof(end_time)); |
| if (resv_ptr->accounts) { |
| name1 = " accounts="; |
| val1 = resv_ptr->accounts; |
| } else |
| name1 = val1 = ""; |
| if (resv_ptr->users) { |
| name2 = " users="; |
| val2 = resv_ptr->users; |
| } else |
| name2 = val2 = ""; |
| info("sched: Updated reservation %s%s%s%s%s nodes=%s start=%s end=%s", |
| resv_ptr->name, name1, val1, name2, val2, |
| resv_ptr->node_list, start_time, end_time); |
| |
| _post_resv_update(resv_ptr, resv_backup); |
| _del_resv_rec(resv_backup); |
| last_resv_update = now; |
| schedule_resv_save(); |
| return error_code; |
| |
| update_failure: |
| _swap_resv(resv_backup, resv_ptr); |
| _del_resv_rec(resv_backup); |
| return error_code; |
| } |
| |
| /* Determine if a running or pending job is using a reservation */ |
| static bool _is_resv_used(slurmctld_resv_t *resv_ptr) |
| { |
| ListIterator job_iterator; |
| struct job_record *job_ptr; |
| bool match = false; |
| |
| job_iterator = list_iterator_create(job_list); |
| while ((job_ptr = (struct job_record *) list_next(job_iterator))) { |
| if ((!IS_JOB_FINISHED(job_ptr)) && |
| (job_ptr->resv_id == resv_ptr->resv_id)) { |
| match = true; |
| break; |
| } |
| } |
| list_iterator_destroy(job_iterator); |
| |
| return match; |
| } |
| |
| /* Clear the reservation points for jobs referencing a defunct reservation */ |
| static void _clear_job_resv(slurmctld_resv_t *resv_ptr) |
| { |
| ListIterator job_iterator; |
| struct job_record *job_ptr; |
| |
| job_iterator = list_iterator_create(job_list); |
| while ((job_ptr = (struct job_record *) list_next(job_iterator))) { |
| if (job_ptr->resv_ptr != resv_ptr) |
| continue; |
| if (!IS_JOB_FINISHED(job_ptr)) { |
| info("Job %u linked to defunct reservation %s, " |
| "clearing that reservation", |
| job_ptr->job_id, job_ptr->resv_name); |
| } |
| job_ptr->resv_id = 0; |
| job_ptr->resv_ptr = NULL; |
| xfree(job_ptr->resv_name); |
| } |
| list_iterator_destroy(job_iterator); |
| } |
| |
| /* Delete an exiting resource reservation */ |
| extern int delete_resv(reservation_name_msg_t *resv_desc_ptr) |
| { |
| ListIterator iter; |
| slurmctld_resv_t *resv_ptr; |
| int rc = SLURM_SUCCESS; |
| time_t now = time(NULL); |
| |
| if (slurm_get_debug_flags() & DEBUG_FLAG_RESERVATION) |
| info("delete_resv: Name=%s", resv_desc_ptr->name); |
| |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((resv_ptr = (slurmctld_resv_t *) list_next(iter))) { |
| if (strcmp(resv_ptr->name, resv_desc_ptr->name)) |
| continue; |
| if (_is_resv_used(resv_ptr)) { |
| rc = ESLURM_RESERVATION_BUSY; |
| break; |
| } |
| |
| if (resv_ptr->maint_set_node) { |
| resv_ptr->maint_set_node = false; |
| _set_nodes_maint(resv_ptr, now); |
| last_node_update = now; |
| } |
| |
| rc = _post_resv_delete(resv_ptr); |
| _clear_job_resv(resv_ptr); |
| list_delete_item(iter); |
| break; |
| } |
| list_iterator_destroy(iter); |
| |
| if (!resv_ptr) { |
| info("Reservation %s not found for deletion", |
| resv_desc_ptr->name); |
| return ESLURM_RESERVATION_INVALID; |
| } |
| |
| last_resv_update = time(NULL); |
| schedule_resv_save(); |
| return rc; |
| } |
| |
| /* Dump the reservation records to a buffer */ |
| extern void show_resv(char **buffer_ptr, int *buffer_size, uid_t uid) |
| { |
| ListIterator iter; |
| slurmctld_resv_t *resv_ptr; |
| uint32_t resv_packed; |
| int tmp_offset; |
| Buf buffer; |
| time_t now = time(NULL); |
| DEF_TIMERS; |
| |
| START_TIMER; |
| if (!resv_list) |
| resv_list = list_create(_del_resv_rec); |
| |
| buffer_ptr[0] = NULL; |
| *buffer_size = 0; |
| |
| buffer = init_buf(BUF_SIZE); |
| |
| /* write header: version and time */ |
| resv_packed = 0; |
| pack32(resv_packed, buffer); |
| pack_time(now, buffer); |
| |
| /* write individual reservation records */ |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((resv_ptr = (slurmctld_resv_t *) list_next(iter))) { |
| if ((slurmctld_conf.private_data & PRIVATE_DATA_RESERVATIONS) |
| && !validate_slurm_user(uid)) { |
| int i = 0; |
| for (i=0; i<resv_ptr->user_cnt; i++) { |
| if (resv_ptr->user_list[i] == uid) |
| break; |
| } |
| |
| if (i >= resv_ptr->user_cnt) |
| continue; |
| } |
| |
| _pack_resv(resv_ptr, buffer, false); |
| resv_packed++; |
| } |
| list_iterator_destroy(iter); |
| |
| /* put the real record count in the message body header */ |
| tmp_offset = get_buf_offset(buffer); |
| set_buf_offset(buffer, 0); |
| pack32(resv_packed, buffer); |
| set_buf_offset(buffer, tmp_offset); |
| |
| *buffer_size = get_buf_offset(buffer); |
| buffer_ptr[0] = xfer_buf_data(buffer); |
| END_TIMER2("show_resv"); |
| } |
| |
| /* Save the state of all reservations to file */ |
| extern int dump_all_resv_state(void) |
| { |
| ListIterator iter; |
| slurmctld_resv_t *resv_ptr; |
| int error_code = 0, log_fd; |
| char *old_file, *new_file, *reg_file; |
| /* Locks: Read node */ |
| slurmctld_lock_t resv_read_lock = |
| { READ_LOCK, NO_LOCK, READ_LOCK, NO_LOCK }; |
| Buf buffer = init_buf(BUF_SIZE); |
| DEF_TIMERS; |
| |
| START_TIMER; |
| if (!resv_list) |
| resv_list = list_create(_del_resv_rec); |
| |
| /* write header: time */ |
| packstr(RESV_STATE_VERSION, buffer); |
| pack_time(time(NULL), buffer); |
| pack32(top_suffix, buffer); |
| |
| /* write reservation records to buffer */ |
| lock_slurmctld(resv_read_lock); |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((resv_ptr = (slurmctld_resv_t *) list_next(iter))) |
| _pack_resv(resv_ptr, buffer, true); |
| list_iterator_destroy(iter); |
| |
| old_file = xstrdup(slurmctld_conf.state_save_location); |
| xstrcat(old_file, "/resv_state.old"); |
| reg_file = xstrdup(slurmctld_conf.state_save_location); |
| xstrcat(reg_file, "/resv_state"); |
| new_file = xstrdup(slurmctld_conf.state_save_location); |
| xstrcat(new_file, "/resv_state.new"); |
| unlock_slurmctld(resv_read_lock); |
| |
| /* write the buffer to file */ |
| lock_state_files(); |
| log_fd = creat(new_file, 0600); |
| if (log_fd < 0) { |
| error("Can't save state, error creating file %s, %m", |
| new_file); |
| error_code = errno; |
| } else { |
| int pos = 0, nwrite = get_buf_offset(buffer), amount, rc; |
| char *data = (char *)get_buf_data(buffer); |
| |
| while (nwrite > 0) { |
| amount = write(log_fd, &data[pos], nwrite); |
| if ((amount < 0) && (errno != EINTR)) { |
| error("Error writing file %s, %m", new_file); |
| error_code = errno; |
| break; |
| } |
| nwrite -= amount; |
| pos += amount; |
| } |
| rc = fsync_and_close(log_fd, "reservation"); |
| if (rc && !error_code) |
| error_code = rc; |
| } |
| if (error_code) |
| (void) unlink(new_file); |
| else { /* file shuffle */ |
| (void) unlink(old_file); |
| if(link(reg_file, old_file)) |
| debug4("unable to create link for %s -> %s: %m", |
| reg_file, old_file); |
| (void) unlink(reg_file); |
| if(link(new_file, reg_file)) |
| debug4("unable to create link for %s -> %s: %m", |
| new_file, reg_file); |
| (void) unlink(new_file); |
| } |
| xfree(old_file); |
| xfree(reg_file); |
| xfree(new_file); |
| unlock_state_files(); |
| |
| free_buf(buffer); |
| END_TIMER2("dump_all_resv_state"); |
| return 0; |
| } |
| |
| /* Validate one reservation record, return true if good */ |
| static bool _validate_one_reservation(slurmctld_resv_t *resv_ptr) |
| { |
| if ((resv_ptr->name == NULL) || (resv_ptr->name[0] == '\0')) { |
| error("Read reservation without name"); |
| return false; |
| } |
| if (resv_ptr->partition) { |
| struct part_record *part_ptr = NULL; |
| part_ptr = find_part_record(resv_ptr->partition); |
| if (!part_ptr) { |
| error("Reservation %s has invalid partition (%s)", |
| resv_ptr->name, resv_ptr->partition); |
| return false; |
| } |
| resv_ptr->part_ptr = part_ptr; |
| } |
| if (resv_ptr->accounts) { |
| int account_cnt = 0, i, rc; |
| char **account_list; |
| rc = _build_account_list(resv_ptr->accounts, |
| &account_cnt, &account_list); |
| if (rc) { |
| error("Reservation %s has invalid accounts (%s)", |
| resv_ptr->name, resv_ptr->accounts); |
| return false; |
| } |
| for (i=0; i<resv_ptr->account_cnt; i++) |
| xfree(resv_ptr->account_list[i]); |
| xfree(resv_ptr->account_list); |
| resv_ptr->account_cnt = account_cnt; |
| resv_ptr->account_list = account_list; |
| } |
| if (resv_ptr->licenses) { |
| bool valid; |
| if (resv_ptr->license_list) |
| list_destroy(resv_ptr->license_list); |
| resv_ptr->license_list = license_validate(resv_ptr->licenses, |
| &valid); |
| if (!valid) { |
| error("Reservation %s has invalid licenses (%s)", |
| resv_ptr->name, resv_ptr->licenses); |
| return false; |
| } |
| } |
| if (resv_ptr->users) { |
| int rc, user_cnt = 0; |
| uid_t *user_list = NULL; |
| rc = _build_uid_list(resv_ptr->users, |
| &user_cnt, &user_list); |
| if (rc) { |
| error("Reservation %s has invalid users (%s)", |
| resv_ptr->name, resv_ptr->users); |
| return false; |
| } |
| xfree(resv_ptr->user_list); |
| resv_ptr->user_cnt = user_cnt; |
| resv_ptr->user_list = user_list; |
| } |
| if (resv_ptr->node_list) { /* Change bitmap last */ |
| bitstr_t *node_bitmap; |
| if (strcasecmp(resv_ptr->node_list, "ALL") == 0) { |
| node_bitmap = bit_alloc(node_record_count); |
| bit_nset(node_bitmap, 0, (node_record_count - 1)); |
| } else if (node_name2bitmap(resv_ptr->node_list, |
| false, &node_bitmap)) { |
| error("Reservation %s has invalid nodes (%s)", |
| resv_ptr->name, resv_ptr->node_list); |
| return false; |
| } |
| FREE_NULL_BITMAP(resv_ptr->node_bitmap); |
| resv_ptr->node_bitmap = node_bitmap; |
| } |
| return true; |
| } |
| |
| /* |
| * Validate all reservation records, reset bitmaps, etc. |
| * Purge any invalid reservation. |
| */ |
| static void _validate_all_reservations(void) |
| { |
| ListIterator iter; |
| slurmctld_resv_t *resv_ptr; |
| struct job_record *job_ptr; |
| char *tmp; |
| uint32_t res_num; |
| |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((resv_ptr = (slurmctld_resv_t *) list_next(iter))) { |
| if (!_validate_one_reservation(resv_ptr)) { |
| error("Purging invalid reservation record %s", |
| resv_ptr->name); |
| _post_resv_delete(resv_ptr); |
| _clear_job_resv(resv_ptr); |
| list_delete_item(iter); |
| } else { |
| _set_assoc_list(resv_ptr); |
| tmp = strrchr(resv_ptr->name, '_'); |
| if (tmp) { |
| res_num = atoi(tmp + 1); |
| top_suffix = MAX(top_suffix, res_num); |
| } |
| } |
| } |
| list_iterator_destroy(iter); |
| |
| /* Validate all job reservation pointers */ |
| iter = list_iterator_create(job_list); |
| while ((job_ptr = (struct job_record *) list_next(iter))) { |
| if (job_ptr->resv_name == NULL) |
| continue; |
| |
| if ((job_ptr->resv_ptr == NULL) || |
| (job_ptr->resv_ptr->magic != RESV_MAGIC)) { |
| job_ptr->resv_ptr = (slurmctld_resv_t *) |
| list_find_first(resv_list, |
| _find_resv_name, |
| job_ptr->resv_name); |
| } |
| if (!job_ptr->resv_ptr) { |
| error("JobId %u linked to defunct reservation %s", |
| job_ptr->job_id, job_ptr->resv_name); |
| job_ptr->resv_id = 0; |
| xfree(job_ptr->resv_name); |
| } |
| } |
| list_iterator_destroy(iter); |
| |
| } |
| |
| /* |
| * Validate the the reserved nodes are not DOWN or DRAINED and |
| * select different nodes as needed. |
| */ |
| static void _validate_node_choice(slurmctld_resv_t *resv_ptr) |
| { |
| bitstr_t *tmp_bitmap = NULL; |
| int i; |
| resv_desc_msg_t resv_desc; |
| |
| if (resv_ptr->flags & RESERVE_FLAG_SPEC_NODES) |
| return; |
| |
| i = bit_overlap(resv_ptr->node_bitmap, avail_node_bitmap); |
| if (i == resv_ptr->node_cnt) |
| return; |
| |
| /* Reservation includes DOWN, DRAINED/DRAINING, FAILING or |
| * NO_RESPOND nodes. Generate new request using _select_nodes() |
| * in attempt to replace this nodes */ |
| memset(&resv_desc, 0, sizeof(resv_desc_msg_t)); |
| resv_desc.start_time = resv_ptr->start_time; |
| resv_desc.end_time = resv_ptr->end_time; |
| resv_desc.features = resv_ptr->features; |
| resv_desc.node_cnt = resv_ptr->node_cnt - i; |
| i = _select_nodes(&resv_desc, &resv_ptr->part_ptr, &tmp_bitmap); |
| xfree(resv_desc.node_list); |
| xfree(resv_desc.partition); |
| if (i == SLURM_SUCCESS) { |
| bit_and(resv_ptr->node_bitmap, avail_node_bitmap); |
| bit_or(resv_ptr->node_bitmap, tmp_bitmap); |
| FREE_NULL_BITMAP(tmp_bitmap); |
| xfree(resv_ptr->node_list); |
| resv_ptr->node_list = bitmap2node_name(resv_ptr->node_bitmap); |
| info("modified reservation %s due to unusable nodes, " |
| "new nodes: %s", resv_ptr->name, resv_ptr->node_list); |
| } else if (difftime(resv_ptr->start_time, time(NULL)) < 600) { |
| info("reservation %s contains unusable nodes, " |
| "can't reallocate now", resv_ptr->name); |
| } else { |
| debug("reservation %s contains unusable nodes, " |
| "can't reallocate now", resv_ptr->name); |
| } |
| } |
| |
| /* Open the reservation state save file, or backup if necessary. |
| * state_file IN - the name of the state save file used |
| * RET the file description to read from or error code |
| */ |
| static int _open_resv_state_file(char **state_file) |
| { |
| int state_fd; |
| struct stat stat_buf; |
| |
| *state_file = xstrdup(slurmctld_conf.state_save_location); |
| xstrcat(*state_file, "/resv_state"); |
| state_fd = open(*state_file, O_RDONLY); |
| if (state_fd < 0) { |
| error("Could not open reservation state file %s: %m", |
| *state_file); |
| } else if (fstat(state_fd, &stat_buf) < 0) { |
| error("Could not stat reservation state file %s: %m", |
| *state_file); |
| (void) close(state_fd); |
| } else if (stat_buf.st_size < 10) { |
| error("Reservation state file %s too small", *state_file); |
| (void) close(state_fd); |
| } else /* Success */ |
| return state_fd; |
| |
| error("NOTE: Trying backup state save file. Reservations may be lost"); |
| xstrcat(*state_file, ".old"); |
| state_fd = open(*state_file, O_RDONLY); |
| return state_fd; |
| } |
| |
| /* |
| * Load the reservation state from file, recover on slurmctld restart. |
| * Reset reservation pointers for all jobs. |
| * Execute this after loading the configuration file data. |
| * IN recover - 0 = validate current reservations ONLY if already recovered, |
| * otherwise recover from disk |
| * 1+ = recover all reservation state from disk |
| * RET SLURM_SUCCESS or error code |
| * NOTE: READ lock_slurmctld config before entry |
| */ |
| extern int load_all_resv_state(int recover) |
| { |
| char *state_file, *data = NULL, *ver_str = NULL; |
| time_t now; |
| uint32_t data_size = 0, uint32_tmp; |
| int data_allocated, data_read = 0, error_code = 0, state_fd; |
| Buf buffer; |
| slurmctld_resv_t *resv_ptr = NULL; |
| |
| last_resv_update = time(NULL); |
| if ((recover == 0) && resv_list) { |
| _validate_all_reservations(); |
| return SLURM_SUCCESS; |
| } |
| |
| /* Read state file and validate */ |
| if (resv_list) |
| list_flush(resv_list); |
| else |
| resv_list = list_create(_del_resv_rec); |
| |
| /* read the file */ |
| lock_state_files(); |
| state_fd = _open_resv_state_file(&state_file); |
| if (state_fd < 0) { |
| info("No reservation state file (%s) to recover", |
| state_file); |
| error_code = ENOENT; |
| } else { |
| data_allocated = BUF_SIZE; |
| data = xmalloc(data_allocated); |
| while (1) { |
| data_read = read(state_fd, &data[data_size], |
| BUF_SIZE); |
| if (data_read < 0) { |
| if (errno == EINTR) |
| continue; |
| else { |
| error("Read error on %s: %m", |
| state_file); |
| break; |
| } |
| } else if (data_read == 0) /* eof */ |
| break; |
| data_size += data_read; |
| data_allocated += data_read; |
| xrealloc(data, data_allocated); |
| } |
| close(state_fd); |
| } |
| xfree(state_file); |
| unlock_state_files(); |
| |
| buffer = create_buf(data, data_size); |
| |
| safe_unpackstr_xmalloc( &ver_str, &uint32_tmp, buffer); |
| debug3("Version string in resv_state header is %s", ver_str); |
| if ((!ver_str) || (strcmp(ver_str, RESV_STATE_VERSION) != 0)) { |
| error("************************************************************"); |
| error("Can not recover reservation state, data version incompatible"); |
| error("************************************************************"); |
| xfree(ver_str); |
| free_buf(buffer); |
| schedule_resv_save(); /* Schedule save with new format */ |
| return EFAULT; |
| } |
| xfree(ver_str); |
| safe_unpack_time(&now, buffer); |
| safe_unpack32(&top_suffix, buffer); |
| |
| while (remaining_buf(buffer) > 0) { |
| resv_ptr = xmalloc(sizeof(slurmctld_resv_t)); |
| xassert(resv_ptr->magic = RESV_MAGIC); /* Sets value */ |
| safe_unpackstr_xmalloc(&resv_ptr->accounts, |
| &uint32_tmp, buffer); |
| safe_unpack_time(&resv_ptr->end_time, buffer); |
| safe_unpackstr_xmalloc(&resv_ptr->features, |
| &uint32_tmp, buffer); |
| safe_unpackstr_xmalloc(&resv_ptr->licenses, |
| &uint32_tmp, buffer); |
| safe_unpackstr_xmalloc(&resv_ptr->name, &uint32_tmp, buffer); |
| safe_unpack32(&resv_ptr->node_cnt, buffer); |
| safe_unpackstr_xmalloc(&resv_ptr->node_list, |
| &uint32_tmp, buffer); |
| safe_unpackstr_xmalloc(&resv_ptr->partition, |
| &uint32_tmp, buffer); |
| safe_unpack_time(&resv_ptr->start_time_first, buffer); |
| safe_unpack16(&resv_ptr->flags, buffer); |
| safe_unpackstr_xmalloc(&resv_ptr->users,&uint32_tmp, buffer); |
| |
| /* Fields saved for internal use only (save state) */ |
| safe_unpackstr_xmalloc(&resv_ptr->assoc_list, |
| &uint32_tmp, buffer); |
| safe_unpack32(&resv_ptr->cpu_cnt, buffer); |
| safe_unpack32(&resv_ptr->resv_id, buffer); |
| safe_unpack_time(&resv_ptr->start_time_prev, buffer); |
| safe_unpack_time(&resv_ptr->start_time, buffer); |
| safe_unpack32(&resv_ptr->duration, buffer); |
| |
| list_append(resv_list, resv_ptr); |
| info("Recovered state of reservation %s", resv_ptr->name); |
| } |
| |
| _validate_all_reservations(); |
| info("Recovered state of %d reservations", list_count(resv_list)); |
| free_buf(buffer); |
| return error_code; |
| |
| unpack_error: |
| _validate_all_reservations(); |
| if (state_fd >= 0) |
| error("Incomplete reservation data checkpoint file"); |
| info("Recovered state of %d reservations", list_count(resv_list)); |
| if (resv_ptr) |
| _del_resv_rec(resv_ptr); |
| free_buf(buffer); |
| return EFAULT; |
| } |
| |
| /* |
| * Determine if a job request can use the specified reservations |
| * |
| * IN/OUT job_ptr - job to validate, set its resv_id and resv_flags |
| * RET SLURM_SUCCESS or error code (not found or access denied) |
| */ |
| extern int validate_job_resv(struct job_record *job_ptr) |
| { |
| slurmctld_resv_t *resv_ptr = NULL; |
| int rc; |
| |
| xassert(job_ptr); |
| |
| if ((job_ptr->resv_name == NULL) || (job_ptr->resv_name[0] == '\0')) { |
| xfree(job_ptr->resv_name); |
| job_ptr->resv_id = 0; |
| job_ptr->resv_flags = 0; |
| job_ptr->resv_ptr = NULL; |
| return SLURM_SUCCESS; |
| } |
| |
| if (!resv_list) |
| return ESLURM_RESERVATION_INVALID; |
| |
| /* Find the named reservation */ |
| resv_ptr = (slurmctld_resv_t *) list_find_first (resv_list, |
| _find_resv_name, job_ptr->resv_name); |
| if (!resv_ptr) { |
| info("Reservation name not found (%s)", job_ptr->resv_name); |
| return ESLURM_RESERVATION_INVALID; |
| } |
| |
| rc = _valid_job_access_resv(job_ptr, resv_ptr); |
| if (rc == SLURM_SUCCESS) { |
| job_ptr->resv_id = resv_ptr->resv_id; |
| job_ptr->resv_flags = resv_ptr->flags; |
| job_ptr->resv_ptr = resv_ptr; |
| } |
| return rc; |
| } |
| |
| static int _resize_resv(slurmctld_resv_t *resv_ptr, uint32_t node_cnt) |
| { |
| bitstr_t *tmp1_bitmap = NULL, *tmp2_bitmap = NULL; |
| int delta_node_cnt, i; |
| resv_desc_msg_t resv_desc; |
| |
| delta_node_cnt = resv_ptr->node_cnt - node_cnt; |
| if (delta_node_cnt == 0) /* Already correct node count */ |
| return SLURM_SUCCESS; |
| |
| if (delta_node_cnt > 0) { /* Must decrease node count */ |
| if (bit_overlap(resv_ptr->node_bitmap, idle_node_bitmap)) { |
| /* Start by eliminating idle nodes from reservation */ |
| tmp1_bitmap = bit_copy(resv_ptr->node_bitmap); |
| bit_and(tmp1_bitmap, idle_node_bitmap); |
| i = bit_set_count(tmp1_bitmap); |
| if (i > delta_node_cnt) { |
| tmp2_bitmap = bit_pick_cnt(tmp1_bitmap, |
| delta_node_cnt); |
| bit_not(tmp2_bitmap); |
| bit_and(resv_ptr->node_bitmap, tmp2_bitmap); |
| FREE_NULL_BITMAP(tmp1_bitmap); |
| FREE_NULL_BITMAP(tmp2_bitmap); |
| delta_node_cnt = 0; /* ALL DONE */ |
| } else if (i) { |
| bit_not(idle_node_bitmap); |
| bit_and(resv_ptr->node_bitmap, |
| idle_node_bitmap); |
| bit_not(idle_node_bitmap); |
| resv_ptr->node_cnt = bit_set_count( |
| resv_ptr->node_bitmap); |
| delta_node_cnt = resv_ptr->node_cnt - |
| node_cnt; |
| } |
| FREE_NULL_BITMAP(tmp1_bitmap); |
| } |
| if (delta_node_cnt > 0) { |
| /* Now eliminate allocated nodes from reservation */ |
| tmp1_bitmap = bit_pick_cnt(resv_ptr->node_bitmap, |
| node_cnt); |
| FREE_NULL_BITMAP(resv_ptr->node_bitmap); |
| resv_ptr->node_bitmap = tmp1_bitmap; |
| } |
| xfree(resv_ptr->node_list); |
| resv_ptr->node_list = bitmap2node_name(resv_ptr->node_bitmap); |
| resv_ptr->node_cnt = node_cnt; |
| return SLURM_SUCCESS; |
| } |
| |
| /* Must increase node count. Make this look like new request so |
| * we can use _select_nodes() for selecting the nodes */ |
| memset(&resv_desc, 0, sizeof(resv_desc_msg_t)); |
| resv_desc.start_time = resv_ptr->start_time; |
| resv_desc.end_time = resv_ptr->end_time; |
| resv_desc.features = resv_ptr->features; |
| resv_desc.flags = resv_ptr->flags; |
| resv_desc.node_cnt = 0 - delta_node_cnt; |
| i = _select_nodes(&resv_desc, &resv_ptr->part_ptr, &tmp1_bitmap); |
| xfree(resv_desc.node_list); |
| xfree(resv_desc.partition); |
| if (i == SLURM_SUCCESS) { |
| bit_or(resv_ptr->node_bitmap, tmp1_bitmap); |
| FREE_NULL_BITMAP(tmp1_bitmap); |
| xfree(resv_ptr->node_list); |
| resv_ptr->node_list = bitmap2node_name(resv_ptr->node_bitmap); |
| resv_ptr->node_cnt = node_cnt; |
| } |
| return i; |
| } |
| |
| /* Given a reservation create request, select appropriate nodes for use */ |
| static int _select_nodes(resv_desc_msg_t *resv_desc_ptr, |
| struct part_record **part_ptr, |
| bitstr_t **resv_bitmap) |
| { |
| slurmctld_resv_t *resv_ptr; |
| bitstr_t *node_bitmap; |
| ListIterator iter; |
| int i, rc = SLURM_SUCCESS; |
| time_t now = time(NULL); |
| |
| if (*part_ptr == NULL) { |
| *part_ptr = default_part_loc; |
| if (*part_ptr == NULL) |
| return ESLURM_DEFAULT_PARTITION_NOT_SET; |
| xfree(resv_desc_ptr->partition); /* should be no-op */ |
| resv_desc_ptr->partition = xstrdup((*part_ptr)->name); |
| } |
| |
| /* Start with all nodes in the partition */ |
| node_bitmap = bit_copy((*part_ptr)->node_bitmap); |
| |
| /* Don't use node already reserved */ |
| if (!(resv_desc_ptr->flags & RESERVE_FLAG_OVERLAP)) { |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((resv_ptr = (slurmctld_resv_t *) list_next(iter))) { |
| if (resv_ptr->end_time <= now) |
| _advance_resv_time(resv_ptr); |
| if ((resv_ptr->node_bitmap == NULL) || |
| (resv_ptr->start_time >= resv_desc_ptr->end_time) || |
| (resv_ptr->end_time <= resv_desc_ptr->start_time)) |
| continue; |
| bit_not(resv_ptr->node_bitmap); |
| bit_and(node_bitmap, resv_ptr->node_bitmap); |
| bit_not(resv_ptr->node_bitmap); |
| } |
| list_iterator_destroy(iter); |
| } |
| |
| /* Satisfy feature specification */ |
| if (resv_desc_ptr->features) { |
| int op_code = FEATURE_OP_AND, last_op_code = FEATURE_OP_AND; |
| char *features = xstrdup(resv_desc_ptr->features); |
| char *sep_ptr, *token = features; |
| bitstr_t *feature_bitmap = bit_copy(node_bitmap); |
| struct features_record *feature_ptr; |
| ListIterator feature_iter; |
| bool match; |
| |
| if (feature_bitmap == NULL) |
| fatal("bit_copy malloc failure"); |
| |
| while (1) { |
| for (i=0; ; i++) { |
| if (token[i] == '\0') { |
| sep_ptr = NULL; |
| break; |
| } else if (token[i] == '|') { |
| op_code = FEATURE_OP_OR; |
| token[i] = '\0'; |
| sep_ptr = &token[i]; |
| break; |
| } else if ((token[i] == '&') || |
| (token[i] == ',')) { |
| op_code = FEATURE_OP_AND; |
| token[i] = '\0'; |
| sep_ptr = &token[i]; |
| break; |
| } |
| } |
| |
| match = false; |
| feature_iter = list_iterator_create(feature_list); |
| if (feature_iter == NULL) |
| fatal("list_iterator_create malloc failure"); |
| while ((feature_ptr = (struct features_record *) |
| list_next(feature_iter))) { |
| if (strcmp(token, feature_ptr->name)) |
| continue; |
| if (last_op_code == FEATURE_OP_OR) { |
| bit_or(feature_bitmap, |
| feature_ptr->node_bitmap); |
| } else { |
| bit_and(feature_bitmap, |
| feature_ptr->node_bitmap); |
| } |
| match = true; |
| break; |
| } |
| list_iterator_destroy(feature_iter); |
| if (!match) { |
| info("reservation feature invalid: %s", token); |
| rc = ESLURM_INVALID_FEATURE; |
| bit_nclear(feature_bitmap, 0, |
| (node_record_count - 1)); |
| break; |
| } |
| if (sep_ptr == NULL) |
| break; |
| token = sep_ptr + 1; |
| last_op_code = op_code; |
| } |
| xfree(features); |
| bit_and(node_bitmap, feature_bitmap); |
| FREE_NULL_BITMAP(feature_bitmap); |
| } |
| |
| if ((resv_desc_ptr->flags & RESERVE_FLAG_MAINT) == 0) { |
| /* Nodes must be available */ |
| bit_and(node_bitmap, avail_node_bitmap); |
| } |
| *resv_bitmap = NULL; |
| if (rc != SLURM_SUCCESS) |
| ; |
| else if (bit_set_count(node_bitmap) < resv_desc_ptr->node_cnt) |
| verbose("reservation requests more nodes than are available"); |
| else if ((i = bit_overlap(node_bitmap, idle_node_bitmap)) >= |
| resv_desc_ptr->node_cnt) { /* Reserve idle nodes */ |
| bit_and(node_bitmap, idle_node_bitmap); |
| *resv_bitmap = bit_pick_cnt(node_bitmap, |
| resv_desc_ptr->node_cnt); |
| } else if (resv_desc_ptr->flags & RESERVE_FLAG_IGN_JOBS) { |
| /* Reserve nodes that are idle first, then busy nodes */ |
| *resv_bitmap = _pick_idle_nodes2(node_bitmap, |
| resv_desc_ptr); |
| } else { |
| /* Reserve nodes that are or will be idle. |
| * This algorithm is slower than above logic that just |
| * selects from the idle nodes. */ |
| *resv_bitmap = _pick_idle_nodes(node_bitmap, resv_desc_ptr); |
| } |
| |
| FREE_NULL_BITMAP(node_bitmap); |
| if (*resv_bitmap == NULL) { |
| if (rc == SLURM_SUCCESS) |
| rc = ESLURM_NODES_BUSY; |
| return rc; |
| } |
| |
| resv_desc_ptr->node_list = bitmap2node_name(*resv_bitmap); |
| return SLURM_SUCCESS; |
| } |
| |
| /* |
| * Select nodes for a reservation to use |
| * IN,OUT avail_nodes - nodes to choose from with proper features, partition |
| * destructively modified by this function |
| * IN resv_desc_ptr - reservation request |
| * RET bitmap of selected nodes or NULL if request can not be satisfied |
| */ |
| static bitstr_t *_pick_idle_nodes(bitstr_t *avail_nodes, |
| resv_desc_msg_t *resv_desc_ptr) |
| { |
| ListIterator job_iterator; |
| struct job_record *job_ptr; |
| |
| job_iterator = list_iterator_create(job_list); |
| while ((job_ptr = (struct job_record *) list_next(job_iterator))) { |
| if (!IS_JOB_RUNNING(job_ptr) || |
| (job_ptr->end_time < resv_desc_ptr->start_time)) |
| continue; |
| bit_not(job_ptr->node_bitmap); |
| bit_and(avail_nodes, job_ptr->node_bitmap); |
| bit_not(job_ptr->node_bitmap); |
| } |
| list_iterator_destroy(job_iterator); |
| |
| return bit_pick_cnt(avail_nodes, resv_desc_ptr->node_cnt); |
| } |
| |
| /* |
| * Select nodes for a reservation to use |
| * IN,OUT avail_nodes - nodes to choose from with proper features, partition |
| * destructively modified by this function |
| * IN resv_desc_ptr - reservation request |
| * RET bitmap of selected nodes or NULL if request can not be satisfied |
| */ |
| static bitstr_t *_pick_idle_nodes2(bitstr_t *avail_nodes, |
| resv_desc_msg_t *resv_desc_ptr) |
| { |
| ListIterator job_iterator; |
| struct job_record *job_ptr; |
| bitstr_t *tmp_bitmap; |
| |
| job_iterator = list_iterator_create(job_list); |
| while ((job_ptr = (struct job_record *) list_next(job_iterator))) { |
| if (!IS_JOB_RUNNING(job_ptr) || |
| (job_ptr->end_time < resv_desc_ptr->start_time)) |
| continue; |
| tmp_bitmap = bit_copy(avail_nodes); |
| if (tmp_bitmap == NULL) |
| fatal("malloc failure"); |
| bit_not(job_ptr->node_bitmap); |
| bit_and(avail_nodes, job_ptr->node_bitmap); |
| bit_not(job_ptr->node_bitmap); |
| if (bit_set_count(avail_nodes) < resv_desc_ptr->node_cnt) { |
| /* Removed too many nodes, put them back */ |
| bit_or(avail_nodes, tmp_bitmap); |
| } |
| FREE_NULL_BITMAP(tmp_bitmap); |
| } |
| list_iterator_destroy(job_iterator); |
| |
| return bit_pick_cnt(avail_nodes, resv_desc_ptr->node_cnt); |
| } |
| |
| /* Determine if a job has access to a reservation |
| * RET SLURM_SUCCESS if true, ESLURM_RESERVATION_ACCESS otherwise */ |
| static int _valid_job_access_resv(struct job_record *job_ptr, |
| slurmctld_resv_t *resv_ptr) |
| { |
| int i; |
| |
| /* Determine if we have access */ |
| if (accounting_enforce & ACCOUNTING_ENFORCE_ASSOCS) { |
| char tmp_char[30]; |
| slurmdb_association_rec_t *assoc; |
| if (!resv_ptr->assoc_list) { |
| error("Reservation %s has no association list. " |
| "Checking user/account lists", |
| resv_ptr->name); |
| goto no_assocs; |
| } |
| |
| if (!job_ptr->assoc_ptr) { |
| slurmdb_association_rec_t assoc_rec; |
| /* This should never be called, but just to be |
| * safe we will try to fill it in. */ |
| memset(&assoc_rec, 0, |
| sizeof(slurmdb_association_rec_t)); |
| assoc_rec.id = job_ptr->assoc_id; |
| if (assoc_mgr_fill_in_assoc( |
| acct_db_conn, &assoc_rec, |
| accounting_enforce, |
| (slurmdb_association_rec_t **) |
| &job_ptr->assoc_ptr)) |
| goto end_it; |
| } |
| |
| /* Check to see if the association is here or the parent |
| * association is listed in the valid associations. */ |
| assoc = job_ptr->assoc_ptr; |
| while (assoc) { |
| snprintf(tmp_char, sizeof(tmp_char), ",%u,", assoc->id); |
| if (strstr(resv_ptr->assoc_list, tmp_char)) |
| return SLURM_SUCCESS; |
| assoc = assoc->usage->parent_assoc_ptr; |
| } |
| } else { |
| no_assocs: for (i=0; i<resv_ptr->user_cnt; i++) { |
| if (job_ptr->user_id == resv_ptr->user_list[i]) |
| return SLURM_SUCCESS; |
| } |
| for (i=0; (i<resv_ptr->account_cnt) && job_ptr->account; i++) { |
| if (resv_ptr->account_list[i] && |
| (strcmp(job_ptr->account, |
| resv_ptr->account_list[i]) == 0)) { |
| return SLURM_SUCCESS; |
| } |
| } |
| } |
| |
| end_it: |
| info("Security violation, uid=%u attempt to use reservation %s", |
| job_ptr->user_id, resv_ptr->name); |
| return ESLURM_RESERVATION_ACCESS; |
| } |
| |
| /* |
| * Determine if a job can start now based only upon reservations |
| * |
| * IN job_ptr - job to test |
| * RET SLURM_SUCCESS if runable now, otherwise an error code |
| */ |
| extern int job_test_resv_now(struct job_record *job_ptr) |
| { |
| slurmctld_resv_t * resv_ptr; |
| time_t now; |
| |
| if (job_ptr->resv_name == NULL) |
| return SLURM_SUCCESS; |
| |
| resv_ptr = (slurmctld_resv_t *) list_find_first (resv_list, |
| _find_resv_name, job_ptr->resv_name); |
| job_ptr->resv_ptr = resv_ptr; |
| if (!resv_ptr) |
| return ESLURM_RESERVATION_INVALID; |
| |
| if (_valid_job_access_resv(job_ptr, resv_ptr) != SLURM_SUCCESS) |
| return ESLURM_RESERVATION_ACCESS; |
| now = time(NULL); |
| if (now < resv_ptr->start_time) { |
| /* reservation starts later */ |
| return ESLURM_INVALID_TIME_VALUE; |
| } |
| if (now > resv_ptr->end_time) { |
| /* reservation ended earlier */ |
| return ESLURM_RESERVATION_INVALID; |
| } |
| if (resv_ptr->node_cnt == 0) { |
| /* empty reservation treated like it will start later */ |
| return ESLURM_INVALID_TIME_VALUE; |
| } |
| |
| return SLURM_SUCCESS; |
| } |
| |
| /* Adjust a job's time_limit and end_time as needed to avoid using |
| * reserved resources. Don't go below job's time_min value. */ |
| extern void job_time_adj_resv(struct job_record *job_ptr) |
| { |
| ListIterator iter; |
| slurmctld_resv_t * resv_ptr; |
| time_t now = time(NULL); |
| int32_t resv_begin_time; |
| |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((resv_ptr = (slurmctld_resv_t *) list_next(iter))) { |
| if (resv_ptr->end_time <= now) |
| _advance_resv_time(resv_ptr); |
| if ((job_ptr->resv_ptr == resv_ptr) || |
| (resv_ptr->start_time <= now)) |
| continue; /* authorized user of reservation */ |
| if (resv_ptr->start_time >= job_ptr->end_time) |
| continue; /* reservation starts after job ends */ |
| if ((resv_ptr->node_bitmap == NULL) || |
| (bit_overlap(resv_ptr->node_bitmap, |
| job_ptr->node_bitmap) == 0)) |
| continue; /* disjoint resources */ |
| resv_begin_time = difftime(resv_ptr->start_time, now) / 60; |
| job_ptr->time_limit = MIN(job_ptr->time_limit,resv_begin_time); |
| } |
| list_iterator_destroy(iter); |
| job_ptr->time_limit = MAX(job_ptr->time_limit, job_ptr->time_min); |
| job_ptr->end_time = job_ptr->start_time + (job_ptr->time_limit * 60); |
| } |
| |
| /* For a given license_list, return the total count of licenses of the |
| * specified name */ |
| static int _license_cnt(List license_list, char *lic_name) |
| { |
| int lic_cnt = 0; |
| ListIterator iter; |
| licenses_t *license_ptr; |
| |
| if (license_list == NULL) |
| return lic_cnt; |
| |
| iter = list_iterator_create(license_list); |
| if (iter == NULL) |
| fatal("list_interator_create malloc failure"); |
| while ((license_ptr = list_next(iter))) { |
| if (strcmp(license_ptr->name, lic_name) == 0) |
| lic_cnt += license_ptr->total; |
| } |
| list_iterator_destroy(iter); |
| |
| return lic_cnt; |
| } |
| |
| static uint32_t _get_job_duration(struct job_record *job_ptr) |
| { |
| uint32_t duration; |
| uint16_t time_slices = 1; |
| |
| if (job_ptr->time_limit == INFINITE) |
| duration = ONE_YEAR; |
| else if (job_ptr->time_limit != NO_VAL) |
| duration = (job_ptr->time_limit * 60); |
| else { /* partition time limit */ |
| if (job_ptr->part_ptr->max_time == INFINITE) |
| duration = ONE_YEAR; |
| else |
| duration = (job_ptr->part_ptr->max_time * 60); |
| } |
| if (job_ptr->part_ptr) |
| time_slices = job_ptr->part_ptr->max_share & ~SHARED_FORCE; |
| if ((duration != ONE_YEAR) && (time_slices > 1) && |
| (slurm_get_preempt_mode() & PREEMPT_MODE_GANG)) { |
| /* FIXME: Ideally we figure out how many jobs are actually |
| * time-slicing on each node rather than using the maximum |
| * value. */ |
| duration *= time_slices; |
| } |
| return duration; |
| } |
| |
| /* |
| * Determine how many licenses of the give type the specified job is |
| * prevented from using due to reservations |
| * |
| * IN job_ptr - job to test |
| * IN lic_name - name of license |
| * IN when - when the job is expected to start |
| * RET number of licenses of this type the job is prevented from using |
| */ |
| extern int job_test_lic_resv(struct job_record *job_ptr, char *lic_name, |
| time_t when) |
| { |
| slurmctld_resv_t * resv_ptr; |
| time_t job_start_time, job_end_time, now = time(NULL); |
| ListIterator iter; |
| int resv_cnt = 0; |
| |
| job_start_time = when; |
| job_end_time = when + _get_job_duration(job_ptr); |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((resv_ptr = (slurmctld_resv_t *) list_next(iter))) { |
| if (resv_ptr->end_time <= now) |
| _advance_resv_time(resv_ptr); |
| if ((resv_ptr->start_time >= job_end_time) || |
| (resv_ptr->end_time <= job_start_time)) |
| continue; /* reservation at different time */ |
| |
| if (job_ptr->resv_name && |
| (strcmp(job_ptr->resv_name, resv_ptr->name) == 0)) |
| continue; /* job can use this reservation */ |
| |
| resv_cnt += _license_cnt(resv_ptr->license_list, lic_name); |
| } |
| list_iterator_destroy(iter); |
| |
| /* info("job %u blocked from %d licenses of type %s", |
| job_ptr->job_id, resv_cnt, lic_name); */ |
| return resv_cnt; |
| } |
| |
| /* |
| * Determine which nodes a job can use based upon reservations |
| * IN job_ptr - job to test |
| * IN/OUT when - when we want the job to start (IN) |
| * when the reservation is available (OUT) |
| * IN move_time - if true, then permit the start time to advance from |
| * "when" as needed IF job has no reservervation |
| * OUT node_bitmap - nodes which the job can use, caller must free unless error |
| * RET SLURM_SUCCESS if runable now |
| * ESLURM_RESERVATION_ACCESS access to reservation denied |
| * ESLURM_RESERVATION_INVALID reservation invalid |
| * ESLURM_INVALID_TIME_VALUE reservation invalid at time "when" |
| * ESLURM_NODES_BUSY job has no reservation, but required nodes are |
| * reserved |
| */ |
| extern int job_test_resv(struct job_record *job_ptr, time_t *when, |
| bool move_time, bitstr_t **node_bitmap) |
| { |
| slurmctld_resv_t * resv_ptr, *res2_ptr; |
| time_t job_start_time, job_end_time, lic_resv_time; |
| time_t now = time(NULL); |
| ListIterator iter; |
| int i, rc = SLURM_SUCCESS; |
| |
| job_start_time = *when; |
| job_end_time = *when + _get_job_duration(job_ptr); |
| *node_bitmap = (bitstr_t *) NULL; |
| |
| if (job_ptr->resv_name) { |
| bool overlap_resv = false; |
| resv_ptr = (slurmctld_resv_t *) list_find_first (resv_list, |
| _find_resv_name, job_ptr->resv_name); |
| job_ptr->resv_ptr = resv_ptr; |
| if (!resv_ptr) |
| return ESLURM_RESERVATION_INVALID; |
| if (_valid_job_access_resv(job_ptr, resv_ptr) != SLURM_SUCCESS) |
| return ESLURM_RESERVATION_ACCESS; |
| if (resv_ptr->end_time <= now) |
| _advance_resv_time(resv_ptr); |
| if (*when < resv_ptr->start_time) { |
| /* reservation starts later */ |
| *when = resv_ptr->start_time; |
| return ESLURM_INVALID_TIME_VALUE; |
| } |
| if (resv_ptr->node_cnt == 0) { |
| /* empty reservation treated like it will start later */ |
| *when = now + 600; |
| return ESLURM_INVALID_TIME_VALUE; |
| } |
| if (*when > resv_ptr->end_time) { |
| /* reservation ended earlier */ |
| *when = resv_ptr->end_time; |
| job_ptr->priority = 0; /* administrative hold */ |
| return ESLURM_RESERVATION_INVALID; |
| } |
| if (job_ptr->details->req_node_bitmap && |
| !bit_super_set(job_ptr->details->req_node_bitmap, |
| resv_ptr->node_bitmap)) { |
| return ESLURM_RESERVATION_INVALID; |
| } |
| *node_bitmap = bit_copy(resv_ptr->node_bitmap); |
| |
| /* if there are any overlapping reservations, we need to |
| * prevent the job from using those nodes (e.g. MAINT nodes) */ |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((res2_ptr = (slurmctld_resv_t *) list_next(iter))) { |
| if ((resv_ptr->flags & RESERVE_FLAG_MAINT) || |
| (resv_ptr->flags & RESERVE_FLAG_OVERLAP) || |
| (res2_ptr == resv_ptr) || |
| (res2_ptr->node_bitmap == NULL) || |
| (res2_ptr->start_time >= job_end_time) || |
| (res2_ptr->end_time <= job_start_time)) |
| continue; |
| bit_not(res2_ptr->node_bitmap); |
| bit_and(*node_bitmap, res2_ptr->node_bitmap); |
| bit_not(res2_ptr->node_bitmap); |
| overlap_resv = true; |
| } |
| list_iterator_destroy(iter); |
| |
| if (slurm_get_debug_flags() & DEBUG_FLAG_RESERVATION) { |
| char *nodes=bitmap2node_name(*node_bitmap); |
| info("job_test_resv: job:%u reservation:%s nodes:%s", |
| job_ptr->job_id, nodes, job_ptr->resv_name); |
| xfree(nodes); |
| } |
| return SLURM_SUCCESS; |
| } |
| |
| job_ptr->resv_ptr = NULL; /* should be redundant */ |
| *node_bitmap = bit_alloc(node_record_count); |
| bit_nset(*node_bitmap, 0, (node_record_count - 1)); |
| if (list_count(resv_list) == 0) |
| return SLURM_SUCCESS; |
| |
| /* Job has no reservation, try to find time when this can |
| * run and get it's required nodes (if any) */ |
| for (i=0; ; i++) { |
| lic_resv_time = (time_t) 0; |
| |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((resv_ptr = (slurmctld_resv_t *) list_next(iter))) { |
| if (resv_ptr->end_time <= now) |
| _advance_resv_time(resv_ptr); |
| if ((resv_ptr->node_bitmap == NULL) || |
| (resv_ptr->start_time >= job_end_time) || |
| (resv_ptr->end_time <= job_start_time)) |
| continue; |
| if (job_ptr->details->req_node_bitmap && |
| bit_overlap(job_ptr->details->req_node_bitmap, |
| resv_ptr->node_bitmap)) { |
| *when = resv_ptr->end_time; |
| rc = ESLURM_NODES_BUSY; |
| break; |
| } |
| if (license_list_overlap(job_ptr->license_list, |
| resv_ptr->license_list)) { |
| if ((lic_resv_time == (time_t) 0) || |
| (lic_resv_time > resv_ptr->end_time)) |
| lic_resv_time = resv_ptr->end_time; |
| } |
| bit_not(resv_ptr->node_bitmap); |
| bit_and(*node_bitmap, resv_ptr->node_bitmap); |
| bit_not(resv_ptr->node_bitmap); |
| } |
| list_iterator_destroy(iter); |
| |
| if ((rc == SLURM_SUCCESS) && move_time) { |
| if (license_job_test(job_ptr, job_start_time) |
| == EAGAIN) { |
| /* Need to postpone for licenses */ |
| rc = ESLURM_NODES_BUSY; |
| *when = lic_resv_time; |
| } |
| } |
| if (rc == SLURM_SUCCESS) |
| break; |
| /* rc == ESLURM_NODES_BUSY here from above break */ |
| if (move_time && (i<10)) { /* Retry for later start time */ |
| bit_nset(*node_bitmap, 0, (node_record_count - 1)); |
| rc = SLURM_SUCCESS; |
| continue; |
| } |
| FREE_NULL_BITMAP(*node_bitmap); |
| break; /* Give up */ |
| } |
| |
| return rc; |
| } |
| |
| /* Begin scan of all jobs for valid reservations */ |
| extern void begin_job_resv_check(void) |
| { |
| ListIterator iter; |
| slurmctld_resv_t *resv_ptr; |
| slurm_ctl_conf_t *conf; |
| |
| if (!resv_list) |
| return; |
| |
| conf = slurm_conf_lock(); |
| resv_over_run = conf->resv_over_run; |
| slurm_conf_unlock(); |
| if (resv_over_run == (uint16_t) INFINITE) |
| resv_over_run = ONE_YEAR; |
| else |
| resv_over_run *= 60; |
| |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((resv_ptr = (slurmctld_resv_t *) list_next(iter))) { |
| resv_ptr->job_pend_cnt = 0; |
| resv_ptr->job_run_cnt = 0; |
| } |
| list_iterator_destroy(iter); |
| } |
| |
| /* Test a particular job for valid reservation |
| * |
| * RET ESLURM_INVALID_TIME_VALUE if reservation is terminated |
| * SLURM_SUCCESS if reservation is still valid |
| */ |
| extern int job_resv_check(struct job_record *job_ptr) |
| { |
| bool run_flag = false; |
| |
| if (!job_ptr->resv_name) |
| return SLURM_SUCCESS; |
| |
| if (IS_JOB_RUNNING(job_ptr) || IS_JOB_SUSPENDED(job_ptr)) |
| run_flag = true; |
| else if (IS_JOB_PENDING(job_ptr)) |
| run_flag = false; |
| else |
| return SLURM_SUCCESS; |
| |
| xassert(job_ptr->resv_ptr->magic == RESV_MAGIC); |
| if (run_flag) |
| job_ptr->resv_ptr->job_run_cnt++; |
| else |
| job_ptr->resv_ptr->job_pend_cnt++; |
| |
| if ((job_ptr->resv_ptr->end_time + resv_over_run) < time(NULL)) |
| return ESLURM_INVALID_TIME_VALUE; |
| return SLURM_SUCCESS; |
| } |
| |
| /* Advance a expired reservation's time stamps one day or one week |
| * as appropriate. */ |
| static void _advance_resv_time(slurmctld_resv_t *resv_ptr) |
| { |
| int day_cnt = 0; |
| char *interval = ""; |
| |
| if (resv_ptr->flags & RESERVE_FLAG_DAILY) { |
| day_cnt = 1; |
| interval = "day"; |
| } else if (resv_ptr->flags & RESERVE_FLAG_WEEKLY) { |
| day_cnt = 7; |
| interval = "week"; |
| } |
| |
| if (day_cnt) { |
| verbose("Advance reservation %s one %s", resv_ptr->name, |
| interval); |
| resv_ptr->start_time = resv_ptr->start_time_first; |
| _advance_time(&resv_ptr->start_time, day_cnt); |
| resv_ptr->start_time_prev = resv_ptr->start_time; |
| resv_ptr->start_time_first = resv_ptr->start_time; |
| _advance_time(&resv_ptr->end_time, day_cnt); |
| _post_resv_create(resv_ptr); |
| last_resv_update = time(NULL); |
| schedule_resv_save(); |
| } |
| } |
| |
| /* Finish scan of all jobs for valid reservations |
| * |
| * Purge vestigial reservation records. |
| * Advance daily or weekly reservations that are no longer |
| * being actively used. |
| */ |
| extern void fini_job_resv_check(void) |
| { |
| ListIterator iter; |
| slurmctld_resv_t *resv_ptr; |
| time_t now = time(NULL); |
| |
| if (!resv_list) |
| return; |
| |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((resv_ptr = (slurmctld_resv_t *) list_next(iter))) { |
| if (resv_ptr->end_time > now) { /* reservation not over */ |
| _validate_node_choice(resv_ptr); |
| continue; |
| } |
| _advance_resv_time(resv_ptr); |
| if ((resv_ptr->job_pend_cnt == 0) && |
| (resv_ptr->job_run_cnt == 0) && |
| (resv_ptr->maint_set_node == 0) && |
| ((resv_ptr->flags & RESERVE_FLAG_DAILY ) == 0) && |
| ((resv_ptr->flags & RESERVE_FLAG_WEEKLY) == 0)) { |
| debug("Purging vestigial reservation record %s", |
| resv_ptr->name); |
| _clear_job_resv(resv_ptr); |
| list_delete_item(iter); |
| last_resv_update = now; |
| schedule_resv_save(); |
| } |
| |
| } |
| list_iterator_destroy(iter); |
| } |
| |
| /* send all reservations to accounting. Only needed at |
| * first registration |
| */ |
| extern int send_resvs_to_accounting(void) |
| { |
| ListIterator itr = NULL; |
| slurmctld_resv_t *resv_ptr; |
| |
| if(!resv_list) |
| return SLURM_SUCCESS; |
| |
| itr = list_iterator_create(resv_list); |
| while ((resv_ptr = list_next(itr))) { |
| _post_resv_create(resv_ptr); |
| } |
| list_iterator_destroy(itr); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| |
| /* Set or clear NODE_STATE_MAINT for node_state as needed */ |
| extern void set_node_maint_mode(void) |
| { |
| ListIterator iter; |
| slurmctld_resv_t *resv_ptr; |
| time_t now = time(NULL); |
| |
| if (!resv_list) |
| return; |
| |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((resv_ptr = (slurmctld_resv_t *) list_next(iter))) { |
| if ((resv_ptr->flags & RESERVE_FLAG_MAINT) == 0) |
| continue; |
| if ((now >= resv_ptr->start_time) && |
| (now < resv_ptr->end_time )) { |
| if (!resv_ptr->maint_set_node) { |
| resv_ptr->maint_set_node = true; |
| _set_nodes_maint(resv_ptr, now); |
| last_node_update = now; |
| } |
| } else if (resv_ptr->maint_set_node) { |
| resv_ptr->maint_set_node = false; |
| _set_nodes_maint(resv_ptr, now); |
| last_node_update = now; |
| } |
| } |
| list_iterator_destroy(iter); |
| } |
| |
| extern void update_assocs_in_resvs(void) |
| { |
| slurmctld_resv_t *resv_ptr = NULL; |
| ListIterator iter = NULL; |
| |
| if (!resv_list) |
| error("No reservation list given for updating associations"); |
| |
| iter = list_iterator_create(resv_list); |
| if (!iter) |
| fatal("malloc: list_iterator_create"); |
| while ((resv_ptr = list_next(iter))) { |
| _set_assoc_list(resv_ptr); |
| } |
| list_iterator_destroy(iter); |
| } |
| |
| static void _set_nodes_maint(slurmctld_resv_t *resv_ptr, time_t now) |
| { |
| int i, i_first, i_last; |
| struct node_record *node_ptr; |
| |
| if (!resv_ptr->node_bitmap) { |
| error("reservation %s lacks a bitmap", resv_ptr->name); |
| return; |
| } |
| |
| i_first = bit_ffs(resv_ptr->node_bitmap); |
| i_last = bit_fls(resv_ptr->node_bitmap); |
| for (i=i_first; i<=i_last; i++) { |
| if (!bit_test(resv_ptr->node_bitmap, i)) |
| continue; |
| |
| node_ptr = node_record_table_ptr + i; |
| if (resv_ptr->maint_set_node) |
| node_ptr->node_state |= NODE_STATE_MAINT; |
| else |
| node_ptr->node_state &= (~NODE_STATE_MAINT); |
| /* mark that this node is now down and in maint mode |
| * or was removed from maint mode */ |
| if (IS_NODE_DOWN(node_ptr) || IS_NODE_DRAIN(node_ptr) || |
| IS_NODE_FAIL(node_ptr)) { |
| clusteracct_storage_g_node_down( |
| acct_db_conn, |
| node_ptr, now, NULL, |
| slurm_get_slurm_user_id()); |
| } |
| } |
| } |