From 80875b329a171cf624c47e83a31ea236fd8ae621 Mon Sep 17 00:00:00 2001 From: Moe Jette <jette1@llnl.gov> Date: Fri, 23 Jan 2009 19:00:32 +0000 Subject: [PATCH] Add basic infrastructure for resource reservation. Still under development svn m_e_r_g_e -r16224:16292 https://eris.llnl.gov/svn/slurm/branches/slurm-1.4.resv --- slurm/slurm.h.in | 162 +++- slurm/slurm_errno.h | 1 + src/api/Makefile.am | 1 + src/api/Makefile.in | 8 +- src/api/init_msg.c | 25 +- src/api/reservation_info.c | 199 +++++ src/api/update_config.c | 80 +- src/common/slurm_errno.c | 2 + src/common/slurm_protocol_defs.c | 85 +- src/common/slurm_protocol_defs.h | 19 +- src/common/slurm_protocol_pack.c | 257 +++++- src/scontrol/Makefile.am | 2 + src/scontrol/Makefile.in | 8 +- src/scontrol/create_res.c | 310 +++++++ src/scontrol/info_part.c | 2 +- src/scontrol/info_res.c | 139 ++++ src/scontrol/scontrol.c | 59 +- src/scontrol/scontrol.h | 4 + src/scontrol/update_part.c | 169 ++-- src/slurmctld/partition_mgr.c | 8 +- src/slurmctld/proc_req.c | 222 +++++- src/slurmctld/read_config.c | 2 + src/slurmctld/reservation.c | 1286 +++++++++++++++++++++++++++++- src/slurmctld/reservation.h | 34 +- src/slurmctld/slurmctld.h | 2 +- src/slurmctld/state_save.c | 27 +- src/slurmctld/state_save.h | 6 +- 27 files changed, 2989 insertions(+), 130 deletions(-) create mode 100644 src/api/reservation_info.c create mode 100644 src/scontrol/create_res.c create mode 100644 src/scontrol/info_res.c diff --git a/slurm/slurm.h.in b/slurm/slurm.h.in index 69565e663c6..69322c6d8ca 100644 --- a/slurm/slurm.h.in +++ b/slurm/slurm.h.in @@ -956,6 +956,57 @@ typedef struct partition_info_msg { partition_info_t *partition_array; /* the partition records */ } partition_info_msg_t; +/* + * Resource reservation data structures. + * Create, show, modify and delete functions are required + */ +#define RESERVE_TYPE_MAINT 0x0001 + +typedef struct reserve_info { + char *name; /* name of reservation */ + time_t start_time; /* start time of reservation */ + time_t end_time; /* end time of reservation */ + uint16_t type; /* see RESERVE_TYPE_* above */ + uint32_t node_cnt; /* count of nodes required */ + char *node_list; /* list of reserved nodes or ALL */ + char *features; /* required node features */ + char *partition; /* name of partition to be used */ +/* Access control specification for the reservation */ + char *users; /* names of users permitted to use */ + char *accounts; /* names of accounts permitted to use */ +} reserve_info_t; + +typedef struct reserve_info_msg { + time_t last_update; /* time of latest info */ + uint32_t record_count; /* number of records */ + reserve_info_t *reservation_array; /* the reservation records */ +} reserve_info_msg_t; + +typedef struct reserve_request_msg { + char *name; /* name of reservation */ + time_t start_time; /* start time of reservation */ + time_t end_time; /* end time of reservation */ + uint32_t duration; /* duration of reservation in seconds */ + uint16_t type; /* see RESERVE_TYPE_* above */ + uint32_t node_cnt; /* count of nodes required */ + char *node_list; /* list of reserved nodes or ALL */ + char *features; /* required node features */ + char *partition; /* name of partition to be used */ +/* Access control specification for the reservation */ + char *users; /* names of users permitted to use */ + char *accounts; /* names of accounts permitted to use */ +} reserve_request_msg_t; + +typedef struct reserve_response_msg { + char *name; /* name of reservation */ +} reserve_response_msg_t; + +typedef struct reservation_name_msg { + char *name; /* name of reservation just created or + * to be delete */ +} reservation_name_msg_t; + + #define DEBUG_FLAG_SELECT_TYPE 0x00000001 /* SelectType plugin */ #define DEBUG_FLAG_STEPS 0x00000002 /* slurmctld steps */ #define DEBUG_FLAG_TRIGGERS 0x00000004 /* slurmctld triggers */ @@ -964,12 +1015,12 @@ typedef struct partition_info_msg { typedef struct slurm_ctl_conf { time_t last_update; /* last update time of the build parameters */ uint16_t accounting_storage_enforce; /* job requires valid association: - * user/account/partition/cluster */ - char *accounting_storage_host; /* accounting storage host */ - char *accounting_storage_loc; /* accounting storage (db table) - * location */ - char *accounting_storage_pass; /* accounting storage - password */ + * user/account/partition/cluster */ + char *accounting_storage_host; /* accounting storage host */ + char *accounting_storage_loc; /* accounting storage (db table) + * location */ + char *accounting_storage_pass; /* accounting storage + * password */ uint32_t accounting_storage_port;/* node accountinging storage port */ char *accounting_storage_type; /* accounting storage type */ char *accounting_storage_user; /* accounting storage user */ @@ -1855,7 +1906,7 @@ extern int slurm_update_node PARAMS(( update_node_msg_t * node_msg )); /* * slurm_init_part_desc_msg - initialize partition descriptor with * default values - * OUT job_desc_msg - user defined partition descriptor + * IN/OUT update_part_msg - user defined partition descriptor */ extern void slurm_init_part_desc_msg PARAMS((update_part_msg_t * update_part_msg )); @@ -1912,6 +1963,14 @@ extern void slurm_print_partition_info PARAMS(( */ extern char *slurm_sprint_partition_info PARAMS(( partition_info_t * part_ptr, int one_liner )); + +/* + * slurm_create_partition - create a new partition, only usable by user root + * IN part_msg - description of partition configuration + * RET 0 on success, otherwise return -1 and set errno to indicate the error + */ +extern int slurm_create_partition PARAMS(( update_part_msg_t * part_msg )); + /* * slurm_update_partition - issue RPC to update a partition's configuration * per request, only usable by user root @@ -1928,6 +1987,95 @@ extern int slurm_update_partition PARAMS(( update_part_msg_t * part_msg )); */ extern int slurm_delete_partition PARAMS(( delete_part_msg_t * part_msg )); +/*****************************************************************************\ + * SLURM PARTITION CONFIGURATION READ/PRINT/UPDATE FUNCTIONS +\*****************************************************************************/ + +/* + * slurm_init_resv_desc_msg - initialize reservation descriptor with + * default values + * OUT job_desc_msg - user defined partition descriptor + */ +extern void slurm_init_resv_desc_msg PARAMS(( + reserve_request_msg_t * update_resv_msg )); +/* + * slurm_create_reservation - create a new reservation, only usable by user root + * IN resv_msg - description of reservation + * RET name of reservation on success (caller must free the memory), + * otherwise return NULL and set errno to indicate the error + */ +extern char * slurm_create_reservation PARAMS(( + reserve_request_msg_t * resv_msg )); + +/* + * slurm_update_reservation - modify an existing reservation, only usable by + * user root + * IN resv_msg - description of reservation + * RET 0 on success, otherwise return -1 and set errno to indicate the error + */ +extern int slurm_update_reservation PARAMS((reserve_request_msg_t * resv_msg)); + +/* + * slurm_delete_reservation - issue RPC to delete a reservation, only usable + * by user root + * IN resv_msg - description of reservation to delete + * RET 0 on success, otherwise return -1 and set errno to indicate the error + */ +extern int slurm_delete_reservation PARAMS(( + reservation_name_msg_t * resv_msg )); + +/* + * slurm_load_reservations - issue RPC to get all slurm reservation + * configuration information if changed since update_time + * IN update_time - time of current configuration data + * IN reserve_info_msg_pptr - place to store a reservation configuration + * pointer + * RET 0 or a slurm error code + * NOTE: free the response using slurm_free_reservation_info_msg + */ +extern int slurm_load_reservations PARAMS(( time_t update_time, + reserve_info_msg_t **resp )); + +/* + * slurm_print_reservation_info_msg - output information about all Slurm + * reservations based upon message as loaded using slurm_load_reservation + * IN out - file to write to + * IN resv_info_ptr - reservation information message pointer + * IN one_liner - print as a single line if true + */ +void slurm_print_reservation_info_msg PARAMS(( FILE* out, + reserve_info_msg_t * resv_info_ptr, int one_liner )); + +/* + * slurm_print_reservation_info - output information about a specific Slurm + * reservation based upon message as loaded using slurm_load_reservation + * IN out - file to write to + * IN resv_ptr - an individual reservation information record pointer + * IN one_liner - print as a single line if true + */ +void slurm_print_reservation_info PARAMS(( FILE* out, + reserve_info_t * resv_ptr, int one_liner )); + +/* + * slurm_sprint_reservation_info - output information about a specific Slurm + * reservation based upon message as loaded using slurm_load_reservations + * IN resv_ptr - an individual reservation information record pointer + * IN one_liner - print as a single line if true + * RET out - char * containing formatted output (must be freed after call) + * NULL is returned on failure. + */ +char *slurm_sprint_reservation_info PARAMS(( reserve_info_t * resv_ptr, + int one_liner )); + +/* + * slurm_free_reservation_info_msg - free the reservation information + * response message + * IN msg - pointer to reservation information response message + * NOTE: buffer is loaded by slurm_load_reservation + */ +extern void slurm_free_reservation_info_msg PARAMS(( + reserve_info_msg_t * resv_info_ptr )); + /*****************************************************************************\ * SLURM PING/RECONFIGURE/SHUTDOWN FUNCTIONS \*****************************************************************************/ diff --git a/slurm/slurm_errno.h b/slurm/slurm_errno.h index 6ab77838ab6..e66498fe430 100644 --- a/slurm/slurm_errno.h +++ b/slurm/slurm_errno.h @@ -161,6 +161,7 @@ enum { ESLURM_INVALID_TIME_LIMIT, ESLURM_RESERVATION_ACCESS, ESLURM_RESERVATION_INVALID, + ESLURM_INVALID_TIME_VALUE, /* switch specific error codes, specific values defined in plugin module */ ESLURM_SWITCH_MIN = 3000, diff --git a/src/api/Makefile.am b/src/api/Makefile.am index 0000a85496a..ef5543cba7a 100644 --- a/src/api/Makefile.am +++ b/src/api/Makefile.am @@ -76,6 +76,7 @@ slurmapi_src = \ node_info.c \ node_select_info.c node_select_info.h \ partition_info.c \ + reservation_info.c \ signal.c \ slurm_pmi.c slurm_pmi.h \ step_ctx.c step_ctx.h \ diff --git a/src/api/Makefile.in b/src/api/Makefile.in index 55ef23745d0..ce861a4f972 100644 --- a/src/api/Makefile.in +++ b/src/api/Makefile.in @@ -100,9 +100,9 @@ libslurmhelper_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am__objects_1 = allocate.lo allocate_msg.lo cancel.lo checkpoint.lo \ complete.lo config_info.lo init_msg.lo job_info.lo \ job_step_info.lo node_info.lo node_select_info.lo \ - partition_info.lo signal.lo slurm_pmi.lo step_ctx.lo \ - step_io.lo step_launch.lo pmi_server.lo submit.lo suspend.lo \ - triggers.lo reconfigure.lo update_config.lo + partition_info.lo reservation_info.lo signal.lo slurm_pmi.lo \ + step_ctx.lo step_io.lo step_launch.lo pmi_server.lo submit.lo \ + suspend.lo triggers.lo reconfigure.lo update_config.lo am_libslurmhelper_la_OBJECTS = $(am__objects_1) libslurmhelper_la_OBJECTS = $(am_libslurmhelper_la_OBJECTS) PROGRAMS = $(noinst_PROGRAMS) @@ -371,6 +371,7 @@ slurmapi_src = \ node_info.c \ node_select_info.c node_select_info.h \ partition_info.c \ + reservation_info.c \ signal.c \ slurm_pmi.c slurm_pmi.h \ step_ctx.c step_ctx.h \ @@ -536,6 +537,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pmi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pmi_server.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reconfigure.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reservation_info.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signal.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slurm_pmi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/step_ctx.Plo@am__quote@ diff --git a/src/api/init_msg.c b/src/api/init_msg.c index 95d9ea6187d..b23c73eac16 100644 --- a/src/api/init_msg.c +++ b/src/api/init_msg.c @@ -2,7 +2,7 @@ * init_msg.c - initialize RPC messages contents ***************************************************************************** * Copyright (C) 2002-2007 The Regents of the University of California. - * Copyright (C) 2008 Lawrence Livermore National Security. + * Copyright (C) 2008-2009 Lawrence Livermore National Security. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Morris Jette <jette1@llnl.gov>. * LLNL-CODE-402394. @@ -51,7 +51,7 @@ /* * slurm_init_job_desc_msg - initialize job descriptor with * default values - * OUT job_desc_msg - user defined job descriptor + * IN/OUT job_desc_msg - user defined job descriptor */ void slurm_init_job_desc_msg(job_desc_msg_t * job_desc_msg) { @@ -144,7 +144,7 @@ void slurm_init_job_desc_msg(job_desc_msg_t * job_desc_msg) /* * slurm_init_part_desc_msg - initialize partition descriptor with * default values - * OUT job_desc_msg - user defined partition descriptor + * IN/OUT update_part_msg - user defined partition descriptor */ void slurm_init_part_desc_msg (update_part_msg_t * update_part_msg) { @@ -162,3 +162,22 @@ void slurm_init_part_desc_msg (update_part_msg_t * update_part_msg) update_part_msg->state_up = (uint16_t) NO_VAL; } +/* + * slurm_init_resv_desc_msg - initialize reservation descriptor with + * default values + * OUT job_desc_msg - user defined partition descriptor + */ +void slurm_init_resv_desc_msg (reserve_request_msg_t * update_resv_msg) +{ + update_resv_msg->name = NULL; + update_resv_msg->start_time = (time_t) NO_VAL; + update_resv_msg->end_time = (time_t) NO_VAL; + update_resv_msg->duration = NO_VAL; + update_resv_msg->type = (uint16_t) NO_VAL; + update_resv_msg->node_cnt = NO_VAL; + update_resv_msg->node_list = NULL; + update_resv_msg->features = NULL; + update_resv_msg->partition = NULL; + update_resv_msg->users = NULL; + update_resv_msg->accounts = NULL; +} diff --git a/src/api/reservation_info.c b/src/api/reservation_info.c new file mode 100644 index 00000000000..50bd390f4c1 --- /dev/null +++ b/src/api/reservation_info.c @@ -0,0 +1,199 @@ +/*****************************************************************************\ + * reseration_info.c - get/print the reservation state information of slurm + ***************************************************************************** + * Copyright (C) 2009 Lawrence Livermore National Security. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * Written by Morris Jette <jette1@llnl.gov> et. al. + * LLNL-CODE-402394. + * + * This file is part of SLURM, a resource management program. + * For details, see <http://www.llnl.gov/linux/slurm/>. + * + * 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 + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include <slurm/slurm.h> + +#include "src/api/job_info.h" +#include "src/common/parse_time.h" +#include "src/common/slurm_protocol_api.h" +#include "src/common/xmalloc.h" +#include "src/common/xstring.h" + +/* + * slurm_print_reservation_info_msg - output information about all Slurm + * reservations based upon message as loaded using slurm_load_reservation + * IN out - file to write to + * IN resv_info_ptr - reservation information message pointer + * IN one_liner - print as a single line if true + */ +void slurm_print_reservation_info_msg ( FILE* out, + reserve_info_msg_t * resv_info_ptr, int one_liner ) +{ + int i ; + reserve_info_t * resv_ptr = resv_info_ptr->reservation_array ; + char time_str[32]; + + slurm_make_time_str( (time_t *)&resv_info_ptr->last_update, time_str, + sizeof(time_str)); + fprintf( out, "Reservation data as of %s, record count %d\n", + time_str, resv_info_ptr->record_count); + + for (i = 0; i < resv_info_ptr->record_count; i++) { + slurm_print_reservation_info ( out, & resv_ptr[i], one_liner ); + } + +} + +/* + * slurm_print_reservation_info - output information about a specific Slurm + * reservation based upon message as loaded using slurm_load_reservation + * IN out - file to write to + * IN resv_ptr - an individual reservation information record pointer + * IN one_liner - print as a single line if true + */ +void slurm_print_reservation_info ( FILE* out, reserve_info_t * resv_ptr, + int one_liner ) +{ + char *print_this = slurm_sprint_reservation_info(resv_ptr, one_liner); + fprintf ( out, "%s", print_this); + xfree(print_this); +} + + +/* + * slurm_sprint_reservation_info - output information about a specific Slurm + * reservation based upon message as loaded using slurm_load_reservations + * IN resv_ptr - an individual reservation information record pointer + * IN one_liner - print as a single line if true + * RET out - char * containing formatted output (must be freed after call) + * NULL is returned on failure. + */ +char *slurm_sprint_reservation_info ( reserve_info_t * resv_ptr, + int one_liner ) +{ + char tmp1[32], tmp2[32], *tmp3; + char tmp_line[MAXHOSTRANGELEN]; + char *out = NULL; + + /****** Line 1 ******/ + slurm_make_time_str(&resv_ptr->start_time, tmp1, sizeof(tmp1)); + slurm_make_time_str(&resv_ptr->end_time, tmp2, sizeof(tmp2)); + snprintf(tmp_line, sizeof(tmp_line), + "ReservationName=%s StartTime=%s EndTime=%s Duration=%u", + resv_ptr->name, tmp1, tmp2, + (uint32_t) (difftime(resv_ptr->end_time, + resv_ptr->start_time) / 60)); + xstrcat(out, tmp_line); + + if (one_liner) + xstrcat(out, " "); + else + xstrcat(out, "\n "); + + /****** Line 2 ******/ + if (resv_ptr->type == RESERVE_TYPE_MAINT) + tmp3 = "MAINT"; + else + tmp3 = ""; + snprintf(tmp_line, sizeof(tmp_line), + "Nodes=%s NodeCnt=%u Features=%s PartitionName=%s Type=%s", + resv_ptr->node_list, resv_ptr->node_cnt, + resv_ptr->features, resv_ptr->partition, tmp3); + xstrcat(out, tmp_line); + if (one_liner) + xstrcat(out, " "); + else + xstrcat(out, "\n "); + + /****** Line 3 ******/ + snprintf(tmp_line, sizeof(tmp_line), + "Users=%s Accounts=%s", + resv_ptr->users, resv_ptr->accounts); + xstrcat(out, tmp_line); + if (one_liner) + xstrcat(out, "\n"); + else + xstrcat(out, "\n\n"); + + return out; +} + + + +/* + * slurm_load_reservations - issue RPC to get all slurm reservation + * configuration information if changed since update_time + * IN update_time - time of current configuration data + * IN reserve_info_msg_pptr - place to store a reservation configuration + * pointer + * RET 0 or a slurm error code + * NOTE: free the response using slurm_free_reservation_info_msg + */ +extern int slurm_load_reservations (time_t update_time, + reserve_info_msg_t **resp) +{ + int rc; + slurm_msg_t req_msg; + slurm_msg_t resp_msg; + resv_info_request_msg_t req; + + slurm_msg_t_init(&req_msg); + slurm_msg_t_init(&resp_msg); + + req.last_update = update_time; + req_msg.msg_type = REQUEST_RESERVATION_INFO; + req_msg.data = &req; + + if (slurm_send_recv_controller_msg(&req_msg, &resp_msg) < 0) + return SLURM_ERROR; + + switch (resp_msg.msg_type) { + case RESPONSE_RESERVATION_INFO: + *resp = (reserve_info_msg_t *) resp_msg.data; + break; + case RESPONSE_SLURM_RC: + rc = ((return_code_msg_t *) resp_msg.data)->return_code; + slurm_free_return_code_msg(resp_msg.data); + if (rc) + slurm_seterrno_ret(rc); + *resp = NULL; + break; + default: + slurm_seterrno_ret(SLURM_UNEXPECTED_MSG_ERROR); + break; + } + + return SLURM_PROTOCOL_SUCCESS; +} diff --git a/src/api/update_config.c b/src/api/update_config.c index bcb54abd74d..fed93a03431 100644 --- a/src/api/update_config.c +++ b/src/api/update_config.c @@ -2,7 +2,8 @@ * update_config.c - request that slurmctld update its configuration * $Id$ ***************************************************************************** - * Copyright (C) 2002 The Regents of the University of California. + * Copyright (C) 2002-2007 The Regents of the University of California. + * Copyright (C) 2008-2009 Lawrence Livermore National Security. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Morris Jette <jette1@llnl.gov> and Kevin Tew <tew1@llnl.gov>. * LLNL-CODE-402394. @@ -43,6 +44,7 @@ #include <errno.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <slurm/slurm.h> @@ -74,6 +76,17 @@ slurm_update_node ( update_node_msg_t * node_msg ) return _slurm_update ((void *) node_msg, REQUEST_UPDATE_NODE); } +/* + * slurm_create_partition - create a new partition, only usable by user root + * IN part_msg - description of partition configuration + * RET 0 on success, otherwise return -1 and set errno to indicate the error + */ +int +slurm_create_partition ( update_part_msg_t * part_msg ) +{ + return _slurm_update ((void *) part_msg, REQUEST_CREATE_PARTITION); +} + /* * slurm_update_partition - issue RPC to a partition's configuration per * request, only usable by user root @@ -89,7 +102,7 @@ slurm_update_partition ( update_part_msg_t * part_msg ) /* * slurm_delete_partition - issue RPC to delete a partition, only usable * by user root - * IN part_msg - description of partition updates + * IN part_msg - description of partition to delete * RET 0 on success, otherwise return -1 and set errno to indicate the error */ int @@ -98,6 +111,69 @@ slurm_delete_partition ( delete_part_msg_t * part_msg ) return _slurm_update ((void *) part_msg, REQUEST_DELETE_PARTITION); } +/* + * slurm_create_reservation - create a new reservation, only usable by user root + * IN resv_msg - description of reservation + * RET name of reservation on success (caller must free the memory), + * otherwise return NULL and set errno to indicate the error + */ +char * +slurm_create_reservation (reserve_request_msg_t * resv_msg ) +{ + int rc; + char *resv_name = NULL; + slurm_msg_t req_msg; + slurm_msg_t resp_msg; + reservation_name_msg_t *resp; + + slurm_msg_t_init(&req_msg); + slurm_msg_t_init(&resp_msg); + + req_msg.msg_type = REQUEST_CREATE_RESERVATION; + req_msg.data = resv_msg; + + rc = slurm_send_recv_controller_msg(&req_msg, &resp_msg); + switch (resp_msg.msg_type) { + case RESPONSE_CREATE_RESERVATION: + resp = (reservation_name_msg_t *) resp_msg.data; + resv_name = strdup(resp->name); + break; + case RESPONSE_SLURM_RC: + rc = ((return_code_msg_t *) resp_msg.data)->return_code; + if (rc) + slurm_seterrno(rc); + break; + default: + slurm_seterrno(SLURM_UNEXPECTED_MSG_ERROR); + } + slurm_free_msg_data(resp_msg.msg_type, resp_msg.data); + return resv_name; +} + +/* + * slurm_update_reservation - modify an existing reservation, only usable by + * user root + * IN resv_msg - description of reservation + * RET 0 on success, otherwise return -1 and set errno to indicate the error + */ +extern int slurm_update_reservation ( reserve_request_msg_t * resv_msg ) +{ + return _slurm_update ((void *) resv_msg, REQUEST_UPDATE_RESERVATION); +} + +/* + * slurm_delete_reservation - issue RPC to delete a reservation, only usable + * by user root + * IN resv_msg - description of reservation to delete + * RET 0 on success, otherwise return -1 and set errno to indicate the error + */ +int +slurm_delete_reservation ( reservation_name_msg_t * resv_msg ) +{ + return _slurm_update ((void *) resv_msg, REQUEST_DELETE_RESERVATION); +} + + /* _slurm_update - issue RPC for all update requests */ static int _slurm_update (void *data, slurm_msg_type_t msg_type) diff --git a/src/common/slurm_errno.c b/src/common/slurm_errno.c index fe1f2c4f757..2dfabfe37b5 100644 --- a/src/common/slurm_errno.c +++ b/src/common/slurm_errno.c @@ -226,6 +226,8 @@ static slurm_errtab_t slurm_errtab[] = { "Access denied to requested reservation" }, { ESLURM_RESERVATION_INVALID, "Requested reservation is invalid" }, + { ESLURM_INVALID_TIME_VALUE, + "Invalid time specified" }, /* slurmd error codes */ diff --git a/src/common/slurm_protocol_defs.c b/src/common/slurm_protocol_defs.c index 32b062d4045..c4a82526928 100644 --- a/src/common/slurm_protocol_defs.c +++ b/src/common/slurm_protocol_defs.c @@ -4,7 +4,7 @@ * the slurm daemons directly, not for user client use. ***************************************************************************** * Copyright (C) 2002-2007 The Regents of the University of California. - * Copyright (C) 2008 Lawrence Livermore National Security. + * Copyright (C) 2008-2009 Lawrence Livermore National Security. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Kevin Tew <tew1@llnl.gov> et. al. * LLNL-CODE-402394. @@ -67,6 +67,9 @@ static void _slurm_free_node_info_members (node_info_t * node); static void _free_all_partitions (partition_info_msg_t *msg); static void _slurm_free_partition_info_members (partition_info_t * part); +static void _free_all_reservations(reserve_info_msg_t *msg); +static void _slurm_free_reserve_info_members (reserve_info_t * part); + static void _free_all_step_info (job_step_info_response_msg_t *msg); static void _slurm_free_job_step_info_members (job_step_info_t * msg); static void _make_lower(char *change); @@ -280,6 +283,12 @@ void inline slurm_free_part_info_request_msg( xfree(msg); } +void inline slurm_free_resv_info_request_msg( + resv_info_request_msg_t *msg) +{ + xfree(msg); +} + void slurm_free_job_desc_msg(job_desc_msg_t * msg) { int i; @@ -432,6 +441,27 @@ void slurm_free_delete_part_msg(delete_part_msg_t * msg) } } +void slurm_free_update_resv_msg(reserve_request_msg_t * msg) +{ + if (msg) { + xfree(msg->accounts); + xfree(msg->features); + xfree(msg->partition); + xfree(msg->name); + xfree(msg->node_list); + xfree(msg->users); + xfree(msg); + } +} + +void slurm_free_resv_name_msg(reservation_name_msg_t * msg) +{ + if (msg) { + xfree(msg->name); + xfree(msg); + } +} + void slurm_free_job_step_create_request_msg(job_step_create_request_msg_t * msg) { @@ -1295,6 +1325,49 @@ static void _slurm_free_partition_info_members(partition_info_t * part) } } +/* + * slurm_free_reserve_info_msg - free the reservation information + * response message + * IN msg - pointer to reservation information response message + * NOTE: buffer is loaded by slurm_load_reservation + */ +void slurm_free_reservation_info_msg(reserve_info_msg_t * msg) +{ + if (msg) { + if (msg->reservation_array) { + _free_all_reservations(msg); + xfree(msg->reservation_array); + } + xfree(msg); + } +} + +static void _free_all_reservations(reserve_info_msg_t *msg) +{ + int i; + + if ((msg == NULL) || + (msg->reservation_array == NULL)) + return; + + for (i = 0; i < msg->record_count; i++) + _slurm_free_reserve_info_members( + &msg->reservation_array[i]); + +} + +static void _slurm_free_reserve_info_members(reserve_info_t * resv) +{ + if (resv) { + xfree(resv->accounts); + xfree(resv->features); + xfree(resv->name); + xfree(resv->node_list); + xfree(resv->partition); + xfree(resv->users); + } +} + extern void slurm_free_file_bcast_msg(file_bcast_msg_t *msg) { if (msg) { @@ -1438,12 +1511,21 @@ extern int slurm_free_msg_data(slurm_msg_type_t type, void *data) case REQUEST_UPDATE_NODE: slurm_free_update_node_msg(data); break; + case REQUEST_CREATE_PARTITION: case REQUEST_UPDATE_PARTITION: slurm_free_update_part_msg(data); break; case REQUEST_DELETE_PARTITION: slurm_free_delete_part_msg(data); break; + case REQUEST_CREATE_RESERVATION: + case REQUEST_UPDATE_RESERVATION: + slurm_free_update_resv_msg(data); + break; + case REQUEST_DELETE_RESERVATION: + case RESPONSE_CREATE_RESERVATION: + slurm_free_resv_name_msg(data); + break; case REQUEST_NODE_REGISTRATION_STATUS: slurm_free_node_registration_status_msg(data); break; @@ -1535,6 +1617,7 @@ extern int slurm_free_msg_data(slurm_msg_type_t type, void *data) case REQUEST_DAEMON_STATUS: case REQUEST_HEALTH_CHECK: case ACCOUNTING_FIRST_REG: + case REQUEST_RESERVATION_INFO: /* No body to free */ break; case ACCOUNTING_UPDATE_MSG: diff --git a/src/common/slurm_protocol_defs.h b/src/common/slurm_protocol_defs.h index 71f488ae9a0..e8a02052ab8 100644 --- a/src/common/slurm_protocol_defs.h +++ b/src/common/slurm_protocol_defs.h @@ -2,7 +2,7 @@ * slurm_protocol_defs.h - definitions used for RPCs ***************************************************************************** * Copyright (C) 2002-2007 The Regents of the University of California. - * Copyright (C) 2008 Lawrence Livermore National Security. + * Copyright (C) 2008-2009 Lawrence Livermore National Security. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Kevin Tew <tew1@llnl.gov>. * LLNL-CODE-402394. @@ -119,11 +119,18 @@ typedef enum { REQUEST_JOB_INFO_SINGLE, REQUEST_SHARE_INFO, RESPONSE_SHARE_INFO, + REQUEST_RESERVATION_INFO, + RESPONSE_RESERVATION_INFO, REQUEST_UPDATE_JOB = 3001, REQUEST_UPDATE_NODE, - REQUEST_UPDATE_PARTITION, + REQUEST_CREATE_PARTITION, REQUEST_DELETE_PARTITION, + REQUEST_UPDATE_PARTITION, + REQUEST_CREATE_RESERVATION, + RESPONSE_CREATE_RESERVATION, + REQUEST_DELETE_RESERVATION, + REQUEST_UPDATE_RESERVATION, REQUEST_RESOURCE_ALLOCATION = 4001, RESPONSE_RESOURCE_ALLOCATION, @@ -371,6 +378,10 @@ typedef struct part_info_request_msg { uint16_t show_flags; } part_info_request_msg_t; +typedef struct resv_info_request_msg { + time_t last_update; +} resv_info_request_msg_t; + typedef struct complete_job_allocation { uint32_t job_id; uint32_t job_rc; @@ -795,6 +806,7 @@ void inline slurm_free_job_step_info_request_msg( job_step_info_request_msg_t *msg); void inline slurm_free_node_info_request_msg(node_info_request_msg_t *msg); void inline slurm_free_part_info_request_msg(part_info_request_msg_t *msg); +void inline slurm_free_resv_info_request_msg(resv_info_request_msg_t *msg); void inline slurm_free_set_debug_level_msg(set_debug_level_msg_t *msg); void inline slurm_destroy_association_shares_object(void *object); void inline slurm_free_shares_request_msg(shares_request_msg_t *msg); @@ -825,6 +837,9 @@ void inline slurm_free_job_launch_msg(batch_job_launch_msg_t * msg); void inline slurm_free_update_node_msg(update_node_msg_t * msg); void inline slurm_free_update_part_msg(update_part_msg_t * msg); void inline slurm_free_delete_part_msg(delete_part_msg_t * msg); +void inline slurm_free_update_resv_msg(reserve_request_msg_t * msg); +void inline slurm_free_resv_name_msg(reservation_name_msg_t * msg); +void inline slurm_free_resv_info_msg(reserve_info_msg_t * msg); void inline slurm_free_job_step_create_request_msg(job_step_create_request_msg_t * msg); void inline diff --git a/src/common/slurm_protocol_pack.c b/src/common/slurm_protocol_pack.c index 0503a91a69c..4272c92b71a 100644 --- a/src/common/slurm_protocol_pack.c +++ b/src/common/slurm_protocol_pack.c @@ -69,6 +69,7 @@ #define _pack_node_select_info_msg(msg,buf) _pack_buffer_msg(msg,buf) #define _pack_node_info_msg(msg,buf) _pack_buffer_msg(msg,buf) #define _pack_partition_info_msg(msg,buf) _pack_buffer_msg(msg,buf) +#define _pack_reserve_info_msg(msg,buf) _pack_buffer_msg(msg,buf) static void _pack_assoc_shares_object(void *in, Buf buffer); static int _unpack_assoc_shares_object(void **object, Buf buffer); @@ -151,15 +152,24 @@ static int _unpack_job_step_create_response_msg( static void _pack_part_info_request_msg(part_info_request_msg_t * msg, Buf buffer); - static int _unpack_part_info_request_msg(part_info_request_msg_t ** msg, Buf buffer); +static void _pack_resv_info_request_msg(resv_info_request_msg_t * msg, + Buf buffer); +static int _unpack_resv_info_request_msg(resv_info_request_msg_t ** + msg, Buf buffer); + static int _unpack_partition_info_msg(partition_info_msg_t ** msg, Buf buffer); static int _unpack_partition_info_members(partition_info_t * part, Buf buffer); +static int _unpack_reserve_info_msg(reserve_info_msg_t ** msg, + Buf buffer); +static int _unpack_reserve_info_members(reserve_info_t * resv, + Buf buffer); + static void _pack_launch_tasks_request_msg(launch_tasks_request_msg_t * msg, Buf buffer); static int _unpack_launch_tasks_request_msg(launch_tasks_request_msg_t ** @@ -358,6 +368,11 @@ static void _pack_accounting_update_msg(accounting_update_msg_t *msg, static int _unpack_accounting_update_msg(accounting_update_msg_t **msg, Buf buffer); +static void _pack_update_resv_msg(reserve_request_msg_t * msg, Buf buffer); +static int _unpack_update_resv_msg(reserve_request_msg_t ** msg, Buf buffer); +static void _pack_resv_name_msg(reservation_name_msg_t * msg, Buf buffer); +static int _unpack_resv_name_msg(reservation_name_msg_t ** msg, Buf buffer); + /* pack_header * packs a slurm protocol header that proceeds every slurm message * IN header - the header structure to pack @@ -451,6 +466,10 @@ pack_msg(slurm_msg_t const *msg, Buf buffer) _pack_part_info_request_msg((part_info_request_msg_t *) msg->data, buffer); break; + case REQUEST_RESERVATION_INFO: + _pack_resv_info_request_msg((resv_info_request_msg_t *) + msg->data, buffer); + break; case REQUEST_BUILD_INFO: case REQUEST_ACCTING_INFO: _pack_last_update_msg((last_update_msg_t *) @@ -523,6 +542,7 @@ pack_msg(slurm_msg_t const *msg, Buf buffer) _pack_update_node_msg((update_node_msg_t *) msg->data, buffer); break; + case REQUEST_CREATE_PARTITION: case REQUEST_UPDATE_PARTITION: _pack_update_partition_msg((update_part_msg_t *) msg-> data, buffer); @@ -531,6 +551,19 @@ pack_msg(slurm_msg_t const *msg, Buf buffer) _pack_delete_partition_msg((delete_part_msg_t *) msg-> data, buffer); break; + case REQUEST_CREATE_RESERVATION: + case REQUEST_UPDATE_RESERVATION: + _pack_update_resv_msg((reserve_request_msg_t *) msg-> + data, buffer); + break; + case RESPONSE_RESERVATION_INFO: + _pack_reserve_info_msg((slurm_msg_t *) msg, buffer); + break; + case REQUEST_DELETE_RESERVATION: + case RESPONSE_CREATE_RESERVATION: + _pack_resv_name_msg((reservation_name_msg_t *) msg-> + data, buffer); + break; case REQUEST_REATTACH_TASKS: _pack_reattach_tasks_request_msg( (reattach_tasks_request_msg_t *) msg->data, buffer); @@ -787,6 +820,10 @@ unpack_msg(slurm_msg_t * msg, Buf buffer) rc = _unpack_part_info_request_msg((part_info_request_msg_t **) & (msg->data), buffer); break; + case REQUEST_RESERVATION_INFO: + rc = _unpack_resv_info_request_msg((resv_info_request_msg_t **) + & (msg->data), buffer); + break; case REQUEST_BUILD_INFO: case REQUEST_ACCTING_INFO: rc = _unpack_last_update_msg((last_update_msg_t **) & @@ -864,6 +901,7 @@ unpack_msg(slurm_msg_t * msg, Buf buffer) rc = _unpack_update_node_msg((update_node_msg_t **) & (msg->data), buffer); break; + case REQUEST_CREATE_PARTITION: case REQUEST_UPDATE_PARTITION: rc = _unpack_update_partition_msg((update_part_msg_t **) & (msg->data), buffer); @@ -872,6 +910,20 @@ unpack_msg(slurm_msg_t * msg, Buf buffer) rc = _unpack_delete_partition_msg((delete_part_msg_t **) & (msg->data), buffer); break; + case REQUEST_CREATE_RESERVATION: + case REQUEST_UPDATE_RESERVATION: + rc = _unpack_update_resv_msg((reserve_request_msg_t **) + &(msg->data), buffer); + break; + case REQUEST_DELETE_RESERVATION: + case RESPONSE_CREATE_RESERVATION: + rc = _unpack_resv_name_msg((reservation_name_msg_t **) + &(msg->data), buffer); + break; + case RESPONSE_RESERVATION_INFO: + rc = _unpack_reserve_info_msg((reserve_info_msg_t **) + &(msg->data), buffer); + break; case REQUEST_LAUNCH_TASKS: rc = _unpack_launch_tasks_request_msg( (launch_tasks_request_msg_t **) @@ -1746,6 +1798,57 @@ unpack_error: return SLURM_ERROR; } +static void +_pack_update_resv_msg(reserve_request_msg_t * msg, Buf buffer) +{ + xassert(msg != NULL); + + packstr(msg->name, buffer); + pack_time(msg->start_time, buffer); + pack_time(msg->end_time, buffer); + pack32(msg->duration, buffer); + pack16(msg->type, buffer); + pack32(msg->node_cnt, buffer); + packstr(msg->node_list, buffer); + packstr(msg->features, buffer); + packstr(msg->partition, buffer); + + packstr(msg->users, buffer); + packstr(msg->accounts, buffer); +} + +static int +_unpack_update_resv_msg(reserve_request_msg_t ** msg, Buf buffer) +{ + uint32_t uint32_tmp; + reserve_request_msg_t *tmp_ptr; + + xassert(msg != NULL); + + /* alloc memory for structure */ + tmp_ptr = xmalloc(sizeof(reserve_request_msg_t)); + *msg = tmp_ptr; + + safe_unpackstr_xmalloc(&tmp_ptr->name, &uint32_tmp, buffer); + safe_unpack_time(&tmp_ptr->start_time, buffer); + safe_unpack_time(&tmp_ptr->end_time, buffer); + safe_unpack32(&tmp_ptr->duration, buffer); + safe_unpack16(&tmp_ptr->type, buffer); + safe_unpack32(&tmp_ptr->node_cnt, buffer); + safe_unpackstr_xmalloc(&tmp_ptr->node_list, &uint32_tmp, buffer); + safe_unpackstr_xmalloc(&tmp_ptr->features, &uint32_tmp, buffer); + safe_unpackstr_xmalloc(&tmp_ptr->partition, &uint32_tmp, buffer); + + safe_unpackstr_xmalloc(&tmp_ptr->users, &uint32_tmp, buffer); + safe_unpackstr_xmalloc(&tmp_ptr->accounts, &uint32_tmp, buffer); + return SLURM_SUCCESS; + +unpack_error: + slurm_free_update_resv_msg(tmp_ptr); + *msg = NULL; + return SLURM_ERROR; +} + static void _pack_delete_partition_msg(delete_part_msg_t * msg, Buf buffer) { @@ -1775,6 +1878,35 @@ unpack_error: return SLURM_ERROR; } +static void +_pack_resv_name_msg(reservation_name_msg_t * msg, Buf buffer) +{ + xassert(msg != NULL); + + packstr(msg->name, buffer); +} + +static int +_unpack_resv_name_msg(reservation_name_msg_t ** msg, Buf buffer) +{ + uint32_t uint32_tmp; + reservation_name_msg_t *tmp_ptr; + + xassert(msg != NULL); + + /* alloc memory for structure */ + tmp_ptr = xmalloc(sizeof(reservation_name_msg_t)); + *msg = tmp_ptr; + + safe_unpackstr_xmalloc(&tmp_ptr->name, &uint32_tmp, buffer); + return SLURM_SUCCESS; + +unpack_error: + slurm_free_resv_name_msg(tmp_ptr); + *msg = NULL; + return SLURM_ERROR; +} + static void _pack_job_step_create_request_msg(job_step_create_request_msg_t * msg, Buf buffer) @@ -2050,7 +2182,7 @@ _unpack_partition_info_msg(partition_info_msg_t ** msg, Buf buffer) partition = (*msg)->partition_array = xmalloc(sizeof(partition_info_t) * (*msg)->record_count); - /* load individual job info */ + /* load individual partition info */ for (i = 0; i < (*msg)->record_count; i++) { if (_unpack_partition_info_members(&partition[i], buffer)) goto unpack_error; @@ -2108,48 +2240,62 @@ unpack_error: return SLURM_ERROR; } -/* pack_job_step_info_members - * pack selected fields of the description of a job into a buffer - * IN job_id, step_id, user_id, start_time, partition, nodes - job info - * IN/OUT buffer - destination of the pack, contains pointers that are - * automatically updated - */ -/* void */ -/* pack_job_step_info_members(uint32_t job_id, uint16_t step_id, */ -/* uint32_t user_id, uint32_t num_tasks, */ -/* time_t start_time, char *partition, */ -/* char *nodes, char *name, char *network, */ -/* Buf buffer) */ -/* { */ -/* pack32((uint32_t)job_id, buffer); */ -/* pack16((uint16_t)step_id, buffer); */ -/* pack32((uint32_t)user_id, buffer); */ -/* pack32((uint32_t)num_tasks, buffer); */ - -/* pack_time(start_time, buffer); */ -/* packstr(partition, buffer); */ -/* packstr(nodes, buffer); */ -/* packstr(name, buffer); */ -/* packstr(network, buffer); */ -/* } */ - -/* pack_job_step_info - * packs a slurm job steps info - * IN step - pointer to the job step info - * IN/OUT buffer - destination of the pack, contains pointers that are - * automatically updated - */ -/* void */ -/* pack_job_step_info(job_step_info_t * step, Buf buffer) */ -/* { */ -/* pack_job_step_info_members(step->job_id, */ -/* step->step_id, */ -/* step->user_id, */ -/* step->num_tasks, */ -/* step->start_time, */ -/* step->partition, step->nodes, */ -/* step->name, step->network, buffer); */ -/* } */ +static int +_unpack_reserve_info_msg(reserve_info_msg_t ** msg, Buf buffer) +{ + int i; + reserve_info_t *reserve = NULL; + + xassert(msg != NULL); + *msg = xmalloc(sizeof(reserve_info_msg_t)); + + /* load buffer's header (data structure version and time) */ + safe_unpack32(&((*msg)->record_count), buffer); + safe_unpack_time(&((*msg)->last_update), buffer); + + reserve = (*msg)->reservation_array = + xmalloc(sizeof(reserve_info_t) * (*msg)->record_count); + + /* load individual reservation records */ + for (i = 0; i < (*msg)->record_count; i++) { + if (_unpack_reserve_info_members(&reserve[i], buffer)) + goto unpack_error; + } + return SLURM_SUCCESS; + +unpack_error: + slurm_free_reservation_info_msg(*msg); + *msg = NULL; + return SLURM_ERROR; +} + + +static int +_unpack_reserve_info_members(reserve_info_t * resv, Buf buffer) +{ + uint32_t uint32_tmp; + + safe_unpackstr_xmalloc(&resv->accounts, &uint32_tmp, buffer); + safe_unpack_time(&resv->end_time, buffer); + safe_unpackstr_xmalloc(&resv->features, &uint32_tmp, buffer); + safe_unpackstr_xmalloc(&resv->name, &uint32_tmp, buffer); + safe_unpack32(&resv->node_cnt, buffer); + safe_unpackstr_xmalloc(&resv->node_list,&uint32_tmp, buffer); + safe_unpackstr_xmalloc(&resv->partition,&uint32_tmp, buffer); + safe_unpack_time(&resv->start_time, buffer); + safe_unpack16(&resv->type, buffer); + safe_unpackstr_xmalloc(&resv->users, &uint32_tmp, buffer); + return SLURM_SUCCESS; + +unpack_error: + xfree(resv->accounts); + xfree(resv->features); + xfree(resv->name); + xfree(resv->node_list); + xfree(resv->partition); + xfree(resv->users); + return SLURM_ERROR; +} /* _unpack_job_step_info_members * unpacks a set of slurm job step info for one job step @@ -3853,6 +3999,29 @@ unpack_error: return SLURM_ERROR; } +static void +_pack_resv_info_request_msg(resv_info_request_msg_t * msg, Buf buffer) +{ + pack_time(msg->last_update, buffer); +} + +static int +_unpack_resv_info_request_msg(resv_info_request_msg_t ** msg, Buf buffer) +{ + resv_info_request_msg_t* resv_info; + + resv_info = xmalloc(sizeof(resv_info_request_msg_t)); + *msg = resv_info; + + safe_unpack_time(&resv_info->last_update, buffer); + return SLURM_SUCCESS; + +unpack_error: + slurm_free_resv_info_request_msg(resv_info); + *msg = NULL; + return SLURM_ERROR; +} + static void _pack_slurm_addr_array(slurm_addr * slurm_address, uint32_t size_val, Buf buffer) diff --git a/src/scontrol/Makefile.am b/src/scontrol/Makefile.am index b0ec55397ce..7e5907dbb0e 100644 --- a/src/scontrol/Makefile.am +++ b/src/scontrol/Makefile.am @@ -7,9 +7,11 @@ INCLUDES = -I$(top_srcdir) bin_PROGRAMS = scontrol scontrol_SOURCES = \ + create_res.c \ info_job.c \ info_node.c \ info_part.c \ + info_res.c \ scontrol.c \ scontrol.h \ update_job.c \ diff --git a/src/scontrol/Makefile.in b/src/scontrol/Makefile.in index d5faafec505..c96a4b78f40 100644 --- a/src/scontrol/Makefile.in +++ b/src/scontrol/Makefile.in @@ -74,8 +74,8 @@ CONFIG_CLEAN_FILES = am__installdirs = "$(DESTDIR)$(bindir)" binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) PROGRAMS = $(bin_PROGRAMS) -am_scontrol_OBJECTS = info_job.$(OBJEXT) info_node.$(OBJEXT) \ - info_part.$(OBJEXT) scontrol.$(OBJEXT) update_job.$(OBJEXT) \ +am_scontrol_OBJECTS = create_res.$(OBJEXT) info_job.$(OBJEXT) info_node.$(OBJEXT) \ + info_part.$(OBJEXT) info_res.$(OBJEXT) scontrol.$(OBJEXT) update_job.$(OBJEXT) \ update_node.$(OBJEXT) update_part.$(OBJEXT) scontrol_OBJECTS = $(am_scontrol_OBJECTS) am__DEPENDENCIES_1 = $(top_builddir)/src/api/libslurm.o @@ -273,9 +273,11 @@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign INCLUDES = -I$(top_srcdir) scontrol_SOURCES = \ + create_res.c \ info_job.c \ info_node.c \ info_part.c \ + info_res.c \ scontrol.c \ scontrol.h \ update_job.c \ @@ -359,9 +361,11 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/create_res.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info_job.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info_node.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info_part.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info_res.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scontrol.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/update_job.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/update_node.Po@am__quote@ diff --git a/src/scontrol/create_res.c b/src/scontrol/create_res.c new file mode 100644 index 00000000000..bd22f958f57 --- /dev/null +++ b/src/scontrol/create_res.c @@ -0,0 +1,310 @@ +/*****************************************************************************\ + * create_res.c - reservation creation function for scontrol. + ***************************************************************************** + * Copyright (C) 2009 Lawrence Livermore National Security. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * Written by David Bremer <dbremer@llnl.gov> + * LLNL-CODE-402394. + * + * This file is part of SLURM, a resource management program. + * For details, see <http://www.llnl.gov/linux/slurm/>. + * + * SLURM is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * In addition, as a special exception, the copyright holders give permission + * to link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. You must obey the GNU + * General Public License in all respects for all of the code used other than + * OpenSSL. If you modify file(s) with this exception, you may extend this + * exception to your version of the file(s), but you are not obligated to do + * so. If you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files in + * the program, then also delete it here. + * + * SLURM is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with SLURM; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +\*****************************************************************************/ + +#include "src/scontrol/scontrol.h" +#include "src/slurmctld/reservation.h" + + +/* + * process_plus_minus is used to convert a string like + * Users+=a,b,c + * to Users=+a,+b,+c + */ + +static char * +process_plus_minus(char plus_or_minus, char *src) +{ + int num_commas = 0; + int ii; + int srclen = strlen(src); + char *dst, *ret; + + for (ii=0; ii<srclen; ii++) { + if (src[ii] == ',') + num_commas++; + } + ret = dst = malloc(srclen + 2 + num_commas); + + *dst++ = plus_or_minus; + for (ii=0; ii<srclen; ii++) { + if (*src == ',') { + *dst++ = *src++; + *dst++ = plus_or_minus; + } else { + *dst++ = *src++; + } + } + *dst = '\0'; + + return ret; +} + + + +/* + * scontrol_parse_res_options parse options for creating or updating a + reservation + * IN argc - count of arguments + * IN argv - list of arguments + * IN msg - a string to append to any error message + * OUT resv_msg_ptr - struct holding reservation parameters + * OUT free_user_str - bool indicating that resv_msg_ptr->users should be freed + * OUT free_acct_str - bool indicating that resv_msg_ptr->accounts should be freed + * RET 0 on success, -1 on err and prints message + */ +extern int +scontrol_parse_res_options(int argc, char *argv[], const char *msg, + reserve_request_msg_t *resv_msg_ptr, + int *free_user_str, int *free_acct_str) +{ + int i; + int duration = -3; /* -1 == INFINITE, -2 == error, -3 == not set */ + + *free_user_str = 0; + *free_acct_str = 0; + + for (i=0; i<argc; i++) { + if (strncasecmp(argv[i], "ReservationName=", 16) == 0) { + resv_msg_ptr->name = &argv[i][16]; + + } else if (strncasecmp(argv[i], "StartTime=", 10) == 0) { + time_t t = parse_time(&argv[i][10], 0); + if (t == 0) { + //TODO: Set errno here instead of exit_code? + exit_code = 1; + error("Invalid start time %s. %s", argv[i], msg); + return -1; + } + resv_msg_ptr->start_time = t; + + } else if (strncasecmp(argv[i], "EndTime=", 8) == 0) { + time_t t = parse_time(&argv[i][8], 0); + if (t == 0) { + //TODO: Set errno here instead of exit_code? + exit_code = 1; + error("Invalid end time %s. %s", argv[i], msg); + return -1; + } + resv_msg_ptr->end_time = t; + + } else if (strncasecmp(argv[i], "Duration=", 9) == 0) { + /* -1 == INFINITE, -2 == error, -3 == not set */ + duration = time_str2mins(&argv[i][9]); + if (duration < 0 && duration != INFINITE) { + //TODO: Set errno here instead of exit_code? + exit_code = 1; + error("Invalid duration %s. %s", argv[i], msg); + return -1; + } + resv_msg_ptr->duration = (uint32_t)duration; + + } else if (strncasecmp(argv[i], "Type=", 5) == 0) { + char *typestr = &argv[i][5]; + if (strncasecmp(typestr, "Maintenance", 5) == 0) { + resv_msg_ptr->type = RESERVE_TYPE_MAINT; + } else { + exit_code = 1; + error("Invalid type %s. %s", argv[i], msg); + return -1; + } + } else if (strncasecmp(argv[i], "NodeCnt=", 8) == 0) { + char *endptr = NULL; + resv_msg_ptr->node_cnt = strtol(&argv[i][8], &endptr, 10); + + if (endptr == NULL || *endptr != '\0' || + argv[i][8] == '\0') { + exit_code = 1; + error("Invalid node count %s. %s", argv[i], msg); + return -1; + } + } else if (strncasecmp(argv[i], "Nodes=", 6) == 0) { + resv_msg_ptr->node_list = &argv[i][6]; + } else if (strncasecmp(argv[i], "Features=", 9) == 0) { + resv_msg_ptr->features = &argv[i][9]; + } else if (strncasecmp(argv[i], "PartitionName=", 14) == 0) { + resv_msg_ptr->partition = &argv[i][14]; + } else if (strncasecmp(argv[i], "Users=", 6) == 0) { + resv_msg_ptr->users = &argv[i][6]; + } else if (strncasecmp(argv[i], "Users+=", 7) == 0) { + resv_msg_ptr->users = process_plus_minus('+', &argv[i][7]); + *free_user_str = 1; + } else if (strncasecmp(argv[i], "Users-=", 7) == 0) { + resv_msg_ptr->users = process_plus_minus('-', &argv[i][7]); + *free_user_str = 1; + } else if (strncasecmp(argv[i], "Accounts=", 9) == 0) { + resv_msg_ptr->accounts = &argv[i][9]; + } else if (strncasecmp(argv[i], "Accounts+=", 10) == 0) { + resv_msg_ptr->accounts = process_plus_minus('+', &argv[i][10]); + *free_acct_str = 1; + } else if (strncasecmp(argv[i], "Accounts-=", 10) == 0) { + resv_msg_ptr->accounts = process_plus_minus('-', &argv[i][10]); + *free_acct_str = 1; + } else if (strncasecmp(argv[i], "res", 3) == 0) { + continue; + } else { + exit_code = 1; + error("Unknown parameter %s. %s", argv[i], msg); + return -1; + } + } + return 0; +} + + + +/* + * scontrol_update_res - update the slurm reservation configuration per the + * supplied arguments + * IN argc - count of arguments + * IN argv - list of arguments + * RET 0 if no slurm error, errno otherwise. parsing error prints + * error message and returns 0. + */ +extern int +scontrol_update_res(int argc, char *argv[]) +{ + reserve_request_msg_t resv_msg; + int err, ret = 0; + int free_user_str = 0, free_acct_str = 0; + + slurm_init_resv_desc_msg (&resv_msg); + err = scontrol_parse_res_options(argc, argv, "No reservation update.", + &resv_msg, &free_user_str, &free_acct_str); + if (err) + goto SCONTROL_UPDATE_RES_CLEANUP; + + if (resv_msg.name == NULL) { + exit_code = 1; + error("ReservationName must be given. No reservation update."); + goto SCONTROL_UPDATE_RES_CLEANUP; + } + + err = slurm_update_reservation(&resv_msg); + if (err) { + exit_code = 1; + slurm_perror("Error updating the reservation."); + ret = slurm_get_errno(); + } else { + printf("Reservation updated.\n"); + } + +SCONTROL_UPDATE_RES_CLEANUP: + if (free_user_str) free(resv_msg.users); + if (free_acct_str) free(resv_msg.accounts); + return ret; +} + + + +/* + * scontrol_create_res - create the slurm reservation configuration per the + * supplied arguments + * IN argc - count of arguments + * IN argv - list of arguments + * RET 0 if no slurm error, errno otherwise. parsing error prints + * error message and returns 0. + */ +extern int +scontrol_create_res(int argc, char *argv[]) +{ + reserve_request_msg_t resv_msg; + char *new_res_name = NULL; + int free_user_str = 0, free_acct_str = 0; + int err, ret = 0; + + slurm_init_resv_desc_msg (&resv_msg); + err = scontrol_parse_res_options(argc, argv, "No reservation created.", + &resv_msg, &free_user_str, &free_acct_str); + if (err) + goto SCONTROL_CREATE_RES_CLEANUP; + + if (resv_msg.start_time == (time_t)NO_VAL) { + exit_code = 1; + error("A start time must be given. No reservation created."); + goto SCONTROL_CREATE_RES_CLEANUP; + } + if (resv_msg.end_time == (time_t)NO_VAL && + resv_msg.duration == (uint32_t)NO_VAL) { + exit_code = 1; + error("An end time or duration must be given. " + "No reservation created."); + goto SCONTROL_CREATE_RES_CLEANUP; + } + if (resv_msg.end_time != (time_t)NO_VAL && + resv_msg.duration != (uint32_t)NO_VAL && + resv_msg.start_time + resv_msg.duration*60 != resv_msg.end_time) { + exit_code = 1; + error("StartTime + Duration does not equal EndTime. " + "No reservation created."); + goto SCONTROL_CREATE_RES_CLEANUP; + } + if (resv_msg.start_time > resv_msg.end_time && + resv_msg.end_time != (time_t)NO_VAL) { + exit_code = 1; + error("Start time cannot be after end time. " + "No reservation created."); + goto SCONTROL_CREATE_RES_CLEANUP; + } + if (resv_msg.node_cnt == NO_VAL && resv_msg.node_list == NULL) { + exit_code = 1; + error("Either Nodes or NodeCnt must be specified. " + "No reservation created."); + goto SCONTROL_CREATE_RES_CLEANUP; + } + if (resv_msg.users == NULL && resv_msg.accounts == NULL) { + exit_code = 1; + error("Either Users or Accounts must be specified. " + "No reservation created."); + goto SCONTROL_CREATE_RES_CLEANUP; + } + + new_res_name = slurm_create_reservation(&resv_msg); + if (!new_res_name) { + exit_code = 1; + slurm_perror("Error creating the reservation"); + ret = slurm_get_errno(); + } else { + printf("Reservation created: %s\n", new_res_name); + free(new_res_name); + } +SCONTROL_CREATE_RES_CLEANUP: + if (free_user_str) + free(resv_msg.users); + if (free_acct_str) + free(resv_msg.accounts); + return ret; +} diff --git a/src/scontrol/info_part.c b/src/scontrol/info_part.c index 8c9e0606968..9d5a64abd25 100644 --- a/src/scontrol/info_part.c +++ b/src/scontrol/info_part.c @@ -79,7 +79,7 @@ scontrol_load_partitions (partition_info_msg_t **part_buffer_pptr) } /* - * scontrol_print_par - print the specified partition's information + * scontrol_print_part - print the specified partition's information * IN partition_name - NULL to print information about all partition */ extern void diff --git a/src/scontrol/info_res.c b/src/scontrol/info_res.c new file mode 100644 index 00000000000..db74df81f86 --- /dev/null +++ b/src/scontrol/info_res.c @@ -0,0 +1,139 @@ +/*****************************************************************************\ + * info_res.c - reservation information functions for scontrol. + ***************************************************************************** + * Copyright (C) 2009 Lawrence Livermore National Security. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * Written by David Bremer <dbremer@llnl.gov> + * LLNL-CODE-402394. + * + * This file is part of SLURM, a resource management program. + * For details, see <http://www.llnl.gov/linux/slurm/>. + * + * SLURM is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * In addition, as a special exception, the copyright holders give permission + * to link the code of portions of this program with the OpenSSL library under + * certain conditions as described in each individual source file, and + * distribute linked combinations including the two. You must obey the GNU + * General Public License in all respects for all of the code used other than + * OpenSSL. If you modify file(s) with this exception, you may extend this + * exception to your version of the file(s), but you are not obligated to do + * so. If you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files in + * the program, then also delete it here. + * + * SLURM is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with SLURM; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +\*****************************************************************************/ + +#include "scontrol.h" + +/* Load current reservation table information into *res_buffer_pptr */ +extern int +scontrol_load_reservations(reserve_info_msg_t **res_buffer_pptr) +{ + int error_code; + static reserve_info_msg_t *old_res_info_ptr = NULL; + reserve_info_msg_t *res_info_ptr = NULL; + + if (old_res_info_ptr) { + error_code = slurm_load_reservations ( + old_res_info_ptr->last_update, + &res_info_ptr); + if (error_code == SLURM_SUCCESS) { + slurm_free_reservation_info_msg (old_res_info_ptr); + + } else if (slurm_get_errno () == SLURM_NO_CHANGE_IN_DATA) { + res_info_ptr = old_res_info_ptr; + error_code = SLURM_SUCCESS; + if (quiet_flag == -1) + printf ("slurm_load_reservations: no change in data\n"); + } + } + else + error_code = slurm_load_reservations((time_t) NULL, + &res_info_ptr); + + if (error_code == SLURM_SUCCESS) { + old_res_info_ptr = res_info_ptr; + *res_buffer_pptr = res_info_ptr; + } + + return error_code; +} + +/* + * scontrol_print_res - print the specified reservation's information + * IN reservation_name - NULL to print information about all reservations + */ +extern void +scontrol_print_res (char *reservation_name) +{ + int error_code, i, print_cnt = 0; + reserve_info_msg_t *res_info_ptr = NULL; + reserve_info_t *res_ptr = NULL; + + error_code = scontrol_load_reservations(&res_info_ptr); + if (error_code) { + exit_code = 1; + if (quiet_flag != 1) + slurm_perror ("slurm_load_reservations error"); + return; + } + + if (quiet_flag == -1) { + char time_str[32]; + slurm_make_time_str ((time_t *)&res_info_ptr->last_update, + time_str, sizeof(time_str)); + printf ("last_update_time=%s, records=%d\n", + time_str, res_info_ptr->record_count); + } + + res_ptr = res_info_ptr->reservation_array; + for (i = 0; i < res_info_ptr->record_count; i++) { + if (reservation_name && + strcmp (reservation_name, res_ptr[i].name) != 0) + continue; + print_cnt++; + slurm_print_reservation_info (stdout, & res_ptr[i], + one_liner ) ; + if (reservation_name) + break; + } + + if (print_cnt == 0) { + if (reservation_name) { + exit_code = 1; + if (quiet_flag != 1) + printf ("Reservation %s not found\n", + reservation_name); + } else if (quiet_flag != 1) + printf ("No reservations in the system\n"); + } +} + + + + + + + + + + + + + + + + + diff --git a/src/scontrol/scontrol.c b/src/scontrol/scontrol.c index 8118836f10d..9a2c3186ec0 100644 --- a/src/scontrol/scontrol.c +++ b/src/scontrol/scontrol.c @@ -51,6 +51,7 @@ int one_liner; /* one record per line if =1 */ int quiet_flag; /* quiet=1, verbose=-1, normal=0 */ int verbosity; /* count of "-v" options */ +static void _create_it (int argc, char *argv[]); static void _delete_it (int argc, char *argv[]); static int _get_command (int *argc, char *argv[]); static void _ping_slurmctld(char *control_machine, char *backup_controller); @@ -489,6 +490,15 @@ _process_command (int argc, char *argv[]) } scontrol_print_completing(); } + else if (strncasecmp (argv[0], "create", 1) == 0) { + if (argc < 2) { + exit_code = 1; + fprintf (stderr, "too few arguments for %s keyword\n", + argv[0]); + return 0; + } + _create_it ((argc - 1), &argv[1]); + } else if (strncasecmp (argv[0], "exit", 1) == 0) { if (argc > 1) { exit_code = 1; @@ -751,6 +761,12 @@ _process_command (int argc, char *argv[]) else scontrol_print_part (NULL); } + else if (strncasecmp (argv[1], "reservations", 3) == 0) { + if (argc > 2) + scontrol_print_res(argv[2]); + else + scontrol_print_res(NULL); + } else if (strncasecmp (argv[1], "slurmd", 6) == 0) { if (argc > 2) _print_slurmd(argv[2]); @@ -867,6 +883,35 @@ _process_command (int argc, char *argv[]) return 0; } + +/* + * _create_it - create a slurm configuration per the supplied arguments + * IN argc - count of arguments + * IN argv - list of arguments + */ +static void +_create_it (int argc, char *argv[]) +{ + int i; + for (i=0; i<argc; i++) { + if (strncasecmp (argv[i], "res", 3) == 0) { + scontrol_create_res(argc, argv); + break; + } else if (strncasecmp (argv[i], "par", 3) == 0) { + scontrol_create_part(argc, argv); + break; + } + } + + if (i >= argc) { + exit_code = 1; + error("Invalid creation entity: %s\n", argv[0]); + } +} + + + + /* * _delete_it - delete the slurm the specified slurm entity * IN argc - count of arguments @@ -875,16 +920,23 @@ _process_command (int argc, char *argv[]) static void _delete_it (int argc, char *argv[]) { - delete_part_msg_t part_msg; - /* First identify the entity type to delete */ if (strncasecmp (argv[0], "PartitionName=", 14) == 0) { + delete_part_msg_t part_msg; part_msg.name = argv[0] + 14; if (slurm_delete_partition(&part_msg)) { char errmsg[64]; snprintf(errmsg, 64, "delete_partition %s", argv[0]); slurm_perror(errmsg); } + } else if (strncasecmp (argv[0], "ReservationName=", 16) == 0) { + reservation_name_msg_t res_msg; + res_msg.name = argv[0] + 16; + if (slurm_delete_reservation(&res_msg)) { + char errmsg[64]; + snprintf(errmsg, 64, "delete_reservation %s", argv[0]); + slurm_perror(errmsg); + } } else { exit_code = 1; fprintf(stderr, "Invalid deletion entity: %s\n", argv[1]); @@ -919,6 +971,9 @@ _update_it (int argc, char *argv[]) } else if (strncasecmp (argv[i], "SubBPName=", 10) == 0) { error_code = _update_bluegene_subbp (argc, argv); break; + } else if (strncasecmp (argv[i], "ReservationName=", 16) == 0) { + error_code = scontrol_update_res (argc, argv); + break; } } diff --git a/src/scontrol/scontrol.h b/src/scontrol/scontrol.h index 3fd1d289736..b19588ed4d0 100644 --- a/src/scontrol/scontrol.h +++ b/src/scontrol/scontrol.h @@ -115,13 +115,17 @@ extern void scontrol_print_node (char *node_name, node_info_msg_t *node_info_ptr); extern void scontrol_print_node_list (char *node_list); extern void scontrol_print_part (char *partition_name); +extern void scontrol_print_res (char *reservation_name); extern void scontrol_print_step (char *job_step_id_str); extern int scontrol_requeue(char *job_step_id_str); extern int scontrol_suspend(char *op, char *job_id_str); extern int scontrol_update_job (int argc, char *argv[]); extern int scontrol_update_node (int argc, char *argv[]); extern int scontrol_update_part (int argc, char *argv[]); +extern int scontrol_update_res (int argc, char *argv[]); extern void scontrol_list_pids(const char *jobid_str, const char *node_name); +extern int scontrol_create_part(int argc, char *argv[]); +extern int scontrol_create_res(int argc, char *argv[]); #endif diff --git a/src/scontrol/update_part.c b/src/scontrol/update_part.c index 6ec952c928b..38e77a56e38 100644 --- a/src/scontrol/update_part.c +++ b/src/scontrol/update_part.c @@ -38,155 +38,181 @@ #include "src/common/proc_args.h" #include "src/scontrol/scontrol.h" - -/* - * scontrol_update_part - update the slurm partition configuration per the - * supplied arguments - * IN argc - count of arguments - * IN argv - list of arguments - * RET 0 if no slurm error, errno otherwise. parsing error prints - * error message and returns 0 - */ extern int -scontrol_update_part (int argc, char *argv[]) +scontrol_parse_part_options (int argc, char *argv[], int *update_cnt_ptr, + update_part_msg_t *part_msg_ptr) { - int i, min, max, update_cnt = 0; - update_part_msg_t part_msg; + int i, min, max; + if (!update_cnt_ptr) { + error("scontrol_parse_part_options internal error, " + "update_cnt_ptr == NULL"); + exit_code = 1; + return -1; + } + if (!part_msg_ptr) { + error("scontrol_parse_part_options internal error, " + "part_msg_ptr == NULL"); + exit_code = 1; + return -1; + } - slurm_init_part_desc_msg ( &part_msg ); for (i=0; i<argc; i++) { if (strncasecmp(argv[i], "PartitionName=", 14) == 0) - part_msg.name = &argv[i][14]; + part_msg_ptr->name = &argv[i][14]; else if (strncasecmp(argv[i], "MaxTime=", 8) == 0) { int max_time = time_str2mins(&argv[i][8]); if ((max_time < 0) && (max_time != INFINITE)) { exit_code = 1; error("Invalid input %s", argv[i]); - return 0; + return -1; } - part_msg.max_time = max_time; - update_cnt++; + part_msg_ptr->max_time = max_time; + (*update_cnt_ptr)++; } else if (strncasecmp(argv[i], "MaxNodes=", 9) == 0) { if ((strcasecmp(&argv[i][9],"UNLIMITED") == 0) || (strcasecmp(&argv[i][8],"INFINITE") == 0)) - part_msg.max_nodes = (uint32_t) INFINITE; + part_msg_ptr->max_nodes = (uint32_t) INFINITE; else { min = 1; get_resource_arg_range(&argv[i][9], "MaxNodes", &min, &max, true); - part_msg.max_nodes = min; + part_msg_ptr->max_nodes = min; } - update_cnt++; + (*update_cnt_ptr)++; } else if (strncasecmp(argv[i], "MinNodes=", 9) == 0) { min = 1; get_resource_arg_range(&argv[i][9], "MinNodes", &min, &max, true); - part_msg.min_nodes = min; - update_cnt++; + part_msg_ptr->min_nodes = min; + (*update_cnt_ptr)++; } else if (strncasecmp(argv[i], "Default=", 8) == 0) { if (strcasecmp(&argv[i][8], "NO") == 0) - part_msg.default_part = 0; + part_msg_ptr->default_part = 0; else if (strcasecmp(&argv[i][8], "YES") == 0) - part_msg.default_part = 1; + part_msg_ptr->default_part = 1; else { exit_code = 1; error("Invalid input: %s", argv[i]); error("Acceptable Default values " "are YES and NO"); - return 0; + return -1; } - update_cnt++; + (*update_cnt_ptr)++; } else if (strncasecmp(argv[i], "Hidden=", 4) == 0) { if (strcasecmp(&argv[i][7], "NO") == 0) - part_msg.hidden = 0; + part_msg_ptr->hidden = 0; else if (strcasecmp(&argv[i][7], "YES") == 0) - part_msg.hidden = 1; + part_msg_ptr->hidden = 1; else { exit_code = 1; error("Invalid input: %s", argv[i]); error("Acceptable Hidden values " "are YES and NO"); - return 0; + return -1; } - update_cnt++; + (*update_cnt_ptr)++; } else if (strncasecmp(argv[i], "RootOnly=", 4) == 0) { if (strcasecmp(&argv[i][9], "NO") == 0) - part_msg.root_only = 0; + part_msg_ptr->root_only = 0; else if (strcasecmp(&argv[i][9], "YES") == 0) - part_msg.root_only = 1; + part_msg_ptr->root_only = 1; else { exit_code = 1; error("Invalid input: %s", argv[i]); error("Acceptable RootOnly values " "are YES and NO"); - return 0; + return -1; } - update_cnt++; + (*update_cnt_ptr)++; } else if (strncasecmp(argv[i], "Shared=", 7) == 0) { if (strncasecmp(&argv[i][7], "NO", 2) == 0) { - part_msg.max_share = 1; + part_msg_ptr->max_share = 1; } else if (strncasecmp(&argv[i][7], "EXCLUSIVE", 9) == 0) { - part_msg.max_share = 0; + part_msg_ptr->max_share = 0; } else if (strncasecmp(&argv[i][7], "YES:", 4) == 0) { - part_msg.max_share = (uint16_t) strtol(&argv[i][11], + part_msg_ptr->max_share = (uint16_t) strtol(&argv[i][11], (char **) NULL, 10); } else if (strncasecmp(&argv[i][7], "YES", 3) == 0) { - part_msg.max_share = (uint16_t) 4; + part_msg_ptr->max_share = (uint16_t) 4; } else if (strncasecmp(&argv[i][7], "FORCE:", 6) == 0) { - part_msg.max_share = (uint16_t) strtol(&argv[i][13], + part_msg_ptr->max_share = (uint16_t) strtol(&argv[i][13], (char **) NULL, 10) | SHARED_FORCE; } else if (strncasecmp(&argv[i][7], "FORCE", 5) == 0) { - part_msg.max_share = (uint16_t) 4 | + part_msg_ptr->max_share = (uint16_t) 4 | SHARED_FORCE; } else { exit_code = 1; error("Invalid input: %s", argv[i]); error("Acceptable Shared values are " "NO, EXCLUSIVE, YES:#, and FORCE:#"); - return 0; + return -1; } - update_cnt++; + (*update_cnt_ptr)++; } else if (strncasecmp(argv[i], "Priority=", 9) == 0) { - part_msg.priority = (uint16_t) strtol(&argv[i][9], + part_msg_ptr->priority = (uint16_t) strtol(&argv[i][9], (char **) NULL, 10); - update_cnt++; + (*update_cnt_ptr)++; } else if (strncasecmp(argv[i], "State=", 6) == 0) { if (strcasecmp(&argv[i][6], "DOWN") == 0) - part_msg.state_up = 0; + part_msg_ptr->state_up = 0; else if (strcasecmp(&argv[i][6], "UP") == 0) - part_msg.state_up = 1; + part_msg_ptr->state_up = 1; else { exit_code = 1; error("Invalid input: %s", argv[i]); error("Acceptable State values " "are UP and DOWN"); - return 0; + return -1; } - update_cnt++; + (*update_cnt_ptr)++; } else if (strncasecmp(argv[i], "Nodes=", 6) == 0) { - part_msg.nodes = &argv[i][6]; - update_cnt++; + part_msg_ptr->nodes = &argv[i][6]; + (*update_cnt_ptr)++; } else if (strncasecmp(argv[i], "AllowGroups=", 12) == 0) { - part_msg.allow_groups = &argv[i][12]; - update_cnt++; + part_msg_ptr->allow_groups = &argv[i][12]; + (*update_cnt_ptr)++; } else { exit_code = 1; error("Invalid input: %s", argv[i]); error("Request aborted"); - return 0; + return -1; } } + return 0; +} + + + +/* + * scontrol_update_part - update the slurm partition configuration per the + * supplied arguments + * IN argc - count of arguments + * IN argv - list of arguments + * RET 0 if no slurm error, errno otherwise. parsing error prints + * error message and returns 0 + */ +extern int +scontrol_update_part (int argc, char *argv[]) +{ + int update_cnt = 0; + update_part_msg_t part_msg; + + slurm_init_part_desc_msg ( &part_msg ); + scontrol_parse_part_options (argc, argv, &update_cnt, &part_msg); + + if (exit_code != 0) + return 0; if (update_cnt == 0) { exit_code = 1; @@ -200,3 +226,40 @@ scontrol_update_part (int argc, char *argv[]) } else return 0; } + + + +/* + * scontrol_create_part - create a slurm partition configuration per the + * supplied arguments + * IN argc - count of arguments + * IN argv - list of arguments + * RET 0 if no slurm error, errno otherwise. parsing error prints + * error message and returns 0 + */ +extern int +scontrol_create_part (int argc, char *argv[]) +{ + int update_cnt = 0; + update_part_msg_t part_msg; + + slurm_init_part_desc_msg ( &part_msg ); + scontrol_parse_part_options (argc, argv, &update_cnt, &part_msg); + + if (exit_code != 0) + return 0; + + if (slurm_create_partition(&part_msg)) { + exit_code = 1; + return slurm_get_errno (); + } + return 0; +} + + + + + + + + diff --git a/src/slurmctld/partition_mgr.c b/src/slurmctld/partition_mgr.c index 6952c03ebc6..fd8a2601c30 100644 --- a/src/slurmctld/partition_mgr.c +++ b/src/slurmctld/partition_mgr.c @@ -299,6 +299,8 @@ int dump_all_part_state(void) /* write partition records to buffer */ lock_slurmctld(part_read_lock); part_iterator = list_iterator_create(part_list); + if (!part_iterator) + fatal("list_iterator_create malloc"); while ((part_ptr = (struct part_record *) list_next(part_iterator))) { xassert (part_ptr->magic == PART_MAGIC); _dump_part_state(part_ptr, buffer); @@ -714,7 +716,7 @@ extern void pack_all_part(char **buffer_ptr, int *buffer_size, buffer = init_buf(BUF_SIZE); - /* write haeader: version and time */ + /* write header: version and time */ parts_packed = 0; pack32(parts_packed, buffer); pack_time(now, buffer); @@ -750,8 +752,8 @@ extern void pack_all_part(char **buffer_ptr, int *buffer_size, * IN/OUT buffer - buffer in which data is placed, pointers automatically * updated * global: default_part_loc - pointer to the default partition - * NOTE: if you make any changes here be sure to make the corresponding - * changes to load_part_config in api/partition_info.c + * NOTE: if you make any changes here be sure to make the corresponding changes + * to _unpack_partition_info_members() in common/slurm_protocol_pack.c */ void pack_part(struct part_record *part_ptr, Buf buffer) { diff --git a/src/slurmctld/proc_req.c b/src/slurmctld/proc_req.c index 64fc8fba033..ab148171ae4 100644 --- a/src/slurmctld/proc_req.c +++ b/src/slurmctld/proc_req.c @@ -2,7 +2,7 @@ * proc_req.c - process incomming messages to slurmctld ***************************************************************************** * Copyright (C) 2002-2007 The Regents of the University of California. - * Copyright (C) 2008 Lawrence Livermore National Security. + * Copyright (C) 2008-2009 Lawrence Livermore National Security. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Morris Jette <jette@llnl.gov>, et. al. * LLNL-CODE-402394. @@ -75,6 +75,7 @@ #include "src/slurmctld/locks.h" #include "src/slurmctld/proc_req.h" #include "src/slurmctld/read_config.h" +#include "src/slurmctld/reservation.h" #include "src/slurmctld/sched_plugin.h" #include "src/slurmctld/slurmctld.h" #include "src/slurmctld/srun_comm.h" @@ -113,6 +114,10 @@ inline static void _slurm_rpc_job_alloc_info(slurm_msg_t * msg); inline static void _slurm_rpc_job_alloc_info_lite(slurm_msg_t * msg); inline static void _slurm_rpc_ping(slurm_msg_t * msg); inline static void _slurm_rpc_reconfigure_controller(slurm_msg_t * msg); +inline static void _slurm_rpc_resv_create(slurm_msg_t * msg); +inline static void _slurm_rpc_resv_update(slurm_msg_t * msg); +inline static void _slurm_rpc_resv_delete(slurm_msg_t * msg); +inline static void _slurm_rpc_resv_show(slurm_msg_t * msg); inline static void _slurm_rpc_requeue(slurm_msg_t * msg); inline static void _slurm_rpc_shutdown_controller(slurm_msg_t * msg); inline static void _slurm_rpc_shutdown_controller_immediate(slurm_msg_t * @@ -253,6 +258,7 @@ void slurmctld_req (slurm_msg_t * msg) _slurm_rpc_update_node(msg); slurm_free_update_node_msg(msg->data); break; + case REQUEST_CREATE_PARTITION: case REQUEST_UPDATE_PARTITION: _slurm_rpc_update_partition(msg); slurm_free_update_part_msg(msg->data); @@ -261,6 +267,22 @@ void slurmctld_req (slurm_msg_t * msg) _slurm_rpc_delete_partition(msg); slurm_free_delete_part_msg(msg->data); break; + case REQUEST_CREATE_RESERVATION: + _slurm_rpc_resv_create(msg); + slurm_free_update_resv_msg(msg->data); + break; + case REQUEST_UPDATE_RESERVATION: + _slurm_rpc_resv_update(msg); + slurm_free_update_resv_msg(msg->data); + break; + case REQUEST_DELETE_RESERVATION: + _slurm_rpc_resv_delete(msg); + slurm_free_resv_name_msg(msg->data); + break; + case REQUEST_RESERVATION_INFO: + _slurm_rpc_resv_show(msg); + /* No body to free */ + break; case REQUEST_NODE_REGISTRATION_STATUS: error("slurmctld is talking with itself. " "SlurmctldPort == SlurmdPort"); @@ -2323,6 +2345,204 @@ static void _slurm_rpc_delete_partition(slurm_msg_t * msg) } } +/* _slurm_rpc_resv_create - process RPC to create a reservation */ +static void _slurm_rpc_resv_create(slurm_msg_t * msg) +{ + int error_code = SLURM_SUCCESS; + DEF_TIMERS; + reserve_request_msg_t *resv_desc_ptr = (reserve_request_msg_t *) + msg->data; + /* Locks: write node, read partition */ + slurmctld_lock_t node_write_lock = { + NO_LOCK, NO_LOCK, WRITE_LOCK, READ_LOCK }; + uid_t uid = g_slurm_auth_get_uid(msg->auth_cred, NULL); + + START_TIMER; + debug2("Processing RPC: REQUEST_CREATE_RESERVATION from uid=%u", + (unsigned int) uid); + if (!validate_super_user(uid)) { + error_code = ESLURM_USER_ID_MISSING; + error + ("Security violation, CREATE_RESERVATION RPC from uid=%u", + (unsigned int) uid); + } + + if (error_code == SLURM_SUCCESS) { + /* do RPC call */ + lock_slurmctld(node_write_lock); + error_code = create_resv(resv_desc_ptr); + unlock_slurmctld(node_write_lock); + END_TIMER2("_slurm_rpc_resv_create"); + } + + /* return result */ + if (error_code) { + info("_slurm_rpc_resv_create reservation=%s: %s", + resv_desc_ptr->name, slurm_strerror(error_code)); + slurm_send_rc_msg(msg, error_code); + } else { + slurm_msg_t response_msg; + reservation_name_msg_t resv_resp_msg; + + debug2("_slurm_rpc_resv_create complete for %s %s", + resv_desc_ptr->name, TIME_STR); + /* send reservation name */ + slurm_msg_t_init(&response_msg); + resv_resp_msg.name = resv_desc_ptr->name; + response_msg.msg_type = RESPONSE_CREATE_RESERVATION; + response_msg.data = &resv_resp_msg; + slurm_send_node_msg(msg->conn_fd, &response_msg); + + /* NOTE: These functions provide their own locks */ + if (schedule()) { + schedule_job_save(); + schedule_node_save(); + } + } +} + +/* _slurm_rpc_resv_update - process RPC to update a reservation */ +static void _slurm_rpc_resv_update(slurm_msg_t * msg) +{ + int error_code = SLURM_SUCCESS; + DEF_TIMERS; + reserve_request_msg_t *resv_desc_ptr = (reserve_request_msg_t *) + msg->data; + /* Locks: write node, read partition */ + slurmctld_lock_t node_write_lock = { + NO_LOCK, NO_LOCK, WRITE_LOCK, READ_LOCK }; + uid_t uid = g_slurm_auth_get_uid(msg->auth_cred, NULL); + + START_TIMER; + debug2("Processing RPC: REQUEST_UPDATE_RESERVATION from uid=%u", + (unsigned int) uid); + if (!validate_super_user(uid)) { + error_code = ESLURM_USER_ID_MISSING; + error + ("Security violation, UPDATE_RESERVATION RPC from uid=%u", + (unsigned int) uid); + } + + if (error_code == SLURM_SUCCESS) { + /* do RPC call */ + lock_slurmctld(node_write_lock); + error_code = update_resv(resv_desc_ptr); + unlock_slurmctld(node_write_lock); + END_TIMER2("_slurm_rpc_resv_update"); + } + + /* return result */ + if (error_code) { + info("_slurm_rpc_resv_update reservation=%s: %s", + resv_desc_ptr->name, slurm_strerror(error_code)); + slurm_send_rc_msg(msg, error_code); + } else { + debug2("_slurm_rpc_resv_update complete for %s %s", + resv_desc_ptr->name, TIME_STR); + slurm_send_rc_msg(msg, SLURM_SUCCESS); + + /* NOTE: These functions provide their own locks */ + if (schedule()) { + schedule_job_save(); + schedule_node_save(); + } + } +} + +/* _slurm_rpc_resv_delete - process RPC to delete a reservation */ +static void _slurm_rpc_resv_delete(slurm_msg_t * msg) +{ + /* init */ + int error_code = SLURM_SUCCESS; + DEF_TIMERS; + reservation_name_msg_t *resv_desc_ptr = (reservation_name_msg_t *) + msg->data; + /* Locks: write node */ + slurmctld_lock_t node_write_lock = { + NO_LOCK, NO_LOCK, WRITE_LOCK, NO_LOCK }; + uid_t uid = g_slurm_auth_get_uid(msg->auth_cred, NULL); + + START_TIMER; + debug2("Processing RPC: REQUEST_DELETE_RESERVTION from uid=%u", + (unsigned int) uid); + if (!validate_super_user(uid)) { + error_code = ESLURM_USER_ID_MISSING; + error + ("Security violation, DELETE_RESERVTION RPC from uid=%u", + (unsigned int) uid); + } + + if (error_code == SLURM_SUCCESS) { + /* do RPC call */ + lock_slurmctld(node_write_lock); + error_code = delete_resv(resv_desc_ptr); + unlock_slurmctld(node_write_lock); + END_TIMER2("_slurm_rpc_resv_delete"); + } + + /* return result */ + if (error_code) { + info("_slurm_rpc_delete_reservation partition=%s: %s", + resv_desc_ptr->name, slurm_strerror(error_code)); + slurm_send_rc_msg(msg, error_code); + } else { + info("_slurm_rpc_delete_reservation complete for %s %s", + resv_desc_ptr->name, TIME_STR); + slurm_send_rc_msg(msg, SLURM_SUCCESS); + + /* NOTE: These functions provide their own locks */ + if (schedule()) { + schedule_job_save(); + schedule_node_save(); + } + + } +} + +/* _slurm_rpc_resv_show - process RPC to dump reservation info */ +static void _slurm_rpc_resv_show(slurm_msg_t * msg) +{ + resv_info_request_msg_t *resv_req_msg = (resv_info_request_msg_t *) + msg->data; + DEF_TIMERS; + /* Locks: read node */ + slurmctld_lock_t node_read_lock = { + NO_LOCK, NO_LOCK, READ_LOCK, NO_LOCK }; + uid_t uid = g_slurm_auth_get_uid(msg->auth_cred, NULL); + slurm_msg_t response_msg; + char *dump; + int dump_size; + + START_TIMER; + debug2("Processing RPC: REQUEST_RESERVATION_INFO from uid=%u", + (unsigned int) uid); + if ((slurmctld_conf.private_data & PRIVATE_DATA_PARTITIONS) && + (!validate_super_user(uid))) { + debug2("Security violation, REQUEST_RESERVATION_INFO " + "RPC from uid=%d", uid); + slurm_send_rc_msg(msg, ESLURM_ACCESS_DENIED); + } else if ((resv_req_msg->last_update - 1) >= last_resv_update) { + debug2("_slurm_rpc_resv_show, no change"); + slurm_send_rc_msg(msg, SLURM_NO_CHANGE_IN_DATA); + } else { + lock_slurmctld(node_read_lock); + show_resv(&dump, &dump_size, uid); + unlock_slurmctld(node_read_lock); + END_TIMER2("_slurm_rpc_resv_show"); + + /* init response_msg structure */ + slurm_msg_t_init(&response_msg); + response_msg.address = msg->address; + response_msg.msg_type = RESPONSE_RESERVATION_INFO; + response_msg.data = dump; + response_msg.data_size = dump_size; + + /* send message */ + slurm_send_node_msg(msg->conn_fd, &response_msg); + xfree(dump); + } +} + /* determine of nodes are ready for the job */ static void _slurm_rpc_job_ready(slurm_msg_t * msg) { diff --git a/src/slurmctld/read_config.c b/src/slurmctld/read_config.c index d77b249c397..50a2cf62295 100644 --- a/src/slurmctld/read_config.c +++ b/src/slurmctld/read_config.c @@ -76,6 +76,7 @@ #include "src/slurmctld/node_scheduler.h" #include "src/slurmctld/proc_req.h" #include "src/slurmctld/read_config.h" +#include "src/slurmctld/reservation.h" #include "src/slurmctld/sched_plugin.h" #include "src/slurmctld/slurmctld.h" #include "src/slurmctld/trigger_mgr.h" @@ -854,6 +855,7 @@ int read_slurm_conf(int recover) (void) _sync_nodes_to_comp_job();/* must follow select_g_node_init() */ load_part_uid_allow_list(1); + load_all_resv_state(recover); if (recover >= 1) (void) trigger_state_restore(); diff --git a/src/slurmctld/reservation.c b/src/slurmctld/reservation.c index cdc60b33254..faee8bbbe47 100644 --- a/src/slurmctld/reservation.c +++ b/src/slurmctld/reservation.c @@ -15,7 +15,7 @@ * 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 + * 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 @@ -35,4 +35,1288 @@ * 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/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/pack.h" +#include "src/common/parse_time.h" +#include "src/common/uid.h" +#include "src/common/xassert.h" +#include "src/common/xmalloc.h" +#include "src/common/xstring.h" + +#include "src/slurmctld/locks.h" +#include "src/slurmctld/slurmctld.h" +#include "src/slurmctld/state_save.h" + +#define _RESV_DEBUG 1 +#define RESV_MAGIC 0x3b82 + +/* Change RESV_STATE_VERSION value when changing the state save format */ +#define RESV_STATE_VERSION "VER001" + +time_t last_resv_update = (time_t) 0; + +typedef struct slurmctld_resv { + char *accounts; /* names of accounts permitted to use */ + int account_cnt; /* count of accounts permitted to use */ + char **account_list; /* list of accounts permitted to use */ + char *assoc_list; /* list of associations */ + uint32_t cpu_cnt; /* number of reserved CPUs */ + time_t end_time; /* end time of reservation */ + char *features; /* required node features */ + uint16_t magic; /* magic cookie, RESV_MAGIC */ + char *name; /* name of reservation */ + uint32_t node_cnt; /* count of nodes required */ + char *node_list; /* list of reserved nodes or ALL */ + bitstr_t *node_bitmap; /* bitmap of reserved nodes */ + char *partition; /* name of partition to be used */ + struct part_record *part_ptr; /* pointer to partition used */ + uint32_t resv_id; /* unique reservation ID, internal use */ + time_t start_time; /* start time of reservation */ + uint16_t type; /* see RESERVE_TYPE_* above */ + char *users; /* names of users permitted to use */ + int user_cnt; /* count of users permitted to use */ + uid_t *user_list; /* array of users permitted to use */ +} slurmctld_resv_t; + +List resv_list = (List) NULL; +uint32_t top_suffix = 0; + +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 _del_resv_rec(void *x); +static void _deleted_resv(struct slurmctld_resv *resv_ptr); +static void _dump_resv_req(reserve_request_msg_t *resv_ptr, char *mode); +static int _find_resv_name(void *x, void *key); +static void _generate_resv_name(reserve_request_msg_t *resv_ptr); +static bool _is_account_valid(char *account); +static void _pack_resv(struct slurmctld_resv *resv_ptr, Buf buffer, + bool internal); +static void _set_assoc_list(struct slurmctld_resv *resv_ptr); +static void _set_resv_id(struct slurmctld_resv *resv_ptr); +static int _update_account_list(struct slurmctld_resv *resv_ptr, + char *accounts); +static int _update_uid_list(struct slurmctld_resv *resv_ptr, char *users); +static void _updated_resv(struct slurmctld_resv *resv_ptr); +static void _validate_all_reservations(void); +static bool _validate_one_reservation(slurmctld_resv_t *resv_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); + 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); + xfree(resv_ptr->name); + if (resv_ptr->node_bitmap) + bit_free(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_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(reserve_request_msg_t *resv_ptr, char *mode) +{ +#ifdef _RESV_DEBUG + char start_str[32] = "", end_str[32] = "", *type_str; + int duration; + + slurm_make_time_str(&resv_ptr->start_time,start_str,sizeof(start_str)); + slurm_make_time_str(&resv_ptr->end_time, end_str, sizeof(end_str)); + if (resv_ptr->type == RESERVE_TYPE_MAINT) + type_str = "MAINT"; + else + type_str = ""; + if (resv_ptr->duration == NO_VAL) + duration = -1; + else + duration = resv_ptr->duration; + + info("%s: Name=%s StartTime=%s EndTime=%s Duration=%d " + "Type=%s NodeCnt=%u NodeList=%s Features=%s " + "PartitionName=%s Users=%s Accounts=%s", + mode, resv_ptr->name, start_str, end_str, duration, + type_str, resv_ptr->node_cnt, resv_ptr->node_list, + resv_ptr->features, resv_ptr->partition, + resv_ptr->users, resv_ptr->accounts); +#endif +} + +static void _generate_resv_name(reserve_request_msg_t *resv_ptr) +{ + char *key, *name, *sep, tmp[32]; + 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); + strcat(name, "_"); + len++; + + if (top_suffix > 0xffffff00) + top_suffix = 0; /* Wrap around */ + snprintf(tmp, sizeof(tmp), "%d", ++top_suffix); + strcat(name, tmp); + resv_ptr->name = name; +} + +/* Validate an account name */ +static bool _is_account_valid(char *account) +{ + /* FIXME: Need to add logic here */ + return true; +} + +/* Set a association list based upon accounts and users */ +static void _set_assoc_list(struct slurmctld_resv *resv_ptr) +{ + /* FIXME: Need to add logic here to get assoc_list from slurmdbd + * based upon resv_ptr->account_cnt and account_list plus + * user_cnt and user_list */ + xfree(resv_ptr->assoc_list); /* clear for modify */ + resv_ptr->assoc_list = xstrdup("TBD"); /* FOR TESTING ONLY */ +} + +/* Set a unique reservation id */ +static void _set_resv_id(struct slurmctld_resv *resv_ptr) +{ + /* FIXME: Need to add logic here to get resv_id from slurmdbd */ + /* NOTE: resv_id is zero on entry */ + resv_ptr->resv_id = top_suffix; /* FOR TESTING ONLY */ +} + +/* Note that a reservation has been deleted */ +static void _deleted_resv(struct slurmctld_resv *resv_ptr) +{ + /* FIXME: Need to add logic here to notify slurmdbd */ +} + +/* Note that a reservation has been updated */ +static void _updated_resv(struct slurmctld_resv *resv_ptr) +{ + /* FIXME: Need to add logic here to notify slurmdbd */ +} + +/* + * 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, *tmp, *tok; + int ac_cnt = 0, i; + char **ac_list; + + *account_cnt = 0; + *account_list = (char **) NULL; + + if (!accounts) + return ESLURM_INVALID_BANK_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_BANK_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(struct slurmctld_resv *resv_ptr, + char *accounts) +{ + char *last, *tmp, *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_BANK_ACCOUNT; + + i = strlen(accounts); + ac_list = xmalloc(sizeof(char *) * (i + 2)); + ac_type = xmalloc(sizeof(int) * (i + 2)); + tmp = xstrdup(accounts); + tok = strtok_r(tmp, ",", &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 (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); + } + xfree(tmp); + + if ((plus_account == 0) && (minus_account == 0)) { + /* Just a reset of account list */ + xfree(resv_ptr->accounts); + resv_ptr->accounts = xstrdup(accounts); + xfree(resv_ptr->account_list); + resv_ptr->account_list = ac_list; + resv_ptr->account_cnt = ac_cnt; + 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); + return SLURM_SUCCESS; + + inval: for (i=0; i<ac_cnt; i++) + xfree(ac_list[i]); + xfree(ac_list); + xfree(ac_type); + xfree(tmp); + return ESLURM_INVALID_BANK_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, *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) { + u_tmp = uid_from_string(tok); + if (u_tmp == (uid_t) -1) { + 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(struct slurmctld_resv *resv_ptr, char *users) +{ + char *last, *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)); + tmp = xstrdup(users); + tok = strtok_r(tmp, ",", &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 (plus_user || minus_user) { + info("Reservation user expression invalid %s", users); + goto inval; + } else + u_type[u_cnt] = 3; /* set */ + u_tmp = uid_from_string(tok); + if (u_tmp == (uid_t) -1) { + 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); + } + xfree(tmp); + + if ((plus_user == 0) && (minus_user == 0)) { + /* Just a reset of user list */ + xfree(resv_ptr->users); + xfree(resv_ptr->user_list); + resv_ptr->users = xstrdup(users); + resv_ptr->user_cnt = u_cnt; + resv_ptr->user_list = u_list; + 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_list); + xfree(u_name); + xfree(u_type); + return SLURM_SUCCESS; + + inval: xfree(tmp); + xfree(u_list); + xfree(u_name); + xfree(u_type); + return ESLURM_USER_ID_MISSING; +} + +/* + * _pack_resv - dump all configuration information about a specific reservation + * in machine independent form (for network transmission) + * 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(struct slurmctld_resv *resv_ptr, Buf buffer, + bool internal) +{ + packstr(resv_ptr->accounts, buffer); + pack_time(resv_ptr->end_time, buffer); + packstr(resv_ptr->features, buffer); + packstr(resv_ptr->name, buffer); + pack32(resv_ptr->node_cnt, buffer); + packstr(resv_ptr->node_list, buffer); + packstr(resv_ptr->partition, buffer); + pack_time(resv_ptr->start_time, buffer); + pack16(resv_ptr->type, 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); + } +} + +/* Create a resource reservation */ +extern int create_resv(reserve_request_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; + + 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 requestion 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 requestion has invalid end time"); + rc = ESLURM_INVALID_TIME_VALUE; + goto bad_parse; + } + } 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->type == (uint16_t) NO_VAL) + resv_desc_ptr->type = 0; + else if (resv_desc_ptr->type > RESERVE_TYPE_MAINT) { + info("Invalid reservation type %u ignored", + resv_desc_ptr->type); + resv_desc_ptr->type = 0; + } + 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_BANK_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->name) { + resv_ptr = (slurmctld_resv_t *) list_find_first (resv_list, + _find_resv_name, resv_desc_ptr->name); + if (resv_ptr) { + info("Reservation requestion 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; + /* Same as explicitly created name, retry */ + } + } + if (resv_desc_ptr->node_list) { + if (strcmp(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)) { + rc = ESLURM_INVALID_NODE_NAME; + goto bad_parse; + } + if (resv_desc_ptr->node_cnt == NO_VAL) + resv_desc_ptr->node_cnt = 0; + } else if (resv_desc_ptr->node_cnt == NO_VAL) { + info("Reservation request lacks node specification"); + rc = ESLURM_INVALID_NODE_NAME; + goto bad_parse; + } + + /* 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->end_time = resv_desc_ptr->end_time; + resv_ptr->features = resv_desc_ptr->features; + resv_desc_ptr->features = NULL; /* Nothing left to free */ + 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->type = resv_desc_ptr->type; + 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_resv_id(resv_ptr); + _set_assoc_list(resv_ptr); + + info("Created reservation %s for accounts=%s users=%s", + resv_ptr->name, resv_ptr->accounts, resv_ptr->users); + 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 (node_bitmap) + bit_free(node_bitmap); + xfree(user_list); + return rc; +} + +/* Update an exiting resource reservation */ +extern int update_resv(reserve_request_msg_t *resv_desc_ptr) +{ + time_t now = time(NULL); + slurmctld_resv_t *resv_ptr; + int error_code = SLURM_SUCCESS; + + 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; + + /* Process the request */ + last_resv_update = now; + if (resv_desc_ptr->start_time != (time_t) NO_VAL) { + if (resv_desc_ptr->start_time < (now - 60)) { + info("Reservation requestion has invalid start time"); + error_code = ESLURM_INVALID_TIME_VALUE; + goto fini; + } + resv_ptr->start_time = resv_desc_ptr->start_time; + } + if (resv_desc_ptr->end_time != (time_t) NO_VAL) { + if (resv_desc_ptr->end_time < (now - 60)) { + info("Reservation requestion has invalid end time"); + error_code = ESLURM_INVALID_TIME_VALUE; + goto fini; + } + resv_ptr->end_time = resv_desc_ptr->end_time; + } + if (resv_desc_ptr->duration != NO_VAL) { + resv_ptr->end_time = resv_ptr->start_time + + (resv_desc_ptr->duration * 60); + } + if (resv_desc_ptr->type != (uint16_t) NO_VAL) { + if (resv_desc_ptr->type > RESERVE_TYPE_MAINT) { + error("Invalid reservation type %u ignored", + resv_desc_ptr->type); + } else + resv_ptr->type = resv_desc_ptr->type; + } + 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 fini; + } + 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->node_cnt != NO_VAL) + resv_ptr->node_cnt = resv_desc_ptr->node_cnt; + if (resv_desc_ptr->accounts) { + int rc; + rc = _update_account_list(resv_ptr, resv_desc_ptr->accounts); + if (rc) { + error_code = rc; + goto fini; + } + } + if (resv_desc_ptr->features) { + xfree(resv_ptr->features); + resv_ptr->features = resv_desc_ptr->features; + resv_desc_ptr->features = NULL; /* Nothing left to free */ + } + if (resv_desc_ptr->users) { + int rc; + rc = _update_uid_list(resv_ptr, + resv_desc_ptr->users); + if (rc) { + error_code = rc; + goto fini; + } + } + if (resv_desc_ptr->node_list) { /* Change bitmap last */ + bitstr_t *node_bitmap; + if (strcmp(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 fini; + } + 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; + } + +fini: last_resv_update = now; + _set_assoc_list(resv_ptr); + _updated_resv(resv_ptr); + schedule_resv_save(); + return error_code; +} + +/* Delete an exiting resource reservation */ +extern int delete_resv(reservation_name_msg_t *resv_desc_ptr) +{ + ListIterator iter; + slurmctld_resv_t *resv_ptr; + +#ifdef _RESV_DEBUG + info("delete_resv: Name=%s", resv_desc_ptr->name); +#endif + + 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; + _deleted_resv(resv_ptr); + list_delete_item(iter); + last_resv_update = time(NULL); + break; + } + list_iterator_destroy(iter); + + if (!resv_ptr) { + info("Reservation %s not found for deletion", + resv_desc_ptr->name); + return ESLURM_RESERVATION_INVALID; + } + + schedule_resv_save(); + return SLURM_SUCCESS; +} + +/* 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))) { + _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); + time_t now = time(NULL); + 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))) { + if (resv_ptr->end_time > now) { + _pack_resv(resv_ptr, buffer, true); + } else { + debug("Purging vestigial reservation record %s", + resv_ptr->name); + list_delete_item(iter); + } + } + list_iterator_destroy(iter); + /* Maintain config read lock until we copy state_save_location *\ + \* unlock_slurmctld(resv_read_lock); - see below */ + + /* write the buffer to file */ + 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); + 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; + 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; + } + fsync(log_fd); + close(log_fd); + } + if (error_code) + (void) unlink(new_file); + else { /* file shuffle */ + (void) unlink(old_file); + (void) link(reg_file, old_file); + (void) unlink(reg_file); + (void) link(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->type > RESERVE_TYPE_MAINT) { + error("Reservation %s has invalid type (%u)", + resv_ptr->name, resv_ptr->type); + 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->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 (strcmp(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; + time_t now = time(NULL); + 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 (resv_ptr->end_time < now) { + debug("Purging vestigial reservation record %s", + resv_ptr->name); + list_delete_item(iter); + } else if (!_validate_one_reservation(resv_ptr)) { + error("Purging invalid reservation record %s", + resv_ptr->name); + 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); +} + +/* + * Load the reservation state from file, recover on slurmctld restart. + * execute this after loading the configuration file data. + * IN recover - 0 = no change + * 1 = validate existing (in memory) reservations + * 2 = 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) { + if (resv_list) + _validate_all_reservations(); + else + resv_list = list_create(_del_resv_rec); + return SLURM_SUCCESS; + } + + if (resv_list) + list_flush(resv_list); + else + resv_list = list_create(_del_resv_rec); + + /* read the file */ + state_file = xstrdup(slurmctld_conf.state_save_location); + xstrcat(state_file, "/resv_state"); + lock_state_files(); + state_fd = open(state_file, O_RDONLY); + 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 incompatable"); + error("************************************************************"); + xfree(ver_str); + free_buf(buffer); + 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->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, buffer); + safe_unpack16(&resv_ptr->type, 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); + + 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(); + 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; +} diff --git a/src/slurmctld/reservation.h b/src/slurmctld/reservation.h index ae173b82449..2ae54e56a98 100644 --- a/src/slurmctld/reservation.h +++ b/src/slurmctld/reservation.h @@ -15,7 +15,7 @@ * 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 + * 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 @@ -38,4 +38,36 @@ #ifndef _RESERVATION_H #define _RESERVATION_H +#include <time.h> +#include <unistd.h> +#include <slurm/slurm.h> + +extern time_t last_resv_update; + +/* Create a resource reservation */ +extern int create_resv(reserve_request_msg_t *resv_desc_ptr); + +/* Update an exiting resource reservation */ +extern int update_resv(reserve_request_msg_t *resv_desc_ptr); + +/* Delete an exiting resource reservation */ +extern int delete_resv(reservation_name_msg_t *resv_desc_ptr); + +/* Dump the reservation records to a buffer */ +extern void show_resv(char **buffer_ptr, int *buffer_size, uid_t uid); + +/* Save the state of all reservations to file */ +extern int dump_all_resv_state(void); + +/* + * Load the reservation state from file, recover on slurmctld restart. + * execute this after loading the configuration file data. + * IN recover - 0 = no change + * 1 = validate existing (in memory) reservations + * 2 = 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); + #endif /* !_RESERVATION_H */ diff --git a/src/slurmctld/slurmctld.h b/src/slurmctld/slurmctld.h index a7cb55d14bd..b20d729d9d6 100644 --- a/src/slurmctld/slurmctld.h +++ b/src/slurmctld/slurmctld.h @@ -2,7 +2,7 @@ * slurmctld.h - definitions of functions and structures for slurmcltd use ***************************************************************************** * Copyright (C) 2002-2007 The Regents of the University of California. - * Copyright (C) 2008 Lawrence Livermore National Security. + * Copyright (C) 2008-2009 Lawrence Livermore National Security. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Morris Jette <jette1@llnl.gov> et. al. * LLNL-CODE-402394. diff --git a/src/slurmctld/state_save.c b/src/slurmctld/state_save.c index 105b624bfd8..23bc77c1044 100644 --- a/src/slurmctld/state_save.c +++ b/src/slurmctld/state_save.c @@ -2,6 +2,7 @@ * state_save.c - Keep saved slurmctld state current ***************************************************************************** * Copyright (C) 2004-2007 The Regents of the University of California. + * Copyright (C) 2008-2009 Lawrence Livermore National Security. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Morris Jette <jette1@llnl.gov> * LLNL-CODE-402394. @@ -44,12 +45,14 @@ #endif /* WITH_PTHREADS */ #include "src/common/macros.h" +#include "src/slurmctld/reservation.h" #include "src/slurmctld/slurmctld.h" #include "src/slurmctld/trigger_mgr.h" static pthread_mutex_t state_save_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t state_save_cond = PTHREAD_COND_INITIALIZER; -static int save_jobs = 0, save_nodes = 0, save_parts = 0, save_triggers = 0; +static int save_jobs = 0, save_nodes = 0, save_parts = 0; +static int save_triggers = 0, save_resv = 0; static bool run_save_thread = true; /* Queue saving of job state information */ @@ -79,6 +82,15 @@ extern void schedule_part_save(void) pthread_cond_broadcast(&state_save_cond); } +/* Queue saving of reservation state information */ +extern void schedule_resv_save(void) +{ + slurm_mutex_lock(&state_save_lock); + save_resv++; + slurm_mutex_unlock(&state_save_lock); + pthread_cond_broadcast(&state_save_cond); +} + /* Queue saving of trigger state information */ extern void schedule_trigger_save(void) { @@ -113,7 +125,7 @@ extern void *slurmctld_state_save(void *no_data) slurm_mutex_lock(&state_save_lock); while (1) { if (save_jobs + save_nodes + save_parts + - save_triggers) + save_resv + save_triggers) break; /* do the work */ else if (!run_save_thread) { run_save_thread = true; @@ -157,6 +169,17 @@ extern void *slurmctld_state_save(void *no_data) if (run_save) (void)dump_all_part_state(); + /* save reservation info if necessary */ + run_save = false; + slurm_mutex_lock(&state_save_lock); + if (save_resv) { + run_save = true; + save_resv = 0; + } + slurm_mutex_unlock(&state_save_lock); + if (run_save) + (void)dump_all_resv_state(); + /* save trigger info if necessary */ run_save = false; slurm_mutex_lock(&state_save_lock); diff --git a/src/slurmctld/state_save.h b/src/slurmctld/state_save.h index d800752ad16..181b58747b0 100644 --- a/src/slurmctld/state_save.h +++ b/src/slurmctld/state_save.h @@ -2,6 +2,7 @@ * state_save.h - Definitions for keeping saved slurmctld state current ***************************************************************************** * Copyright (C) 2004-2007 The Regents of the University of California. + * Copyright (C) 2008-2009 Lawrence Livermore National Security. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Morris Jette <jette1@llnl.gov> * LLNL-CODE-402394. @@ -15,7 +16,7 @@ * 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 + * 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 @@ -47,6 +48,9 @@ extern void schedule_node_save(void); /* Queue saving of partition state information */ extern void schedule_part_save(void); +/* Queue saving of reservation state information */ +extern void schedule_resv_save(void); + /* Queue saving of trigger state information */ extern void schedule_trigger_save(void); -- GitLab