blob: b34f9aa715d8a910ba6f83f020e1d0339a294abc [file] [log] [blame]
/*****************************************************************************\
* job_submit_lua.c - Set defaults in job submit request specifications.
*****************************************************************************
* Copyright (C) 2010 Lawrence Livermore National Security.
* Copyright (C) SchedMD LLC.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Danny Auble <da@llnl.gov>
* CODE-OCEC-09-009. All rights reserved.
*
* 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 <dlfcn.h>
#include <inttypes.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include "slurm/slurm.h"
#include "slurm/slurm_errno.h"
#include "src/common/slurm_xlator.h"
#include "src/common/assoc_mgr.h"
#include "src/interfaces/gres.h"
#include "src/common/uid.h"
#include "src/lua/slurm_lua.h"
#include "src/slurmctld/locks.h"
#include "src/slurmctld/slurmctld.h"
#include "src/slurmctld/reservation.h"
#define _DEBUG 0
#define MIN_ACCTG_FREQUENCY 30
/*
* These variables are required by the generic plugin interface. If they
* are not found in the plugin, the plugin loader will ignore it.
*
* plugin_name - a string giving a human-readable description of the
* plugin. There is no maximum length, but the symbol must refer to
* a valid string.
*
* plugin_type - a string suggesting the type of the plugin or its
* applicability to a particular form of data or method of data handling.
* If the low-level plugin API is used, the contents of this string are
* unimportant and may be anything. Slurm uses the higher-level plugin
* interface which requires this string to be of the form
*
* <application>/<method>
*
* where <application> is a description of the intended application of
* the plugin (e.g., "auth" for Slurm authentication) and <method> is a
* description of how this plugin satisfies that application. Slurm will
* only load authentication plugins if the plugin_type string has a prefix
* of "auth/".
*
* plugin_version - an unsigned 32-bit integer containing the Slurm version
* (major.minor.micro combined into a single number).
*/
const char plugin_name[] = "Job submit lua plugin";
const char plugin_type[] = "job_submit/lua";
const uint32_t plugin_version = SLURM_VERSION_NUMBER;
static char *lua_script_path;
static time_t lua_script_last_loaded = (time_t) 0;
static lua_State *L = NULL;
static char *user_msg = NULL;
time_t last_lua_jobs_update = (time_t) 0;
time_t last_lua_resv_update = (time_t) 0;
static const char *req_fxns[] = {
"slurm_job_submit",
"slurm_job_modify",
NULL
};
/*
* Mutex for protecting multi-threaded access to this plugin.
* (Only 1 thread at a time should be in here)
*/
static pthread_mutex_t lua_lock = PTHREAD_MUTEX_INITIALIZER;
typedef struct {
uint32_t submit_uid;
uint32_t user_id;
} foreach_part_list_args_t;
/*****************************************************************************\
* We've provided a simple example of the type of things you can do with this
* plugin. If you develop another plugin that may be of interest to others
* please post it to slurm-dev@schedmd.com Thanks!
\*****************************************************************************/
/* Get the default account for a user (or NULL if not present) */
static char *_get_default_account(uint32_t user_id)
{
slurmdb_user_rec_t user;
memset(&user, 0, sizeof(slurmdb_user_rec_t));
user.uid = user_id;
if (assoc_mgr_fill_in_user(acct_db_conn, &user, accounting_enforce,
NULL, false) == SLURM_SUCCESS) {
return user.default_acct;
} else {
return NULL;
}
}
static int _fill_assoc(uint32_t user_id, char *account, char *partition,
slurmdb_assoc_rec_t *assoc)
{
xassert(assoc);
memset(assoc, 0, sizeof(slurmdb_assoc_rec_t));
assoc->uid = user_id;
assoc->partition = partition;
if (account) {
assoc->acct = account;
} else {
assoc->acct = _get_default_account(user_id);
}
return assoc_mgr_fill_in_assoc(acct_db_conn, assoc, accounting_enforce,
NULL, false);
}
/* Get the default QOS for an association (or NULL if not present) */
static char *_get_default_qos(uint32_t user_id, char *account, char *partition)
{
slurmdb_assoc_rec_t assoc;
slurmdb_qos_rec_t qos;
uint32_t qos_id = 0;
if (_fill_assoc(user_id, account, partition, &assoc) == SLURM_SUCCESS)
qos_id = assoc.def_qos_id;
if (!qos_id)
return NULL;
memset(&qos, 0, sizeof(slurmdb_qos_rec_t));
qos.id = qos_id;
if (assoc_mgr_fill_in_qos(acct_db_conn, &qos, accounting_enforce, NULL,
false) == SLURM_SUCCESS) {
return qos.name;
} else {
return NULL;
}
}
static int _qos_id_to_qos_name(void *x, void *arg)
{
char *qos_id = x;
list_t *qos_name_list = arg;
slurmdb_qos_rec_t qos = { 0 };
qos.id = atoi(qos_id);
if (assoc_mgr_fill_in_qos(acct_db_conn, &qos, accounting_enforce, NULL,
false) != SLURM_SUCCESS) {
return 0;
}
slurm_addto_char_list(qos_name_list, qos.name);
return 0;
}
/* Get all possible QOS for an association (or NULL if not present) */
static char *_get_assoc_qos(uint32_t user_id, char *account, char *partition)
{
slurmdb_assoc_rec_t assoc;
list_t *qos_name_list;
char *qos_name_list_str;
list_t *qos_list = NULL;
if (_fill_assoc(user_id, account, partition, &assoc) == SLURM_SUCCESS)
qos_list = assoc.qos_list;
if (!qos_list)
return NULL;
qos_name_list = list_create(xfree_ptr);
list_for_each_ro(qos_list, _qos_id_to_qos_name, qos_name_list);
qos_name_list_str = slurm_char_list_to_xstr(qos_name_list);
list_destroy(qos_name_list);
return qos_name_list_str;
}
/* Get the comment for an association (or NULL if not present) */
static char *_get_assoc_comment(uint32_t user_id, char *account,
char *partition)
{
slurmdb_assoc_rec_t assoc;
char *comment = NULL;
if (_fill_assoc(user_id, account, partition, &assoc) == SLURM_SUCCESS)
comment = assoc.comment;
return comment;
}
/* Get fields in an existing slurmctld job_record */
static int _job_rec_field_index(lua_State *L)
{
const char *name = luaL_checkstring(L, 2);
job_record_t *job_ptr;
lua_getmetatable(L, -2);
lua_getfield(L, -1, "_job_rec_ptr");
job_ptr = lua_touserdata(L, -1);
return slurm_lua_job_record_field(L, job_ptr, name);
}
static int _foreach_update_jobs_global(void *x, void *arg)
{
char job_id_buf[11]; /* Big enough for a uint32_t */
job_record_t *job_ptr = x;
lua_State *st = arg;
/*
* Create an empty table, with a metatable that looks up the
* data for the individual job.
*/
lua_newtable(st);
lua_newtable(st);
lua_pushcfunction(st, _job_rec_field_index);
lua_setfield(st, -2, "__index");
/*
* Store the job_record in the metatable, so the index
* function knows which job it's getting data for.
*/
lua_pushlightuserdata(st, job_ptr);
lua_setfield(st, -2, "_job_rec_ptr");
lua_setmetatable(st, -2);
/* Lua copies passed strings, so we can reuse the buffer. */
snprintf(job_id_buf, sizeof(job_id_buf), "%d", job_ptr->job_id);
lua_setfield(st, -2, job_id_buf);
return 0;
}
/* Get the list of existing slurmctld job records. */
static void _update_jobs_global(lua_State *st)
{
if (last_lua_jobs_update >= last_job_update) {
return;
}
lua_getglobal(st, "slurm");
lua_newtable(st);
list_for_each(job_list, _foreach_update_jobs_global, st);
last_lua_jobs_update = last_job_update;
lua_setfield(st, -2, "jobs");
lua_pop(st, 1);
}
static int _resv_field(const slurmctld_resv_t *resv_ptr,
const char *name)
{
if (resv_ptr == NULL) {
error("_resv_field: resv_ptr is NULL");
lua_pushnil(L);
} else if (!xstrcmp(name, "accounts")) {
lua_pushstring(L, resv_ptr->accounts);
} else if (!xstrcmp(name, "assoc_list")) {
lua_pushstring(L, resv_ptr->assoc_list);
} else if (!xstrcmp(name, "duration")) {
lua_pushnumber(L, resv_ptr->duration);
} else if (!xstrcmp(name, "end_time")) {
lua_pushnumber(L, resv_ptr->end_time);
} else if (!xstrcmp(name, "features")) {
lua_pushstring(L, resv_ptr->features);
} else if (!xstrcmp(name, "flags")) {
lua_pushnumber(L, resv_ptr->flags);
} else if (!xstrcmp(name, "full_nodes")) {
lua_pushboolean(L,
(resv_ptr->ctld_flags & RESV_CTLD_FULL_NODE) ?
true : false);
} else if (!xstrcmp(name, "flags_set_node")) {
lua_pushboolean(L,
(resv_ptr->ctld_flags &
RESV_CTLD_NODE_FLAGS_SET) ? true : false);
} else if (!xstrcmp(name, "licenses")) {
lua_pushstring(L, resv_ptr->licenses);
} else if (!xstrcmp(name, "node_cnt")) {
lua_pushnumber(L, resv_ptr->node_cnt);
} else if (!xstrcmp(name, "node_list")) {
lua_pushstring(L, resv_ptr->node_list);
} else if (!xstrcmp(name, "partition")) {
lua_pushstring(L, resv_ptr->partition);
} else if (!xstrcmp(name, "start_time")) {
lua_pushnumber(L, resv_ptr->start_time);
} else if (!xstrcmp(name, "users")) {
lua_pushstring(L, resv_ptr->users);
} else {
lua_pushnil(L);
}
return 1;
}
/* Get fields in an existing slurmctld reservation record */
static int _resv_field_index(lua_State *L)
{
const char *name = luaL_checkstring(L, 2);
slurmctld_resv_t *resv_ptr;
lua_getmetatable(L, -2);
lua_getfield(L, -1, "_resv_ptr");
resv_ptr = lua_touserdata(L, -1);
return _resv_field(resv_ptr, name);
}
static int _foreach_update_resvs_global(void *x, void *arg)
{
slurmctld_resv_t *resv_ptr = x;
lua_State *st = arg;
/*
* Create an empty table, with a metatable that looks up the
* data for the individual reservation.
*/
lua_newtable(st);
lua_newtable(st);
lua_pushcfunction(st, _resv_field_index);
lua_setfield(st, -2, "__index");
/*
* Store the slurmctld_resv_t in the metatable, so the index
* function knows which reservation it's getting data for.
*/
lua_pushlightuserdata(st, resv_ptr);
lua_setfield(st, -2, "_resv_ptr");
lua_setmetatable(st, -2);
lua_setfield(st, -2, resv_ptr->name);
return 0;
}
/* Get the list of existing slurmctld reservation records. */
static void _update_resvs_global(lua_State *st)
{
if (last_lua_resv_update >= last_resv_update) {
return;
}
lua_getglobal(st, "slurm");
lua_newtable(st);
list_for_each(resv_list, _foreach_update_resvs_global, st);
last_lua_resv_update = last_resv_update;
lua_setfield(st, -2, "reservations");
lua_pop(st, 1);
}
/* Set fields in the job request structure on job submit or modify */
static int _set_job_env_field(lua_State *L)
{
const char *name, *value_str;
job_desc_msg_t *job_desc;
char *name_eq = NULL;
int i, j, name_len;
name = luaL_checkstring(L, 2);
name_eq = xstrdup(name);
xstrcat(name_eq, "=");
name_len = strlen(name_eq);
lua_getmetatable(L, -3);
lua_getfield(L, -1, "_job_desc");
job_desc = lua_touserdata(L, -1);
if (job_desc == NULL) {
error("%s: job_desc is NULL", __func__);
} else if (job_desc->environment == NULL) {
if (job_desc->script)
error("%s: %s: job_desc->environment is NULL.",
plugin_type, __func__);
else
info("job_desc->environment only accessible for batch jobs. ");
lua_pushnil(L);
} else {
value_str = luaL_checkstring(L, 3);
for (i = 0; job_desc->environment[i]; i++) {
if (!xstrncmp(job_desc->environment[i], name_eq,
name_len)) {
job_desc->environment[i][name_len] = '\0';
xstrcat(job_desc->environment[i], value_str);
break;
}
}
if (!job_desc->environment[i]) {
job_desc->environment = xrealloc(job_desc->environment,
sizeof(char*) * (i+2));
for (j = i; j >= 1; j--) {
job_desc->environment[j] =
job_desc->environment[j-1];
}
job_desc->environment[0] = xstrdup(name_eq);
xstrcat(job_desc->environment[0], value_str);
job_desc->env_size++;
}
}
xfree(name_eq);
return 0;
}
static int _job_env_field(const job_desc_msg_t *job_desc, const char *name)
{
char *name_eq = "";
int i, name_len;
name_eq = xstrdup(name);
xstrcat(name_eq, "=");
name_len = strlen(name_eq);
if (job_desc == NULL) {
error("%s: job_desc is NULL", __func__);
lua_pushnil(L);
} else if (job_desc->environment == NULL) {
if (job_desc->script)
error("%s: %s: job_desc->environment is NULL.",
plugin_type, __func__);
else
info("job_desc->environment only accessible for batch jobs.");
lua_pushnil(L);
} else {
for (i = 0; job_desc->environment[i]; i++) {
if (!xstrncmp(job_desc->environment[i], name_eq,
name_len)) {
lua_pushstring(L, job_desc->environment[i] +
name_len);
break;
}
}
if (!job_desc->environment[i])
lua_pushnil(L);
}
xfree(name_eq);
return 1;
}
/* Get fields in the job request record on job submit or modify */
static int _get_job_env_field_name(lua_State *L)
{
const job_desc_msg_t *job_desc = lua_touserdata(L, 1);
const char *name = luaL_checkstring(L, 2);
return _job_env_field(job_desc, name);
}
/* Get fields in an existing slurmctld job_descriptor record */
static int _job_env_field_index(lua_State *L)
{
const char *name;
job_desc_msg_t *job_desc;
name = luaL_checkstring(L, 2);
lua_getmetatable(L, -2);
lua_getfield(L, -1, "_job_desc");
job_desc = lua_touserdata(L, -1);
return _job_env_field(job_desc, name);
}
static void _push_job_env(job_desc_msg_t *job_desc)
{
lua_newtable(L);
lua_newtable(L);
lua_pushcfunction(L, _job_env_field_index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, _set_job_env_field);
lua_setfield(L, -2, "__newindex");
/* Store the job descriptor in the metatable, so the index
* function knows which struct it's getting data for.
*/
lua_pushlightuserdata(L, job_desc);
lua_setfield(L, -2, "_job_desc");
lua_setmetatable(L, -2);
}
static int _get_job_req_field(const job_desc_msg_t *job_desc, const char *name)
{
int i;
if (job_desc == NULL) {
error("%s: job_desc is NULL", __func__);
lua_pushnil(L);
} else if (!xstrcmp(name, "account")) {
lua_pushstring(L, job_desc->account);
} else if (!xstrcmp(name, "acctg_freq")) {
lua_pushstring(L, job_desc->acctg_freq);
} else if (!xstrcmp(name, "admin_comment")) {
lua_pushstring(L, job_desc->admin_comment);
} else if (!xstrcmp(name, "alloc_node")) {
lua_pushstring(L, job_desc->alloc_node);
} else if (!xstrcmp(name, "argc")) {
lua_pushnumber(L, job_desc->argc);
} else if (!xstrcmp(name, "argv")) {
if ((job_desc->argc == 0) ||
(job_desc->argv == NULL)) {
lua_pushnil(L);
} else {
lua_newtable(L);
for (i = 0; i < job_desc->argc; i++) {
if (job_desc->argv[i] != NULL) {
lua_pushnumber(L, i);
lua_pushstring(L, job_desc->argv[i]);
lua_settable(L, -3);
}
}
}
} else if (!xstrcmp(name, "array_inx")) {
lua_pushstring(L, job_desc->array_inx);
} else if (!xstrcmp(name, "assoc_comment")) {
lua_pushstring(L, _get_assoc_comment(job_desc->user_id,
job_desc->account,
job_desc->partition));
} else if (!xstrcmp(name, "assoc_qos")) {
char *assoc_qos_str = _get_assoc_qos(job_desc->user_id,
job_desc->account,
job_desc->partition);
lua_pushstring(L, assoc_qos_str);
xfree(assoc_qos_str);
} else if (!xstrcmp(name, "batch_features")) {
lua_pushstring(L, job_desc->batch_features);
} else if (!xstrcmp(name, "begin_time")) {
lua_pushnumber(L, job_desc->begin_time);
} else if (!xstrcmp(name, "bitflags")) {
lua_pushnumber(L, job_desc->bitflags);
} else if (!xstrcmp(name, "boards_per_node")) {
lua_pushnumber(L, job_desc->boards_per_node);
} else if (!xstrcmp(name, "burst_buffer")) {
lua_pushstring(L, job_desc->burst_buffer);
} else if (!xstrcmp(name, "clusters")) {
lua_pushstring(L, job_desc->clusters);
} else if (!xstrcmp(name, "comment")) {
lua_pushstring(L, job_desc->comment);
} else if (!xstrcmp(name, "container")) {
lua_pushstring(L, job_desc->container);
} else if (!xstrcmp(name, "contiguous")) {
lua_pushnumber(L, job_desc->contiguous);
} else if (!xstrcmp(name, "cores_per_socket")) {
lua_pushnumber(L, job_desc->cores_per_socket);
} else if (!xstrcmp(name, "cpu_freq_min")) {
lua_pushnumber(L, job_desc->cpu_freq_min);
} else if (!xstrcmp(name, "cpu_freq_max")) {
lua_pushnumber(L, job_desc->cpu_freq_max);
} else if (!xstrcmp(name, "cpu_freq_gov")) {
lua_pushnumber(L, job_desc->cpu_freq_gov);
} else if (!xstrcmp(name, "cpus_per_task")) {
lua_pushnumber(L, job_desc->cpus_per_task);
} else if (!xstrcmp(name, "cpus_per_tres")) {
lua_pushstring(L, job_desc->cpus_per_tres);
} else if (!xstrcmp(name, "cron_job")) {
lua_pushboolean(L, job_desc->bitflags & CRON_JOB);
} else if (!xstrcmp(name, "default_account")) {
lua_pushstring(L, _get_default_account(job_desc->user_id));
} else if (!xstrcmp(name, "default_qos")) {
lua_pushstring(L, _get_default_qos(job_desc->user_id,
job_desc->account,
job_desc->partition));
} else if (!xstrcmp(name, "delay_boot")) {
lua_pushnumber(L, job_desc->delay_boot);
} else if (!xstrcmp(name, "dependency")) {
lua_pushstring(L, job_desc->dependency);
} else if (!xstrcmp(name, "end_time")) {
lua_pushnumber(L, job_desc->end_time);
} else if (!xstrcmp(name, "environment")) {
_push_job_env((job_desc_msg_t *) job_desc); // No const
} else if (!xstrcmp(name, "exc_nodes")) {
lua_pushstring(L, job_desc->exc_nodes);
} else if (!xstrcmp(name, "extra")) {
lua_pushstring(L, job_desc->extra);
} else if (!xstrcmp(name, "features")) {
lua_pushstring(L, job_desc->features);
} else if (!xstrcmp(name, "gres")) {
/* "gres" replaced by "tres_per_node" in v18.08 */
lua_pushstring(L, job_desc->tres_per_node);
} else if (!xstrcmp(name, "group_id")) {
lua_pushnumber(L, job_desc->group_id);
} else if (!xstrcmp(name, "immediate")) {
lua_pushnumber(L, job_desc->immediate);
} else if (!xstrcmp(name, "licenses")) {
lua_pushstring(L, job_desc->licenses);
} else if (!xstrcmp(name, "mail_type")) {
lua_pushnumber(L, job_desc->mail_type);
} else if (!xstrcmp(name, "mail_user")) {
lua_pushstring(L, job_desc->mail_user);
} else if (!xstrcmp(name, "max_cpus")) {
lua_pushnumber(L, job_desc->max_cpus);
} else if (!xstrcmp(name, "max_nodes")) {
lua_pushnumber(L, job_desc->max_nodes);
} else if (!xstrcmp(name, "mcs_label")) {
lua_pushstring(L, job_desc->mcs_label);
} else if (!xstrcmp(name, "mem_per_tres")) {
lua_pushstring(L, job_desc->mem_per_tres);
} else if (!xstrcmp(name, "min_cpus")) {
lua_pushnumber(L, job_desc->min_cpus);
} else if (!xstrcmp(name, "min_mem_per_node") &&
(job_desc->pn_min_memory != NO_VAL64) &&
!(job_desc->pn_min_memory & MEM_PER_CPU)) {
lua_pushnumber(L, job_desc->pn_min_memory);
} else if (!xstrcmp(name, "min_mem_per_cpu") &&
(job_desc->pn_min_memory != NO_VAL64) &&
(job_desc->pn_min_memory & MEM_PER_CPU)) {
lua_pushnumber(L, (job_desc->pn_min_memory & (~MEM_PER_CPU)));
} else if (!xstrcmp(name, "min_nodes")) {
lua_pushnumber(L, job_desc->min_nodes);
} else if (!xstrcmp(name, "name")) {
lua_pushstring(L, job_desc->name);
} else if (!xstrcmp(name, "network")) {
lua_pushstring(L, job_desc->network);
} else if (!xstrcmp(name, "nice")) {
/*
* nice will be NO_VAL when unset or offset by NICE_OFFSET.
* Decrement nice by NICE_OFFSET in job_submit.lua if the value
* needs to be human readable.
*/
lua_pushnumber(L, job_desc->nice);
} else if (!xstrcmp(name, "ntasks_per_board")) {
lua_pushnumber(L, job_desc->ntasks_per_board);
} else if (!xstrcmp(name, "ntasks_per_core")) {
lua_pushnumber(L, job_desc->ntasks_per_core);
} else if (!xstrcmp(name, "ntasks_per_gpu")) {
lua_pushnumber(L, job_desc->ntasks_per_tres);
} else if (!xstrcmp(name, "ntasks_per_node")) {
lua_pushnumber(L, job_desc->ntasks_per_node);
} else if (!xstrcmp(name, "ntasks_per_socket")) {
lua_pushnumber(L, job_desc->ntasks_per_socket);
} else if (!xstrcmp(name, "ntasks_per_tres")) {
lua_pushnumber(L, job_desc->ntasks_per_tres);
} else if (!xstrcmp(name, "num_tasks")) {
lua_pushnumber(L, job_desc->num_tasks);
/* Continue support for old hetjob terminology. */
} else if (!xstrcmp(name, "pack_job_offset") ||
!xstrcmp(name, "het_job_offset")) {
lua_pushnumber(L, job_desc->het_job_offset);
} else if (!xstrcmp(name, "partition")) {
lua_pushstring(L, job_desc->partition);
} else if (!xstrcmp(name, "prefer")) {
lua_pushstring(L, job_desc->prefer);
} else if (!xstrcmp(name, "pn_min_cpus")) {
lua_pushnumber(L, job_desc->pn_min_cpus);
} else if (!xstrcmp(name, "pn_min_memory")) {
/*
* FIXME: Remove this in the future, lua can't handle 64bit
* numbers!!!. Use min_mem_per_node|cpu instead.
*/
lua_pushnumber(L, job_desc->pn_min_memory);
} else if (!xstrcmp(name, "pn_min_tmp_disk")) {
lua_pushnumber(L, job_desc->pn_min_tmp_disk);
} else if (!xstrcmp(name, "priority")) {
lua_pushnumber(L, job_desc->priority);
} else if (!xstrcmp(name, "qos")) {
lua_pushstring(L, job_desc->qos);
} else if (!xstrcmp(name, "reboot")) {
lua_pushnumber(L, job_desc->reboot);
} else if (!xstrcmp(name, "req_context")) {
lua_pushstring(L, job_desc->req_context);
} else if (!xstrcmp(name, "req_nodes")) {
lua_pushstring(L, job_desc->req_nodes);
} else if (!xstrcmp(name, "req_switch")) {
lua_pushnumber(L, job_desc->req_switch);
} else if (!xstrcmp(name, "requeue")) {
lua_pushnumber(L, job_desc->requeue);
} else if (!xstrcmp(name, "reservation")) {
lua_pushstring(L, job_desc->reservation);
} else if (!xstrcmp(name, "script")) {
lua_pushstring(L, job_desc->script);
} else if (!xstrcmp(name, "shared") ||
!xstrcmp(name, "oversubscribe")) {
lua_pushnumber(L, job_desc->shared);
} else if (!xstrcmp(name, "selinux_context")) {
lua_pushstring(L, job_desc->selinux_context);
} else if (!xstrcmp(name, "site_factor")) {
if (job_desc->site_factor == NO_VAL)
lua_pushnumber(L, job_desc->site_factor);
else
lua_pushnumber(L,
(((int64_t)job_desc->site_factor)
- NICE_OFFSET));
} else if (!xstrcmp(name, "sockets_per_board")) {
lua_pushnumber(L, job_desc->sockets_per_board);
} else if (!xstrcmp(name, "sockets_per_node")) {
lua_pushnumber(L, job_desc->sockets_per_node);
} else if (!xstrcmp(name, "spank_job_env")) {
if ((job_desc->spank_job_env_size == 0) ||
(job_desc->spank_job_env == NULL)) {
lua_pushnil(L);
} else {
lua_newtable(L);
for (i = 0; i < job_desc->spank_job_env_size; i++) {
if (job_desc->spank_job_env[i] != NULL) {
lua_pushnumber(L, i);
lua_pushstring(
L, job_desc->spank_job_env[i]);
lua_settable(L, -3);
}
}
}
} else if (!xstrcmp(name, "spank_job_env_size")) {
lua_pushnumber(L, job_desc->spank_job_env_size);
} else if (!xstrcmp(name, "std_err")) {
lua_pushstring(L, job_desc->std_err);
} else if (!xstrcmp(name, "std_in")) {
lua_pushstring(L, job_desc->std_in);
} else if (!xstrcmp(name, "std_out")) {
lua_pushstring(L, job_desc->std_out);
} else if (!xstrcmp(name, "threads_per_core")) {
lua_pushnumber(L, job_desc->threads_per_core);
} else if (!xstrcmp(name, "time_limit")) {
lua_pushnumber(L, job_desc->time_limit);
} else if (!xstrcmp(name, "time_min")) {
lua_pushnumber(L, job_desc->time_min);
} else if (!xstrcmp(name, "tres_bind")) {
lua_pushstring(L, job_desc->tres_bind);
} else if (!xstrcmp(name, "tres_freq")) {
lua_pushstring(L, job_desc->tres_freq);
} else if (!xstrcmp(name, "tres_per_job")) {
lua_pushstring(L, job_desc->tres_per_job);
} else if (!xstrcmp(name, "tres_per_node")) {
lua_pushstring(L, job_desc->tres_per_node);
} else if (!xstrcmp(name, "tres_per_socket")) {
lua_pushstring(L, job_desc->tres_per_socket);
} else if (!xstrcmp(name, "tres_per_task")) {
lua_pushstring(L, job_desc->tres_per_task);
} else if (!xstrcmp(name, "user_id")) {
lua_pushnumber(L, job_desc->user_id);
} else if (!xstrcmp(name, "user_name")) {
if (job_desc->id) {
identity_t *id = job_desc->id;
lua_pushstring(L, id->pw_name);
} else {
char *username =
uid_to_string_or_null(job_desc->user_id);
lua_pushstring(L, username);
xfree(username);
}
} else if (!xstrcmp(name, "wait4switch")) {
lua_pushnumber(L, job_desc->wait4switch);
} else if (!xstrcmp(name, "work_dir")) {
lua_pushstring(L, job_desc->work_dir);
} else if (!xstrcmp(name, "wckey")) {
lua_pushstring(L, job_desc->wckey);
} else {
lua_pushnil(L);
}
return 1;
}
/* Get fields in the job request record on job submit or modify */
static int _get_job_req_field_name(lua_State *L)
{
const job_desc_msg_t *job_desc = lua_touserdata(L, 1);
const char *name = luaL_checkstring(L, 2);
return _get_job_req_field(job_desc, name);
}
/* Get fields in an existing slurmctld job_descriptor record */
static int _get_job_req_field_index(lua_State *L)
{
const char *name;
job_desc_msg_t *job_desc;
name = luaL_checkstring(L, 2);
lua_getmetatable(L, -2);
lua_getfield(L, -1, "_job_desc");
job_desc = lua_touserdata(L, -1);
return _get_job_req_field(job_desc, name);
}
/* Set fields in the job request structure on job submit or modify */
static int _set_job_req_field(lua_State *L)
{
const char *name, *value_str;
job_desc_msg_t *job_desc;
name = luaL_checkstring(L, 2);
lua_getmetatable(L, -3);
lua_getfield(L, -1, "_job_desc");
job_desc = lua_touserdata(L, -1);
if (job_desc == NULL) {
error("%s: job_desc is NULL", __func__);
} else if (!xstrcmp(name, "account")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->account);
if (strlen(value_str))
job_desc->account = xstrdup(value_str);
} else if (!xstrcmp(name, "acctg_freq")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->acctg_freq);
if (strlen(value_str))
job_desc->acctg_freq = xstrdup(value_str);
} else if (!xstrcmp(name, "admin_comment")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->admin_comment);
if (strlen(value_str))
job_desc->admin_comment = xstrdup(value_str);
} else if (!xstrcmp(name, "array_inx")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->array_inx);
if (strlen(value_str))
job_desc->array_inx = xstrdup(value_str);
} else if (!xstrcmp(name, "batch_features")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->batch_features);
if (strlen(value_str))
job_desc->batch_features = xstrdup(value_str);
} else if (!xstrcmp(name, "begin_time")) {
job_desc->begin_time = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "bitflags")) {
job_desc->bitflags = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "burst_buffer")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->burst_buffer);
if (strlen(value_str))
job_desc->burst_buffer = xstrdup(value_str);
} else if (!xstrcmp(name, "clusters")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->clusters);
if (strlen(value_str))
job_desc->clusters = xstrdup(value_str);
} else if (!xstrcmp(name, "comment")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->comment);
if (strlen(value_str))
job_desc->comment = xstrdup(value_str);
} else if (!xstrcmp(name, "container")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->container);
if (strlen(value_str))
job_desc->container = xstrdup(value_str);
} else if (!xstrcmp(name, "contiguous")) {
job_desc->contiguous = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "cores_per_socket")) {
job_desc->cores_per_socket = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "cpu_freq_min")) {
job_desc->cpu_freq_min = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "cpu_freq_max")) {
job_desc->cpu_freq_max = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "cpu_freq_gov")) {
job_desc->cpu_freq_gov = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "cpus_per_task")) {
job_desc->cpus_per_task = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "cpus_per_tres")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->cpus_per_tres);
if (strlen(value_str))
job_desc->cpus_per_tres = xstrdup(value_str);
} else if (!xstrcmp(name, "dependency")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->dependency);
if (strlen(value_str))
job_desc->dependency = xstrdup(value_str);
} else if (!xstrcmp(name, "delay_boot")) {
job_desc->delay_boot = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "end_time")) {
job_desc->end_time = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "extra")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->extra);
if (strlen(value_str))
job_desc->extra = xstrdup(value_str);
} else if (!xstrcmp(name, "exc_nodes")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->exc_nodes);
if (strlen(value_str))
job_desc->exc_nodes = xstrdup(value_str);
} else if (!xstrcmp(name, "features")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->features);
if (strlen(value_str))
job_desc->features = xstrdup(value_str);
} else if (!xstrcmp(name, "gres")) {
/* "gres" replaced by "tres_per_node" in v18.08 */
value_str = luaL_checkstring(L, 3);
xfree(job_desc->tres_per_node);
if (strlen(value_str))
job_desc->tres_per_node =
gres_prepend_tres_type(value_str);
} else if (!xstrcmp(name, "immediate")) {
job_desc->immediate = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "licenses")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->licenses);
if (strlen(value_str))
job_desc->licenses = xstrdup(value_str);
} else if (!xstrcmp(name, "mail_type")) {
job_desc->mail_type = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "mail_user")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->mail_user);
if (strlen(value_str))
job_desc->mail_user = xstrdup(value_str);
} else if (!xstrcmp(name, "max_cpus")) {
job_desc->max_cpus = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "max_nodes")) {
job_desc->max_nodes = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "mcs_label")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->mcs_label);
if (strlen(value_str))
job_desc->mcs_label = xstrdup(value_str);
} else if (!xstrcmp(name, "mem_per_tres")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->mem_per_tres);
if (strlen(value_str))
job_desc->mem_per_tres = xstrdup(value_str);
} else if (!xstrcmp(name, "min_cpus")) {
job_desc->min_cpus = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "min_mem_per_cpu")) {
job_desc->pn_min_memory = luaL_checknumber(L, 3);
job_desc->pn_min_memory |= MEM_PER_CPU;
} else if (!xstrcmp(name, "min_mem_per_node")) {
job_desc->pn_min_memory = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "min_nodes")) {
job_desc->min_nodes = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "name")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->name);
if (strlen(value_str))
job_desc->name = xstrdup(value_str);
} else if (!xstrcmp(name, "nice")) {
/*
* nice should be NO_VAL when unset or incremented by
* NICE_OFFSET by the job_submit.lua script.
*/
job_desc->nice = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "ntasks_per_gpu")) {
job_desc->ntasks_per_tres = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "ntasks_per_node")) {
job_desc->ntasks_per_node = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "ntasks_per_socket")) {
job_desc->ntasks_per_socket = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "ntasks_per_tres")) {
job_desc->ntasks_per_tres = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "num_tasks")) {
job_desc->num_tasks = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "partition")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->partition);
if (strlen(value_str))
job_desc->partition = xstrdup(value_str);
} else if (!xstrcmp(name, "prefer")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->prefer);
if (strlen(value_str))
job_desc->prefer = xstrdup(value_str);
} else if (!xstrcmp(name, "pn_min_cpus")) {
job_desc->pn_min_cpus = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "pn_min_memory")) {
/*
* FIXME: Remove this in the future, lua can't handle 64bit
* numbers!!!. Use min_mem_per_node|cpu instead.
*/
job_desc->pn_min_memory = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "pn_min_tmp_disk")) {
job_desc->pn_min_tmp_disk = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "priority")) {
job_desc->priority = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "qos")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->qos);
if (strlen(value_str))
job_desc->qos = xstrdup(value_str);
} else if (!xstrcmp(name, "reboot")) {
job_desc->reboot = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "req_nodes")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->req_nodes);
if (strlen(value_str))
job_desc->req_nodes = xstrdup(value_str);
} else if (!xstrcmp(name, "req_switch")) {
job_desc->req_switch = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "requeue")) {
job_desc->requeue = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "reservation")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->reservation);
if (strlen(value_str))
job_desc->reservation = xstrdup(value_str);
} else if (!xstrcmp(name, "script")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->script);
if (strlen(value_str))
job_desc->script = xstrdup(value_str);
} else if (!xstrcmp(name, "selinux_context")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->selinux_context);
if (strlen(value_str))
job_desc->selinux_context = xstrdup(value_str);
} else if (!xstrcmp(name, "shared") ||
!xstrcmp(name, "oversubscribe")) {
job_desc->shared = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "site_factor")) {
job_desc->site_factor = luaL_checknumber(L, 3);
if (job_desc->site_factor != NO_VAL)
job_desc->site_factor =
NICE_OFFSET + job_desc->site_factor;
} else if (!xstrcmp(name, "sockets_per_node")) {
job_desc->sockets_per_node = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "std_err")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->std_err);
if (strlen(value_str))
job_desc->std_err = xstrdup(value_str);
} else if (!xstrcmp(name, "std_in")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->std_in);
if (strlen(value_str))
job_desc->std_in = xstrdup(value_str);
} else if (!xstrcmp(name, "std_out")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->std_out);
if (strlen(value_str))
job_desc->std_out = xstrdup(value_str);
} else if (!xstrcmp(name, "threads_per_core")) {
job_desc->threads_per_core = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "time_limit")) {
job_desc->time_limit = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "time_min")) {
job_desc->time_min = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "tres_bind")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->tres_bind);
if (strlen(value_str))
job_desc->tres_bind = xstrdup(value_str);
} else if (!xstrcmp(name, "tres_freq")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->tres_freq);
if (strlen(value_str))
job_desc->tres_freq = xstrdup(value_str);
} else if (!xstrcmp(name, "tres_per_job")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->tres_per_job);
if (strlen(value_str))
job_desc->tres_per_job = xstrdup(value_str);
} else if (!xstrcmp(name, "tres_per_node")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->tres_per_node);
if (strlen(value_str))
job_desc->tres_per_node = xstrdup(value_str);
} else if (!xstrcmp(name, "tres_per_socket")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->tres_per_socket);
if (strlen(value_str))
job_desc->tres_per_socket = xstrdup(value_str);
} else if (!xstrcmp(name, "tres_per_task")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->tres_per_task);
if (strlen(value_str))
job_desc->tres_per_task = xstrdup(value_str);
} else if (!xstrcmp(name, "wait4switch")) {
job_desc->wait4switch = luaL_checknumber(L, 3);
} else if (!xstrcmp(name, "wckey")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->wckey);
if (strlen(value_str))
job_desc->wckey = xstrdup(value_str);
} else if (!xstrcmp(name, "work_dir")) {
value_str = luaL_checkstring(L, 3);
xfree(job_desc->work_dir);
if (strlen(value_str))
job_desc->work_dir = xstrdup(value_str);
} else {
error("_set_job_field: unrecognized field: %s", name);
}
return 0;
}
static void _push_job_desc(job_desc_msg_t *job_desc)
{
lua_newtable(L);
lua_newtable(L);
lua_pushcfunction(L, _get_job_req_field_index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, _set_job_req_field);
lua_setfield(L, -2, "__newindex");
/* Store the job descriptor in the metatable, so the index
* function knows which struct it's getting data for.
*/
lua_pushlightuserdata(L, job_desc);
lua_setfield(L, -2, "_job_desc");
lua_setmetatable(L, -2);
}
static void _push_job_rec(job_record_t *job_ptr)
{
lua_newtable(L);
lua_newtable(L);
lua_pushcfunction(L, _job_rec_field_index);
lua_setfield(L, -2, "__index");
/* Store the job_ptr in the metatable, so the index
* function knows which struct it's getting data for.
*/
lua_pushlightuserdata(L, job_ptr);
lua_setfield(L, -2, "_job_rec_ptr");
lua_setmetatable(L, -2);
}
/* Get fields in an existing slurmctld partition record
*
* This is an incomplete list of partition record fields. Add more as needed
* and send patches to slurm-dev@schedmd.com
*/
static int _part_rec_field(const part_record_t *part_ptr, const char *name)
{
if (part_ptr == NULL) {
error("_get_part_field: part_ptr is NULL");
lua_pushnil(L);
} else if (!xstrcmp(name, "allow_accounts")) {
lua_pushstring(L, part_ptr->allow_accounts);
} else if (!xstrcmp(name, "allow_alloc_nodes")) {
lua_pushstring(L, part_ptr->allow_alloc_nodes);
} else if (!xstrcmp(name, "allow_groups")) {
lua_pushstring(L, part_ptr->allow_groups);
} else if (!xstrcmp(name, "allow_qos")) {
lua_pushstring(L, part_ptr->allow_qos);
} else if (!xstrcmp(name, "alternate")) {
lua_pushstring(L, part_ptr->alternate);
} else if (!xstrcmp(name, "billing_weights_str")) {
lua_pushstring(L, part_ptr->billing_weights_str);
} else if (!xstrcmp(name, "default_time")) {
lua_pushnumber(L, part_ptr->default_time);
} else if (!xstrcmp(name, "def_mem_per_cpu") &&
(part_ptr->def_mem_per_cpu != NO_VAL64) &&
(part_ptr->def_mem_per_cpu & MEM_PER_CPU)) {
lua_pushnumber(L, part_ptr->def_mem_per_cpu & (~MEM_PER_CPU));
} else if (!xstrcmp(name, "def_mem_per_node") &&
(part_ptr->def_mem_per_cpu != NO_VAL64) &&
!(part_ptr->def_mem_per_cpu & MEM_PER_CPU)) {
lua_pushnumber(L, part_ptr->def_mem_per_cpu);
} else if (!xstrcmp(name, "deny_accounts")) {
lua_pushstring(L, part_ptr->deny_accounts);
} else if (!xstrcmp(name, "deny_qos")) {
lua_pushstring(L, part_ptr->deny_qos);
} else if (!xstrcmp(name, "flag_default")) {
int is_default = 0;
if (part_ptr->flags & PART_FLAG_DEFAULT)
is_default = 1;
lua_pushnumber(L, is_default);
} else if (!xstrcmp(name, "flags")) {
lua_pushnumber(L, part_ptr->flags);
} else if (!xstrcmp(name, "max_cpus_per_node")) {
lua_pushnumber(L, part_ptr->max_cpus_per_node);
} else if (!xstrcmp(name, "max_cpus_per_socket")) {
lua_pushnumber(L, part_ptr->max_cpus_per_socket);
} else if (!xstrcmp(name, "max_mem_per_cpu") &&
(part_ptr->max_mem_per_cpu != NO_VAL64) &&
(part_ptr->max_mem_per_cpu & MEM_PER_CPU)) {
lua_pushnumber(L, part_ptr->max_mem_per_cpu & (~MEM_PER_CPU));
} else if (!xstrcmp(name, "max_mem_per_node") &&
(part_ptr->max_mem_per_cpu != NO_VAL64) &&
!(part_ptr->max_mem_per_cpu & MEM_PER_CPU)) {
lua_pushnumber(L, part_ptr->max_mem_per_cpu);
} else if (!xstrcmp(name, "max_nodes")) {
lua_pushnumber(L, part_ptr->max_nodes);
} else if (!xstrcmp(name, "max_nodes_orig")) {
lua_pushnumber(L, part_ptr->max_nodes_orig);
} else if (!xstrcmp(name, "max_share") ||
!xstrcmp(name, "max_oversubscribe")) {
lua_pushnumber(L, part_ptr->max_share);
} else if (!xstrcmp(name, "max_time")) {
lua_pushnumber(L, part_ptr->max_time);
} else if (!xstrcmp(name, "min_nodes")) {
lua_pushnumber(L, part_ptr->min_nodes);
} else if (!xstrcmp(name, "min_nodes_orig")) {
lua_pushnumber(L, part_ptr->min_nodes_orig);
} else if (!xstrcmp(name, "name")) {
lua_pushstring(L, part_ptr->name);
} else if (!xstrcmp(name, "nodes")) {
lua_pushstring(L, part_ptr->nodes);
} else if (!xstrcmp(name, "priority_job_factor")) {
lua_pushnumber(L, part_ptr->priority_job_factor);
} else if (!xstrcmp(name, "priority_tier")) {
lua_pushnumber(L, part_ptr->priority_tier);
} else if (!xstrcmp(name, "qos")) {
lua_pushstring(L, part_ptr->qos_char);
} else if (!xstrcmp(name, "state_up")) {
lua_pushnumber(L, part_ptr->state_up);
} else if (!xstrcmp(name, "total_cpus")) {
lua_pushnumber(L, part_ptr->total_cpus);
} else if (!xstrcmp(name, "total_nodes")) {
lua_pushnumber(L, part_ptr->total_nodes);
} else {
lua_pushnil(L);
}
return 1;
}
static int _get_part_rec_field (lua_State *L)
{
const part_record_t *part_ptr = lua_touserdata(L, 1);
const char *name = luaL_checkstring(L, 2);
return _part_rec_field(part_ptr, name);
}
static int _part_rec_field_index(lua_State *L)
{
const char *name = luaL_checkstring(L, 2);
part_record_t *part_ptr;
lua_getmetatable(L, -2);
lua_getfield(L, -1, "_part_rec_ptr");
part_ptr = lua_touserdata(L, -1);
return _part_rec_field(part_ptr, name);
}
static bool _user_can_use_part(uint32_t user_id, uint32_t submit_uid,
part_record_t *part_ptr)
{
if (user_id == 0) {
if (part_ptr->flags & PART_FLAG_NO_ROOT)
return false;
return true;
}
if ((part_ptr->flags & PART_FLAG_ROOT_ONLY) && (submit_uid != 0))
return false;
if (!part_ptr->allow_uids_cnt)
return true; /* No user ID filters */
for (int i = 0; i < part_ptr->allow_uids_cnt; i++) {
if (user_id == part_ptr->allow_uids[i])
return true;
}
return false;
}
static int _foreach_push_partition_list(void *x, void *arg)
{
part_record_t *part_ptr = x;
foreach_part_list_args_t *args = arg;
if (!_user_can_use_part(args->user_id, args->submit_uid, part_ptr))
return 0;
/*
* Create an empty table, with a metatable that looks up the
* data for the partition.
*/
lua_newtable(L);
lua_newtable(L);
lua_pushcfunction(L, _part_rec_field_index);
lua_setfield(L, -2, "__index");
/*
* Store the part_record in the metatable, so the index
* function knows which job it's getting data for.
*/
lua_pushlightuserdata(L, part_ptr);
lua_setfield(L, -2, "_part_rec_ptr");
lua_setmetatable(L, -2);
lua_setfield(L, -2, part_ptr->name);
return 0;
}
static void _push_partition_list(uint32_t user_id, uint32_t submit_uid)
{
foreach_part_list_args_t args = {
.submit_uid = submit_uid,
.user_id = user_id,
};
lua_newtable(L);
list_for_each(part_list, _foreach_push_partition_list, &args);
}
static int _log_lua_user_msg (lua_State *L)
{
const char *msg = lua_tostring(L, -1);
char *tmp = NULL;
if (user_msg) {
xstrfmtcat(tmp, "%s\n%s", user_msg, msg);
xfree(user_msg);
user_msg = tmp;
tmp = NULL;
} else {
user_msg = xstrdup(msg);
}
return (0);
}
static const struct luaL_Reg slurm_functions [] = {
{ "user_msg", _log_lua_user_msg },
{ NULL, NULL }
};
static void _register_local_output_functions(lua_State *L)
{
slurm_lua_table_register(L, NULL, slurm_functions);
/* Must be always done after we register the slurm_functions */
lua_setglobal(L, "slurm");
last_lua_jobs_update = 0;
_update_jobs_global(L);
last_lua_resv_update = 0;
_update_resvs_global(L);
}
static void _register_lua_slurm_struct_functions(lua_State *st)
{
lua_pushcfunction(st, _get_job_env_field_name);
lua_setglobal(st, "_get_job_env_field_name");
lua_pushcfunction(st, _get_job_req_field_name);
lua_setglobal(st, "_get_job_req_field_name");
lua_pushcfunction(st, _set_job_env_field);
lua_setglobal(st, "_set_job_env_field");
lua_pushcfunction(st, _set_job_req_field);
lua_setglobal(st, "_set_job_req_field");
lua_pushcfunction(st, _get_part_rec_field);
lua_setglobal(st, "_get_part_rec_field");
}
static void _loadscript_extra(lua_State *st)
{
/* local setup */
_register_local_output_functions(st);
_register_lua_slurm_struct_functions(st);
}
/*
* NOTE: The init callback should never be called multiple times,
* let alone called from multiple threads. Therefore, locking
* is unnecessary here.
*/
extern int init(void)
{
int rc = SLURM_SUCCESS;
if ((rc = slurm_lua_init()) != SLURM_SUCCESS)
return rc;
lua_script_path = get_extra_conf_path("job_submit.lua");
return slurm_lua_loadscript(&L, "job_submit/lua",
lua_script_path, req_fxns,
&lua_script_last_loaded,
_loadscript_extra, NULL);
}
extern void fini(void)
{
if (L) {
debug3("%s: Unloading Lua script", __func__);
lua_close(L);
L = NULL;
lua_script_last_loaded = 0;
}
xfree(lua_script_path);
slurm_lua_fini();
}
/* Lua script hook called for "submit job" event. */
extern int job_submit(job_desc_msg_t *job_desc, uint32_t submit_uid,
char **err_msg)
{
int rc;
slurm_mutex_lock (&lua_lock);
rc = slurm_lua_loadscript(&L, "job_submit/lua",
lua_script_path, req_fxns,
&lua_script_last_loaded, _loadscript_extra,
NULL);
if (rc != SLURM_SUCCESS)
goto out;
/*
* All lua script functions should have been verified during
* initialization:
*/
lua_getglobal(L, "slurm_job_submit");
if (lua_isnil(L, -1))
goto out;
_update_jobs_global(L);
_update_resvs_global(L);
_push_job_desc(job_desc);
_push_partition_list(job_desc->user_id, submit_uid);
lua_pushnumber(L, submit_uid);
slurm_lua_stack_dump(
"job_submit/lua", "job_submit, before lua_pcall", L);
if (lua_pcall(L, 3, 1, 0) != 0) {
error("%s/lua: %s: %s",
__func__, lua_script_path, lua_tostring(L, -1));
} else {
if (lua_isnumber(L, -1)) {
rc = lua_tonumber(L, -1);
} else {
info("%s/lua: %s: non-numeric return code",
__func__, lua_script_path);
rc = SLURM_SUCCESS;
}
lua_pop(L, 1);
}
slurm_lua_stack_dump(
"job_submit/lua", "job_submit, after lua_pcall", L);
if (user_msg) {
*err_msg = user_msg;
user_msg = NULL;
}
out: slurm_mutex_unlock (&lua_lock);
return rc;
}
/* Lua script hook called for "modify job" event. */
extern int job_modify(job_desc_msg_t *job_desc, job_record_t *job_ptr,
uint32_t submit_uid, char **err_msg)
{
int rc;
slurm_mutex_lock (&lua_lock);
rc = slurm_lua_loadscript(&L, "job_submit/lua",
lua_script_path, req_fxns,
&lua_script_last_loaded, _loadscript_extra,
NULL);
if (rc != SLURM_SUCCESS)
goto out;
/*
* All lua script functions should have been verified during
* initialization:
*/
lua_getglobal(L, "slurm_job_modify");
if (lua_isnil(L, -1))
goto out;
_update_jobs_global(L);
_update_resvs_global(L);
_push_job_desc(job_desc);
_push_job_rec(job_ptr);
_push_partition_list(job_ptr->user_id, submit_uid);
lua_pushnumber(L, submit_uid);
slurm_lua_stack_dump(
"job_submit/lua", "job_modify, before lua_pcall", L);
if (lua_pcall(L, 4, 1, 0) != 0) {
error("%s/lua: %s: %s",
__func__, lua_script_path, lua_tostring(L, -1));
} else {
if (lua_isnumber(L, -1)) {
rc = lua_tonumber(L, -1);
} else {
info("%s/lua: %s: non-numeric return code",
__func__, lua_script_path);
rc = SLURM_SUCCESS;
}
lua_pop(L, 1);
}
slurm_lua_stack_dump(
"job_submit/lua", "job_modify, after lua_pcall", L);
if (user_msg) {
*err_msg = user_msg;
user_msg = NULL;
}
out: slurm_mutex_unlock (&lua_lock);
return rc;
}