blob: 1cabeada8f03a55ab90c6caa9cf8d3e2fff76789 [file] [log] [blame]
/*****************************************************************************\
* reservations.c - Slurm REST API reservations http operations handlers
*****************************************************************************
* Copyright (C) 2020 UT-Battelle, LLC.
* Written by Matt Ezell <ezellma@ornl.gov>
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include "src/common/slurm_protocol_defs.h"
#include "src/common/xassert.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/interfaces/data_parser.h"
#include "src/slurmrestd/operations.h"
#include "api.h"
typedef struct {
openapi_ctxt_t *ctxt;
reserve_info_msg_t *res_info_ptr;
} foreach_mod_resvs_args_t;
/* validate resv create resv_desc_msg */
static int _validate_each_resv_create_desc(resv_desc_msg_t *resv_msg,
openapi_ctxt_t *ctxt)
{
char *error_msg;
if (validate_resv_create_desc(resv_msg, &error_msg, NULL))
return resp_error(ctxt, ESLURM_RESERVATION_INVALID,
"validate_resv_create_desc", "%s", error_msg);
return SLURM_SUCCESS;
}
/* Create reservation from desc */
static int _create_resv(resv_desc_msg_t *resv_msg, openapi_ctxt_t *ctxt)
{
char *new_res_name = NULL;
int rc = SLURM_SUCCESS;
if (!(new_res_name = slurm_create_reservation(resv_msg))) {
rc = errno;
if (((rc == ESLURM_REQUESTED_NODE_CONFIG_UNAVAILABLE) ||
(rc == ESLURM_NODES_BUSY)) && !resv_msg->node_list) {
resp_error(ctxt, rc, "slurm_create_reservation",
"Error creating reservation %s. Note, unless nodes are directly requested a reservation must exist in a single partition. If no partition is requested the default partition is assumed.",
resv_msg->name);
} else {
resp_error(ctxt, rc, "slurm_create_reservation",
"Error creating reservation %s",
resv_msg->name);
}
return rc;
}
free(new_res_name);
return rc;
}
/* Update reservation from desc */
static int _update_resv(resv_desc_msg_t *resv_msg, openapi_ctxt_t *ctxt)
{
int rc;
if ((rc = slurm_update_reservation(resv_msg))) {
if (errno)
rc = errno;
resp_error(ctxt, rc, "slurm_update_reservation",
"Error updating reservation %s", resv_msg->name);
}
return rc;
}
static int _load_reservations(reserve_info_msg_t **res_info_ptr,
openapi_ctxt_t *ctxt)
{
int rc = SLURM_SUCCESS;
xassert(res_info_ptr && !*res_info_ptr);
errno = 0;
if ((rc = slurm_load_reservations(0, res_info_ptr)) || !*res_info_ptr) {
if (rc == SLURM_ERROR && errno)
rc = errno;
resp_error(ctxt, rc, "slurm_load_reservations()",
"Unable to query reservations");
}
return rc;
}
static int _zero_unused_flag(void *x, void *args)
{
resv_desc_msg_t *resv_msg = x;
if (resv_msg->flags == NO_VAL64)
resv_msg->flags = 0;
return SLURM_SUCCESS;
}
static int _set_unused_flag(void *x, void *args)
{
resv_desc_msg_t *resv_msg = x;
if (!resv_msg->flags)
resv_msg->flags = NO_VAL64;
return SLURM_SUCCESS;
}
static int _parse_resv_desc_list(openapi_ctxt_t *ctxt,
openapi_reservation_mod_request_t *resv_req)
{
int rc = SLURM_SUCCESS;
char *empty_list_msg =
"No reservation descriptions specified in reservations array";
xassert(resv_req && !resv_req->reservations);
if (!ctxt->query) {
rc = ESLURM_REST_INVALID_QUERY;
resp_error(ctxt, rc, __func__,
"unexpected empty query for reservation creation");
return rc;
}
if (DATA_PARSE(ctxt->parser, RESERVATION_MOD_REQ, *resv_req,
ctxt->query, ctxt->parent_path)) {
rc = ESLURM_REST_INVALID_QUERY;
resp_error(ctxt, rc, __func__,
"Rejecting request. Failure parsing parameters");
FREE_NULL_LIST(resv_req->reservations);
return rc;
}
if (resv_req->reservations && list_count(resv_req->reservations))
list_for_each(resv_req->reservations, _set_unused_flag, NULL);
else if (resv_req->reservations)
resp_warn(ctxt, __func__, "%s", empty_list_msg);
else {
rc = ESLURM_REST_INVALID_QUERY;
resp_error(ctxt, rc, __func__, "%s", empty_list_msg);
}
return rc;
}
/* return true on error else false - meant for use in list_find_first */
static int _check_resv_name(void *x, void *args)
{
resv_desc_msg_t *resv_msg = x;
openapi_ctxt_t *ctxt = args;
if (resv_msg->name == NULL) {
resp_error(ctxt, ESLURM_RESERVATION_INVALID, __func__,
"Reservation must be given");
return true;
}
return false;
}
static bool _does_resv_exist(char *resv_name, reserve_info_msg_t *res_info_ptr)
{
for (int i = 0; i < res_info_ptr->record_count; i++) {
const char *name = res_info_ptr->reservation_array[i].name;
if (!xstrcasecmp(resv_name, name))
return true;
}
return false;
}
static int _create_or_update_each_resv(void *x, void *arg)
{
resv_desc_msg_t *resv_msg = x;
foreach_mod_resvs_args_t *args = arg;
int rc = SLURM_SUCCESS;
/* Check if the resv already exists - if so update it else create it */
if (_does_resv_exist(resv_msg->name, args->res_info_ptr))
rc = _update_resv(resv_msg, args->ctxt);
else if (!(rc = _validate_each_resv_create_desc(resv_msg, args->ctxt)))
rc = _create_resv(resv_msg, args->ctxt);
rc = rc ? rc : args->ctxt->rc;
args->ctxt->rc = args->ctxt->rc ? args->ctxt->rc : rc;
return rc;
}
/* Create or update reservations */
static int _mod_reservations(openapi_ctxt_t *ctxt)
{
openapi_reservation_mod_request_t resv_req = { 0 };
int rc = SLURM_SUCCESS;
foreach_mod_resvs_args_t args = {
.ctxt = ctxt,
};
if ((rc = _parse_resv_desc_list(ctxt, &resv_req)))
return rc;
if (list_find_first(resv_req.reservations, _check_resv_name, ctxt)) {
FREE_NULL_LIST(resv_req.reservations);
return ctxt->rc;
}
if (!(rc = _load_reservations(&args.res_info_ptr, ctxt)))
list_for_each(resv_req.reservations,
_create_or_update_each_resv, &args);
if (!rc && !ctxt->rc) {
list_for_each(resv_req.reservations, _zero_unused_flag, NULL);
DUMP_OPENAPI_RESP_SINGLE(OPENAPI_RESERVATION_MOD_RESP,
resv_req.reservations, ctxt);
}
slurm_free_reservation_info_msg(args.res_info_ptr);
FREE_NULL_LIST(resv_req.reservations);
return rc ? rc : ctxt->rc;
}
static int _get_reservations(openapi_ctxt_t *ctxt)
{
int rc = SLURM_SUCCESS;
reserve_info_msg_t *res_info_ptr = NULL;
openapi_reservation_query_t query = {0};
openapi_resp_reserve_info_msg_t resp = {0};
if (DATA_PARSE(ctxt->parser, OPENAPI_RESERVATION_QUERY, query,
ctxt->query, ctxt->parent_path)) {
resp_error(ctxt, ESLURM_REST_INVALID_QUERY, __func__,
"Rejecting request. Failure parsing query");
goto done;
}
errno = 0;
if ((rc = slurm_load_reservations(query.update_time, &res_info_ptr))) {
if (rc == SLURM_ERROR)
rc = errno;
resp_error(ctxt, rc, "slurm_load_reservations()",
"Unable to query reservations");
goto done;
}
if (res_info_ptr) {
resp.last_update = res_info_ptr->last_update;
resp.reservations = res_info_ptr;
}
DATA_DUMP(ctxt->parser, OPENAPI_RESERVATION_RESP, resp, ctxt->resp);
done:
slurm_free_reservation_info_msg(res_info_ptr);
return rc;
}
extern int op_handler_reservations(openapi_ctxt_t *ctxt)
{
int rc = SLURM_SUCCESS;
if (ctxt->method == HTTP_REQUEST_GET) {
rc = _get_reservations(ctxt);
} else if (ctxt->method == HTTP_REQUEST_POST) {
rc = _mod_reservations(ctxt);
} else {
resp_error(ctxt, ESLURM_REST_INVALID_QUERY, __func__,
"Unsupported HTTP method requested: %s",
get_http_method_string(ctxt->method));
}
return rc;
}
static int _parse_resv_name_param(openapi_ctxt_t *ctxt,
openapi_reservation_param_t *params)
{
xassert(params);
if (DATA_PARSE(ctxt->parser, OPENAPI_RESERVATION_PARAM, *params,
ctxt->parameters, ctxt->parent_path)) {
xfree(params->reservation_name);
return resp_error(ctxt, ESLURM_REST_INVALID_QUERY, __func__,
"Rejecting request. Failure parsing parameters");
}
return SLURM_SUCCESS;
}
static int _get_single_reservation(openapi_ctxt_t *ctxt)
{
openapi_reservation_param_t params = { 0 };
openapi_reservation_query_t query = { 0 };
int rc = SLURM_SUCCESS;
reserve_info_msg_t *res_info_ptr = NULL;
reserve_info_t *res = NULL;
if (_parse_resv_name_param(ctxt, &params))
goto done;
if (DATA_PARSE(ctxt->parser, OPENAPI_RESERVATION_QUERY, query,
ctxt->query, ctxt->parent_path)) {
resp_error(ctxt, ESLURM_REST_INVALID_QUERY, __func__,
"Rejecting request. Failure parsing query");
goto done;
}
errno = 0;
if ((rc = slurm_load_reservations(query.update_time, &res_info_ptr)) ||
!res_info_ptr || !res_info_ptr->record_count) {
if (rc == SLURM_ERROR)
rc = errno;
resp_error(ctxt, rc, "slurm_load_reservations()",
"Unable to query reservations");
goto done;
}
for (int i = 0; !rc && i < res_info_ptr->record_count; i++) {
const char *n = res_info_ptr->reservation_array[i].name;
if (!xstrcasecmp(params.reservation_name, n)) {
res = &res_info_ptr->reservation_array[i];
break;
}
}
if (!res && params.reservation_name) {
resp_error(ctxt, ESLURM_REST_INVALID_QUERY, __func__,
"Unable to find reservation %s",
params.reservation_name);
} else {
reserve_info_msg_t r = {
.last_update = res_info_ptr->last_update,
.record_count = 1,
.reservation_array = res,
};
openapi_resp_reserve_info_msg_t resp = {
.reservations = &r,
.last_update = res_info_ptr->last_update,
};
DATA_DUMP(ctxt->parser, OPENAPI_RESERVATION_RESP, resp,
ctxt->resp);
}
done:
slurm_free_reservation_info_msg(res_info_ptr);
xfree(params.reservation_name);
return rc;
}
static int _parse_resv_desc(openapi_ctxt_t *ctxt, resv_desc_msg_t *resv_msg)
{
int rc = SLURM_SUCCESS;
xassert(resv_msg);
if (!ctxt->query) {
rc = ESLURM_REST_INVALID_QUERY;
resp_error(ctxt, rc, __func__,
"unexpected empty query for reservation creation");
return rc;
}
slurm_init_resv_desc_msg(resv_msg);
resv_msg->flags = 0; /* required for parsing */
if (DATA_PARSE(ctxt->parser, RESERVATION_DESC_MSG, *resv_msg,
ctxt->query, ctxt->parent_path)) {
rc = ESLURM_REST_INVALID_QUERY;
resp_error(ctxt, rc, __func__,
"Rejecting request. Failure parsing parameters");
slurm_free_resv_desc_members(resv_msg);
return rc;
}
if (!resv_msg->flags)
resv_msg->flags = NO_VAL64;
return rc;
}
/* Create or update reservations */
static int _mod_reservation(openapi_ctxt_t *ctxt)
{
resv_desc_msg_t resv_msg = { 0 };
int rc = SLURM_SUCCESS;
foreach_mod_resvs_args_t args = {
.ctxt = ctxt,
};
if (ctxt->method != HTTP_REQUEST_POST)
return resp_error(ctxt, ESLURM_REST_INVALID_QUERY, __func__,
"Unsupported HTTP method requested: %s",
get_http_method_string(ctxt->method));
if ((rc = _parse_resv_desc(ctxt, &resv_msg)))
return rc;
if (resv_msg.name == NULL) {
rc = resp_error(ctxt, ESLURM_RESERVATION_INVALID, __func__,
"Reservation must be given.");
slurm_free_resv_desc_members(&resv_msg);
return rc;
}
if (!(rc = _load_reservations(&args.res_info_ptr, ctxt)))
rc = _create_or_update_each_resv(&resv_msg, &args);
if (!rc) {
list_t *resv_list = list_create(NULL);
list_append(resv_list, &resv_msg);
_zero_unused_flag(&resv_msg, NULL);
DUMP_OPENAPI_RESP_SINGLE(OPENAPI_RESERVATION_MOD_RESP,
resv_list, ctxt);
FREE_NULL_LIST(resv_list);
rc = ctxt->rc;
}
slurm_free_resv_desc_members(&resv_msg);
slurm_free_reservation_info_msg(args.res_info_ptr);
return rc;
}
static int _delete_resv(openapi_ctxt_t *ctxt)
{
int rc = SLURM_SUCCESS;
openapi_reservation_param_t params = { 0 };
reservation_name_msg_t resv_name_msg = { 0 };
if ((rc = _parse_resv_name_param(ctxt, &params)))
return rc;
SWAP(resv_name_msg.name, params.reservation_name);
if ((rc = slurm_delete_reservation(&resv_name_msg))) {
if (errno && rc == SLURM_ERROR)
rc = errno;
resp_error(ctxt, rc, "slurm_delete_reservation",
"Error deleting reservation %s", resv_name_msg.name);
}
xfree(resv_name_msg.name);
return rc;
}
extern int op_handler_reservation(openapi_ctxt_t *ctxt)
{
int rc = SLURM_SUCCESS;
if (ctxt->method == HTTP_REQUEST_GET) {
rc = _get_single_reservation(ctxt);
} else if (ctxt->method == HTTP_REQUEST_POST) {
rc = _mod_reservation(ctxt);
} else if (ctxt->method == HTTP_REQUEST_DELETE) {
rc = _delete_resv(ctxt);
} else {
resp_error(ctxt, ESLURM_REST_INVALID_QUERY, __func__,
"Unsupported HTTP method requested: %s",
get_http_method_string(ctxt->method));
}
return rc;
}