| /*****************************************************************************\ |
| * jobcomp_mysql.c - Store/Get all information in a mysql storage. |
| ***************************************************************************** |
| * 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 Danny Auble <da@llnl.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. |
| \*****************************************************************************/ |
| |
| /* |
| * We can't include common/slurm_xlator.h here since it contains |
| * list_push and list_pop which are mysql macros what for some reason |
| * are given to us. So if you need something from the header just |
| * copy it here. |
| */ |
| #define error slurm_error |
| |
| #include "mysql_jobcomp_process.h" |
| #include <pwd.h> |
| #include <grp.h> |
| #include <sys/types.h> |
| #include "src/common/id_util.h" |
| #include "src/common/parse_time.h" |
| #include "src/interfaces/select.h" |
| |
| /* |
| * 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., "jobacct" for Slurm job completion logging) and <method> |
| * is a description of how this plugin satisfies that application. Slurm will |
| * only load job completion logging plugins if the plugin_type string has a |
| * prefix of "jobacct/". |
| * |
| * plugin_version - an unsigned 32-bit integer containing the Slurm version |
| * (major.minor.micro combined into a single number). |
| */ |
| const char plugin_name[] = "Job completion MYSQL plugin"; |
| const char plugin_type[] = "jobcomp/mysql"; |
| const uint32_t plugin_version = SLURM_VERSION_NUMBER; |
| |
| const char default_job_comp_loc[] = "slurm_jobcomp_db"; |
| |
| mysql_conn_t *jobcomp_mysql_conn = NULL; |
| |
| char *jobcomp_table = "jobcomp_table"; |
| storage_field_t jobcomp_table_fields[] = { |
| { "jobid", "int not null" }, |
| { "uid", "int unsigned not null" }, |
| { "user_name", "tinytext not null" }, |
| { "gid", "int unsigned not null" }, |
| { "group_name", "tinytext not null" }, |
| { "name", "tinytext not null" }, |
| { "state", "int unsigned not null" }, |
| { "partition", "tinytext not null" }, |
| { "timelimit", "tinytext not null" }, |
| { "starttime", "int unsigned default 0 not null" }, |
| { "endtime", "int unsigned default 0 not null" }, |
| { "nodelist", "text" }, |
| { "nodecnt", "int unsigned not null" }, |
| { "proc_cnt", "int unsigned not null" }, |
| { "connect_type", "tinytext" }, |
| { "reboot", "tinytext" }, |
| { "rotate", "tinytext" }, |
| { "maxprocs", "int unsigned default 0 not null" }, |
| { "geometry", "tinytext" }, |
| { "start", "tinytext" }, |
| { "blockid", "tinytext" }, |
| { NULL, NULL} |
| }; |
| |
| static int _mysql_jobcomp_check_tables() |
| { |
| if (mysql_db_create_table(jobcomp_mysql_conn, jobcomp_table, |
| jobcomp_table_fields, |
| ", primary key (jobid, starttime, endtime))") |
| == SLURM_ERROR) |
| return SLURM_ERROR; |
| |
| return SLURM_SUCCESS; |
| } |
| |
| extern int init(void) |
| { |
| static int first = 1; |
| |
| if (first) { |
| /* since this can be loaded from many different places |
| only tell us once. */ |
| verbose("%s loaded", plugin_name); |
| first = 0; |
| } else { |
| debug4("%s loaded", plugin_name); |
| } |
| |
| return SLURM_SUCCESS; |
| } |
| |
| extern void fini(void) |
| { |
| if (jobcomp_mysql_conn) { |
| destroy_mysql_conn(jobcomp_mysql_conn); |
| jobcomp_mysql_conn = NULL; |
| } |
| } |
| |
| extern int jobcomp_p_set_location(void) |
| { |
| mysql_db_info_t *db_info; |
| int rc = SLURM_SUCCESS; |
| char *db_name = NULL; |
| |
| if (jobcomp_mysql_conn && mysql_db_ping(jobcomp_mysql_conn) == 0) |
| return SLURM_SUCCESS; |
| |
| if (!slurm_conf.job_comp_loc) { |
| slurm_conf.job_comp_loc = xstrdup(default_job_comp_loc); |
| db_name = slurm_conf.job_comp_loc; |
| } else { |
| if (xstrchr(slurm_conf.job_comp_loc, '.') || |
| xstrchr(slurm_conf.job_comp_loc, '/')) { |
| debug("%s doesn't look like a database name using %s", |
| slurm_conf.job_comp_loc, default_job_comp_loc); |
| db_name = (char *) default_job_comp_loc; |
| } else |
| db_name = slurm_conf.job_comp_loc; |
| } |
| |
| debug2("mysql_connect() called for db %s", db_name); |
| /* Just make sure our connection is gone. */ |
| fini(); |
| jobcomp_mysql_conn = create_mysql_conn(0, 0, NULL); |
| |
| db_info = create_mysql_db_info(SLURM_MYSQL_PLUGIN_JC); |
| |
| mysql_db_get_db_connection(jobcomp_mysql_conn, db_name, db_info); |
| |
| rc = _mysql_jobcomp_check_tables(); |
| |
| destroy_mysql_db_info(db_info); |
| |
| if (rc == SLURM_SUCCESS) |
| debug("Jobcomp database init finished"); |
| else |
| debug("Jobcomp database init failed"); |
| return rc; |
| } |
| |
| extern int jobcomp_p_record_job_end(job_record_t *job_ptr, uint32_t event) |
| { |
| int rc = SLURM_SUCCESS; |
| char *usr_str = NULL, *grp_str = NULL, lim_str[32], *jname = NULL; |
| char *partition = NULL; |
| uint32_t job_state; |
| char *query = NULL, *on_dup = NULL; |
| uint32_t time_limit; |
| time_t start_time, end_time; |
| |
| if (!jobcomp_mysql_conn || mysql_db_ping(jobcomp_mysql_conn) != 0) { |
| if (jobcomp_p_set_location()) |
| return SLURM_ERROR; |
| } |
| |
| usr_str = user_from_job(job_ptr); |
| grp_str = group_from_job(job_ptr); |
| partition = job_ptr->part_ptr ? job_ptr->part_ptr->name : |
| job_ptr->partition; |
| |
| if ((job_ptr->time_limit == NO_VAL) && job_ptr->part_ptr) |
| time_limit = job_ptr->part_ptr->max_time; |
| else |
| time_limit = job_ptr->time_limit; |
| if (time_limit == INFINITE) |
| strcpy(lim_str, "UNLIMITED"); |
| else { |
| snprintf(lim_str, sizeof(lim_str), "%lu", |
| (unsigned long) time_limit); |
| } |
| |
| /* Job will typically be COMPLETING when this is called. |
| * We remove the flags to get the eventual completion state: |
| * JOB_FAILED, JOB_TIMEOUT, etc. */ |
| if (IS_JOB_RESIZING(job_ptr)) { |
| job_state = JOB_RESIZING; |
| if (job_ptr->resize_time) |
| start_time = job_ptr->resize_time; |
| else |
| start_time = job_ptr->start_time; |
| end_time = time(NULL); |
| } else { |
| job_state = job_ptr->job_state & JOB_STATE_BASE; |
| if (job_ptr->resize_time) |
| start_time = job_ptr->resize_time; |
| else if (job_ptr->start_time > job_ptr->end_time) { |
| /* Job cancelled while pending and |
| * expected start time is in the future. */ |
| start_time = 0; |
| } else |
| start_time = job_ptr->start_time; |
| end_time = job_ptr->end_time; |
| } |
| |
| if (job_ptr->name && job_ptr->name[0]) |
| jname = slurm_add_slash_to_quotes(job_ptr->name); |
| else |
| jname = xstrdup("allocation"); |
| |
| query = xstrdup_printf( |
| "insert into %s (jobid, uid, user_name, gid, group_name, " |
| "name, state, proc_cnt, `partition`, timelimit, " |
| "starttime, endtime, nodecnt", |
| jobcomp_table); |
| |
| if (job_ptr->nodes) |
| xstrcat(query, ", nodelist"); |
| if (job_ptr->details && (job_ptr->details->max_cpus != NO_VAL)) |
| xstrcat(query, ", maxprocs"); |
| xstrfmtcat(query, ") values (%u, %u, '%s', %u, '%s', '%s', %u, %u, " |
| "'%s', '%s', %ld, %ld, %u", |
| job_ptr->job_id, job_ptr->user_id, usr_str, |
| job_ptr->group_id, grp_str, jname, |
| job_state, job_ptr->total_cpus, partition, lim_str, |
| start_time, end_time, job_ptr->node_cnt); |
| |
| xstrfmtcat(on_dup, "uid=%u, user_name='%s', gid=%u, group_name='%s', " |
| "name='%s', state=%u, proc_cnt=%u, `partition`='%s', " |
| "timelimit='%s', nodecnt=%u", |
| job_ptr->user_id, usr_str, job_ptr->group_id, grp_str, jname, |
| job_state, job_ptr->total_cpus, partition, lim_str, |
| job_ptr->node_cnt); |
| |
| if (job_ptr->nodes) { |
| xstrfmtcat(query, ", '%s'", job_ptr->nodes); |
| xstrfmtcat(on_dup, ", nodelist='%s'", job_ptr->nodes); |
| } |
| |
| if (job_ptr->details && (job_ptr->details->max_cpus != NO_VAL)) { |
| xstrfmtcat(query, ", '%u'", job_ptr->details->max_cpus); |
| xstrfmtcat(on_dup, ", maxprocs='%u'", |
| job_ptr->details->max_cpus); |
| } |
| |
| xstrfmtcat(query, ") ON DUPLICATE KEY UPDATE %s;", on_dup); |
| |
| debug3("(%s:%d) query\n%s", |
| THIS_FILE, __LINE__, query); |
| rc = mysql_db_query(jobcomp_mysql_conn, query); |
| xfree(usr_str); |
| xfree(grp_str); |
| xfree(jname); |
| xfree(query); |
| xfree(on_dup); |
| |
| return rc; |
| } |
| |
| /* |
| * get info from the storage |
| * in/out job_list list of job_rec_t * |
| * note list needs to be freed when called |
| */ |
| extern list_t *jobcomp_p_get_jobs(slurmdb_job_cond_t *job_cond) |
| { |
| list_t *job_list = NULL; |
| |
| if (!jobcomp_mysql_conn || mysql_db_ping(jobcomp_mysql_conn) != 0) { |
| if (jobcomp_p_set_location()) |
| return job_list; |
| } |
| |
| job_list = mysql_jobcomp_process_get_jobs(job_cond); |
| |
| return job_list; |
| } |
| |
| extern int jobcomp_p_record_job_start(job_record_t *job_ptr, uint32_t event) |
| { |
| return SLURM_SUCCESS; |
| } |