| /*****************************************************************************\ |
| * job_submit_lua.c - Set defaults in job submit request specifications. |
| ***************************************************************************** |
| * Copyright (C) 2010 Lawrence Livermore National Security. |
| * 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 <http://www.schedmd.com/slurmdocs/>. |
| * 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. |
| \*****************************************************************************/ |
| |
| #if HAVE_CONFIG_H |
| # include "config.h" |
| #endif |
| |
| #if HAVE_STDINT_H |
| # include <stdint.h> |
| #endif |
| #if HAVE_INTTYPES_H |
| # include <inttypes.h> |
| #endif |
| |
| #include <stdio.h> |
| |
| #include <sys/types.h> |
| #include <signal.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <dlfcn.h> |
| #include <pthread.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/slurmctld/slurmctld.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 - specifies the version number of the plugin. |
| * min_plug_version - specifies the minumum version number of incoming |
| * messages that this plugin can accept |
| */ |
| const char plugin_name[] = "Job submit lua plugin"; |
| const char plugin_type[] = "job_submit/lua"; |
| const uint32_t plugin_version = 100; |
| const uint32_t min_plug_version = 100; |
| |
| static const char lua_script_path[] = DEFAULT_SCRIPT_DIR "/job_submit.lua"; |
| static lua_State *L = NULL; |
| |
| /* |
| * Mutex for protecting multi-threaded access to this plugin. |
| * (Only 1 thread at a time should be in here) |
| */ |
| #ifdef WITH_PTHREADS |
| static pthread_mutex_t lua_lock = PTHREAD_MUTEX_INITIALIZER; |
| #endif |
| |
| /*****************************************************************************\ |
| * 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@lists.llnl.gov Thanks! |
| \*****************************************************************************/ |
| |
| /* Generic stack dump function for debugging purposes */ |
| static void _stack_dump (char *header, lua_State *L) |
| { |
| #if _DEBUG |
| int i; |
| int top = lua_gettop(L); |
| |
| info("%s: dumping job_submit/lua stack, %d elements", header, top); |
| for (i = 1; i <= top; i++) { /* repeat for each level */ |
| int type = lua_type(L, i); |
| switch (type) { |
| case LUA_TSTRING: |
| info("string[%d]:%s", i, lua_tostring(L, i)); |
| break; |
| case LUA_TBOOLEAN: |
| info("boolean[%d]:%s", i, |
| lua_toboolean(L, i) ? "true" : "false"); |
| break; |
| case LUA_TNUMBER: |
| info("number[%d]:%d", i, |
| (int) lua_tonumber(L, i)); |
| break; |
| default: |
| info("other[%d]:%s", i, lua_typename(L, type)); |
| break; |
| } |
| } |
| #endif |
| } |
| |
| /* |
| * Lua interface to SLURM log facility: |
| */ |
| static int _log_lua_msg (lua_State *L) |
| { |
| const char *prefix = "job_submit.lua"; |
| int level = 0; |
| const char *msg; |
| |
| /* |
| * Optional numeric prefix indicating the log level |
| * of the message. |
| */ |
| |
| /* |
| * Pop message off the lua stack |
| */ |
| msg = lua_tostring(L, -1); |
| lua_pop (L, 1); |
| /* |
| * Pop level off stack: |
| */ |
| level = (int)lua_tonumber (L, -1); |
| lua_pop (L, 1); |
| |
| /* |
| * Call appropriate slurm log function based on log-level argument |
| */ |
| if (level > 4) |
| debug4 ("%s: %s", prefix, msg); |
| else if (level == 4) |
| debug3 ("%s: %s", prefix, msg); |
| else if (level == 3) |
| debug2 ("%s: %s", prefix, msg); |
| else if (level == 2) |
| debug ("%s: %s", prefix, msg); |
| else if (level == 1) |
| verbose ("%s: %s", prefix, msg); |
| else if (level == 0) |
| info ("%s: %s", prefix, msg); |
| return (0); |
| } |
| |
| static int _log_lua_error (lua_State *L) |
| { |
| const char *prefix = "job_submit.lua"; |
| const char *msg = lua_tostring (L, -1); |
| error ("%s: %s", prefix, msg); |
| return (0); |
| } |
| |
| static const struct luaL_Reg slurm_functions [] = { |
| { "log", _log_lua_msg }, |
| { "error", _log_lua_error }, |
| { NULL, NULL } |
| }; |
| |
| static void _register_lua_slurm_output_functions (void) |
| { |
| /* |
| * Register slurm output functions in a global "slurm" table |
| */ |
| lua_newtable (L); |
| luaL_register (L, NULL, slurm_functions); |
| |
| /* |
| * Create more user-friendly lua versions of SLURM log functions. |
| */ |
| luaL_loadstring (L, "slurm.error (string.format(unpack({...})))"); |
| lua_setfield (L, -2, "log_error"); |
| luaL_loadstring (L, "slurm.log (0, string.format(unpack({...})))"); |
| lua_setfield (L, -2, "log_info"); |
| luaL_loadstring (L, "slurm.log (1, string.format(unpack({...})))"); |
| lua_setfield (L, -2, "log_verbose"); |
| luaL_loadstring (L, "slurm.log (2, string.format(unpack({...})))"); |
| lua_setfield (L, -2, "log_debug"); |
| luaL_loadstring (L, "slurm.log (3, string.format(unpack({...})))"); |
| lua_setfield (L, -2, "log_debug2"); |
| luaL_loadstring (L, "slurm.log (4, string.format(unpack({...})))"); |
| lua_setfield (L, -2, "log_debug3"); |
| luaL_loadstring (L, "slurm.log (5, string.format(unpack({...})))"); |
| lua_setfield (L, -2, "log_debug4"); |
| |
| /* |
| * slurm.SUCCESS, slurm.FAILURE and slurm.ERROR |
| */ |
| lua_pushnumber (L, SLURM_FAILURE); |
| lua_setfield (L, -2, "FAILURE"); |
| lua_pushnumber (L, SLURM_ERROR); |
| lua_setfield (L, -2, "ERROR"); |
| lua_pushnumber (L, SLURM_SUCCESS); |
| lua_setfield (L, -2, "SUCCESS"); |
| |
| lua_setglobal (L, "slurm"); |
| } |
| |
| /* Get fields in an existing slurmctld job record |
| * NOTE: This is an incomplete list of job record fields. |
| * Add more as needed and send patches to slurm-dev@llnl.gov */ |
| static int _get_job_rec_field (lua_State *L) |
| { |
| const struct job_record *job_ptr = lua_touserdata(L, 1); |
| const char *name = luaL_checkstring(L, 2); |
| |
| if (job_ptr == NULL) { |
| error("_get_job_field: job_ptr is NULL"); |
| lua_pushnil (L); |
| } else if (!strcmp(name, "account")) { |
| lua_pushstring (L, job_ptr->account); |
| } else if (!strcmp(name, "comment")) { |
| lua_pushstring (L, job_ptr->comment); |
| } else if (!strcmp(name, "direct_set_prio")) { |
| lua_pushnumber (L, job_ptr->direct_set_prio); |
| } else if (!strcmp(name, "gres")) { |
| lua_pushstring (L, job_ptr->gres); |
| } else if (!strcmp(name, "job_id")) { |
| lua_pushnumber (L, job_ptr->job_id); |
| } else if (!strcmp(name, "job_state")) { |
| lua_pushnumber (L, job_ptr->job_state); |
| } else if (!strcmp(name, "licenses")) { |
| lua_pushstring (L, job_ptr->licenses); |
| } else if (!strcmp(name, "max_cpus")) { |
| if (job_ptr->details) |
| lua_pushnumber (L, job_ptr->details->max_cpus); |
| else |
| lua_pushnumber (L, 0); |
| } else if (!strcmp(name, "max_nodes")) { |
| if (job_ptr->details) |
| lua_pushnumber (L, job_ptr->details->max_nodes); |
| else |
| lua_pushnumber (L, 0); |
| } else if (!strcmp(name, "min_cpus")) { |
| if (job_ptr->details) |
| lua_pushnumber (L, job_ptr->details->min_cpus); |
| else |
| lua_pushnumber (L, 0); |
| } else if (!strcmp(name, "min_nodes")) { |
| if (job_ptr->details) |
| lua_pushnumber (L, job_ptr->details->min_nodes); |
| else |
| lua_pushnumber (L, 0); |
| } else if (!strcmp(name, "nice")) { |
| if (job_ptr->details) |
| lua_pushnumber (L, job_ptr->details->nice); |
| else |
| lua_pushnumber (L, (uint16_t)NO_VAL); |
| } else if (!strcmp(name, "partition")) { |
| lua_pushstring (L, job_ptr->partition); |
| } else if (!strcmp(name, "priority")) { |
| lua_pushnumber (L, job_ptr->priority); |
| } else if (!strcmp(name, "req_switch")) { |
| lua_pushnumber (L, job_ptr->req_switch); |
| } else if (!strcmp(name, "time_limit")) { |
| lua_pushnumber (L, job_ptr->time_limit); |
| } else if (!strcmp(name, "time_min")) { |
| lua_pushnumber (L, job_ptr->time_min); |
| } else if (!strcmp(name, "wait4switch")) { |
| lua_pushnumber (L, job_ptr->wait4switch); |
| } else if (!strcmp(name, "wckey")) { |
| lua_pushstring (L, job_ptr->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 (lua_State *L) |
| { |
| const struct job_descriptor *job_desc = lua_touserdata(L, 1); |
| const char *name = luaL_checkstring(L, 2); |
| |
| if (job_desc == NULL) { |
| error("_get_job_req_field: job_desc is NULL"); |
| lua_pushnil (L); |
| } else if (!strcmp(name, "account")) { |
| lua_pushstring (L, job_desc->account); |
| } else if (!strcmp(name, "acctg_freq")) { |
| lua_pushnumber (L, job_desc->acctg_freq); |
| } else if (!strcmp(name, "begin_time")) { |
| lua_pushnumber (L, job_desc->begin_time); |
| } else if (!strcmp(name, "comment")) { |
| lua_pushstring (L, job_desc->comment); |
| } else if (!strcmp(name, "contiguous")) { |
| lua_pushnumber (L, job_desc->contiguous); |
| } else if (!strcmp(name, "cores_per_socket")) { |
| lua_pushnumber (L, job_desc->cores_per_socket); |
| } else if (!strcmp(name, "dependency")) { |
| lua_pushstring (L, job_desc->dependency); |
| } else if (!strcmp(name, "end_time")) { |
| lua_pushnumber (L, job_desc->end_time); |
| } else if (!strcmp(name, "exc_nodes")) { |
| lua_pushstring (L, job_desc->exc_nodes); |
| } else if (!strcmp(name, "features")) { |
| lua_pushstring (L, job_desc->features); |
| } else if (!strcmp(name, "gres")) { |
| lua_pushstring (L, job_desc->gres); |
| } else if (!strcmp(name, "group_id")) { |
| lua_pushnumber (L, job_desc->group_id); |
| } else if (!strcmp(name, "licenses")) { |
| lua_pushstring (L, job_desc->licenses); |
| } else if (!strcmp(name, "max_cpus")) { |
| lua_pushnumber (L, job_desc->max_cpus); |
| } else if (!strcmp(name, "max_nodes")) { |
| lua_pushnumber (L, job_desc->max_nodes); |
| } else if (!strcmp(name, "min_cpus")) { |
| lua_pushnumber (L, job_desc->min_cpus); |
| } else if (!strcmp(name, "min_nodes")) { |
| lua_pushnumber (L, job_desc->min_nodes); |
| } else if (!strcmp(name, "name")) { |
| lua_pushstring (L, job_desc->name); |
| } else if (!strcmp(name, "nice")) { |
| lua_pushnumber (L, job_desc->nice); |
| } else if (!strcmp(name, "ntasks_per_node")) { |
| lua_pushnumber (L, job_desc->ntasks_per_node); |
| } else if (!strcmp(name, "num_tasks")) { |
| lua_pushnumber (L, job_desc->num_tasks); |
| } else if (!strcmp(name, "partition")) { |
| lua_pushstring (L, job_desc->partition); |
| } else if (!strcmp(name, "pn_min_cpus")) { |
| lua_pushnumber (L, job_desc->pn_min_cpus); |
| } else if (!strcmp(name, "pn_min_memory")) { |
| lua_pushnumber (L, job_desc->pn_min_memory); |
| } else if (!strcmp(name, "pn_min_tmp_disk")) { |
| lua_pushnumber (L, job_desc->pn_min_tmp_disk); |
| } else if (!strcmp(name, "priority")) { |
| lua_pushnumber (L, job_desc->priority); |
| } else if (!strcmp(name, "qos")) { |
| lua_pushstring (L, job_desc->qos); |
| } else if (!strcmp(name, "req_nodes")) { |
| lua_pushstring (L, job_desc->req_nodes); |
| } else if (!strcmp(name, "req_switch")) { |
| lua_pushnumber (L, job_desc->req_switch); |
| } else if (!strcmp(name, "requeue")) { |
| lua_pushnumber (L, job_desc->requeue); |
| } else if (!strcmp(name, "reservation")) { |
| lua_pushstring (L, job_desc->reservation); |
| } else if (!strcmp(name, "shared")) { |
| lua_pushnumber (L, job_desc->shared); |
| } else if (!strcmp(name, "sockets_per_node")) { |
| lua_pushnumber (L, job_desc->sockets_per_node); |
| } else if (!strcmp(name, "threads_per_core")) { |
| lua_pushnumber (L, job_desc->threads_per_core); |
| } else if (!strcmp(name, "time_limit")) { |
| lua_pushnumber (L, job_desc->time_limit); |
| } else if (!strcmp(name, "time_min")) { |
| lua_pushnumber (L, job_desc->time_min); |
| } else if (!strcmp(name, "user_id")) { |
| lua_pushnumber (L, job_desc->user_id); |
| } else if (!strcmp(name, "wait4switch")) { |
| lua_pushnumber (L, job_desc->wait4switch); |
| } else if (!strcmp(name, "wckey")) { |
| lua_pushstring (L, job_desc->wckey); |
| } else { |
| lua_pushnil (L); |
| } |
| |
| return 1; |
| } |
| |
| /* Set fields in the job request structure on job submit or modify */ |
| static int _set_job_req_field (lua_State *L) |
| { |
| struct job_descriptor *job_desc = lua_touserdata(L, 1); |
| const char *name, *value_str; |
| |
| name = luaL_checkstring(L, 2); |
| if (job_desc == NULL) { |
| error("_set_job_req_field: job_desc is NULL"); |
| } else if (!strcmp(name, "account")) { |
| value_str = luaL_checkstring(L, 3); |
| xfree(job_desc->account); |
| if (strlen(value_str)) |
| job_desc->account = xstrdup(value_str); |
| } else if (!strcmp(name, "acctg_freq")) { |
| job_desc->acctg_freq = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "begin_time")) { |
| job_desc->begin_time = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "comment")) { |
| value_str = luaL_checkstring(L, 3); |
| xfree(job_desc->comment); |
| if (strlen(value_str)) |
| job_desc->comment = xstrdup(value_str); |
| } else if (!strcmp(name, "contiguous")) { |
| job_desc->contiguous = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "cores_per_socket")) { |
| job_desc->cores_per_socket = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "dependency")) { |
| value_str = luaL_checkstring(L, 3); |
| xfree(job_desc->dependency); |
| if (strlen(value_str)) |
| job_desc->dependency = xstrdup(value_str); |
| } else if (!strcmp(name, "end_time")) { |
| job_desc->end_time = luaL_checknumber(L, 3); |
| } else if (!strcmp(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 (!strcmp(name, "features")) { |
| value_str = luaL_checkstring(L, 3); |
| xfree(job_desc->features); |
| if (strlen(value_str)) |
| job_desc->features = xstrdup(value_str); |
| } else if (!strcmp(name, "gres")) { |
| value_str = luaL_checkstring(L, 3); |
| xfree(job_desc->gres); |
| if (strlen(value_str)) |
| job_desc->gres = xstrdup(value_str); |
| } else if (!strcmp(name, "licenses")) { |
| value_str = luaL_checkstring(L, 3); |
| xfree(job_desc->licenses); |
| if (strlen(value_str)) |
| job_desc->licenses = xstrdup(value_str); |
| } else if (!strcmp(name, "max_cpus")) { |
| job_desc->max_cpus = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "max_nodes")) { |
| job_desc->max_nodes = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "min_cpus")) { |
| job_desc->min_cpus = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "min_nodes")) { |
| job_desc->min_nodes = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "name")) { |
| value_str = luaL_checkstring(L, 3); |
| xfree(job_desc->name); |
| if (strlen(value_str)) |
| job_desc->name = xstrdup(value_str); |
| } else if (!strcmp(name, "nice")) { |
| job_desc->nice = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "ntasks_per_node")) { |
| job_desc->ntasks_per_node = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "num_tasks")) { |
| job_desc->num_tasks = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "partition")) { |
| value_str = luaL_checkstring(L, 3); |
| xfree(job_desc->partition); |
| if (strlen(value_str)) |
| job_desc->partition = xstrdup(value_str); |
| } else if (!strcmp(name, "pn_min_cpus")) { |
| job_desc->pn_min_cpus = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "pn_min_memory")) { |
| job_desc->pn_min_memory = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "pn_min_tmp_disk")) { |
| job_desc->pn_min_tmp_disk = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "priority")) { |
| job_desc->priority = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "qos")) { |
| value_str = luaL_checkstring(L, 3); |
| xfree(job_desc->qos); |
| if (strlen(value_str)) |
| job_desc->qos = xstrdup(value_str); |
| } else if (!strcmp(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 (!strcmp(name, "req_switch")) { |
| job_desc->req_switch = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "requeue")) { |
| job_desc->requeue = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "reservation")) { |
| value_str = luaL_checkstring(L, 3); |
| xfree(job_desc->reservation); |
| if (strlen(value_str)) |
| job_desc->reservation = xstrdup(value_str); |
| } else if (!strcmp(name, "shared")) { |
| job_desc->shared = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "sockets_per_node")) { |
| job_desc->sockets_per_node = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "threads_per_core")) { |
| job_desc->threads_per_core = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "time_limit")) { |
| job_desc->time_limit = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "time_min")) { |
| job_desc->time_min = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "wait4switch")) { |
| job_desc->wait4switch = luaL_checknumber(L, 3); |
| } else if (!strcmp(name, "wckey")) { |
| value_str = luaL_checkstring(L, 3); |
| xfree(job_desc->wckey); |
| if (strlen(value_str)) |
| job_desc->wckey = xstrdup(value_str); |
| } else { |
| error("_set_job_field: unrecognized field: %s", name); |
| } |
| |
| return 0; |
| } |
| |
| /* Get fields in an existing slurmctld partition record |
| * NOTE: This is an incomplete list of partition record fields. |
| * Add more as needed and send patches to slurm-dev@llnl.gov */ |
| static int _get_part_rec_field (lua_State *L) |
| { |
| const struct part_record *part_ptr = lua_touserdata(L, 1); |
| const char *name = luaL_checkstring(L, 2); |
| |
| if (part_ptr == NULL) { |
| error("_get_part_field: part_ptr is NULL"); |
| lua_pushnil (L); |
| } else if (!strcmp(name, "default_time")) { |
| lua_pushnumber (L, part_ptr->default_time); |
| } else if (!strcmp(name, "flag_default")) { |
| int is_default = 0; |
| if (part_ptr->flags & PART_FLAG_DEFAULT) |
| is_default = 1; |
| lua_pushnumber (L, is_default); |
| } else if (!strcmp(name, "flags")) { |
| lua_pushnumber (L, part_ptr->flags); |
| } else if (!strcmp(name, "max_nodes")) { |
| lua_pushnumber (L, part_ptr->max_nodes); |
| } else if (!strcmp(name, "max_nodes_orig")) { |
| lua_pushnumber (L, part_ptr->max_nodes_orig); |
| } else if (!strcmp(name, "max_time")) { |
| lua_pushnumber (L, part_ptr->max_time); |
| } else if (!strcmp(name, "min_nodes")) { |
| lua_pushnumber (L, part_ptr->min_nodes); |
| } else if (!strcmp(name, "min_nodes_orig")) { |
| lua_pushnumber (L, part_ptr->min_nodes_orig); |
| } else if (!strcmp(name, "name")) { |
| lua_pushstring (L, part_ptr->name); |
| } else if (!strcmp(name, "nodes")) { |
| lua_pushstring (L, part_ptr->nodes); |
| } else if (!strcmp(name, "priority")) { |
| lua_pushnumber (L, part_ptr->priority); |
| } else if (!strcmp(name, "state_up")) { |
| lua_pushnumber (L, part_ptr->state_up); |
| } else { |
| lua_pushnil (L); |
| } |
| |
| return 1; |
| } |
| #if 0 |
| /* Filter before packing list of partitions */ |
| char *allow_groups; /* comma delimited list of groups */ |
| uid_t *allow_uids; /* zero terminated list of allowed users */ |
| #endif |
| |
| static void _register_lua_slurm_struct_functions (void) |
| { |
| lua_pushcfunction(L, _get_job_rec_field); |
| lua_setglobal(L, "_get_job_rec_field"); |
| lua_pushcfunction(L, _get_job_req_field); |
| lua_setglobal(L, "_get_job_req_field"); |
| lua_pushcfunction(L, _set_job_req_field); |
| lua_setglobal(L, "_set_job_req_field"); |
| lua_pushcfunction(L, _get_part_rec_field); |
| lua_setglobal(L, "_get_part_rec_field"); |
| } |
| |
| /* |
| * check that global symbol [name] in lua script is a function |
| */ |
| static int _check_lua_script_function(const char *name) |
| { |
| int rc = 0; |
| lua_getglobal(L, name); |
| if (!lua_isfunction(L, -1)) |
| rc = -1; |
| lua_pop(L, -1); |
| return (rc); |
| } |
| |
| /* |
| * Verify all required functions are defined in the job_submit/lua script |
| */ |
| static int _check_lua_script_functions() |
| { |
| int rc = 0; |
| int i; |
| const char *fns[] = { |
| "slurm_job_submit", |
| "slurm_job_modify", |
| NULL |
| }; |
| |
| i = 0; |
| do { |
| if (_check_lua_script_function(fns[i]) < 0) { |
| error("job_submit/lua: %s: " |
| "missing required function %s", |
| lua_script_path, fns[i]); |
| rc = -1; |
| } |
| } while (fns[++i]); |
| |
| return (rc); |
| } |
| |
| static bool _user_can_use_part(uint32_t user_id, uint32_t submit_uid, |
| struct part_record *part_ptr) |
| { |
| int i; |
| |
| 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 == NULL) |
| return true; /* No user ID filters */ |
| |
| for (i=0; part_ptr->allow_uids[i]; i++) { |
| if (user_id == part_ptr->allow_uids[i]) |
| return true; |
| } |
| return false; |
| } |
| |
| static void _push_partition_list(uint32_t user_id, uint32_t submit_uid) |
| { |
| int i = 1; |
| ListIterator part_iterator; |
| struct part_record *part_ptr; |
| |
| lua_newtable(L); |
| 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))) { |
| if (!_user_can_use_part(user_id, submit_uid, part_ptr)) |
| continue; |
| lua_pushlightuserdata (L, part_ptr); |
| lua_rawseti(L, -2, i++); |
| } |
| list_iterator_destroy(part_iterator); |
| } |
| |
| static void _push_job_desc(struct job_descriptor *job_desc) |
| { |
| lua_newtable(L); |
| lua_pushlightuserdata(L, job_desc); |
| lua_setfield(L, -2, "job_desc_ptr"); |
| } |
| |
| static void _push_job_rec(struct job_record *job_ptr) |
| { |
| lua_newtable(L); |
| lua_pushlightuserdata (L, job_ptr); |
| lua_setfield(L, -2, "job_rec_ptr"); |
| } |
| |
| /* |
| * NOTE: The init callback should never be called multiple times, |
| * let alone called from multiple threads. Therefore, locking |
| * is unnecessary here. |
| */ |
| int init (void) |
| { |
| int rc = SLURM_SUCCESS; |
| |
| /* |
| * Need to dlopen() liblua.so with RTLD_GLOBAL in order to |
| * ensure symbols from liblua are available to libs opened |
| * by any lua scripts. |
| */ |
| if (!dlopen("liblua.so", RTLD_NOW | RTLD_GLOBAL) && |
| !dlopen("liblua5.1.so", RTLD_NOW | RTLD_GLOBAL) && |
| !dlopen("liblua5.1.so.0", RTLD_NOW | RTLD_GLOBAL)) { |
| return (error("Failed to open liblua.so: %s", dlerror())); |
| } |
| |
| /* |
| * Initilize lua |
| */ |
| L = luaL_newstate(); |
| luaL_openlibs(L); |
| if (luaL_loadfile(L, lua_script_path)) { |
| return error("lua: %s: %s", lua_script_path, |
| lua_tostring(L, -1)); |
| } |
| |
| /* |
| * Register SLURM functions in lua state: |
| * logging and slurm structure read/write functions |
| */ |
| _register_lua_slurm_output_functions(); |
| _register_lua_slurm_struct_functions(); |
| |
| /* |
| * Run the user script: |
| */ |
| if (lua_pcall(L, 0, 1, 0) != 0) { |
| return error("job_submit/lua: %s: %s", |
| lua_script_path, lua_tostring (L, -1)); |
| } |
| |
| /* |
| * Get any return code from the lua script |
| */ |
| rc = (int) lua_tonumber(L, -1); |
| lua_pop (L, 1); |
| if (rc != SLURM_SUCCESS) |
| return rc; |
| |
| /* |
| * Check for required lua script functions: |
| */ |
| return (_check_lua_script_functions()); |
| } |
| |
| int fini (void) |
| { |
| lua_close (L); |
| return SLURM_SUCCESS; |
| } |
| |
| |
| /* Lua script hook called for "submit job" event. */ |
| extern int job_submit(struct job_descriptor *job_desc, uint32_t submit_uid) |
| { |
| int rc = SLURM_ERROR; |
| slurm_mutex_lock (&lua_lock); |
| |
| /* |
| * All lua script functions should have been verified during |
| * initialization: |
| */ |
| lua_getglobal(L, "slurm_job_submit"); |
| if (lua_isnil(L, -1)) |
| goto out; |
| |
| _push_job_desc(job_desc); |
| _push_partition_list(job_desc->user_id, submit_uid); |
| _stack_dump("job_submit, before lua_pcall", L); |
| if (lua_pcall (L, 2, 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); |
| } |
| _stack_dump("job_submit, after lua_pcall", L); |
| |
| out: slurm_mutex_unlock (&lua_lock); |
| return rc; |
| } |
| |
| /* Lua script hook called for "modify job" event. */ |
| extern int job_modify(struct job_descriptor *job_desc, |
| struct job_record *job_ptr, uint32_t submit_uid) |
| { |
| int rc = SLURM_ERROR; |
| slurm_mutex_lock (&lua_lock); |
| |
| /* |
| * All lua script functions should have been verified during |
| * initialization: |
| */ |
| lua_getglobal(L, "slurm_job_modify"); |
| if (lua_isnil(L, -1)) |
| goto out; |
| |
| _push_job_desc(job_desc); |
| _push_job_rec(job_ptr); |
| _push_partition_list(job_ptr->user_id, submit_uid); |
| _stack_dump("job_modify, 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); |
| } |
| _stack_dump("job_modify, after lua_pcall", L); |
| |
| out: slurm_mutex_unlock (&lua_lock); |
| return rc; |
| } |