diff --git a/0002-add-pgsql-plugin.patch b/0002-add-pgsql-plugin.patch new file mode 100644 index 0000000000000000000000000000000000000000..6b3a2dc61a2b4369908a88b81eca43aa618b738d --- /dev/null +++ b/0002-add-pgsql-plugin.patch @@ -0,0 +1,35852 @@ +From 1c27a7e906fae665e89d3690d2ab7bd496a5560f Mon Sep 17 00:00:00 2001 +From: motodiary +Date: Fri, 19 Apr 2024 16:56:11 +0800 +Subject: [PATCH] add pgsql plugin + +--- + auxdir/x_ac_databases.m4 | 68 + + configure.ac | 2 + + src/common/read_config.h | 1 + + src/database/Makefile.am | 13 +- + src/database/pgsql_common.c | 1414 +++++ + src/database/pgsql_common.h | 192 + + src/plugins/accounting_storage/Makefile.am | 2 +- + .../accounting_storage/pgsql/Makefile.am | 46 + + .../accounting_storage/pgsql/Makefile.in | 1085 ++++ + .../pgsql/accounting_storage_pgsql.c | 3395 ++++++++++++ + .../pgsql/accounting_storage_pgsql.h | 207 + + .../accounting_storage/pgsql/as_pgsql_acct.c | 795 +++ + .../accounting_storage/pgsql/as_pgsql_acct.h | 56 + + .../pgsql/as_pgsql_archive.c | 4737 +++++++++++++++++ + .../pgsql/as_pgsql_archive.h | 50 + + .../accounting_storage/pgsql/as_pgsql_assoc.c | 3779 +++++++++++++ + .../accounting_storage/pgsql/as_pgsql_assoc.h | 64 + + .../pgsql/as_pgsql_cluster.c | 1750 ++++++ + .../pgsql/as_pgsql_cluster.h | 81 + + .../pgsql/as_pgsql_convert.c | 366 ++ + .../pgsql/as_pgsql_convert.h | 66 + + .../pgsql/as_pgsql_federation.c | 738 +++ + .../pgsql/as_pgsql_federation.h | 57 + + .../pgsql/as_pgsql_fix_runaway_jobs.c | 200 + + .../pgsql/as_pgsql_fix_runaway_jobs.h | 46 + + .../accounting_storage/pgsql/as_pgsql_job.c | 1791 +++++++ + .../accounting_storage/pgsql/as_pgsql_job.h | 64 + + .../pgsql/as_pgsql_jobacct_process.c | 1843 +++++++ + .../pgsql/as_pgsql_jobacct_process.h | 63 + + .../pgsql/as_pgsql_problems.c | 425 ++ + .../pgsql/as_pgsql_problems.h | 55 + + .../accounting_storage/pgsql/as_pgsql_qos.c | 1549 ++++++ + .../accounting_storage/pgsql/as_pgsql_qos.h | 56 + + .../pgsql/as_pgsql_resource.c | 1255 +++++ + .../pgsql/as_pgsql_resource.h | 56 + + .../accounting_storage/pgsql/as_pgsql_resv.c | 747 +++ + .../accounting_storage/pgsql/as_pgsql_resv.h | 54 + + .../pgsql/as_pgsql_rollup.c | 2081 ++++++++ + .../pgsql/as_pgsql_rollup.h | 57 + + .../accounting_storage/pgsql/as_pgsql_tres.c | 316 ++ + .../accounting_storage/pgsql/as_pgsql_tres.h | 48 + + .../accounting_storage/pgsql/as_pgsql_txn.c | 459 ++ + .../accounting_storage/pgsql/as_pgsql_txn.h | 47 + + .../accounting_storage/pgsql/as_pgsql_usage.c | 976 ++++ + .../accounting_storage/pgsql/as_pgsql_usage.h | 60 + + .../accounting_storage/pgsql/as_pgsql_user.c | 1530 ++++++ + .../accounting_storage/pgsql/as_pgsql_user.h | 63 + + .../accounting_storage/pgsql/as_pgsql_wckey.c | 910 ++++ + .../accounting_storage/pgsql/as_pgsql_wckey.h | 55 + + src/plugins/jobcomp/Makefile.am | 2 +- + src/plugins/jobcomp/Makefile.in | 8 +- + src/plugins/jobcomp/pgsql/Makefile.am | 26 + + src/plugins/jobcomp/pgsql/Makefile.in | 846 +++ + src/plugins/jobcomp/pgsql/jobcomp_pgsql.c | 429 ++ + .../jobcomp/pgsql/pgsql_jobcomp_process.c | 187 + + .../jobcomp/pgsql/pgsql_jobcomp_process.h | 89 + + 56 files changed, 35350 insertions(+), 7 deletions(-) + create mode 100755 src/database/pgsql_common.c + create mode 100755 src/database/pgsql_common.h + create mode 100755 src/plugins/accounting_storage/pgsql/Makefile.am + create mode 100755 src/plugins/accounting_storage/pgsql/Makefile.in + create mode 100755 src/plugins/accounting_storage/pgsql/accounting_storage_pgsql.c + create mode 100755 src/plugins/accounting_storage/pgsql/accounting_storage_pgsql.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_acct.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_acct.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_archive.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_archive.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_assoc.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_assoc.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_cluster.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_cluster.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_convert.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_convert.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_federation.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_federation.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_fix_runaway_jobs.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_fix_runaway_jobs.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_job.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_job.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_jobacct_process.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_jobacct_process.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_problems.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_problems.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_qos.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_qos.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_resource.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_resource.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_resv.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_resv.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_rollup.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_rollup.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_tres.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_tres.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_txn.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_txn.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_usage.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_usage.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_user.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_user.h + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_wckey.c + create mode 100755 src/plugins/accounting_storage/pgsql/as_pgsql_wckey.h + create mode 100755 src/plugins/jobcomp/pgsql/Makefile.am + create mode 100755 src/plugins/jobcomp/pgsql/Makefile.in + create mode 100755 src/plugins/jobcomp/pgsql/jobcomp_pgsql.c + create mode 100755 src/plugins/jobcomp/pgsql/pgsql_jobcomp_process.c + create mode 100755 src/plugins/jobcomp/pgsql/pgsql_jobcomp_process.h + +diff --git a/auxdir/x_ac_databases.m4 b/auxdir/x_ac_databases.m4 +index 7645433..f8a01ba 100644 +--- a/auxdir/x_ac_databases.m4 ++++ b/auxdir/x_ac_databases.m4 +@@ -80,4 +80,72 @@ AC_DEFUN([X_AC_DATABASES], + fi + fi + AM_CONDITIONAL(WITH_MYSQL, test x"$ac_have_mysql" = x"yes") ++ ++ #Check for ODBC ++ ac_have_ODBC="no" ++ _x_ac_ODBC_bin="no" ++ ### Check for odbc_config program ++ AC_ARG_WITH( ++ [odbc_config], ++ AS_HELP_STRING(--with-odbc_config=PATH, ++ Specify path of directory where odbc_config binary exists), ++ [_x_ac_ODBC_bin="$withval"]) ++ ++ if test x$_x_ac_ODBC_bin = xno; then ++ AC_PATH_PROGS(HAVEODBCCONFIG, [odbc_config], no) ++ else ++ AC_PATH_PROGS(HAVEODBCCONFIG, [odbc_config], no, $_x_ac_ODBC_bin) ++ fi ++ ++ if test x$HAVEODBCCONFIG = xno; then ++ AC_MSG_WARN([*** odbc_config not found. Evidently no ODBC development libs installed on system.]) ++ else ++ # check for unixODBC version ++ odbc_config_major_version=`$HAVEODBCCONFIG --version | \ ++ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[a-zA-Z0-9]]*\)/\1/'` ++ odbc_config_minor_version=`$HAVEODBCCONFIG --version | \ ++ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[a-zA-Z0-9]]*\)/\2/'` ++ odbc_config_micro_version=`$HAVEODBCCONFIG --version | \ ++ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[a-zA-Z0-9]]*\)/\3/'` ++ ++ if test $odbc_config_major_version -lt 2; then ++ if test -z "$with_odbc_config"; then ++ AC_MSG_WARN([*** ODBC-$odbc_config_major_version.$odbc_config_minor_version.$odbc_config_micro_version available, we need >= unixODBC-2.3.7 installed for the ODBC interface.]) ++ else ++ AC_MSG_ERROR([*** ODBC-$odbc_config_major_version.$odbc_config_minor_version.$odbc_config_micro_version available, we need >= unixODBC-2.3.7 installed for the ODBC interface.]) ++ fi ++ ac_have_ODBC="no" ++ else ++ ODBC_CFLAGS=`-I$HAVEODBCCONFIG --include-prefix` ++ ODBC_LIBS=`$HAVEODBCCONFIG --libs` ++ save_CFLAGS="$CFLAGS" ++ save_LIBS="$LIBS" ++ CFLAGS="$ODBC_CFLAGS $save_CFLAGS" ++ LIBS="$ODBC_LIBS $save_LIBS" ++ AC_TRY_LINK([#include ],[ ++ SQLHENV EnvironmentHandle; ++ SQLRETURN Ret; ++ Ret = SQLAllocEnv(EnvironmentHandle);; ++ ], ++ [ac_have_ODBC="yes"], ++ [ac_have_ODBC="no"]) ++ CFLAGS="$save_CFLAGS" ++ LIBS="$save_LIBS" ++ if test "$ac_have_ODBC" = yes; then ++ AC_MSG_RESULT([ODBC $odbc_config_major_version.$odbc_config_minor_version.$odbc_config_micro_version test program built properly.]) ++ AC_SUBST(ODBC_LIBS) ++ AC_SUBST(ODBC_CFLAGS) ++ AC_DEFINE(HAVE_ODBC, 1, [Define to 1 if using ODBC libaries]) ++ else ++ ODBC_CFLAGS="" ++ ODBC_LIBS="" ++ if test -z "$with_odbc_config"; then ++ AC_MSG_WARN([*** ODBC test program execution failed. A thread-safe ODBC library is required.]) ++ else ++ AC_MSG_ERROR([*** ODBC test program execution failed. A thread-safe ODBC library is required.]) ++ fi ++ fi ++ fi ++ fi ++ AM_CONDITIONAL(WITH_ODBC, test x"$ac_have_ODBC" = x"yes") + ]) +diff --git a/configure.ac b/configure.ac +index 7106516..18bc7fd 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -460,6 +460,7 @@ AC_CONFIG_FILES([Makefile + src/plugins/accounting_storage/Makefile + src/plugins/accounting_storage/common/Makefile + src/plugins/accounting_storage/mysql/Makefile ++ src/plugins/accounting_storage/pgsql/Makefile + src/plugins/accounting_storage/none/Makefile + src/plugins/accounting_storage/slurmdbd/Makefile + src/plugins/acct_gather_energy/Makefile +@@ -530,6 +531,7 @@ AC_CONFIG_FILES([Makefile + src/plugins/jobcomp/none/Makefile + src/plugins/jobcomp/script/Makefile + src/plugins/jobcomp/mysql/Makefile ++ src/plugins/jobcomp/pgsql/Makefile + src/plugins/job_container/Makefile + src/plugins/job_container/cncu/Makefile + src/plugins/job_container/none/Makefile +diff --git a/src/common/read_config.h b/src/common/read_config.h +index 2ebe33f..fa201cf 100644 +--- a/src/common/read_config.h ++++ b/src/common/read_config.h +@@ -164,6 +164,7 @@ extern uint16_t drop_priv_flag; + #define DEFAULT_STORAGE_USER "root" + #define DEFAULT_STORAGE_PORT 0 + #define DEFAULT_MYSQL_PORT 3306 ++#define DEFAULT_DB_PORT 3306 + #define DEFAULT_SUSPEND_RATE 60 + #define DEFAULT_SUSPEND_TIME 0 + #define DEFAULT_SUSPEND_TIMEOUT 30 +diff --git a/src/database/Makefile.am b/src/database/Makefile.am +index d85d099..caf12ee 100644 +--- a/src/database/Makefile.am ++++ b/src/database/Makefile.am +@@ -15,4 +15,15 @@ MYSQL_LIB = + EXTRA_libslurm_mysql_la_SOURCES = mysql_common.c mysql_common.h + endif + +-noinst_LTLIBRARIES = $(MYSQL_LIB) ++if WITH_ODBC ++PGSQL_LIB = libslurm_pgsql.la ++libslurm_pgsql_la_SOURCES = pgsql_common.c pgsql_common.h ++libslurm_pgsql_la_LIBADD = $(ODBC_LIBS) ++libslurm_pgsql_la_LDFLAGS = $(LIB_LDFLAGS) ++libslurm_pgsql_la_CFLAGS = $(ODBC_CFLAGS) $(AM_CFLAGS) ++else ++PGSQL_LIB = ++EXTRA_libslurm_pgsql_la_SOURCES = pgsql_common.c pgsql_common.h ++endif ++ ++noinst_LTLIBRARIES = $(MYSQL_LIB) $(PGSQL_LIB) +diff --git a/src/database/pgsql_common.c b/src/database/pgsql_common.c +new file mode 100755 +index 0000000..3698609 +--- /dev/null ++++ b/src/database/pgsql_common.c +@@ -0,0 +1,1414 @@ ++/*****************************************************************************\ ++ * pgsql_common.c - common functions for the pgsql storage plugin. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++ * ++ * This file is patterned after jobcomp_linux.c, written by Morris Jette and ++ * Copyright (C) 2002 The Regents of the University of California. ++\*****************************************************************************/ ++ ++#include "config.h" ++ ++#include "pgsql_common.h" ++#include "src/common/log.h" ++#include "src/common/xstring.h" ++#include "src/common/xmalloc.h" ++#include "src/common/timers.h" ++#include "src/common/slurm_protocol_api.h" ++#include "src/common/read_config.h" ++ ++#define MAX_ATTEMPTS 10 ++ ++static char *table_defs_table = "table_defs_table"; ++ ++typedef struct { ++ char *name; ++ char *columns; ++} db_key_t; ++ ++static char *_pgsql_handle_quotes(char *str) ++{ ++ debug4("org str %s", str); ++ char *dup, *copy = NULL; ++ int len = 0; ++ if (!str || !(len = strlen(str))) ++ return NULL; ++ ++ /* make a buffer 2 times the size just to be safe */ ++ copy = dup = xmalloc((2 * len) + 1); ++ if (copy) ++ do { ++ if (*str == '\\' || *str == '"') { ++ *dup++ = '\\'; ++ } else if (*str == '\'') { ++ *dup++ = '\''; ++ } ++ } while ((*dup++ = *str++)); ++ ++ debug4("result str %s", copy); ++ return copy; ++} ++ ++static void _init_pgsql_res(pgsql_res_t** result, pgsql_conn_t *pgsql_conn) ++{ ++ *result = (pgsql_res_t*)malloc(sizeof(pgsql_res_t)); ++ if(*result == NULL){ ++ fatal("%s malloc %ld byte failed.", __func__, sizeof(pgsql_res_t)); ++ return ; ++ } ++ (*result)->stmt = NULL; ++ (*result)->rows = NULL; ++ (*result)->sRowsUsedCount = 0; ++ (*result)->sNumCols = 0; ++ (*result)->sNumRows = 0; ++ strcpy((*result)->state, SUCCESSFUL_COMPLETION); ++ SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_STMT, pgsql_conn->db_conn, &((*result)->stmt)); ++ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ PGSQL_HANDLE_ERROR("SQLAllocHandle", rc, SQL_HANDLE_DBC, pgsql_conn->db_conn, state); ++ (*result)->stmt = SQL_NULL_HSTMT; ++ pgsql_free_result(result); ++ } ++ ++} ++ ++static void _destroy_db_key(void *arg) ++{ ++ db_key_t *db_key = (db_key_t *)arg; ++ ++ if (db_key) { ++ xfree(db_key->name); ++ xfree(db_key->columns); ++ xfree(db_key); ++ } ++} ++ ++/* NOTE: Ensure that pgsql_conn->lock is set on function entry */ ++static void _get_last_result(SQLHSTMT stmt) ++{ ++ SQLRETURN rc = 0; ++ ++ while (1) { ++ rc = SQLMoreResults(stmt); ++ if (rc == SQL_NO_DATA) { ++ // arrive the last ++ break; ++ } else if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ PGSQL_HANDLE_ERROR("SQLMorestmts", rc, SQL_HANDLE_STMT, stmt, state); ++ break; ++ } ++ } ++ ++ return ; ++} ++ ++static void _parse_field_options(char* result, char* field_options) ++{ ++ xassert(str); ++ strcpy(result, field_options); ++ for (size_t i = 0; result[i]; i++) { ++ if (result[i] == '|') { ++ result[i] = ' '; ++ } ++ } ++} ++ ++/* NOTE: Ensure that pgsql_conn->lock is set on function entry */ ++static int _pgsql_query_internal(SQLHDBC db_conn, char *query, SQLHSTMT stmt, SQLCHAR* state) ++{ ++ SQLRETURN rc; ++ int attempt = 0; ++ ++try_again: ++ if (!db_conn) ++ fatal("You haven't inited this storage yet."); ++ ++ rc = SQLExecDirect(stmt, (SQLCHAR*)query, SQL_NTS); ++ if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) { ++ ++ PGSQL_HANDLE_ERROR("SQLExecDirect", rc, SQL_HANDLE_STMT, stmt, state); ++ ++ if (pgsql_db_match_state((char*)state, PGSQL_UNDEFINE_TABLE)) { ++ // Undefined table ++ debug4("This could happen often and is expected."); ++ } else if (pgsql_db_match_state((char*)state, PGSQL_DEADLOCK_DETECTED)) { ++ // Dead lock, retry a few times ++ attempt++; ++ ++ if (attempt < MAX_ATTEMPTS) { ++ error("%s: Serialization failure attempt %u/%u: %s.", ++ __func__, attempt, MAX_ATTEMPTS, state); ++ goto try_again; ++ } else { ++ fatal("%s: unable to resolve serialization failure with attempts %u/%u: %s.", ++ __func__, attempt, MAX_ATTEMPTS, state); ++ } ++ } ++ ++ return SLURM_ERROR; ++ } ++ ++ return SLURM_SUCCESS; ++} ++ ++static char* _handle_statement_in_ending(char** query_ptr, char** correct_query_ptr, char* ending, ++ char* statement_head, char* object_end, char* old_object) ++{ ++ char* temp = NULL; ++ if ((temp = xstrcasestr(ending, statement_head))) { ++ ++ // get complete statement ++ int statement_len = 0; ++ while(temp[statement_len++] != ';') { ++ // do nothing ++ } ++ char* statement = xstrndup(temp, statement_len); ++ ++ if (xstrcasestr(statement, "INDEX")) { ++ // get object name ++ xassert(statement_head && object_end); ++ int start_pos = strlen(statement_head) - 1; ++ int end_pos = xstrcasestr(temp, object_end) - temp - 1; ++ char* new_object = xstrndup(temp + start_pos, end_pos - start_pos + 1); ++ debug4("new_object_name %s old_object_name %s", new_object, old_object); ++ if (old_object) { ++ xstrfmtcat(*query_ptr, "DROP INDEX %s;", old_object); ++ } ++ xstrfmtcat(*correct_query_ptr, "DROP INDEX %s;", new_object); ++ xfree(new_object); ++ } ++// else if (temp2 = xstrcasestr(ending, "SEQUENCE")) { ++// // get object name ++// xassert(statement_head && object_end); ++// int start_pos = strlen(statement_head) - 1; ++// int end_pos = xstrcasestr(temp, object_end) - temp - 1; ++// char* new_object = xstrndup(temp + start_pos, end_pos - start_pos + 1); ++// debug4("object_name %s", new_object); ++// xfree(new_object); ++// //xstrfmtcat(query, "DROP SEQUENCE %s;", new_object); ++// //xstrfmtcat(correct_query, "DROP SEQUENCE %s;", new_object); ++// } ++ ++ // add to query ++ xstrcat(*query_ptr, statement); ++ xstrcat(*correct_query_ptr, statement); ++ ++ xfree(statement); ++ ++ return temp + statement_len; // remaining statement ++ } else { ++ return NULL; ++ } ++ ++} ++ ++/* NOTE: Ensure that pgsql_conn->lock is NOT set on function entry */ ++static int _pgsql_make_table_current(pgsql_conn_t *pgsql_conn, char *table_name, ++ storage_field_t *fields, char *ending) ++{ ++ char *query = NULL; ++ char *correct_query = NULL; ++ pgsql_res_t* result = NULL; ++ pgsql_row row; ++ int i = 0; ++ List columns = NULL; ++ ListIterator itr = NULL; ++ char *col = NULL; ++ bool adding = false; ++ int run_update = 0; ++ char *primary_key = NULL; ++ char *old_primary = NULL; ++ char *old_index = NULL; ++ char *temp = NULL, *temp2 = NULL; ++ List keys_list = NULL; ++ db_key_t *db_key = NULL; ++ ++ debug4("%s table_name %s", __func__, table_name); ++ DEF_TIMERS; ++ ++ /* figure out the unique index in the table */ ++ query = xstrdup_printf("SELECT \ ++ i.relname AS index_name, \ ++ a.attname AS column_name, \ ++ CASE WHEN c.contype = 'p' THEN 'primary' ELSE '' END AS is_primary \ ++ FROM \ ++ pg_class t \ ++ JOIN pg_index ix ON t.oid = ix.indrelid \ ++ JOIN pg_class i ON i.oid = ix.indexrelid \ ++ JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey) \ ++ LEFT JOIN pg_constraint c ON c.conname = i.relname AND c.contype = 'p' \ ++ WHERE \ ++ t.relname = '%s' \ ++ AND ix.indisunique = true;", ++ table_name); ++ debug4("figure out the unique keys in the table %s. query:\n %s", table_name, query); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ xfree(query); ++ ++ // index_name|column_name|is_primary row[0] is the index name ++ while ((row = pgsql_fetch_row(result))) { ++ if (!xstrcasecmp(row[2], "primary")) { ++ xassert(!old_primary); ++ old_primary = xstrdup(row[0]); ++ debug4("%s old_primary %s", table_name, old_primary); ++ } else if (!old_index) { ++ old_index = xstrdup(row[0]); ++ } ++ } ++ pgsql_free_result(&result); ++ ++ /* figure out the non-unique keys in the table */ ++ query = xstrdup_printf("SELECT \ ++ i.relname AS index_name, \ ++ a.attname AS column_name, \ ++ CASE WHEN c.contype = 'p' THEN 'primary' ELSE '' END AS is_primary \ ++ FROM \ ++ pg_class t \ ++ JOIN pg_index ix ON t.oid = ix.indrelid \ ++ JOIN pg_class i ON i.oid = ix.indexrelid \ ++ JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey) \ ++ LEFT JOIN pg_constraint c ON c.conname = i.relname AND c.contype = 'p' \ ++ WHERE \ ++ t.relname = '%s' \ ++ AND ix.indisunique = false;", ++ table_name); ++ debug4("figure out the non-unique keys in the table %s. query:\n %s", table_name, query); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ xfree(old_primary); ++ xfree(old_index); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ itr = NULL; ++ keys_list = list_create(_destroy_db_key); ++ while ((row = pgsql_fetch_row(result))) { ++ // index_name|column_name|is_primary ++ if (!itr) ++ itr = list_iterator_create(keys_list); ++ else ++ list_iterator_reset(itr); ++ while ((db_key = list_next(itr))) { ++ if (!xstrcmp(db_key->name, row[0])) ++ break; ++ } ++ ++ if (db_key) { ++ xstrfmtcat(db_key->columns, ", %s", row[2]); ++ } else { ++ db_key = xmalloc(sizeof(db_key_t)); ++ db_key->name = xstrdup(row[0]); // name ++ db_key->columns = xstrdup(row[2]); // column name ++ list_append(keys_list, db_key); // don't use list_push ++ } ++ } ++ pgsql_free_result(&result); ++ ++ if (itr) { ++ list_iterator_destroy(itr); ++ itr = NULL; ++ } ++ ++ /* figure out the existing columns in the table */ ++ query = xstrdup_printf("SELECT column_name FROM information_schema.columns \ ++ WHERE table_name = '%s';", table_name); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ xfree(old_primary); ++ xfree(old_index); ++ pgsql_free_result(&result); ++ FREE_NULL_LIST(keys_list); ++ return SLURM_ERROR; ++ } ++ ++ xfree(query); ++ columns = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) { ++ col = xstrdup(row[0]); // column name ++ debug4("found column %s", col); ++ list_append(columns, col); ++ } ++ pgsql_free_result(&result); ++ ++ /* ++ * `query` is compared against the current table_defs_table.definition ++ * and run if they are different. `correct_query` is inserted into the ++ * table, so it must be what future `query` schemas will be. ++ * In other words, `query` transitions the table to the new schema, ++ * `correct_query` represents the new schema ++ */ ++ itr = list_iterator_create(columns); ++// query = xstrdup_printf(""); ++// correct_query = xstrdup(query); ++ START_TIMER; ++ while (fields[i].name) { ++ debug4("process field %s", fields[i].name); ++ bool exist = false; ++ list_iterator_reset(itr); ++ // update exist column ++ while ((col = list_next(itr))) { ++ debug4("process column %s", col); ++ if (!xstrcmp(col, fields[i].name)) { ++ exist = 1; ++ // update column type ++ if (!strncmp(fields[i].type, "serial", 6)) { ++ xstrfmtcat(query, "ALTER TABLE %s ALTER COLUMN \"%s\" TYPE integer;", ++ table_name, fields[i].name, fields[i].type); ++ xstrfmtcat(correct_query, "ALTER TABLE %s ALTER COLUMN \"%s\" TYPE integer;", ++ table_name, fields[i].name, fields[i].type); ++ ++ xstrfmtcat(query, "CREATE SEQUENCE IF NOT EXISTS %s_%s_seq OWNED BY %s.%s;", ++ table_name, fields[i].name, table_name, fields[i].name); ++ xstrfmtcat(correct_query, "CREATE SEQUENCE IF NOT EXISTS %s_%s_seq OWNED BY %s.%s;", ++ table_name, fields[i].name, table_name, fields[i].name); ++ ++ xstrfmtcat(query, "ALTER TABLE %s ALTER COLUMN \"%s\" SET DEFAULT nextval('%s_%s_seq');", ++ table_name, fields[i].name, table_name, fields[i].name); ++ xstrfmtcat(correct_query, "ALTER TABLE %s ALTER COLUMN \"%s\" SET DEFAULT nextval('%s_%s_seq');", ++ table_name, fields[i].name, table_name, fields[i].name); ++ } else { ++ xstrfmtcat(query, "ALTER TABLE %s ALTER COLUMN \"%s\" TYPE %s;", ++ table_name, fields[i].name, fields[i].type); ++ xstrfmtcat(correct_query, "ALTER TABLE %s ALTER COLUMN \"%s\" TYPE %s;", ++ table_name, fields[i].name, fields[i].type); ++ } ++ ++ // update column options ++ char* val_str = NULL; ++ char* column_options = xstrdup(fields[i].options); ++ char* token = strtok_r(column_options, "|", &val_str); ++ while (token) { ++ xstrfmtcat(query, "ALTER TABLE %s ALTER COLUMN \"%s\" SET %s;", ++ table_name, fields[i].name, token); ++ xstrfmtcat(correct_query, "ALTER TABLE %s ALTER COLUMN \"%s\" SET %s;", ++ table_name, fields[i].name, token); ++ ++ token = strtok_r(NULL, "|", &val_str); ++ } ++ ++ list_delete_item(itr); ++ xfree(column_options); ++ break; ++ } ++ } ++ ++ // add new column ++ if (!exist) { ++ info("adding column %s in table %s", fields[i].name, table_name); ++ ++ if (!strncmp(fields[i].type, "serial", 6)) { ++ xstrfmtcat(query, "ALTER TABLE %s ADD %s TYPE integer;", ++ table_name, fields[i].name, fields[i].type); ++ xstrfmtcat(correct_query, "ALTER TABLE %s ALTER COLUMN \"%s\" TYPE integer;", ++ table_name, fields[i].name, fields[i].type); ++ ++ xstrfmtcat(query, "CREATE SEQUENCE IF NOT EXISTS %s_%s_seq OWNED BY %s.%s;", ++ table_name, fields[i].name, table_name, fields[i].name); ++ xstrfmtcat(correct_query, "CREATE SEQUENCE IF NOT EXISTS %s_%s_seq OWNED BY %s.%s;", ++ table_name, fields[i].name, table_name, fields[i].name); ++ ++ xstrfmtcat(query, "ALTER TABLE %s ALTER COLUMN \"%s\" SET DEFAULT nextval('%s_%s_seq');", ++ table_name, fields[i].name, table_name, fields[i].name); ++ xstrfmtcat(correct_query, "ALTER TABLE %s ALTER COLUMN \"%s\" SET DEFAULT nextval('%s_%s_seq');", ++ table_name, fields[i].name, table_name, fields[i].name); ++ ++ } else { ++ // define column type ++ xstrfmtcat(query, "ALTER TABLE %s ADD %s %s;", ++ table_name, fields[i].name, fields[i].type); ++ xstrfmtcat(correct_query, "ALTER TABLE %s ALTER COLUMN \"%s\" TYPE %s;", ++ table_name, fields[i].name, fields[i].type); ++ } ++ ++ // set column options ++ char* val_str = NULL; ++ char* column_options = xstrdup(fields[i].options); ++ char* token = strtok_r(column_options, "|", &val_str); ++ while (token) { ++ xstrfmtcat(query, "ALTER TABLE %s ALTER COLUMN \"%s\" SET %s;", ++ table_name, fields[i].name, token); ++ xstrfmtcat(correct_query, "ALTER TABLE %s ALTER COLUMN \"%s\" SET %s;", ++ table_name, fields[i].name, token); ++ ++ token = strtok_r(NULL, "|", &val_str); ++ } ++ ++ adding = true; ++ xfree(column_options); ++ } ++ ++ i++; ++ } ++ ++ // drop deprecated column ++ list_iterator_reset(itr); ++ while ((col = list_next(itr))) { ++ adding = true; ++ info("dropping deprecated column %s from table %s", col, table_name); ++ xstrfmtcat(query, "ALTER TABLE %s DROP %s;", table_name, col); ++ } ++ list_iterator_destroy(itr); ++ FREE_NULL_LIST(columns); ++ ++ // process primary key ++ if ((temp = xstrcasestr(ending, "PRIMARY KEY"))) { ++ int open = 0, close =0; ++ int end = 0; ++ while (temp[end++]) { ++ if (temp[end] == '(') ++ open++; ++ else if (temp[end] == ')') ++ close++; ++ else ++ continue; ++ if (open == close) ++ break; ++ } ++ if (temp[end]) { ++ primary_key = xstrndup(temp, end + 1); ++ if (old_primary) { ++ xstrfmtcat(query, "ALTER TABLE %s DROP CONSTRAINT %s;", ++ table_name, old_primary); ++ } ++ xstrfmtcat(correct_query, "ALTER TABLE %s DROP CONSTRAINT %s;", ++ table_name, primary_key); ++ ++ xstrfmtcat(query, "ALTER TABLE %s ADD %s;", table_name, primary_key); ++ xstrfmtcat(correct_query, "ALTER TABLE %s ADD %s;", table_name, primary_key); ++ ++ xfree(primary_key); ++ } ++ ++ } ++ xfree(old_primary); ++ ++ // process unique index ++ _handle_statement_in_ending(&query, &correct_query, ending, ++ "CREATE UNIQUE INDEX IF NOT EXISTS ", " ON", old_index); ++ xfree(old_index); ++ ++// // process unique index ++// if ((temp = xstrcasestr(ending, " UNIQUE "))) { ++// // "unique_name UNIQUE", get unique_name pos ++// int end_pos = temp - ending - 2; ++// while (end_pos > 0 && ending[end_pos]) { ++// if (ending[end_pos] != ' ') { ++// break; ++// } ++// end_pos--; ++// } ++// ++// // find start pos ++// int start_pos = end_pos; ++// while (start_pos > 0 && ending[start_pos]) { ++// if (ending[start_pos] == ' ') { ++// start_pos++; ++// break; ++// } ++// start_pos--; ++// } ++// char *unique_name = xstrndup(ending[start_pos], end_pos - start_pos + 1); ++// ++// int open = 0, close = 0; ++// /* sizeof includes NULL, and should start 1 back */ ++// int expression_len = sizeof(" UNIQUE ") - 2; ++// while (temp[expression_len++]) { ++// /* find the end of the parenthetical expression */ ++// if (temp[expression_len] == '(') ++// open++; ++// else if (temp[expression_len] == ')') ++// close++; ++// else ++// continue; ++// if (open == close) ++// break; ++// } ++// ++// debug4("unique_name %s, unique_expression: %s", unique_name, unique_expression); ++// if (temp[expression_len]) { ++// expression_len++; ++// unique_expression = xstrndup(temp, expression_len); ++// if (old_index) ++// xstrfmtcat(query, "ALTER TABLE %s DROP CONSTRAINT %s;", ++// table_name, old_index); ++// xstrfmtcat(correct_query, "ALTER TABLE %s DROP CONSTRAINT %s;", ++// table_name, unique_name); ++// ++// xstrfmtcat(query, "ALTER TABLE %s ADD CONSTRAINT %s", table_name, unique_expression); ++// xstrfmtcat(correct_query, "ALTER TABLE %s ADD CONSTRAINT %s", table_name, unique_expression); ++// ++// xfree(unique_expression); ++// } ++// xfree(unique_name); ++// } ++// xfree(old_index); ++ ++ // drop old index ++ itr = list_iterator_create(keys_list); ++ while ((db_key = list_next(itr))) { ++ info("dropping key %s from table %s", db_key->name, table_name); ++ xstrfmtcat(query, "DROP INDEX %s;", db_key->name); ++ } ++ list_iterator_destroy(itr); ++ FREE_NULL_LIST(keys_list); ++ ++ // process non unique index ++ temp2 = ending; ++ while ((temp2 = _handle_statement_in_ending(&query, &correct_query, ++ temp2, "CREATE INDEX IF NOT EXISTS ", " ON", NULL))) { ++ // do nothing ++ } ++ ++// while ((temp = xstrcasestr(temp2, "CREATE INDEX "))) { ++// // "CREATE INDEX index_name ON ...", get index_name pos ++// int start_pos = temp + sizeof("CREATE INDEX ") - 1; ++// int end_pos = xstrcasestr(temp2, "ON") - temp - 2; ++// char* new_index = xstrndup(temp + start_pos, end_pos - start_pos + 1); ++// ++// int open = 0, close = 0; ++// /* sizeof includes NULL, and should start 1 back */ ++// int expression_len = sizeof("CREATE INDEX ") - 2; ++// while (temp[expression_len++]) { ++// /* find the end of the parenthetical expression */ ++// if (temp[expression_len] == '(') ++// open++; ++// else if (temp[expression_len] == ')') ++// close++; ++// else ++// continue; ++// if (open == close) ++// break; ++// } ++// if (temp[expression_len]) { ++// expression_len++; ++// char* index_expression = xstrndup(temp, expression_len); ++// while ((db_key = list_next(itr))) { ++// if (!xstrcmp(db_key->name, new_index)) { ++// list_remove(itr); ++// break; ++// } ++// } ++// list_iterator_reset(itr); ++// if (db_key) { ++// xstrfmtcat(query, "ALTER TABLE %s DROP CONSTRAINT %s;", table_name, db_key->name); ++// _destroy_db_key(db_key); ++// } else { ++// info("adding %s to table %s", new_index, table_name); ++// } ++// xstrfmtcat(correct_query, "ALTER TABLE %s DROP CONSTRAINT %s;", table_name, new_index); ++// ++// xstrfmtcat(query, "%s;", index_expression); ++// xstrfmtcat(correct_query, "%s;", index_expression); ++// ++// xfree(index_expression); ++// xfree(new_index); ++// } ++// temp2 = temp + expression_len; ++// } ++ ++ // process sequence ++ temp2 = ending; ++ while ((temp2 = _handle_statement_in_ending(&query, &correct_query, temp2, ++ "CREATE SEQUENCE IF NOT EXISTS ", NULL, NULL))) { ++ // do nothing ++ } ++ ++ // process ALTER, The ALTER statement must all be at the end of ending ++ temp2 = ending; ++ while ((temp2 = _handle_statement_in_ending(&query, &correct_query, temp2, ++ "ALTER TABLE ", NULL, NULL))) { ++ // do nothing ++ } ++ ++ debug4("%s query\n%s", __func__, query); ++ ++ /* see if we have already done this definition */ ++ if (!adding) { ++ char *quoted = _pgsql_handle_quotes(query); ++ char *query2 = xstrdup_printf("select table_name from " ++ "%s where definition='%s';", ++ table_defs_table, quoted); ++ pgsql_res_t* result = NULL; ++ pgsql_row row; ++ ++ xfree(quoted); ++ run_update = 1; ++ ++ result = pgsql_db_query_ret(pgsql_conn, query2, 0); ++ if (pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ if ((row = pgsql_fetch_row(result))) { ++ run_update = 0; ++ } ++ } ++ pgsql_free_result(&result); ++ xfree(query2); ++ ++ if (run_update) { ++ run_update = 2; ++ query2 = xstrdup_printf("select table_name from " ++ "%s where table_name='%s';", ++ table_defs_table, table_name); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query2, 0); ++ if (pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ if ((row = pgsql_fetch_row(result))) { ++ run_update = 1; ++ } ++ } ++ ++ pgsql_free_result(&result); ++ xfree(query2); ++ } ++ } ++ ++ /* if something has changed run the alter line */ ++ if (run_update || adding) { ++ time_t now = time(NULL); ++ char *query2 = NULL; ++ char *quoted = NULL; ++ ++ if (run_update == 2) ++ debug4("Table %s doesn't exist, adding", table_name); ++ else ++ debug("Table %s has changed. Updating...", table_name); ++ if (pgsql_db_query(pgsql_conn, query)) { ++ xfree(query); ++ return SLURM_ERROR; ++ } ++ quoted = _pgsql_handle_quotes(correct_query); ++ query2 = xstrdup_printf("INSERT INTO %s (creation_time, " ++ "mod_time, table_name, definition) " ++ "VALUES (%ld, %ld, '%s', '%s') " ++ "ON CONFLICT (table_name) DO UPDATE SET " ++ "definition='%s', mod_time=%ld;", ++ table_defs_table, now, now, ++ table_name, quoted, ++ quoted, now); ++ xfree(quoted); ++ if (pgsql_db_query(pgsql_conn, query2)) { ++ xfree(query2); ++ return SLURM_ERROR; ++ } ++ xfree(query2); ++ } ++ ++ xfree(query); ++ xfree(correct_query); ++ query = xstrdup_printf("make table current %s", table_name); ++ END_TIMER2(query); ++ xfree(query); ++ return SLURM_SUCCESS; ++} ++ ++/* NOTE: Ensure that pgsql_conn->lock is set on function entry */ ++static int _create_db(char *db_name, pgsql_db_info_t *db_info) ++{ ++ int rc = SLURM_ERROR; ++ char *db_host = NULL; ++ SQLHENV db_env; ++ SQLHDBC db_conn; ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ ++ rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &db_env); ++ if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) { ++ fatal("Error allocate environment handle %d", rc); ++ return SLURM_ERROR; ++ } ++ ++ rc = SQLSetEnvAttr(db_env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); ++ if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) ++ { ++ fatal("Error SetEnv"); ++ SQLFreeHandle(SQL_HANDLE_ENV, db_env); ++ return SLURM_ERROR; ++ } ++ ++ rc = SQLAllocHandle(SQL_HANDLE_DBC, db_env, &db_conn); ++ if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) { ++ fatal("Error allocate connection handle %d", rc); ++ SQLFreeHandle(SQL_HANDLE_ENV, db_env); ++ return SLURM_ERROR; ++ } ++ ++ db_host = "seasql"; ++ rc = SQLConnect(db_conn, (SQLCHAR*)"seasql", SQL_NTS, ++ (SQLCHAR*)db_info->user, SQL_NTS, ++ (SQLCHAR*)db_info->pass, SQL_NTS); ++ if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO) && db_info->backup) { ++ info("Connection failed to host = %s user = %s port = %u, backup host = %s", ++ db_host, db_info->user, db_info->port, db_info->backup); ++ db_host = db_info->backup; ++ rc = SQLConnect(db_conn, (SQLCHAR*)db_host, SQL_NTS, ++ (SQLCHAR*)db_info->user, SQL_NTS, ++ (SQLCHAR*)db_info->pass, SQL_NTS); ++ } ++ ++ if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) { ++ PGSQL_HANDLE_ERROR("SQLConnect", rc, SQL_HANDLE_DBC, db_conn, state); ++ SQLFreeHandle(SQL_HANDLE_DBC, db_conn); ++ SQLFreeHandle(SQL_HANDLE_ENV, db_env); ++ return SLURM_ERROR; ++ } else { ++ char *create_line = xstrdup_printf("create database %s;", db_name); ++ SQLHSTMT stmt; ++ SQLAllocHandle(SQL_HANDLE_STMT, db_conn, &stmt); ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ rc = _pgsql_query_internal(db_conn, create_line, stmt, state); ++ pgsql_db_free_statement(&stmt); ++ SQLFreeHandle(SQL_HANDLE_DBC, db_conn); ++ SQLFreeHandle(SQL_HANDLE_ENV, db_env); ++ xfree(create_line); ++ return SLURM_SUCCESS; ++ } ++ ++ return rc; ++} ++ ++extern pgsql_conn_t *create_pgsql_conn(int conn_num, bool rollback, ++ char *cluster_name) ++{ ++ pgsql_conn_t *pgsql_conn = xmalloc(sizeof(pgsql_conn_t)); ++ ++ pgsql_conn->rollback = rollback; ++ pgsql_conn->conn = conn_num; ++ pgsql_conn->cluster_name = xstrdup(cluster_name); ++ slurm_mutex_init(&pgsql_conn->lock); ++ pgsql_conn->update_list = list_create(slurmdb_destroy_update_object); ++ pgsql_conn->db_conn = SQL_NULL_HDBC; ++ ++ return pgsql_conn; ++} ++ ++extern int destroy_pgsql_conn(pgsql_conn_t *pgsql_conn) ++{ ++ if (pgsql_conn) { ++ pgsql_db_close_db_connection(pgsql_conn); ++ xfree(pgsql_conn->pre_commit_query); ++ xfree(pgsql_conn->cluster_name); ++ slurm_mutex_destroy(&pgsql_conn->lock); ++ FREE_NULL_LIST(pgsql_conn->update_list); ++ xfree(pgsql_conn); ++ } ++ ++ return SLURM_SUCCESS; ++} ++ ++extern pgsql_db_info_t *create_pgsql_db_info(slurm_pgsql_plugin_type_t type) ++{ ++ pgsql_db_info_t *db_info = xmalloc(sizeof(pgsql_db_info_t)); ++ ++ switch (type) { ++ case SLURM_PGSQL_PLUGIN_AS: ++ db_info->port = slurm_conf.accounting_storage_port; ++ db_info->host = xstrdup(slurm_conf.accounting_storage_host); ++ db_info->backup = ++ xstrdup(slurm_conf.accounting_storage_backup_host); ++ db_info->user = xstrdup(slurm_conf.accounting_storage_user); ++ db_info->pass = xstrdup(slurm_conf.accounting_storage_pass); ++ db_info->params = xstrdup(slurm_conf.accounting_storage_params); ++ break; ++ case SLURM_PGSQL_PLUGIN_JC: ++ if (!slurm_conf.job_comp_port) ++ slurm_conf.job_comp_port = DEFAULT_DB_PORT; ++ db_info->port = slurm_conf.job_comp_port; ++ db_info->host = xstrdup(slurm_conf.job_comp_host); ++ db_info->user = xstrdup(slurm_conf.job_comp_user); ++ db_info->pass = xstrdup(slurm_conf.job_comp_pass); ++ db_info->params = xstrdup(slurm_conf.accounting_storage_params); ++ break; ++ default: ++ xfree(db_info); ++ fatal("Unknown pgsql_db_info %d", type); ++ return NULL; ++ } ++ ++ // allocate ODBC Environment handle and register version ++ long ret = 0; ++ ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &db_info->odbc_env); ++ if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) ++ { ++ fatal("Error AllocHandle"); ++ return NULL; ++ } ++ ret = SQLSetEnvAttr(db_info->odbc_env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); ++ if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) ++ { ++ fatal("Error SetEnv"); ++ SQLFreeHandle(SQL_HANDLE_ENV, db_info->odbc_env); ++ return NULL; ++ } ++ return db_info; ++} ++ ++extern int destroy_pgsql_db_info(pgsql_db_info_t *db_info) ++{ ++ if (db_info) { ++ SQLFreeHandle(SQL_HANDLE_ENV, db_info->odbc_env); ++ xfree(db_info->backup); ++ xfree(db_info->host); ++ xfree(db_info->user); ++ xfree(db_info->pass); ++ xfree(db_info); ++ } ++ return SLURM_SUCCESS; ++} ++ ++extern int pgsql_db_get_db_connection(pgsql_conn_t *pgsql_conn, char *db_name, ++ pgsql_db_info_t *db_info) ++{ ++ SQLRETURN rc = SLURM_SUCCESS; ++ bool storage_init = false; ++ char *db_host = db_info->host; ++ unsigned int my_timeout = 30; ++ ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ ++ xassert(pgsql_conn); ++ ++ slurm_mutex_lock(&pgsql_conn->lock); ++ ++ // allocate connection handle, set timeout ++ rc = SQLAllocHandle(SQL_HANDLE_DBC, db_info->odbc_env, &pgsql_conn->db_conn); ++ if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) { ++ slurm_mutex_unlock(&pgsql_conn->lock); ++ fatal("Error allocate connection handle %d", rc); ++ return rc; ++ } ++ SQLSetConnectAttr(pgsql_conn->db_conn, SQL_LOGIN_TIMEOUT, (SQLPOINTER*)5, 0); ++ SQLSetConnectAttr(pgsql_conn->db_conn, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER)&my_timeout, 0); ++ if (pgsql_conn->rollback) { ++ //SQLSetConnectAttr(pgsql_conn->db_conn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, 0); ++ } ++ ++ while (!storage_init) { ++ debug2("Attempting to connect to %s:%d", db_host, db_info->port); ++ ++ // Connect to DB ++ rc = SQLConnect(pgsql_conn->db_conn, (SQLCHAR*)db_host, SQL_NTS, ++ (SQLCHAR*)db_info->user, SQL_NTS, ++ (SQLCHAR*)db_info->pass, SQL_NTS); ++ if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) { ++ PGSQL_HANDLE_ERROR("pgsql_db_get_db_connection SQLConnect", rc, SQL_HANDLE_DBC, pgsql_conn->db_conn, state); ++ ++ // check database ++ if (pgsql_db_match_state((char*)state, PGSQL_UNABLE_ESTABLISH)) { ++ debug("Database %s not created. Creating", db_name); ++ rc = _create_db(db_name, db_info); ++ continue; ++ } ++ ++ if ((db_host == db_info->host) && db_info->backup) { ++ debug2("Try to connect backup DB"); ++ db_host = db_info->backup; ++ continue; ++ } ++ ++ rc = ESLURM_DB_CONNECTION; ++ SQLFreeHandle(SQL_HANDLE_DBC, pgsql_conn->db_conn); ++ pgsql_conn->db_conn = NULL; ++ break; ++ } ++ ++ storage_init = true; ++ } ++ slurm_mutex_unlock(&pgsql_conn->lock); ++ ++ if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO)) { ++ rc = SLURM_SUCCESS; ++ } ++ errno = rc; ++ return rc; ++} ++ ++extern int pgsql_db_close_db_connection(pgsql_conn_t *pgsql_conn) ++{ ++ slurm_mutex_lock(&pgsql_conn->lock); ++ if (pgsql_conn && pgsql_conn->db_conn) { ++ SQLDisconnect(pgsql_conn->db_conn); ++ SQLFreeHandle(SQL_HANDLE_DBC, pgsql_conn->db_conn); ++ pgsql_conn->db_conn = NULL; ++ } ++ slurm_mutex_unlock(&pgsql_conn->lock); ++ return SLURM_SUCCESS; ++} ++ ++extern int pgsql_db_query(pgsql_conn_t *pgsql_conn, char *query) ++{ ++ int rc = SLURM_SUCCESS; ++ ++ if (!pgsql_conn || !pgsql_conn->db_conn) { ++ fatal("You haven't inited this storage yet."); ++ return 0; /* For CLANG false positive */ ++ } ++ ++ slurm_mutex_lock(&pgsql_conn->lock); ++ ++ SQLHSTMT stmt; ++ SQLAllocHandle(SQL_HANDLE_STMT, pgsql_conn->db_conn, &stmt); ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ debug4("exe SQL:\n %s", query); ++ rc = _pgsql_query_internal(pgsql_conn->db_conn, query, stmt, state); ++ pgsql_db_free_statement(&stmt); ++ ++ slurm_mutex_unlock(&pgsql_conn->lock); ++ ++ return rc; ++} ++ ++/* ++ * Executes a single sql. ++ * Returns the number of affected rows, <0 for failure. ++ */ ++extern long pgsql_db_sql_affected_rows(pgsql_conn_t *pgsql_conn, char *query) ++{ ++ SQLRETURN rc = SLURM_SUCCESS; ++ ++ if (!pgsql_conn || !pgsql_conn->db_conn) { ++ fatal("You haven't inited this storage yet."); ++ return 0; /* For CLANG false positive */ ++ } ++ ++ SQLHSTMT stmt; ++ SQLLEN affected_rows; ++ slurm_mutex_lock(&pgsql_conn->lock); ++ ++ SQLAllocHandle(SQL_HANDLE_STMT, pgsql_conn->db_conn, &stmt); ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ if (SLURM_SUCCESS == _pgsql_query_internal(pgsql_conn->db_conn, query, stmt, state)) ++ rc = SQLRowCount(stmt, &affected_rows); ++ pgsql_db_free_statement(&stmt); ++ ++ slurm_mutex_unlock(&pgsql_conn->lock); ++ ++ if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) { ++ return SLURM_ERROR; ++ } else { ++ return affected_rows; ++ } ++} ++ ++extern int pgsql_db_ping(pgsql_conn_t *pgsql_conn) ++{ ++ if (!pgsql_conn->db_conn) ++ return SLURM_ERROR; ++ ++ SQLUINTEGER status = SQL_CD_FALSE; ++ ++ slurm_mutex_lock(&pgsql_conn->lock); ++ SQLGetConnectAttr(pgsql_conn->db_conn, SQL_ATTR_CONNECTION_DEAD, &status, sizeof(status), NULL); ++ slurm_mutex_unlock(&pgsql_conn->lock); ++ if (status == SQL_CD_TRUE) { ++ return SLURM_ERROR; // the connection has been lost ++ } else { ++ return SLURM_SUCCESS; ++ } ++ ++} ++ ++extern int pgsql_db_commit(pgsql_conn_t *pgsql_conn) ++{ ++ SQLRETURN rc = SLURM_SUCCESS; ++ ++ if (!pgsql_conn->db_conn) ++ return SLURM_ERROR; ++ ++ slurm_mutex_lock(&pgsql_conn->lock); ++ ++ rc = SQLEndTran(SQL_HANDLE_DBC, pgsql_conn->db_conn, SQL_COMMIT); ++ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ PGSQL_HANDLE_ERROR("SQLTransact", rc, SQL_HANDLE_DBC, pgsql_conn->db_conn, state); ++ rc = SLURM_ERROR; ++ } else { ++ rc = SLURM_SUCCESS; ++ } ++ slurm_mutex_unlock(&pgsql_conn->lock); ++ ++ return rc; ++} ++ ++extern int pgsql_db_rollback(pgsql_conn_t *pgsql_conn) ++{ ++ SQLRETURN rc = SLURM_SUCCESS; ++ ++ if (!pgsql_conn->db_conn) ++ return SLURM_ERROR; ++ ++ slurm_mutex_lock(&pgsql_conn->lock); ++ ++ rc = SQLEndTran(SQL_HANDLE_DBC, pgsql_conn->db_conn, SQL_ROLLBACK); ++ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ PGSQL_HANDLE_ERROR("SQLTransact", rc, SQL_HANDLE_DBC, pgsql_conn->db_conn, state); ++ rc = SLURM_ERROR; ++ } else { ++ rc = SLURM_SUCCESS; ++ } ++ slurm_mutex_unlock(&pgsql_conn->lock); ++ ++ return rc; ++} ++ ++ ++/* NOTE: return pgsql_res_t and caller must free */ ++extern pgsql_res_t* pgsql_db_query_ret(pgsql_conn_t *pgsql_conn, ++ char *query, bool last) ++{ ++ pgsql_res_t* result = NULL; ++ slurm_mutex_lock(&pgsql_conn->lock); ++ ++ _init_pgsql_res(&result, pgsql_conn); ++ if(result == NULL){ ++ goto fini; ++ } ++ ++ if (SLURM_SUCCESS == _pgsql_query_internal(pgsql_conn->db_conn, query, result->stmt, (SQLCHAR*)(result->state))) { ++ if (last) { ++ _get_last_result(result->stmt); ++ debug4("_get_last_result for query:\n %s", query); ++ } ++ ++ SQLNumResultCols(result->stmt, &result->sNumCols); ++ SQLRowCount(result->stmt, &result->sNumRows); ++ if (result->sNumRows) { ++ result->rows = (pgsql_row*)malloc(result->sNumRows * sizeof(pgsql_row)); ++ if(result->rows == NULL) { ++ pgsql_free_result(&result); ++ goto fini; ++ } ++ for(int i = 0; i < result->sNumRows; i++) { ++ result->rows[i] = NULL; ++ } ++ } ++ } ++ ++fini: ++ slurm_mutex_unlock(&pgsql_conn->lock); ++ ++ return result; ++} ++ ++extern int pgsql_db_query_check_after(pgsql_conn_t *pgsql_conn, char *query) ++{ ++ int rc = SLURM_SUCCESS; ++ SQLHSTMT stmt; ++ ++ slurm_mutex_lock(&pgsql_conn->lock); ++ ++ SQLAllocHandle(SQL_HANDLE_STMT, pgsql_conn->db_conn, &stmt); ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ rc = _pgsql_query_internal(pgsql_conn->db_conn, query, stmt, state); ++ pgsql_db_free_statement(&stmt); ++ ++ slurm_mutex_unlock(&pgsql_conn->lock); ++ ++ return rc; ++} ++ ++extern uint64_t pgsql_db_insert_ret_id(pgsql_conn_t *pgsql_conn, char *query) ++{ ++ SQLINTEGER new_id = 0; ++ SQLINTEGER len; ++ SQLHSTMT stmt; ++ SQLRETURN rc = 0; ++ ++ slurm_mutex_lock(&pgsql_conn->lock); ++ SQLAllocHandle(SQL_HANDLE_STMT, pgsql_conn->db_conn, &stmt); ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ if (SLURM_SUCCESS == _pgsql_query_internal(pgsql_conn->db_conn, query, stmt, state)) { ++ rc = SQLGetStmtAttr(stmt, SQL_DESC_AUTO_UNIQUE_VALUE, &new_id, sizeof(new_id), &len); ++ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ PGSQL_HANDLE_ERROR("SQLGetStmtAttr", rc, SQL_HANDLE_STMT, stmt, state); ++ new_id = 0; ++ } ++ } ++ pgsql_db_free_statement(&stmt); ++ slurm_mutex_unlock(&pgsql_conn->lock); ++ ++ return new_id; ++ ++} ++ ++/* NOTE: The caller must free stmt_ptr */ ++extern int pgsql_db_query_stmt(pgsql_conn_t *pgsql_conn, ++ char *query, SQLHSTMT* stmt_ptr) ++{ ++ SQLRETURN rc = 0; ++ slurm_mutex_lock(&pgsql_conn->lock); ++ ++ SQLAllocHandle(SQL_HANDLE_STMT, pgsql_conn->db_conn, stmt_ptr); ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ if (SLURM_SUCCESS == _pgsql_query_internal(pgsql_conn->db_conn, query, *stmt_ptr, state)) { ++ rc = SLURM_SUCCESS; // return success and caller must free result ++ } else { ++ rc = SLURM_ERROR; // return error and result is NULL ++ } ++ ++ slurm_mutex_unlock(&pgsql_conn->lock); ++ ++ return rc; ++} ++ ++extern int pgsql_db_create_table(pgsql_conn_t *pgsql_conn, char *table_name, ++ storage_field_t *fields, char *ending) ++{ ++ char *query = NULL; ++ int i = 0, rc; ++ storage_field_t *first_field = fields; ++ ++ if (!fields || !fields->name) { ++ error("Not creating an empty table"); ++ return SLURM_ERROR; ++ } ++ ++ /* We have an internal table called table_defs_table which ++ * contains the definition of each table in the database. To ++ * speed things up we just check against that to see if ++ * anything has changed. ++ */ ++ query = xstrdup_printf("create table if not exists %s " ++ "(creation_time integer not null, " ++ "mod_time integer default 0 not null, " ++ "table_name text not null, " ++ "definition text not null, " ++ "primary key (table_name));", ++ table_defs_table); ++ ++ if (pgsql_db_query(pgsql_conn, query) == SLURM_ERROR) { ++ xfree(query); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ char field_option[FIELD_OPTIONS_MAX_LENGTH]; ++ _parse_field_options(field_option, fields->options); ++ ++ query = xstrdup_printf("create table if not exists %s (\"%s\" %s %s", ++ table_name, fields->name, fields->type, field_option); ++ i = 1; ++ fields++; ++ ++ while (fields && fields->name) { ++ _parse_field_options(field_option, fields->options); ++ xstrfmtcat(query, ", \"%s\" %s %s", fields->name, fields->type, field_option); ++ fields++; ++ i++; ++ } ++ xstrcat(query, ending); ++ ++ if (pgsql_db_query(pgsql_conn, query) == SLURM_ERROR) { ++ xfree(query); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ rc = _pgsql_make_table_current(pgsql_conn, table_name, first_field, ending); ++ return rc; ++} ++ ++/* Let me know if the last statement had rows that were affected. ++* This only gets called by a non-threaded connection, so there is no ++* need to worry about locks. ++*/ ++ extern int last_affected_rows(SQLHSTMT stmt) { ++ SQLLEN rows; ++ SQLRETURN rc; ++ ++ _get_last_result(stmt); ++ rc = SQLRowCount(stmt, &rows); ++ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ PGSQL_HANDLE_ERROR("SQLRowCount", rc, SQL_HANDLE_STMT, stmt, state); ++ rc = SLURM_ERROR; ++ rows = 0; ++ } else { ++ rc = SLURM_SUCCESS; ++ } ++ ++ return rows; ++ } ++ ++extern bool pgsql_db_match_state(char* state, char* pg_err_code) ++{ ++ if (state) { ++ return !strncmp(state, pg_err_code, PGSQL_STATE_LEN); ++ } else { ++ return false; ++ } ++} ++ ++//extern void pgsql_db_state(SQLHSTMT stmt, char* state) ++//{ ++// SQLRETURN ret; ++// SQLCHAR err_msg[SQL_MAX_MESSAGE_LENGTH]; ++// SQLSMALLINT msg_len; ++// SQLINTEGER native_err; ++// ret = SQLGetDiagRec(SQL_HANDLE_STMT, stmt, 1, state, &native_err, err_msg, sizeof(err_msg), &msg_len); ++// if (ret == SQL_INVALID_HANDLE) { ++// memset(state, 0, PGSQL_STATE_LEN + 1); ++// } ++//} ++ ++extern int pgsql_db_free_statement(SQLHSTMT* stmt_ptr) ++{ ++ SQLRETURN rc; ++ rc = SQLFreeHandle(SQL_HANDLE_STMT, *stmt_ptr); ++ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ PGSQL_HANDLE_ERROR("SQLFreeHandle", rc, SQL_HANDLE_STMT, *stmt_ptr, state); ++ rc = SLURM_ERROR; ++ } else { ++ rc = SLURM_SUCCESS; ++ } ++ ++ *stmt_ptr = SQL_NULL_HSTMT; ++ return rc; ++ ++} ++ ++extern pgsql_row pgsql_fetch_row(pgsql_res_t* result) ++{ ++ if(result==NULL || result->stmt==NULL || result->rows==NULL || result->sNumCols<=0 || result->sNumRows<=0 ){ ++ return NULL; ++ } ++ ++ RETCODE pg_ret = SQLFetch(result->stmt); ++ SQLRETURN ret = 0; ++ debug4("%s SQLFetch ret %d", __func__, pg_ret); ++ if(pg_ret != SQL_NO_DATA){ ++ pgsql_row row = (pgsql_row)malloc(result->sNumCols * sizeof(char*)); ++ if(row == NULL){ ++ return NULL; ++ } ++ ++ for(int i=0; isNumCols; i++){ ++ PGSQL_GET_TEXT_DATA(result->stmt, i, row[i], ret); ++ } ++ ++ //bind row to result rows array ++ if(result->sRowsUsedCount >= 0 && result->sRowsUsedCount < result->sNumRows){ ++ result->rows[result->sRowsUsedCount] = row; ++ result->sRowsUsedCount++; ++ } ++ ++ return row; ++ } ++ ++ return NULL; ++} ++ ++extern void pgsql_free_result(pgsql_res_t** result_ptr) ++{ ++ xassert(result_ptr); ++ pgsql_res_t* result = *result_ptr; ++ if (!result) { ++ return ; ++ } ++ ++ if(result->rows!=NULL && result->sNumCols>0 && result->sNumRows>0){ ++ for(int i=0; isNumRows; i++){ ++ for(int j=0; jsNumCols; j++){ ++ xfree(result->rows[i][j]); ++ result->rows[i][j] = NULL; ++ } ++ if(result->rows[i]){ ++ free(result->rows[i]); ++ result->sRowsUsedCount--; ++ result->rows[i] = NULL; ++ } ++ } ++ if(result->rows){ ++ free(result->rows); ++ result->rows = NULL; ++ } ++ } ++ ++ if(result->stmt != SQL_NULL_HSTMT){ ++ pgsql_db_free_statement(&result->stmt); ++ } ++ ++ free(result); ++ *result_ptr = NULL; ++} ++ ++extern int pgsql_num_rows(pgsql_res_t* result) ++{ ++ if(result == NULL){ ++ return 0; ++ } ++ ++ return result->sNumRows; ++} ++ ++extern char* pgsql_errno(pgsql_res_t* result) ++{ ++ if (result) { ++ return result->state; ++ } else { ++ return NULL; ++ } ++} ++ ++/* return last insert id */ ++extern uint64_t pgsql_insert_id(SQLHSTMT stmt) ++{ ++ xassert(stmt); ++ SQLINTEGER last_id = 0; ++ ++ SQLRETURN rc = 0; ++ SQLINTEGER len; ++ rc = SQLGetStmtAttr(stmt, SQL_DESC_AUTO_UNIQUE_VALUE, &last_id, sizeof(last_id), &len); ++ if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { ++ SQLCHAR state[PGSQL_STATE_LEN + 1]; ++ PGSQL_HANDLE_ERROR("SQLGetStmtAttr", rc, SQL_HANDLE_STMT, stmt, state); ++ last_id = 0; ++ } ++ ++ return last_id; ++} ++ +diff --git a/src/database/pgsql_common.h b/src/database/pgsql_common.h +new file mode 100755 +index 0000000..785a3ee +--- /dev/null ++++ b/src/database/pgsql_common.h +@@ -0,0 +1,192 @@ ++/*****************************************************************************\ ++ * pgsql_common.h - common functions for the pgsql storage plugin. ++ ***************************************************************************** ++ * ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++ * ++ * This file is patterned after jobcomp_linux.c, written by Morris Jette and ++ * Copyright (C) 2002 The Regents of the University of California. ++\*****************************************************************************/ ++#ifndef _PGSQL_COMMON_H ++#define _PGSQL_COMMON_H ++ ++#include ++#include ++#include ++ ++#include "slurm/slurm_errno.h" ++#include "src/common/list.h" ++#include "src/common/xstring.h" ++ ++#include ++#include ++#include ++ ++#define PGSQL_STATE_LEN 5 ++/* PostgreSQL SQLState start*/ ++#define SUCCESSFUL_COMPLETION "00000" ++#define PGSQL_NULL_STATE "00001" ++ ++#define PGSQL_UNABLE_ESTABLISH "08001" ++#define PGSQL_DEADLOCK_DETECTED "40P01" ++#define PGSQL_UNDEFINE_TABLE "42P01" ++#define PGSQL_UNDEFINE_COLUMN "42703" ++ ++/* PostgreSQL SQLState end*/ ++ ++typedef enum { ++ SLURM_PGSQL_PLUGIN_NOTSET, ++ SLURM_PGSQL_PLUGIN_AS, /* accounting_storage */ ++ SLURM_PGSQL_PLUGIN_JC, /* jobcomp */ ++} slurm_pgsql_plugin_type_t; ++ ++typedef struct { ++ bool cluster_deleted; ++ char *cluster_name; ++ SQLHDBC db_conn; ++ pthread_mutex_t lock; ++ char *pre_commit_query; ++ bool rollback; ++ List update_list; ++ int conn; ++} pgsql_conn_t; ++ ++typedef struct { ++ char *backup; ++ uint32_t port; // TODO unixODBC看起来不需要维护该成员,考虑是否可删除 ++ char *host; ++ char *user; ++ char *params; ++ char *pass; ++ SQLHENV odbc_env; ++} pgsql_db_info_t; ++ ++typedef struct { ++ char *name; ++ char *type; ++ char* options; ++} storage_field_t; ++#define FIELD_OPTIONS_MAX_LENGTH 128 ++ ++typedef char** pgsql_row; ++ ++typedef struct { ++ SQLHSTMT stmt; ++ pgsql_row* rows; // array of pgsql row ++ SQLSMALLINT sNumCols; ++ SQLLEN sNumRows; ++ SQLLEN sRowsUsedCount; //count of used rows ++ char state[PGSQL_STATE_LEN + 1]; ++} pgsql_res_t; ++ ++#define PGSQL_HANDLE_ERROR(api_name, ret, handle_type, handle, state) \ ++ do { \ ++ SQLCHAR err_msg[SQL_MAX_MESSAGE_LENGTH]; \ ++ SQLSMALLINT msg_len; \ ++ SQLINTEGER native_err; \ ++ SQLGetDiagRec(handle_type, handle, 1, state, &native_err, err_msg, sizeof(err_msg), &msg_len); \ ++ error("%s failed in %s: %d, %s, %s (%d)\n", #api_name, __func__, ret, (SQLCHAR*)state, err_msg, native_err); \ ++ \ ++ } while(0) ++ ++/* ++ * Get text data of specified column. ++ * SQLHSTMT StatementHandle (IN) SQLHSTMT handle ++ * int ColumnNumber (IN) the specified column starts from 0 ++ * char* FinalValue (IN/OUT) copy of string, memory should be released by the caller ++ */ ++#define PGSQL_GET_TEXT_DATA(StatementHandle, ColumnNumber, FinalValue, ret) \ ++ do { \ ++ SQLCHAR tmpChar[128]; \ ++ SQLLEN tmpCharLenOrInd = 0; \ ++ FinalValue = NULL; \ ++ while(true) { \ ++ ret = SQLGetData(StatementHandle, ColumnNumber+1, SQL_C_CHAR, tmpChar, sizeof(tmpChar), &tmpCharLenOrInd); \ ++ if (ret == SQL_SUCCESS && tmpCharLenOrInd != SQL_NULL_DATA) { \ ++ xstrcat(FinalValue, (char*)tmpChar); \ ++ } else { \ ++ debug4("PGSQL_GET_TEXT_DATA ret %d, tmpCharLenOrInd %d, SQL_NULL_DATA %d", ret, tmpCharLenOrInd, SQL_NULL_DATA);\ ++ break; \ ++ }\ ++ } \ ++ } while (0) ++ ++extern pgsql_conn_t *create_pgsql_conn(int conn_num, bool rollback, ++ char *cluster_name); ++extern int destroy_pgsql_conn(pgsql_conn_t *pgsql_conn); ++extern pgsql_db_info_t *create_pgsql_db_info(slurm_pgsql_plugin_type_t type); ++extern int destroy_pgsql_db_info(pgsql_db_info_t *db_info); ++ ++extern int pgsql_db_get_db_connection(pgsql_conn_t *pgsql_conn, char *db_name, ++ pgsql_db_info_t *db_info); ++extern int pgsql_db_close_db_connection(pgsql_conn_t *pgsql_conn); ++extern int pgsql_db_query(pgsql_conn_t *pgsql_conn, char *query); ++extern long pgsql_db_sql_affected_rows(pgsql_conn_t *pgsql_conn, char *query); ++extern int pgsql_db_ping(pgsql_conn_t *pgsql_conn); ++extern int pgsql_db_commit(pgsql_conn_t *pgsql_conn); ++extern int pgsql_db_rollback(pgsql_conn_t *pgsql_conn); ++ ++extern pgsql_res_t* pgsql_db_query_ret(pgsql_conn_t *pgsql_conn, ++ char *query, bool last); ++extern int pgsql_db_query_check_after(pgsql_conn_t *pgsql_conn, char *query); ++ ++extern int pgsql_db_query_stmt(pgsql_conn_t *pgsql_conn, ++ char *query, SQLHSTMT* stmt_ptr); ++ ++extern uint64_t pgsql_db_insert_ret_id(pgsql_conn_t *pgsql_conn, char *query); ++ ++extern int pgsql_db_create_table(pgsql_conn_t *pgsql_conn, char *table_name, ++ storage_field_t *fields, char *ending); ++ ++extern int last_affected_rows(SQLHSTMT stmt); ++ ++extern bool pgsql_db_match_state(char* state, char* pg_err_code); ++ ++//extern void pgsql_db_state(SQLHSTMT stmt, char* state); ++ ++extern int pgsql_db_free_statement(SQLHSTMT* stmt_ptr); ++ ++extern pgsql_row pgsql_fetch_row(pgsql_res_t* result); ++ ++extern void pgsql_free_result(pgsql_res_t** result); ++ ++extern int pgsql_num_rows(pgsql_res_t* result); ++ ++extern char* pgsql_errno(pgsql_res_t* result); ++ ++extern uint64_t pgsql_insert_id(SQLHSTMT stmt); ++ ++ ++ ++#endif +diff --git a/src/plugins/accounting_storage/Makefile.am b/src/plugins/accounting_storage/Makefile.am +index a0793a5..044c62c 100644 +--- a/src/plugins/accounting_storage/Makefile.am ++++ b/src/plugins/accounting_storage/Makefile.am +@@ -1,3 +1,3 @@ + # Makefile for storage plugins + +-SUBDIRS = common mysql none slurmdbd ++SUBDIRS = common mysql pgsql none slurmdbd +diff --git a/src/plugins/accounting_storage/pgsql/Makefile.am b/src/plugins/accounting_storage/pgsql/Makefile.am +new file mode 100755 +index 0000000..bdabfaf +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/Makefile.am +@@ -0,0 +1,46 @@ ++# Makefile for accounting_storage/pgsql plugin ++ ++AUTOMAKE_OPTIONS = foreign ++ ++PLUGIN_FLAGS = -module -avoid-version --export-dynamic ++ ++AM_CPPFLAGS = -DSLURM_PLUGIN_DEBUG -I$(top_srcdir) -I$(top_srcdir)/src/common ++ ++AS_PGSQL_SOURCES = accounting_storage_pgsql.c accounting_storage_pgsql.h \ ++ as_pgsql_acct.c as_pgsql_acct.h \ ++ as_pgsql_tres.c as_pgsql_tres.h \ ++ as_pgsql_archive.c as_pgsql_archive.h \ ++ as_pgsql_assoc.c as_pgsql_assoc.h \ ++ as_pgsql_cluster.c as_pgsql_cluster.h \ ++ as_pgsql_convert.c as_pgsql_convert.h \ ++ as_pgsql_federation.c as_pgsql_federation.h \ ++ as_pgsql_fix_runaway_jobs.c as_pgsql_fix_runaway_jobs.h \ ++ as_pgsql_job.c as_pgsql_job.h \ ++ as_pgsql_jobacct_process.c as_pgsql_jobacct_process.h \ ++ as_pgsql_problems.c as_pgsql_problems.h \ ++ as_pgsql_qos.c as_pgsql_qos.h \ ++ as_pgsql_resource.c as_pgsql_resource.h \ ++ as_pgsql_resv.c as_pgsql_resv.h \ ++ as_pgsql_rollup.c as_pgsql_rollup.h \ ++ as_pgsql_txn.c as_pgsql_txn.h \ ++ as_pgsql_usage.c as_pgsql_usage.h \ ++ as_pgsql_user.c as_pgsql_user.h \ ++ as_pgsql_wckey.c as_pgsql_wckey.h ++ ++if WITH_ODBC ++pkglib_LTLIBRARIES = accounting_storage_pgsql.la ++ ++# pgsql storage plugin. ++accounting_storage_pgsql_la_SOURCES = $(AS_PGSQL_SOURCES) ++accounting_storage_pgsql_la_LDFLAGS = $(PLUGIN_FLAGS) ++accounting_storage_pgsql_la_CFLAGS = $(ODBC_CFLAGS) ++accounting_storage_pgsql_la_LIBADD = \ ++ $(top_builddir)/src/database/libslurm_pgsql.la $(ODBC_LIBS) \ ++ ../common/libaccounting_storage_common.la ++ ++force: ++$(accounting_storage_pgsql_la_LIBADD) : force ++ @cd `dirname $@` && $(MAKE) `basename $@` ++else ++EXTRA_accounting_storage_pgsql_la_SOURCES = $(AS_PGSQL_SOURCES) ++endif +diff --git a/src/plugins/accounting_storage/pgsql/Makefile.in b/src/plugins/accounting_storage/pgsql/Makefile.in +new file mode 100755 +index 0000000..bda45f9 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/Makefile.in +@@ -0,0 +1,1085 @@ ++# Makefile.in generated by automake 1.16.4 from Makefile.am. ++# @configure_input@ ++ ++# Copyright (C) 1994-2021 Free Software Foundation, Inc. ++ ++# This Makefile.in is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY, to the extent permitted by law; without ++# even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++# PARTICULAR PURPOSE. ++ ++@SET_MAKE@ ++ ++# Makefile for accounting_storage/mysql plugin ++ ++VPATH = @srcdir@ ++am__is_gnu_make = { \ ++ if test -z '$(MAKELEVEL)'; then \ ++ false; \ ++ elif test -n '$(MAKE_HOST)'; then \ ++ true; \ ++ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ ++ true; \ ++ else \ ++ false; \ ++ fi; \ ++} ++am__make_running_with_option = \ ++ case $${target_option-} in \ ++ ?) ;; \ ++ *) echo "am__make_running_with_option: internal error: invalid" \ ++ "target option '$${target_option-}' specified" >&2; \ ++ exit 1;; \ ++ esac; \ ++ has_opt=no; \ ++ sane_makeflags=$$MAKEFLAGS; \ ++ if $(am__is_gnu_make); then \ ++ sane_makeflags=$$MFLAGS; \ ++ else \ ++ case $$MAKEFLAGS in \ ++ *\\[\ \ ]*) \ ++ bs=\\; \ ++ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ ++ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ ++ esac; \ ++ fi; \ ++ skip_next=no; \ ++ strip_trailopt () \ ++ { \ ++ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ ++ }; \ ++ for flg in $$sane_makeflags; do \ ++ test $$skip_next = yes && { skip_next=no; continue; }; \ ++ case $$flg in \ ++ *=*|--*) continue;; \ ++ -*I) strip_trailopt 'I'; skip_next=yes;; \ ++ -*I?*) strip_trailopt 'I';; \ ++ -*O) strip_trailopt 'O'; skip_next=yes;; \ ++ -*O?*) strip_trailopt 'O';; \ ++ -*l) strip_trailopt 'l'; skip_next=yes;; \ ++ -*l?*) strip_trailopt 'l';; \ ++ -[dEDm]) skip_next=yes;; \ ++ -[JT]) skip_next=yes;; \ ++ esac; \ ++ case $$flg in \ ++ *$$target_option*) has_opt=yes; break;; \ ++ esac; \ ++ done; \ ++ test $$has_opt = yes ++am__make_dryrun = (target_option=n; $(am__make_running_with_option)) ++am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) ++pkgdatadir = $(datadir)/@PACKAGE@ ++pkgincludedir = $(includedir)/@PACKAGE@ ++pkglibdir = $(libdir)/@PACKAGE@ ++pkglibexecdir = $(libexecdir)/@PACKAGE@ ++am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd ++install_sh_DATA = $(install_sh) -c -m 644 ++install_sh_PROGRAM = $(install_sh) -c ++install_sh_SCRIPT = $(install_sh) -c ++INSTALL_HEADER = $(INSTALL_DATA) ++transform = $(program_transform_name) ++NORMAL_INSTALL = : ++PRE_INSTALL = : ++POST_INSTALL = : ++NORMAL_UNINSTALL = : ++PRE_UNINSTALL = : ++POST_UNINSTALL = : ++build_triplet = @build@ ++host_triplet = @host@ ++target_triplet = @target@ ++subdir = src/plugins/accounting_storage/mysql ++ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 ++am__aclocal_m4_deps = $(top_srcdir)/auxdir/ax_check_compile_flag.m4 \ ++ $(top_srcdir)/auxdir/ax_gcc_builtin.m4 \ ++ $(top_srcdir)/auxdir/ax_lib_hdf5.m4 \ ++ $(top_srcdir)/auxdir/ax_pthread.m4 \ ++ $(top_srcdir)/auxdir/libtool.m4 \ ++ $(top_srcdir)/auxdir/ltoptions.m4 \ ++ $(top_srcdir)/auxdir/ltsugar.m4 \ ++ $(top_srcdir)/auxdir/ltversion.m4 \ ++ $(top_srcdir)/auxdir/lt~obsolete.m4 \ ++ $(top_srcdir)/auxdir/slurm.m4 \ ++ $(top_srcdir)/auxdir/slurmrestd.m4 \ ++ $(top_srcdir)/auxdir/x_ac_affinity.m4 \ ++ $(top_srcdir)/auxdir/x_ac_c99.m4 \ ++ $(top_srcdir)/auxdir/x_ac_cgroup.m4 \ ++ $(top_srcdir)/auxdir/x_ac_cray.m4 \ ++ $(top_srcdir)/auxdir/x_ac_curl.m4 \ ++ $(top_srcdir)/auxdir/x_ac_databases.m4 \ ++ $(top_srcdir)/auxdir/x_ac_debug.m4 \ ++ $(top_srcdir)/auxdir/x_ac_deprecated.m4 \ ++ $(top_srcdir)/auxdir/x_ac_dlfcn.m4 \ ++ $(top_srcdir)/auxdir/x_ac_env.m4 \ ++ $(top_srcdir)/auxdir/x_ac_freeipmi.m4 \ ++ $(top_srcdir)/auxdir/x_ac_http_parser.m4 \ ++ $(top_srcdir)/auxdir/x_ac_hwloc.m4 \ ++ $(top_srcdir)/auxdir/x_ac_json.m4 \ ++ $(top_srcdir)/auxdir/x_ac_jwt.m4 \ ++ $(top_srcdir)/auxdir/x_ac_lua.m4 \ ++ $(top_srcdir)/auxdir/x_ac_lz4.m4 \ ++ $(top_srcdir)/auxdir/x_ac_man2html.m4 \ ++ $(top_srcdir)/auxdir/x_ac_munge.m4 \ ++ $(top_srcdir)/auxdir/x_ac_netloc.m4 \ ++ $(top_srcdir)/auxdir/x_ac_nvml.m4 \ ++ $(top_srcdir)/auxdir/x_ac_ofed.m4 \ ++ $(top_srcdir)/auxdir/x_ac_pam.m4 \ ++ $(top_srcdir)/auxdir/x_ac_pmix.m4 \ ++ $(top_srcdir)/auxdir/x_ac_printf_null.m4 \ ++ $(top_srcdir)/auxdir/x_ac_ptrace.m4 \ ++ $(top_srcdir)/auxdir/x_ac_readline.m4 \ ++ $(top_srcdir)/auxdir/x_ac_rrdtool.m4 \ ++ $(top_srcdir)/auxdir/x_ac_rsmi.m4 \ ++ $(top_srcdir)/auxdir/x_ac_selinux.m4 \ ++ $(top_srcdir)/auxdir/x_ac_setproctitle.m4 \ ++ $(top_srcdir)/auxdir/x_ac_systemd.m4 \ ++ $(top_srcdir)/auxdir/x_ac_ucx.m4 \ ++ $(top_srcdir)/auxdir/x_ac_uid_gid_size.m4 \ ++ $(top_srcdir)/auxdir/x_ac_x11.m4 \ ++ $(top_srcdir)/auxdir/x_ac_yaml.m4 $(top_srcdir)/configure.ac ++am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ ++ $(ACLOCAL_M4) ++DIST_COMMON = $(srcdir)/Makefile.am ++mkinstalldirs = $(install_sh) -d ++CONFIG_HEADER = $(top_builddir)/config.h \ ++ $(top_builddir)/slurm/slurm_version.h ++CONFIG_CLEAN_FILES = ++CONFIG_CLEAN_VPATH_FILES = ++am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; ++am__vpath_adj = case $$p in \ ++ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ ++ *) f=$$p;; \ ++ esac; ++am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; ++am__install_max = 40 ++am__nobase_strip_setup = \ ++ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` ++am__nobase_strip = \ ++ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" ++am__nobase_list = $(am__nobase_strip_setup); \ ++ for p in $$list; do echo "$$p $$p"; done | \ ++ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ ++ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ ++ if (++n[$$2] == $(am__install_max)) \ ++ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ ++ END { for (dir in files) print dir, files[dir] }' ++am__base_list = \ ++ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ ++ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' ++am__uninstall_files_from_dir = { \ ++ test -z "$$files" \ ++ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ ++ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ ++ $(am__cd) "$$dir" && rm -f $$files; }; \ ++ } ++am__installdirs = "$(DESTDIR)$(pkglibdir)" ++LTLIBRARIES = $(pkglib_LTLIBRARIES) ++am__DEPENDENCIES_1 = ++@WITH_MYSQL_TRUE@accounting_storage_mysql_la_DEPENDENCIES = $(top_builddir)/src/database/libslurm_mysql.la \ ++@WITH_MYSQL_TRUE@ $(am__DEPENDENCIES_1) \ ++@WITH_MYSQL_TRUE@ ../common/libaccounting_storage_common.la ++am__objects_1 = \ ++ accounting_storage_mysql_la-accounting_storage_mysql.lo \ ++ accounting_storage_mysql_la-as_mysql_acct.lo \ ++ accounting_storage_mysql_la-as_mysql_tres.lo \ ++ accounting_storage_mysql_la-as_mysql_archive.lo \ ++ accounting_storage_mysql_la-as_mysql_assoc.lo \ ++ accounting_storage_mysql_la-as_mysql_cluster.lo \ ++ accounting_storage_mysql_la-as_mysql_convert.lo \ ++ accounting_storage_mysql_la-as_mysql_federation.lo \ ++ accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.lo \ ++ accounting_storage_mysql_la-as_mysql_job.lo \ ++ accounting_storage_mysql_la-as_mysql_jobacct_process.lo \ ++ accounting_storage_mysql_la-as_mysql_problems.lo \ ++ accounting_storage_mysql_la-as_mysql_qos.lo \ ++ accounting_storage_mysql_la-as_mysql_resource.lo \ ++ accounting_storage_mysql_la-as_mysql_resv.lo \ ++ accounting_storage_mysql_la-as_mysql_rollup.lo \ ++ accounting_storage_mysql_la-as_mysql_txn.lo \ ++ accounting_storage_mysql_la-as_mysql_usage.lo \ ++ accounting_storage_mysql_la-as_mysql_user.lo \ ++ accounting_storage_mysql_la-as_mysql_wckey.lo ++@WITH_MYSQL_TRUE@am_accounting_storage_mysql_la_OBJECTS = \ ++@WITH_MYSQL_TRUE@ $(am__objects_1) ++accounting_storage_mysql_la_OBJECTS = \ ++ $(am_accounting_storage_mysql_la_OBJECTS) ++AM_V_lt = $(am__v_lt_@AM_V@) ++am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) ++am__v_lt_0 = --silent ++am__v_lt_1 = ++accounting_storage_mysql_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ ++ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ ++ $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) \ ++ $(accounting_storage_mysql_la_LDFLAGS) $(LDFLAGS) -o $@ ++@WITH_MYSQL_TRUE@am_accounting_storage_mysql_la_rpath = -rpath \ ++@WITH_MYSQL_TRUE@ $(pkglibdir) ++AM_V_P = $(am__v_P_@AM_V@) ++am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) ++am__v_P_0 = false ++am__v_P_1 = : ++AM_V_GEN = $(am__v_GEN_@AM_V@) ++am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) ++am__v_GEN_0 = @echo " GEN " $@; ++am__v_GEN_1 = ++AM_V_at = $(am__v_at_@AM_V@) ++am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) ++am__v_at_0 = @ ++am__v_at_1 = ++DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -I$(top_builddir)/slurm ++depcomp = $(SHELL) $(top_srcdir)/auxdir/depcomp ++am__maybe_remake_depfiles = depfiles ++am__depfiles_remade = ./$(DEPDIR)/accounting_storage_mysql_la-accounting_storage_mysql.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_acct.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_archive.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_assoc.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_cluster.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_convert.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_federation.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_job.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_jobacct_process.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_problems.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_qos.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_resource.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_resv.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_rollup.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_tres.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_txn.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_usage.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_user.Plo \ ++ ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_wckey.Plo ++am__mv = mv -f ++COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ ++ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) ++LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ ++ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ ++ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ ++ $(AM_CFLAGS) $(CFLAGS) ++AM_V_CC = $(am__v_CC_@AM_V@) ++am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) ++am__v_CC_0 = @echo " CC " $@; ++am__v_CC_1 = ++CCLD = $(CC) ++LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ ++ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ ++ $(AM_LDFLAGS) $(LDFLAGS) -o $@ ++AM_V_CCLD = $(am__v_CCLD_@AM_V@) ++am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) ++am__v_CCLD_0 = @echo " CCLD " $@; ++am__v_CCLD_1 = ++SOURCES = $(accounting_storage_mysql_la_SOURCES) \ ++ $(EXTRA_accounting_storage_mysql_la_SOURCES) ++am__can_run_installinfo = \ ++ case $$AM_UPDATE_INFO_DIR in \ ++ n|no|NO) false;; \ ++ *) (install-info --version) >/dev/null 2>&1;; \ ++ esac ++am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) ++# Read a list of newline-separated strings from the standard input, ++# and print each of them once, without duplicates. Input order is ++# *not* preserved. ++am__uniquify_input = $(AWK) '\ ++ BEGIN { nonempty = 0; } \ ++ { items[$$0] = 1; nonempty = 1; } \ ++ END { if (nonempty) { for (i in items) print i; }; } \ ++' ++# Make sure the list of sources is unique. This is necessary because, ++# e.g., the same source file might be shared among _SOURCES variables ++# for different programs/libraries. ++am__define_uniq_tagged_files = \ ++ list='$(am__tagged_files)'; \ ++ unique=`for i in $$list; do \ ++ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ ++ done | $(am__uniquify_input)` ++ACLOCAL = @ACLOCAL@ ++AMTAR = @AMTAR@ ++AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ ++AR = @AR@ ++AR_FLAGS = @AR_FLAGS@ ++AUTOCONF = @AUTOCONF@ ++AUTOHEADER = @AUTOHEADER@ ++AUTOMAKE = @AUTOMAKE@ ++AWK = @AWK@ ++CC = @CC@ ++CCDEPMODE = @CCDEPMODE@ ++CFLAGS = @CFLAGS@ ++CHECK_CFLAGS = @CHECK_CFLAGS@ ++CHECK_LIBS = @CHECK_LIBS@ ++CPP = @CPP@ ++CPPFLAGS = @CPPFLAGS@ ++CRAY_JOB_CPPFLAGS = @CRAY_JOB_CPPFLAGS@ ++CRAY_JOB_LDFLAGS = @CRAY_JOB_LDFLAGS@ ++CRAY_SELECT_CPPFLAGS = @CRAY_SELECT_CPPFLAGS@ ++CRAY_SELECT_LDFLAGS = @CRAY_SELECT_LDFLAGS@ ++CRAY_SWITCH_CPPFLAGS = @CRAY_SWITCH_CPPFLAGS@ ++CRAY_SWITCH_LDFLAGS = @CRAY_SWITCH_LDFLAGS@ ++CRAY_TASK_CPPFLAGS = @CRAY_TASK_CPPFLAGS@ ++CRAY_TASK_LDFLAGS = @CRAY_TASK_LDFLAGS@ ++CSCOPE = @CSCOPE@ ++CTAGS = @CTAGS@ ++CXX = @CXX@ ++CXXCPP = @CXXCPP@ ++CXXDEPMODE = @CXXDEPMODE@ ++CXXFLAGS = @CXXFLAGS@ ++CYGPATH_W = @CYGPATH_W@ ++DATAWARP_CPPFLAGS = @DATAWARP_CPPFLAGS@ ++DATAWARP_LDFLAGS = @DATAWARP_LDFLAGS@ ++DEFS = @DEFS@ ++DEPDIR = @DEPDIR@ ++DLLTOOL = @DLLTOOL@ ++DL_LIBS = @DL_LIBS@ ++DSYMUTIL = @DSYMUTIL@ ++DUMPBIN = @DUMPBIN@ ++ECHO_C = @ECHO_C@ ++ECHO_N = @ECHO_N@ ++ECHO_T = @ECHO_T@ ++EGREP = @EGREP@ ++ETAGS = @ETAGS@ ++EXEEXT = @EXEEXT@ ++FGREP = @FGREP@ ++FREEIPMI_CPPFLAGS = @FREEIPMI_CPPFLAGS@ ++FREEIPMI_LDFLAGS = @FREEIPMI_LDFLAGS@ ++FREEIPMI_LIBS = @FREEIPMI_LIBS@ ++GLIB_CFLAGS = @GLIB_CFLAGS@ ++GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@ ++GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ ++GLIB_LIBS = @GLIB_LIBS@ ++GLIB_MKENUMS = @GLIB_MKENUMS@ ++GOBJECT_QUERY = @GOBJECT_QUERY@ ++GREP = @GREP@ ++GTK_CFLAGS = @GTK_CFLAGS@ ++GTK_LIBS = @GTK_LIBS@ ++H5CC = @H5CC@ ++H5FC = @H5FC@ ++HAVEMYSQLCONFIG = @HAVEMYSQLCONFIG@ ++HAVE_MAN2HTML = @HAVE_MAN2HTML@ ++HDF5_CC = @HDF5_CC@ ++HDF5_CFLAGS = @HDF5_CFLAGS@ ++HDF5_CPPFLAGS = @HDF5_CPPFLAGS@ ++HDF5_FC = @HDF5_FC@ ++HDF5_FFLAGS = @HDF5_FFLAGS@ ++HDF5_FLIBS = @HDF5_FLIBS@ ++HDF5_LDFLAGS = @HDF5_LDFLAGS@ ++HDF5_LIBS = @HDF5_LIBS@ ++HDF5_TYPE = @HDF5_TYPE@ ++HDF5_VERSION = @HDF5_VERSION@ ++HTTP_PARSER_CPPFLAGS = @HTTP_PARSER_CPPFLAGS@ ++HTTP_PARSER_LDFLAGS = @HTTP_PARSER_LDFLAGS@ ++HWLOC_CPPFLAGS = @HWLOC_CPPFLAGS@ ++HWLOC_LDFLAGS = @HWLOC_LDFLAGS@ ++HWLOC_LIBS = @HWLOC_LIBS@ ++INSTALL = @INSTALL@ ++INSTALL_DATA = @INSTALL_DATA@ ++INSTALL_PROGRAM = @INSTALL_PROGRAM@ ++INSTALL_SCRIPT = @INSTALL_SCRIPT@ ++INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ ++JSON_CPPFLAGS = @JSON_CPPFLAGS@ ++JSON_LDFLAGS = @JSON_LDFLAGS@ ++JWT_CPPFLAGS = @JWT_CPPFLAGS@ ++JWT_LDFLAGS = @JWT_LDFLAGS@ ++LD = @LD@ ++LDFLAGS = @LDFLAGS@ ++LIBCURL = @LIBCURL@ ++LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@ ++LIBOBJS = @LIBOBJS@ ++LIBS = @LIBS@ ++LIBTOOL = @LIBTOOL@ ++LIB_SLURM = @LIB_SLURM@ ++LIB_SLURM_BUILD = @LIB_SLURM_BUILD@ ++LIPO = @LIPO@ ++LN_S = @LN_S@ ++LTLIBOBJS = @LTLIBOBJS@ ++LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ ++LZ4_CPPFLAGS = @LZ4_CPPFLAGS@ ++LZ4_LDFLAGS = @LZ4_LDFLAGS@ ++LZ4_LIBS = @LZ4_LIBS@ ++MAINT = @MAINT@ ++MAKEINFO = @MAKEINFO@ ++MANIFEST_TOOL = @MANIFEST_TOOL@ ++MKDIR_P = @MKDIR_P@ ++MUNGE_CPPFLAGS = @MUNGE_CPPFLAGS@ ++MUNGE_DIR = @MUNGE_DIR@ ++MUNGE_LDFLAGS = @MUNGE_LDFLAGS@ ++MUNGE_LIBS = @MUNGE_LIBS@ ++MYSQL_CFLAGS = @MYSQL_CFLAGS@ ++MYSQL_LIBS = @MYSQL_LIBS@ ++NETLOC_CPPFLAGS = @NETLOC_CPPFLAGS@ ++NETLOC_LDFLAGS = @NETLOC_LDFLAGS@ ++NETLOC_LIBS = @NETLOC_LIBS@ ++NM = @NM@ ++NMEDIT = @NMEDIT@ ++NUMA_LIBS = @NUMA_LIBS@ ++NVML_CPPFLAGS = @NVML_CPPFLAGS@ ++NVML_LIBS = @NVML_LIBS@ ++OBJCOPY = @OBJCOPY@ ++OBJDUMP = @OBJDUMP@ ++OBJEXT = @OBJEXT@ ++OFED_CPPFLAGS = @OFED_CPPFLAGS@ ++OFED_LDFLAGS = @OFED_LDFLAGS@ ++OFED_LIBS = @OFED_LIBS@ ++OTOOL = @OTOOL@ ++OTOOL64 = @OTOOL64@ ++PACKAGE = @PACKAGE@ ++PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ ++PACKAGE_NAME = @PACKAGE_NAME@ ++PACKAGE_STRING = @PACKAGE_STRING@ ++PACKAGE_TARNAME = @PACKAGE_TARNAME@ ++PACKAGE_URL = @PACKAGE_URL@ ++PACKAGE_VERSION = @PACKAGE_VERSION@ ++PAM_DIR = @PAM_DIR@ ++PAM_LIBS = @PAM_LIBS@ ++PATH_SEPARATOR = @PATH_SEPARATOR@ ++PKG_CONFIG = @PKG_CONFIG@ ++PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ ++PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ ++PMIX_V1_CPPFLAGS = @PMIX_V1_CPPFLAGS@ ++PMIX_V1_LDFLAGS = @PMIX_V1_LDFLAGS@ ++PMIX_V2_CPPFLAGS = @PMIX_V2_CPPFLAGS@ ++PMIX_V2_LDFLAGS = @PMIX_V2_LDFLAGS@ ++PMIX_V3_CPPFLAGS = @PMIX_V3_CPPFLAGS@ ++PMIX_V3_LDFLAGS = @PMIX_V3_LDFLAGS@ ++PMIX_V4_CPPFLAGS = @PMIX_V4_CPPFLAGS@ ++PMIX_V4_LDFLAGS = @PMIX_V4_LDFLAGS@ ++PROJECT = @PROJECT@ ++PTHREAD_CC = @PTHREAD_CC@ ++PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ ++PTHREAD_LIBS = @PTHREAD_LIBS@ ++RANLIB = @RANLIB@ ++READLINE_LIBS = @READLINE_LIBS@ ++RELEASE = @RELEASE@ ++RRDTOOL_CPPFLAGS = @RRDTOOL_CPPFLAGS@ ++RRDTOOL_LDFLAGS = @RRDTOOL_LDFLAGS@ ++RRDTOOL_LIBS = @RRDTOOL_LIBS@ ++RSMI_CPPFLAGS = @RSMI_CPPFLAGS@ ++RSMI_LDFLAGS = @RSMI_LDFLAGS@ ++RSMI_LIBS = @RSMI_LIBS@ ++SED = @SED@ ++SET_MAKE = @SET_MAKE@ ++SHELL = @SHELL@ ++SLEEP_CMD = @SLEEP_CMD@ ++SLURMCTLD_PORT = @SLURMCTLD_PORT@ ++SLURMCTLD_PORT_COUNT = @SLURMCTLD_PORT_COUNT@ ++SLURMDBD_PORT = @SLURMDBD_PORT@ ++SLURMD_PORT = @SLURMD_PORT@ ++SLURMRESTD_PORT = @SLURMRESTD_PORT@ ++SLURM_API_AGE = @SLURM_API_AGE@ ++SLURM_API_CURRENT = @SLURM_API_CURRENT@ ++SLURM_API_MAJOR = @SLURM_API_MAJOR@ ++SLURM_API_REVISION = @SLURM_API_REVISION@ ++SLURM_API_VERSION = @SLURM_API_VERSION@ ++SLURM_MAJOR = @SLURM_MAJOR@ ++SLURM_MICRO = @SLURM_MICRO@ ++SLURM_MINOR = @SLURM_MINOR@ ++SLURM_PREFIX = @SLURM_PREFIX@ ++SLURM_VERSION_NUMBER = @SLURM_VERSION_NUMBER@ ++SLURM_VERSION_STRING = @SLURM_VERSION_STRING@ ++STRIP = @STRIP@ ++SUCMD = @SUCMD@ ++SYSTEMD_TASKSMAX_OPTION = @SYSTEMD_TASKSMAX_OPTION@ ++UCX_CPPFLAGS = @UCX_CPPFLAGS@ ++UCX_LDFLAGS = @UCX_LDFLAGS@ ++UCX_LIBS = @UCX_LIBS@ ++UTIL_LIBS = @UTIL_LIBS@ ++VERSION = @VERSION@ ++YAML_CPPFLAGS = @YAML_CPPFLAGS@ ++YAML_LDFLAGS = @YAML_LDFLAGS@ ++_libcurl_config = @_libcurl_config@ ++abs_builddir = @abs_builddir@ ++abs_srcdir = @abs_srcdir@ ++abs_top_builddir = @abs_top_builddir@ ++abs_top_srcdir = @abs_top_srcdir@ ++ac_ct_AR = @ac_ct_AR@ ++ac_ct_CC = @ac_ct_CC@ ++ac_ct_CXX = @ac_ct_CXX@ ++ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ ++ac_have_man2html = @ac_have_man2html@ ++am__include = @am__include@ ++am__leading_dot = @am__leading_dot@ ++am__quote = @am__quote@ ++am__tar = @am__tar@ ++am__untar = @am__untar@ ++ax_pthread_config = @ax_pthread_config@ ++bindir = @bindir@ ++build = @build@ ++build_alias = @build_alias@ ++build_cpu = @build_cpu@ ++build_os = @build_os@ ++build_vendor = @build_vendor@ ++builddir = @builddir@ ++datadir = @datadir@ ++datarootdir = @datarootdir@ ++docdir = @docdir@ ++dvidir = @dvidir@ ++exec_prefix = @exec_prefix@ ++host = @host@ ++host_alias = @host_alias@ ++host_cpu = @host_cpu@ ++host_os = @host_os@ ++host_vendor = @host_vendor@ ++htmldir = @htmldir@ ++includedir = @includedir@ ++infodir = @infodir@ ++install_sh = @install_sh@ ++libdir = @libdir@ ++libexecdir = @libexecdir@ ++libselinux_CFLAGS = @libselinux_CFLAGS@ ++libselinux_LIBS = @libselinux_LIBS@ ++localedir = @localedir@ ++localstatedir = @localstatedir@ ++lua_CFLAGS = @lua_CFLAGS@ ++lua_LIBS = @lua_LIBS@ ++mandir = @mandir@ ++mkdir_p = @mkdir_p@ ++oldincludedir = @oldincludedir@ ++pdfdir = @pdfdir@ ++prefix = @prefix@ ++program_transform_name = @program_transform_name@ ++psdir = @psdir@ ++runstatedir = @runstatedir@ ++sbindir = @sbindir@ ++sharedstatedir = @sharedstatedir@ ++srcdir = @srcdir@ ++sysconfdir = @sysconfdir@ ++systemdsystemunitdir = @systemdsystemunitdir@ ++target = @target@ ++target_alias = @target_alias@ ++target_cpu = @target_cpu@ ++target_os = @target_os@ ++target_vendor = @target_vendor@ ++top_build_prefix = @top_build_prefix@ ++top_builddir = @top_builddir@ ++top_srcdir = @top_srcdir@ ++AUTOMAKE_OPTIONS = foreign ++PLUGIN_FLAGS = -module -avoid-version --export-dynamic ++AM_CPPFLAGS = -DSLURM_PLUGIN_DEBUG -I$(top_srcdir) -I$(top_srcdir)/src/common ++AS_MYSQL_SOURCES = accounting_storage_mysql.c accounting_storage_mysql.h \ ++ as_mysql_acct.c as_mysql_acct.h \ ++ as_mysql_tres.c as_mysql_tres.h \ ++ as_mysql_archive.c as_mysql_archive.h \ ++ as_mysql_assoc.c as_mysql_assoc.h \ ++ as_mysql_cluster.c as_mysql_cluster.h \ ++ as_mysql_convert.c as_mysql_convert.h \ ++ as_mysql_federation.c as_mysql_federation.h \ ++ as_mysql_fix_runaway_jobs.c as_mysql_fix_runaway_jobs.h \ ++ as_mysql_job.c as_mysql_job.h \ ++ as_mysql_jobacct_process.c as_mysql_jobacct_process.h \ ++ as_mysql_problems.c as_mysql_problems.h \ ++ as_mysql_qos.c as_mysql_qos.h \ ++ as_mysql_resource.c as_mysql_resource.h \ ++ as_mysql_resv.c as_mysql_resv.h \ ++ as_mysql_rollup.c as_mysql_rollup.h \ ++ as_mysql_txn.c as_mysql_txn.h \ ++ as_mysql_usage.c as_mysql_usage.h \ ++ as_mysql_user.c as_mysql_user.h \ ++ as_mysql_wckey.c as_mysql_wckey.h ++ ++@WITH_MYSQL_TRUE@pkglib_LTLIBRARIES = accounting_storage_mysql.la ++ ++# Mysql storage plugin. ++@WITH_MYSQL_TRUE@accounting_storage_mysql_la_SOURCES = $(AS_MYSQL_SOURCES) ++@WITH_MYSQL_TRUE@accounting_storage_mysql_la_LDFLAGS = $(PLUGIN_FLAGS) ++@WITH_MYSQL_TRUE@accounting_storage_mysql_la_CFLAGS = $(MYSQL_CFLAGS) ++@WITH_MYSQL_TRUE@accounting_storage_mysql_la_LIBADD = \ ++@WITH_MYSQL_TRUE@ $(top_builddir)/src/database/libslurm_mysql.la $(MYSQL_LIBS) \ ++@WITH_MYSQL_TRUE@ ../common/libaccounting_storage_common.la ++ ++@WITH_MYSQL_FALSE@EXTRA_accounting_storage_mysql_la_SOURCES = $(AS_MYSQL_SOURCES) ++all: all-am ++ ++.SUFFIXES: ++.SUFFIXES: .c .lo .o .obj ++$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) ++ @for dep in $?; do \ ++ case '$(am__configure_deps)' in \ ++ *$$dep*) \ ++ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ ++ && { if test -f $@; then exit 0; else break; fi; }; \ ++ exit 1;; \ ++ esac; \ ++ done; \ ++ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/accounting_storage/mysql/Makefile'; \ ++ $(am__cd) $(top_srcdir) && \ ++ $(AUTOMAKE) --foreign src/plugins/accounting_storage/mysql/Makefile ++Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status ++ @case '$?' in \ ++ *config.status*) \ ++ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ ++ *) \ ++ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ ++ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ ++ esac; ++ ++$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) ++ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ++ ++$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) ++ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ++$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) ++ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ++$(am__aclocal_m4_deps): ++ ++install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) ++ @$(NORMAL_INSTALL) ++ @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ ++ list2=; for p in $$list; do \ ++ if test -f $$p; then \ ++ list2="$$list2 $$p"; \ ++ else :; fi; \ ++ done; \ ++ test -z "$$list2" || { \ ++ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ ++ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ ++ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ ++ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ ++ } ++ ++uninstall-pkglibLTLIBRARIES: ++ @$(NORMAL_UNINSTALL) ++ @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ ++ for p in $$list; do \ ++ $(am__strip_dir) \ ++ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ ++ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ ++ done ++ ++clean-pkglibLTLIBRARIES: ++ -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) ++ @list='$(pkglib_LTLIBRARIES)'; \ ++ locs=`for p in $$list; do echo $$p; done | \ ++ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ ++ sort -u`; \ ++ test -z "$$locs" || { \ ++ echo rm -f $${locs}; \ ++ rm -f $${locs}; \ ++ } ++ ++accounting_storage_mysql.la: $(accounting_storage_mysql_la_OBJECTS) $(accounting_storage_mysql_la_DEPENDENCIES) $(EXTRA_accounting_storage_mysql_la_DEPENDENCIES) ++ $(AM_V_CCLD)$(accounting_storage_mysql_la_LINK) $(am_accounting_storage_mysql_la_rpath) $(accounting_storage_mysql_la_OBJECTS) $(accounting_storage_mysql_la_LIBADD) $(LIBS) ++ ++mostlyclean-compile: ++ -rm -f *.$(OBJEXT) ++ ++distclean-compile: ++ -rm -f *.tab.c ++ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-accounting_storage_mysql.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_acct.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_archive.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_assoc.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_cluster.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_convert.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_federation.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_job.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_jobacct_process.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_problems.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_qos.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_resource.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_resv.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_rollup.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_tres.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_txn.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_usage.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_user.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_wckey.Plo@am__quote@ # am--include-marker ++ ++$(am__depfiles_remade): ++ @$(MKDIR_P) $(@D) ++ @echo '# dummy' >$@-t && $(am__mv) $@-t $@ ++ ++am--depfiles: $(am__depfiles_remade) ++ ++.c.o: ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< ++ ++.c.obj: ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` ++ ++.c.lo: ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< ++ ++accounting_storage_mysql_la-accounting_storage_mysql.lo: accounting_storage_mysql.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-accounting_storage_mysql.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-accounting_storage_mysql.Tpo -c -o accounting_storage_mysql_la-accounting_storage_mysql.lo `test -f 'accounting_storage_mysql.c' || echo '$(srcdir)/'`accounting_storage_mysql.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-accounting_storage_mysql.Tpo $(DEPDIR)/accounting_storage_mysql_la-accounting_storage_mysql.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='accounting_storage_mysql.c' object='accounting_storage_mysql_la-accounting_storage_mysql.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-accounting_storage_mysql.lo `test -f 'accounting_storage_mysql.c' || echo '$(srcdir)/'`accounting_storage_mysql.c ++ ++accounting_storage_mysql_la-as_mysql_acct.lo: as_mysql_acct.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_acct.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_acct.Tpo -c -o accounting_storage_mysql_la-as_mysql_acct.lo `test -f 'as_mysql_acct.c' || echo '$(srcdir)/'`as_mysql_acct.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_acct.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_acct.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_acct.c' object='accounting_storage_mysql_la-as_mysql_acct.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_acct.lo `test -f 'as_mysql_acct.c' || echo '$(srcdir)/'`as_mysql_acct.c ++ ++accounting_storage_mysql_la-as_mysql_tres.lo: as_mysql_tres.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_tres.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_tres.Tpo -c -o accounting_storage_mysql_la-as_mysql_tres.lo `test -f 'as_mysql_tres.c' || echo '$(srcdir)/'`as_mysql_tres.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_tres.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_tres.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_tres.c' object='accounting_storage_mysql_la-as_mysql_tres.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_tres.lo `test -f 'as_mysql_tres.c' || echo '$(srcdir)/'`as_mysql_tres.c ++ ++accounting_storage_mysql_la-as_mysql_archive.lo: as_mysql_archive.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_archive.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_archive.Tpo -c -o accounting_storage_mysql_la-as_mysql_archive.lo `test -f 'as_mysql_archive.c' || echo '$(srcdir)/'`as_mysql_archive.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_archive.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_archive.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_archive.c' object='accounting_storage_mysql_la-as_mysql_archive.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_archive.lo `test -f 'as_mysql_archive.c' || echo '$(srcdir)/'`as_mysql_archive.c ++ ++accounting_storage_mysql_la-as_mysql_assoc.lo: as_mysql_assoc.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_assoc.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_assoc.Tpo -c -o accounting_storage_mysql_la-as_mysql_assoc.lo `test -f 'as_mysql_assoc.c' || echo '$(srcdir)/'`as_mysql_assoc.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_assoc.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_assoc.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_assoc.c' object='accounting_storage_mysql_la-as_mysql_assoc.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_assoc.lo `test -f 'as_mysql_assoc.c' || echo '$(srcdir)/'`as_mysql_assoc.c ++ ++accounting_storage_mysql_la-as_mysql_cluster.lo: as_mysql_cluster.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_cluster.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_cluster.Tpo -c -o accounting_storage_mysql_la-as_mysql_cluster.lo `test -f 'as_mysql_cluster.c' || echo '$(srcdir)/'`as_mysql_cluster.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_cluster.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_cluster.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_cluster.c' object='accounting_storage_mysql_la-as_mysql_cluster.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_cluster.lo `test -f 'as_mysql_cluster.c' || echo '$(srcdir)/'`as_mysql_cluster.c ++ ++accounting_storage_mysql_la-as_mysql_convert.lo: as_mysql_convert.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_convert.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_convert.Tpo -c -o accounting_storage_mysql_la-as_mysql_convert.lo `test -f 'as_mysql_convert.c' || echo '$(srcdir)/'`as_mysql_convert.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_convert.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_convert.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_convert.c' object='accounting_storage_mysql_la-as_mysql_convert.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_convert.lo `test -f 'as_mysql_convert.c' || echo '$(srcdir)/'`as_mysql_convert.c ++ ++accounting_storage_mysql_la-as_mysql_federation.lo: as_mysql_federation.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_federation.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_federation.Tpo -c -o accounting_storage_mysql_la-as_mysql_federation.lo `test -f 'as_mysql_federation.c' || echo '$(srcdir)/'`as_mysql_federation.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_federation.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_federation.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_federation.c' object='accounting_storage_mysql_la-as_mysql_federation.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_federation.lo `test -f 'as_mysql_federation.c' || echo '$(srcdir)/'`as_mysql_federation.c ++ ++accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.lo: as_mysql_fix_runaway_jobs.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.Tpo -c -o accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.lo `test -f 'as_mysql_fix_runaway_jobs.c' || echo '$(srcdir)/'`as_mysql_fix_runaway_jobs.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_fix_runaway_jobs.c' object='accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.lo `test -f 'as_mysql_fix_runaway_jobs.c' || echo '$(srcdir)/'`as_mysql_fix_runaway_jobs.c ++ ++accounting_storage_mysql_la-as_mysql_job.lo: as_mysql_job.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_job.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_job.Tpo -c -o accounting_storage_mysql_la-as_mysql_job.lo `test -f 'as_mysql_job.c' || echo '$(srcdir)/'`as_mysql_job.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_job.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_job.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_job.c' object='accounting_storage_mysql_la-as_mysql_job.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_job.lo `test -f 'as_mysql_job.c' || echo '$(srcdir)/'`as_mysql_job.c ++ ++accounting_storage_mysql_la-as_mysql_jobacct_process.lo: as_mysql_jobacct_process.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_jobacct_process.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_jobacct_process.Tpo -c -o accounting_storage_mysql_la-as_mysql_jobacct_process.lo `test -f 'as_mysql_jobacct_process.c' || echo '$(srcdir)/'`as_mysql_jobacct_process.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_jobacct_process.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_jobacct_process.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_jobacct_process.c' object='accounting_storage_mysql_la-as_mysql_jobacct_process.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_jobacct_process.lo `test -f 'as_mysql_jobacct_process.c' || echo '$(srcdir)/'`as_mysql_jobacct_process.c ++ ++accounting_storage_mysql_la-as_mysql_problems.lo: as_mysql_problems.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_problems.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_problems.Tpo -c -o accounting_storage_mysql_la-as_mysql_problems.lo `test -f 'as_mysql_problems.c' || echo '$(srcdir)/'`as_mysql_problems.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_problems.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_problems.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_problems.c' object='accounting_storage_mysql_la-as_mysql_problems.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_problems.lo `test -f 'as_mysql_problems.c' || echo '$(srcdir)/'`as_mysql_problems.c ++ ++accounting_storage_mysql_la-as_mysql_qos.lo: as_mysql_qos.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_qos.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_qos.Tpo -c -o accounting_storage_mysql_la-as_mysql_qos.lo `test -f 'as_mysql_qos.c' || echo '$(srcdir)/'`as_mysql_qos.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_qos.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_qos.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_qos.c' object='accounting_storage_mysql_la-as_mysql_qos.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_qos.lo `test -f 'as_mysql_qos.c' || echo '$(srcdir)/'`as_mysql_qos.c ++ ++accounting_storage_mysql_la-as_mysql_resource.lo: as_mysql_resource.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_resource.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_resource.Tpo -c -o accounting_storage_mysql_la-as_mysql_resource.lo `test -f 'as_mysql_resource.c' || echo '$(srcdir)/'`as_mysql_resource.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_resource.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_resource.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_resource.c' object='accounting_storage_mysql_la-as_mysql_resource.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_resource.lo `test -f 'as_mysql_resource.c' || echo '$(srcdir)/'`as_mysql_resource.c ++ ++accounting_storage_mysql_la-as_mysql_resv.lo: as_mysql_resv.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_resv.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_resv.Tpo -c -o accounting_storage_mysql_la-as_mysql_resv.lo `test -f 'as_mysql_resv.c' || echo '$(srcdir)/'`as_mysql_resv.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_resv.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_resv.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_resv.c' object='accounting_storage_mysql_la-as_mysql_resv.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_resv.lo `test -f 'as_mysql_resv.c' || echo '$(srcdir)/'`as_mysql_resv.c ++ ++accounting_storage_mysql_la-as_mysql_rollup.lo: as_mysql_rollup.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_rollup.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_rollup.Tpo -c -o accounting_storage_mysql_la-as_mysql_rollup.lo `test -f 'as_mysql_rollup.c' || echo '$(srcdir)/'`as_mysql_rollup.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_rollup.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_rollup.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_rollup.c' object='accounting_storage_mysql_la-as_mysql_rollup.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_rollup.lo `test -f 'as_mysql_rollup.c' || echo '$(srcdir)/'`as_mysql_rollup.c ++ ++accounting_storage_mysql_la-as_mysql_txn.lo: as_mysql_txn.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_txn.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_txn.Tpo -c -o accounting_storage_mysql_la-as_mysql_txn.lo `test -f 'as_mysql_txn.c' || echo '$(srcdir)/'`as_mysql_txn.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_txn.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_txn.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_txn.c' object='accounting_storage_mysql_la-as_mysql_txn.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_txn.lo `test -f 'as_mysql_txn.c' || echo '$(srcdir)/'`as_mysql_txn.c ++ ++accounting_storage_mysql_la-as_mysql_usage.lo: as_mysql_usage.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_usage.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_usage.Tpo -c -o accounting_storage_mysql_la-as_mysql_usage.lo `test -f 'as_mysql_usage.c' || echo '$(srcdir)/'`as_mysql_usage.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_usage.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_usage.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_usage.c' object='accounting_storage_mysql_la-as_mysql_usage.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_usage.lo `test -f 'as_mysql_usage.c' || echo '$(srcdir)/'`as_mysql_usage.c ++ ++accounting_storage_mysql_la-as_mysql_user.lo: as_mysql_user.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_user.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_user.Tpo -c -o accounting_storage_mysql_la-as_mysql_user.lo `test -f 'as_mysql_user.c' || echo '$(srcdir)/'`as_mysql_user.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_user.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_user.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_user.c' object='accounting_storage_mysql_la-as_mysql_user.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_user.lo `test -f 'as_mysql_user.c' || echo '$(srcdir)/'`as_mysql_user.c ++ ++accounting_storage_mysql_la-as_mysql_wckey.lo: as_mysql_wckey.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -MT accounting_storage_mysql_la-as_mysql_wckey.lo -MD -MP -MF $(DEPDIR)/accounting_storage_mysql_la-as_mysql_wckey.Tpo -c -o accounting_storage_mysql_la-as_mysql_wckey.lo `test -f 'as_mysql_wckey.c' || echo '$(srcdir)/'`as_mysql_wckey.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/accounting_storage_mysql_la-as_mysql_wckey.Tpo $(DEPDIR)/accounting_storage_mysql_la-as_mysql_wckey.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='as_mysql_wckey.c' object='accounting_storage_mysql_la-as_mysql_wckey.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(accounting_storage_mysql_la_CFLAGS) $(CFLAGS) -c -o accounting_storage_mysql_la-as_mysql_wckey.lo `test -f 'as_mysql_wckey.c' || echo '$(srcdir)/'`as_mysql_wckey.c ++ ++mostlyclean-libtool: ++ -rm -f *.lo ++ ++clean-libtool: ++ -rm -rf .libs _libs ++ ++ID: $(am__tagged_files) ++ $(am__define_uniq_tagged_files); mkid -fID $$unique ++tags: tags-am ++TAGS: tags ++ ++tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) ++ set x; \ ++ here=`pwd`; \ ++ $(am__define_uniq_tagged_files); \ ++ shift; \ ++ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ ++ test -n "$$unique" || unique=$$empty_fix; \ ++ if test $$# -gt 0; then \ ++ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ ++ "$$@" $$unique; \ ++ else \ ++ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ ++ $$unique; \ ++ fi; \ ++ fi ++ctags: ctags-am ++ ++CTAGS: ctags ++ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) ++ $(am__define_uniq_tagged_files); \ ++ test -z "$(CTAGS_ARGS)$$unique" \ ++ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ ++ $$unique ++ ++GTAGS: ++ here=`$(am__cd) $(top_builddir) && pwd` \ ++ && $(am__cd) $(top_srcdir) \ ++ && gtags -i $(GTAGS_ARGS) "$$here" ++cscopelist: cscopelist-am ++ ++cscopelist-am: $(am__tagged_files) ++ list='$(am__tagged_files)'; \ ++ case "$(srcdir)" in \ ++ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ ++ *) sdir=$(subdir)/$(srcdir) ;; \ ++ esac; \ ++ for i in $$list; do \ ++ if test -f "$$i"; then \ ++ echo "$(subdir)/$$i"; \ ++ else \ ++ echo "$$sdir/$$i"; \ ++ fi; \ ++ done >> $(top_builddir)/cscope.files ++ ++distclean-tags: ++ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags ++check-am: all-am ++check: check-am ++all-am: Makefile $(LTLIBRARIES) ++installdirs: ++ for dir in "$(DESTDIR)$(pkglibdir)"; do \ ++ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ ++ done ++install: install-am ++install-exec: install-exec-am ++install-data: install-data-am ++uninstall: uninstall-am ++ ++install-am: all-am ++ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am ++ ++installcheck: installcheck-am ++install-strip: ++ if test -z '$(STRIP)'; then \ ++ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ ++ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ ++ install; \ ++ else \ ++ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ ++ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ ++ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ ++ fi ++mostlyclean-generic: ++ ++clean-generic: ++ ++distclean-generic: ++ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) ++ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) ++ ++maintainer-clean-generic: ++ @echo "This command is intended for maintainers to use" ++ @echo "it deletes files that may require special tools to rebuild." ++clean: clean-am ++ ++clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \ ++ mostlyclean-am ++ ++distclean: distclean-am ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-accounting_storage_mysql.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_acct.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_archive.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_assoc.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_cluster.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_convert.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_federation.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_job.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_jobacct_process.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_problems.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_qos.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_resource.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_resv.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_rollup.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_tres.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_txn.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_usage.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_user.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_wckey.Plo ++ -rm -f Makefile ++distclean-am: clean-am distclean-compile distclean-generic \ ++ distclean-tags ++ ++dvi: dvi-am ++ ++dvi-am: ++ ++html: html-am ++ ++html-am: ++ ++info: info-am ++ ++info-am: ++ ++install-data-am: ++ ++install-dvi: install-dvi-am ++ ++install-dvi-am: ++ ++install-exec-am: install-pkglibLTLIBRARIES ++ ++install-html: install-html-am ++ ++install-html-am: ++ ++install-info: install-info-am ++ ++install-info-am: ++ ++install-man: ++ ++install-pdf: install-pdf-am ++ ++install-pdf-am: ++ ++install-ps: install-ps-am ++ ++install-ps-am: ++ ++installcheck-am: ++ ++maintainer-clean: maintainer-clean-am ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-accounting_storage_mysql.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_acct.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_archive.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_assoc.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_cluster.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_convert.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_federation.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_fix_runaway_jobs.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_job.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_jobacct_process.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_problems.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_qos.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_resource.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_resv.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_rollup.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_tres.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_txn.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_usage.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_user.Plo ++ -rm -f ./$(DEPDIR)/accounting_storage_mysql_la-as_mysql_wckey.Plo ++ -rm -f Makefile ++maintainer-clean-am: distclean-am maintainer-clean-generic ++ ++mostlyclean: mostlyclean-am ++ ++mostlyclean-am: mostlyclean-compile mostlyclean-generic \ ++ mostlyclean-libtool ++ ++pdf: pdf-am ++ ++pdf-am: ++ ++ps: ps-am ++ ++ps-am: ++ ++uninstall-am: uninstall-pkglibLTLIBRARIES ++ ++.MAKE: install-am install-strip ++ ++.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ ++ clean-generic clean-libtool clean-pkglibLTLIBRARIES \ ++ cscopelist-am ctags ctags-am distclean distclean-compile \ ++ distclean-generic distclean-libtool distclean-tags dvi dvi-am \ ++ html html-am info info-am install install-am install-data \ ++ install-data-am install-dvi install-dvi-am install-exec \ ++ install-exec-am install-html install-html-am install-info \ ++ install-info-am install-man install-pdf install-pdf-am \ ++ install-pkglibLTLIBRARIES install-ps install-ps-am \ ++ install-strip installcheck installcheck-am installdirs \ ++ maintainer-clean maintainer-clean-generic mostlyclean \ ++ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ ++ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ ++ uninstall-pkglibLTLIBRARIES ++ ++.PRECIOUS: Makefile ++ ++ ++@WITH_MYSQL_TRUE@force: ++@WITH_MYSQL_TRUE@$(accounting_storage_mysql_la_LIBADD) : force ++@WITH_MYSQL_TRUE@ @cd `dirname $@` && $(MAKE) `basename $@` ++ ++# Tell versions [3.59,3.63) of GNU make to not export all variables. ++# Otherwise a system limit (for SysV at least) may be exceeded. ++.NOEXPORT: +diff --git a/src/plugins/accounting_storage/pgsql/accounting_storage_pgsql.c b/src/plugins/accounting_storage/pgsql/accounting_storage_pgsql.c +new file mode 100755 +index 0000000..588f5ac +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/accounting_storage_pgsql.c +@@ -0,0 +1,3395 @@ ++/*****************************************************************************\ ++ * accounting_storage_pgsql.c - accounting interface to as_pgsql. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Copyright (C) 2011-2018 SchedMD LLC. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "accounting_storage_pgsql.h" ++#include "as_pgsql_acct.h" ++#include "as_pgsql_tres.h" ++#include "as_pgsql_archive.h" ++#include "as_pgsql_assoc.h" ++#include "as_pgsql_cluster.h" ++#include "as_pgsql_convert.h" ++#include "as_pgsql_federation.h" ++#include "as_pgsql_fix_runaway_jobs.h" ++#include "as_pgsql_job.h" ++#include "as_pgsql_jobacct_process.h" ++#include "as_pgsql_problems.h" ++#include "as_pgsql_qos.h" ++#include "as_pgsql_resource.h" ++#include "as_pgsql_resv.h" ++#include "as_pgsql_rollup.h" ++#include "as_pgsql_txn.h" ++#include "as_pgsql_usage.h" ++#include "as_pgsql_user.h" ++#include "as_pgsql_wckey.h" ++ ++List as_pgsql_cluster_list = NULL; ++/* This total list is only used for converting things, so no ++ need to keep it upto date even though it lives until the ++ end of the life of the slurmdbd. ++*/ ++List as_pgsql_total_cluster_list = NULL; ++pthread_rwlock_t as_pgsql_cluster_list_lock = PTHREAD_RWLOCK_INITIALIZER; ++ ++/* ++ * 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 ++ * ++ * / ++ * ++ * where is a description of the intended application of ++ * the plugin (e.g., "accounting_storage" for Slurm job completion ++ * logging) and ++ * 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 "accounting_storage/". ++ * ++ * plugin_version - an unsigned 32-bit integer containing the Slurm version ++ * (major.minor.micro combined into a single number). ++ */ ++const char plugin_name[] = "Accounting storage pgsql plugin"; ++const char plugin_type[] = "accounting_storage/as_pgsql"; ++const uint32_t plugin_version = SLURM_VERSION_NUMBER; ++ ++static pgsql_db_info_t *pgsql_db_info = NULL; ++static char *pgsql_db_name = NULL; ++ ++#define DELETE_SEC_BACK 86400 ++ ++char *acct_coord_table = "acct_coord_table"; ++char *acct_table = "acct_table"; ++char *tres_table = "tres_table"; ++char *assoc_day_table = "assoc_usage_day_table"; ++char *assoc_hour_table = "assoc_usage_hour_table"; ++char *assoc_month_table = "assoc_usage_month_table"; ++char *assoc_table = "assoc_table"; ++char *clus_res_table = "clus_res_table"; ++char *cluster_day_table = "usage_day_table"; ++char *cluster_hour_table = "usage_hour_table"; ++char *cluster_month_table = "usage_month_table"; ++char *cluster_table = "cluster_table"; ++char *convert_version_table = "convert_version_table"; ++char *federation_table = "federation_table"; ++char *event_table = "event_table"; ++char *job_table = "job_table"; ++char *last_ran_table = "last_ran_table"; ++char *qos_table = "qos_table"; ++char *resv_table = "resv_table"; ++char *res_table = "res_table"; ++char *step_table = "step_table"; ++char *txn_table = "txn_table"; ++char *user_table = "user_table"; ++char *suspend_table = "suspend_table"; ++char *wckey_day_table = "wckey_usage_day_table"; ++char *wckey_hour_table = "wckey_usage_hour_table"; ++char *wckey_month_table = "wckey_usage_month_table"; ++char *wckey_table = "wckey_table"; ++ ++char *event_view = "event_view"; ++char *event_ext_view = "event_ext_view"; ++char *job_view = "job_view"; ++char *job_ext_view = "job_ext_view"; ++char *resv_view = "resv_view"; ++char *resv_ext_view = "resv_ext_view"; ++char *step_view = "step_view"; ++char *step_ext_view = "step_ext_view"; ++ ++bool backup_dbd = 0; ++ ++static char *default_qos_str = NULL; ++ ++enum { ++ JASSOC_JOB, ++ JASSOC_ACCT, ++ JASSOC_USER, ++ JASSOC_PART, ++ JASSOC_COUNT ++}; ++ ++extern int acct_storage_p_close_connection(pgsql_conn_t **pgsql_conn); ++ ++static List _get_cluster_names(pgsql_conn_t *pgsql_conn, bool with_deleted) ++{ ++ pgsql_res_t* result = NULL; ++ pgsql_row row; ++ List ret_list = NULL; ++ ++ char *query = xstrdup_printf("select name from %s", cluster_table); ++ ++ if (!with_deleted) ++ xstrcat(query, " where deleted=0"); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ ret_list = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) { ++ if (row[0] && row[0][0]) ++ list_append(ret_list, xstrdup(row[0])); ++ } ++ pgsql_free_result(&result); ++ ++ return ret_list; ++} ++ ++static int _set_qos_cnt(pgsql_conn_t *pgsql_conn) ++{ ++ pgsql_res_t* result = NULL; ++ pgsql_row row; ++ char *query = xstrdup_printf("select MAX(id) from %s", qos_table); ++ assoc_mgr_lock_t locks = { NO_LOCK, NO_LOCK, WRITE_LOCK, NO_LOCK, ++ NO_LOCK, NO_LOCK, NO_LOCK }; ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ if (!(row = pgsql_fetch_row(result))) { ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ /* Set the current qos_count on the system for ++ generating bitstr of that length. Since 0 isn't ++ possible as an id we add 1 to the total to burn 0 and ++ start at the 1 bit. ++ */ ++ assoc_mgr_lock(&locks); ++ g_qos_count = slurm_atoul(row[0]) + 1; ++ assoc_mgr_unlock(&locks); ++ pgsql_free_result(&result); ++ ++ return SLURM_SUCCESS; ++} ++ ++static void _process_running_jobs_result(char *cluster_name, ++ pgsql_res_t* result, List ret_list) ++{ ++ pgsql_row row; ++ char *object; ++ ++ while ((row = pgsql_fetch_row(result))) { ++ if (!row[JASSOC_USER][0]) { ++ /* This should never happen */ ++ error("How did we get a job running on an association " ++ "that isn't a user association job %s cluster " ++ "'%s' acct '%s'?", row[JASSOC_JOB], ++ cluster_name, row[JASSOC_ACCT]); ++ continue; ++ } ++ object = xstrdup_printf( ++ "JobID = %-10s C = %-10s A = %-10s U = %-9s", ++ row[JASSOC_JOB], cluster_name, row[JASSOC_ACCT], ++ row[JASSOC_USER]); ++ if (row[JASSOC_PART][0]) ++ // see if there is a partition name ++ xstrfmtcat(object, " P = %s", row[JASSOC_PART]); ++ list_append(ret_list, object); ++ } ++} ++ ++/* this function is here to see if any of what we are trying to remove ++ * has jobs that are not completed. If we have jobs and the object is less ++ * than a day old we don't want to delete it, only set the deleted flag. ++ */ ++static bool _check_jobs_before_remove(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, ++ char *assoc_char, ++ List ret_list, ++ bool *already_flushed) ++{ ++ char *query = NULL, *object = NULL; ++ bool rc = 0; ++ int i; ++ pgsql_res_t* result = NULL; ++ ++ /* if this changes you will need to edit the corresponding ++ * enum above in the global settings */ ++ static char *jassoc_req_inx[] = { ++ "t0.id_job", ++ "t1.acct", ++ "t1.user", ++ "t1.partition" ++ }; ++ if (ret_list) { ++ xstrcat(object, jassoc_req_inx[0]); ++ for(i=1; iconn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return rc; ++ } ++ ++ if (pgsql_num_rows(result)) { ++ debug4("We have jobs for this combo"); ++ rc = true; ++ if (ret_list && !(*already_flushed)) { ++ list_flush(ret_list); ++ (*already_flushed) = 1; ++ reset_pgsql_conn(pgsql_conn); ++ } ++ if (ret_list) ++ _process_running_jobs_result(cluster_name, result, ++ ret_list); ++ } ++ ++ pgsql_free_result(&result); ++ return rc; ++} ++ ++/* Same as above but for associations instead of other tables */ ++static bool _check_jobs_before_remove_assoc(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, ++ char *assoc_char, ++ List ret_list, ++ bool *already_flushed) ++{ ++ char *query = NULL, *object = NULL; ++ bool rc = 0; ++ int i; ++ pgsql_res_t* result = NULL; ++ ++ /* if this changes you will need to edit the corresponding ++ * enum above in the global settings */ ++ static char *jassoc_req_inx[] = { ++ "t1.id_job", ++ "t2.acct", ++ "t2.user", ++ "t2.partition" ++ }; ++ ++ if (ret_list) { ++ xstrcat(object, jassoc_req_inx[0]); ++ for(i=1; iconn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return rc; ++ } ++ ++ if (pgsql_num_rows(result)) { ++ debug4("We have jobs for this combo"); ++ rc = true; ++ if (ret_list && !(*already_flushed)) { ++ list_flush(ret_list); ++ (*already_flushed) = 1; ++ reset_pgsql_conn(pgsql_conn); ++ } ++ } ++ ++ if (ret_list) ++ _process_running_jobs_result(cluster_name, result, ret_list); ++ ++ pgsql_free_result(&result); ++ return rc; ++} ++ ++/* Same as above but for things having nothing to do with associations ++ * like qos or wckey */ ++static bool _check_jobs_before_remove_without_assoctable( ++ pgsql_conn_t *pgsql_conn, char *cluster_name, char *where_char) ++{ ++ char *query = NULL; ++ bool rc = 0; ++ pgsql_res_t* result = NULL; ++ ++ query = xstrdup_printf("select id_assoc from \"%s_%s\" " ++ "where (%s) limit 1;", ++ cluster_name, job_table, where_char); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return rc; ++ } ++ ++ if (pgsql_num_rows(result)) { ++ debug4("We have jobs for this combo"); ++ rc = true; ++ } ++ ++ pgsql_free_result(&result); ++ return rc; ++} ++ ++/* Any time a new table is added set it up here */ ++static int _as_pgsql_acct_check_tables(pgsql_conn_t *pgsql_conn) ++{ ++ storage_field_t acct_coord_table_fields[] = { ++ { "creation_time", "bigint", "not null" }, ++ { "mod_time", "bigint", "not null|default 0" }, ++ { "deleted", "smallint", "default 0" }, ++ { "acct", "varchar", "not null" }, ++ { "user", "varchar", "not null" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t acct_table_fields[] = { ++ { "creation_time", "numeric", "not null" }, ++ { "mod_time", "numeric", "default 0|not null" }, ++ { "deleted", "smallint", "default 0" }, ++ { "name", "varchar", "not null" }, ++ { "description", "text", "not null" }, ++ { "organization", "text", "not null" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t tres_table_fields[] = { ++ { "creation_time", "numeric", "not null" }, ++ { "deleted", "smallint", "default 0|not null" }, ++ { "id", "serial", "not null" }, ++ { "type", "varchar", "not null" }, ++ { "name", "varchar", "not null|default ''" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t cluster_table_fields[] = { ++ { "creation_time", "numeric", "not null" }, ++ { "mod_time", "numeric", "default 0|not null" }, ++ { "deleted", "smallint", "default 0" }, ++ { "name", "varchar", "not null" }, ++ { "control_host", "varchar", "not null|default ''" }, ++ { "control_port", "integer", "not null|default 0" }, ++ { "last_port", "integer", "not null|default 0" }, ++ { "rpc_version", "smallint", "not null|default 0" }, ++ { "classification", "smallint", "default 0" }, ++ { "dimensions", "smallint", "default 1" }, ++ { "plugin_id_select", "smallint", "default 0" }, ++ { "flags", "integer", "default 0" }, ++ { "federation", "varchar", "not null" }, ++ { "features", "text", "not null|default ''" }, ++ { "fed_id", "integer", "default 0|not null" }, ++ { "fed_state", "smallint", "not null" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t clus_res_table_fields[] = { ++ { "creation_time", "numeric", "not null" }, ++ { "mod_time", "numeric", "default 0|not null" }, ++ { "deleted", "smallint", "default 0" }, ++ { "cluster", "varchar", "not null" }, ++ { "res_id", "integer", "not null" }, ++ { "percent_allowed", "integer", "default 0" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t convert_version_table_fields[] = { ++ { "mod_time", "numeric", "default 0|not null" }, ++ { "version", "integer", "default 0" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t federation_table_fields[] = { ++ { "creation_time", "integer", "default 0|not null" }, ++ { "mod_time", "integer", "default 0|not null" }, ++ { "deleted", "smallint", "default 0" }, ++ { "name", "varchar", "not null" }, ++ { "flags", "integer", "default 0" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t qos_table_fields[] = { ++ { "creation_time", "bigint", "default 0|not null " }, ++ { "mod_time", "bigint", "default 0|not null " }, ++ { "deleted", "smallint", "default 0" }, ++ { "id", "serial", "not null" }, ++ { "name", "varchar", "not null" }, ++ { "description", "text", "" }, ++ { "flags", "integer", "default 0" }, ++ { "grace_time", "integer", "default null" }, ++ { "max_jobs_pa", "integer", "default null" }, ++ { "max_jobs_per_user", "integer", "default null" }, ++ { "max_jobs_accrue_pa", "integer", "default null" }, ++ { "max_jobs_accrue_pu", "integer", "default null" }, ++ { "min_prio_thresh", "integer", "default null" }, ++ { "max_submit_jobs_pa", "integer", "default null" }, ++ { "max_submit_jobs_per_user", "integer", "default null" }, ++ { "max_tres_pa", "varchar", "not null|default ''" }, ++ { "max_tres_pj", "varchar", "not null|default ''" }, ++ { "max_tres_pn", "varchar", "not null|default ''" }, ++ { "max_tres_pu", "varchar", "not null|default ''" }, ++ { "max_tres_mins_pj", "varchar", "not null|default ''" }, ++ { "max_tres_run_mins_pa", "varchar", "not null|default ''" }, ++ { "max_tres_run_mins_pu", "varchar", "not null|default ''" }, ++ { "min_tres_pj", "varchar", "not null|default ''" }, ++ { "max_wall_duration_per_job", "integer", "default null" }, ++ { "grp_jobs", "integer", "default NULL" }, ++ { "grp_jobs_accrue", "integer", "default NULL" }, ++ { "grp_submit_jobs", "integer", "default NULL" }, ++ { "grp_tres", "text", "not null|default ''" }, ++ { "grp_tres_mins", "text", "not null|default ''" }, ++ { "grp_tres_run_mins", "text", "not null|default ''" }, ++ { "grp_wall", "integer", "default NULL" }, ++ { "preempt", "text", "not null|default ''" }, ++ { "preempt_mode", "integer", "default 0" }, ++ { "preempt_exempt_time", "integer", "default NULL" }, ++ { "priority", "integer", "default 0" }, ++ { "usage_factor", "double precision", "default 1.0|not null" }, ++ { "usage_thres", "double precision", "default NULL" }, ++ { "limit_factor", "double precision", "default NULL"}, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t res_table_fields[] = { ++ { "creation_time", "bigint", "not null" }, ++ { "mod_time", "bigint", "default 0|not null" }, ++ { "deleted", "smallint", "default 0" }, ++ { "id", "serial", "not null" }, ++ { "name", "text", "not null" }, ++ { "description", "text", "default null" }, ++ { "manager", "text", "not null" }, ++ { "server", "text", "not null" }, ++ { "count", "integer", "default 0" }, ++ { "type", "integer", "default 0" }, ++ { "flags", "integer", "default 0" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t txn_table_fields[] = { ++ { "id", "serial", "not null" }, ++ { "timestamp", "bigint", "default 0|not null" }, ++ { "action", "smallint", "not null" }, ++ { "name", "text", "not null" }, ++ { "actor", "varchar", "not null" }, ++ { "cluster", "varchar", "default ''|not null" }, ++ { "info", "bytea", "" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t user_table_fields[] = { ++ { "creation_time", "bigint", "not null" }, ++ { "mod_time", "bigint", "default 0|not null" }, ++ { "deleted", "smallint", "default 0" }, ++ { "name", "varchar", "not null" }, ++ { "admin_level", "smallint", "default 1|not null" }, ++ { NULL, NULL} ++ }; ++ ++ /* ++ * If more limits are added here they need to be added to ++ * get_parent_limits_select in as_pgsql_assoc.c ++ */ ++ char* get_parent_proc = ++ "CREATE OR REPLACE FUNCTION get_parent_limits(\n \ ++ my_table text,\n \ ++ acct text,\n \ ++ cluster text,\n \ ++ without_limits int\n \ ++ )\n \ ++ RETURNS void AS $$\n \ ++ DECLARE\n \ ++ s text;\n \ ++ par_id int;\n \ ++ mj int;\n \ ++ mja int;\n \ ++ mpt int;\n \ ++ msj int;\n \ ++ mwpj int;\n \ ++ mtpj text;\n \ ++ mtpn text;\n \ ++ mtmpj text;\n \ ++ mtrm text;\n \ ++ prio int;\n \ ++ def_qos_id int;\n \ ++ qos text;\n \ ++ delta_qos text;\n \ ++ my_acct text;\n \ ++ my_acct_new text;\n \ ++ BEGIN\n \ ++ par_id := NULL;\n \ ++ mj := NULL;\n \ ++ mja := NULL;\n \ ++ mpt := NULL;\n \ ++ msj := NULL;\n \ ++ mwpj := NULL;\n \ ++ mtpj := '';\n \ ++ mtpn := '';\n \ ++ mtmpj := '';\n \ ++ mtrm := '';\n \ ++ prio := NULL;\n \ ++ def_qos_id := NULL;\n \ ++ qos := '';\n \ ++ delta_qos := '';\n \ ++ my_acct := acct;\n \ ++ \n \ ++ IF without_limits = 1 THEN\n \ ++ mj := 0;\n \ ++ msj := 0;\n \ ++ mwpj := 0;\n \ ++ prio := 0;\n \ ++ def_qos_id := 0;\n \ ++ qos := '1';\n \ ++ END IF;\n \ ++ \n \ ++ LOOP\n \ ++ s := 'SELECT';\n \ ++ \n \ ++ IF par_id IS NULL THEN\n \ ++ s := CONCAT(s, '@par_id := id_assoc,');\n \ ++ END IF;\n \ ++ \n \ ++ IF mj IS NULL THEN\n \ ++ s := CONCAT(s, '@mj := max_jobs,');\n \ ++ END IF;\n \ ++ IF mja IS NULL THEN\n \ ++ \n \ ++ s := CONCAT(s, '@mja := max_jobs_accrue,');\n \ ++ END IF;\n \ ++ \n \ ++ IF mpt IS NULL THEN\n \ ++ s := CONCAT(s, '@mpt := min_prio_thresh,');\n \ ++ END IF;\n \ ++ \n \ ++ IF msj IS NULL THEN\n \ ++ s := CONCAT(s, '@msj := max_submit_jobs,');\n \ ++ END IF;\n \ ++ \n \ ++ IF mwpj IS NULL THEN\n \ ++ s := CONCAT(s, '@mwpj := max_wall_pj,');\n \ ++ END IF;\n \ ++ \n \ ++ IF prio IS NULL THEN\n \ ++ s := CONCAT(s, '@prio := priority,');\n \ ++ END IF;\n \ ++ \n \ ++ IF def_qos_id IS NULL THEN\n \ ++ s := CONCAT(s, '@def_qos_id := def_qos_id,');\n \ ++ END IF;\n \ ++ \n \ ++ IF qos = '' THEN\n \ ++ s := CONCAT(s, '@qos := qos,\n \ ++ @delta_qos := REPLACE(CONCAT(delta_qos, @delta_qos), '',,'', '',''),');\n \ ++ END IF;\n \ ++ \n \ ++ s := CONCAT(s, '\n \ ++ @mtpj := CONCAT(@mtpj,\n \ ++ '',''\n \ ++ CASE WHEN @mtpj != '''' AND max_tres_pj != '''' THEN\n \ ++ ELSE\n \ ++ ''''\n \ ++ END,\n \ ++ max_tres_pj),\n \ ++ \n \ ++ @mtpn := CONCAT(@mtpn,\n \ ++ CASE WHEN @mtpn != '''' AND max_tres_pn != '''' THEN\n \ ++ '',''\n \ ++ ELSE\n \ ++ ''''\n \ ++ END,\n \ ++ max_tres_pn),\n \ ++ \n \ ++ @mtmpj := CONCAT(@mtmpj,\n \ ++ CASE WHEN @mtmpj != '''' AND max_tres_mins_pj != '''' THEN\n \ ++ '',''\n \ ++ ELSE\n \ ++ ''''\n \ ++ END,\n \ ++ max_tres_mins_pj),\n \ ++ \n \ ++ @mtrm := CONCAT(@mtrm,\n \ ++ CASE WHEN @mtrm != '''' AND max_tres_run_mins != '''' THEN\n \ ++ '',''\n \ ++ ELSE\n \ ++ ''''\n \ ++ END,\n \ ++ max_tres_run_mins),\n \ ++ \n \ ++ @my_acct_new := parent_acct\n \ ++ FROM ' || cluster || '_' || my_table || '\n \ ++ WHERE acct = ''' || my_acct || ''' AND user = ''''');\n \ ++ \n \ ++ EXECUTE s;\n \ ++ \n \ ++ my_acct := my_acct_new;\n \ ++ \n \ ++ EXIT WHEN without_limits OR my_acct = '';\n \ ++ END LOOP;\n \ ++ END;\n \ ++ $$ LANGUAGE plpgsql;"; ++ ++ char* get_coord_qos = ++ "CREATE OR REPLACE FUNCTION get_coord_qos(my_table text, acct text, cluster text, coord text) \n \ ++ RETURNS TABLE(qos text, delta_qos text) AS \n \ ++ $$\n \ ++ DECLARE\n \ ++ qos text;\n \ ++ delta_qos text;\n \ ++ found_coord_curr text;\n \ ++ my_acct text;\n \ ++ BEGIN\n \ ++ qos := '';\n \ ++ delta_qos := '';\n \ ++ found_coord_curr := NULL;\n \ ++ my_acct := acct;\n \ ++ \n \ ++ LOOP \n \ ++ EXECUTE 'SELECT t1.qos, REPLACE(COALESCE(t1.delta_qos || '''' || delta_qos, ''''), '','''') ' \n \ ++ 'INTO STRICT qos, delta_qos ' \n \ ++ 'FROM ' || quote_ident(cluster || '_' || my_table) || ' t1 ' \n \ ++ 'LEFT OUTER JOIN acct_coord_table t2 ON t1.acct = t2.acct '\n \ ++ 'WHERE t1.acct = ' || quote_literal(my_acct) || \n \ ++ ' AND t1.user = ''' || '''' || \n \ ++ ' AND (t2.user = ' || quote_literal(coord) || ' OR t2.user IS NULL)'; \n \ ++ \n \ ++ IF found_coord_curr IS NOT NULL THEN \n \ ++ EXIT;\n \ ++ END IF; \n \ ++ \n \ ++ IF found_coord_curr IS NULL THEN \n \ ++ qos := ''; \n \ ++ delta_qos := '';\n \ ++ END IF; \n \ ++ \n \ ++ my_acct := my_acct_new; \n \ ++ \n \ ++ EXIT WHEN qos != '' OR my_acct = ''; \n \ ++ END LOOP; \n \ ++ \n \ ++ RETURN QUERY SELECT REPLACE(COALESCE(qos || '''' || delta_qos, ''''), ',,', ','); \n \ ++ END;\n \ ++ $$ \n \ ++ LANGUAGE plpgsql;"; ++ ++// char *get_coord_qos = ++// "drop procedure if exists get_coord_qos; " ++// "create procedure get_coord_qos(my_table text, acct text, " ++// "cluster text, coord text) " ++// "begin " ++// "set @qos = ''; " ++// "set @delta_qos = ''; " ++// "set @found_coord = NULL; " ++// "set @my_acct = acct; " ++// "REPEAT " ++// "set @s = 'select @qos := t1.qos, " ++// "@delta_qos := REPLACE(CONCAT(t1.delta_qos, @delta_qos), " ++// "\\\',,\\\', \\\',\\\'), @my_acct_new := parent_acct, " ++// "@found_coord_curr := t2.user '; " ++// "set @s = concat(@s, 'from \"', cluster, '_', my_table, '\" " ++// "as t1 left outer join acct_coord_table as t2 on " ++// "t1.acct=t2.acct where t1.acct = @my_acct && t1.user=\\\'\\\' " ++// "&& (t2.user=\\\'', coord, '\\\' || t2.user is null)'); " ++// "prepare query from @s; " ++// "execute query; " ++// "deallocate prepare query; " ++// "if @found_coord_curr is not NULL then " ++// "set @found_coord = @found_coord_curr; " ++// "end if; " ++// "if @found_coord is NULL then " ++// "set @qos = ''; " ++// "set @delta_qos = ''; " ++// "end if; " ++// "set @my_acct = @my_acct_new; " ++// "UNTIL @qos != '' || @my_acct = '' END REPEAT; " ++// "select REPLACE(CONCAT(@qos, @delta_qos), ',,', ','); " ++// "END;"; ++ ++ ++ char *query = NULL; ++ time_t now = time(NULL); ++ char *cluster_name = NULL; ++ int rc = SLURM_SUCCESS, rc2; ++ ListIterator itr = NULL; ++ char* ending = NULL; ++ ++ /* Make the convert version table since we will check that going ++ * forward to see if we need to update or not. ++ */ ++ ++ if (pgsql_db_create_table(pgsql_conn, convert_version_table, ++ convert_version_table_fields, ++ ", primary key (version));") == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ /* Make the cluster table first since we build other tables ++ built off this one */ ++ if (pgsql_db_create_table(pgsql_conn, cluster_table, ++ cluster_table_fields, ++ ", primary key (name));") == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ /* This table needs to be made before conversions also since ++ we add a cluster column. ++ */ ++ if (pgsql_db_create_table(pgsql_conn, txn_table, txn_table_fields, ++ ", primary key (id));") == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ ending = xstrdup_printf(", primary key (id)); CREATE UNIQUE INDEX IF NOT EXISTS %s_udx ON %s " ++ "(substr(type, 1, 42), substr(name, 1, 42));", tres_table, tres_table); ++ xstrfmtcat(ending, "CREATE SEQUENCE IF NOT EXISTS %s_id_seq MINVALUE 1001 START WITH 1001;" ++ "ALTER TABLE %s ALTER COLUMN id SET DEFAULT nextval('%s_id_seq'::regclass);", ++ tres_table, tres_table, tres_table); ++ ++ if (pgsql_db_create_table(pgsql_conn, tres_table, tres_table_fields, ending) ++ == SLURM_ERROR) { ++ xfree(ending); ++ return SLURM_ERROR; ++ } ++ xfree(ending); ++ ++ if (!backup_dbd) { ++ /* We always want CPU to be the first one, so create ++ it now. We also add MEM here, the others tres ++ are site specific and could vary. None but CPU ++ matter on order though. CPU always has to be 1. ++ ++ TRES_OFFSET is needed since there's no way to force ++ the number of first automatic id in pgsql. auto_increment ++ value is lost on pgsqld restart. Bug 4553. ++ */ ++ query = xstrdup_printf( ++ "INSERT INTO %s (creation_time, id, deleted, type) " ++ "VALUES " ++ "(%ld, %d, 0, 'cpu'), " ++ "(%ld, %d, 0, 'mem'), " ++ "(%ld, %d, 0, 'energy'), " ++ "(%ld, %d, 0, 'node'), " ++ "(%ld, %d, 0, 'billing'), " ++ "(%ld, %d, 0, 'vmem'), " ++ "(%ld, %d, 0, 'pages'), " ++ "(%ld, %d, 1, 'dynamic_offset') " ++ "ON CONFLICT (id) DO UPDATE " ++ "SET deleted = EXCLUDED.deleted, type = EXCLUDED.type, id = EXCLUDED.id;", ++ tres_table, ++ now, TRES_CPU, ++ now, TRES_MEM, ++ now, TRES_ENERGY, ++ now, TRES_NODE, ++ now, TRES_BILLING, ++ now, TRES_VMEM, ++ now, TRES_PAGES, ++ now, TRES_OFFSET); ++ ++ DB_DEBUG(DB_TRES, pgsql_conn->conn, "%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) ++ fatal("problem adding static tres"); ++ ++ /* Now insert TRES that have a name */ ++ query = xstrdup_printf( ++ "INSERT INTO %s (creation_time, id, deleted, type, name) VALUES " ++ "(%ld, %d, 0, 'fs', 'disk') " ++ "ON CONFLICT (id) DO UPDATE SET " ++ "deleted = EXCLUDED.deleted, type = EXCLUDED.type, name = EXCLUDED.name, id = EXCLUDED.id;", ++ tres_table, now, TRES_FS_DISK); ++ ++ DB_DEBUG(DB_TRES, pgsql_conn->conn, "%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) ++ fatal("problem adding static tres"); ++ } ++ ++ slurm_rwlock_wrlock(&as_pgsql_cluster_list_lock); ++ if (!(as_pgsql_cluster_list = _get_cluster_names(pgsql_conn, 0))) { ++ error("issue getting contents of %s", cluster_table); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ return SLURM_ERROR; ++ } ++ ++ /* This total list is only used for converting things, so no ++ need to keep it upto date even though it lives until the ++ end of the life of the slurmdbd. ++ */ ++ if (!(as_pgsql_total_cluster_list = ++ _get_cluster_names(pgsql_conn, 1))) { ++ error("issue getting total contents of %s", cluster_table); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ return SLURM_ERROR; ++ } ++ ++ if ((rc = as_pgsql_convert_tables_pre_create(pgsql_conn)) != ++ SLURM_SUCCESS) { ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ error("issue converting tables before create"); ++ return rc; ++ } else if (backup_dbd) { ++ /* ++ * We do not want to create/check the database if we are the ++ * backup (see Bug 3827). This is only handled on the primary. ++ */ ++ ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ ++ /* We do want to set the QOS count though. */ ++ if (rc == SLURM_SUCCESS) ++ rc = _set_qos_cnt(pgsql_conn); ++ ++ return rc; ++ } ++ ++ /* might as well do all the cluster centric tables inside this ++ * lock. We need to do this on all the clusters deleted or ++ * other wise just to make sure everything is kept up to ++ * date. */ ++ itr = list_iterator_create(as_pgsql_total_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ if ((rc = create_cluster_tables(pgsql_conn, cluster_name)) ++ != SLURM_SUCCESS) ++ break; ++ } ++ list_iterator_destroy(itr); ++ if (rc != SLURM_SUCCESS) { ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ return rc; ++ } ++ ++ rc = as_pgsql_convert_tables_post_create(pgsql_conn); ++ ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ if (rc != SLURM_SUCCESS) { ++ error("issue converting tables after create"); ++ return rc; ++ } ++ ++ ending = xstrdup_printf(", primary key (acct, \"user\"));" ++ "CREATE INDEX IF NOT EXISTS %s_user_idx ON %s (\"user\");", ++ acct_coord_table, acct_coord_table); ++ if (pgsql_db_create_table(pgsql_conn, acct_coord_table, ++ acct_coord_table_fields, ending) == SLURM_ERROR) { ++ xfree(ending); ++ return SLURM_ERROR; ++ } ++ xfree(ending); ++ ++ if (pgsql_db_create_table(pgsql_conn, acct_table, acct_table_fields, ++ ", primary key (name));") == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ ending = xstrdup_printf(", primary key (id));" ++ "CREATE UNIQUE INDEX IF NOT EXISTS %s_udex ON %s (name, server, type);", ++ res_table, res_table); ++ if (pgsql_db_create_table(pgsql_conn, res_table, ++ res_table_fields, ending) == SLURM_ERROR) { ++ xfree(ending); ++ return SLURM_ERROR; ++ } ++ xfree(ending); ++ ++ ending = xstrdup_printf(", primary key (res_id, cluster));" ++ "CREATE UNIQUE INDEX IF NOT EXISTS %s_udex ON %s (res_id, substr(cluster, 1, 42));", ++ clus_res_table, clus_res_table); ++ if (pgsql_db_create_table(pgsql_conn, clus_res_table, ++ clus_res_table_fields, ending) == SLURM_ERROR) { ++ xfree(ending); ++ return SLURM_ERROR; ++ } ++ xfree(ending); ++ ++ ending = xstrdup_printf(", primary key (id));" ++ "CREATE UNIQUE INDEX IF NOT EXISTS %s_udex ON %s (substr(name, 1, 42));", ++ qos_table, qos_table); ++ if (pgsql_db_create_table(pgsql_conn, qos_table, ++ qos_table_fields, ending) == SLURM_ERROR) { ++ xfree(ending); ++ return SLURM_ERROR; ++ } else { ++ xfree(ending); ++ int qos_id = 0; ++ if (slurmdbd_conf && slurmdbd_conf->default_qos) { ++ List char_list = list_create(xfree_ptr); ++ char *qos = NULL; ++ ++ slurm_addto_char_list(char_list, ++ slurmdbd_conf->default_qos); ++ /* ++ * NOTE: You have to use slurm_list_pop here, since ++ * pgsql is exporting something of the same type as a ++ * macro, which messes everything up ++ * (my_list.h is the bad boy). ++ */ ++ while ((qos = slurm_list_pop(char_list))) { ++ query = xstrdup_printf( ++ "INSERT INTO %s " ++ "(creation_time, mod_time, name, " ++ "description) " ++ "VALUES (%ld, %ld, '%s', " ++ "'Added as default') " ++ "ON CONFLICT (substr(name, 1, 42)) DO UPDATE " ++ "SET deleted = 0;", ++ qos_table, now, now, qos); ++ ++ DB_DEBUG(DB_QOS, pgsql_conn->conn, "%s", query); ++ qos_id = (int)pgsql_db_insert_ret_id( ++ pgsql_conn, query); ++ if (!qos_id) ++ fatal("problem added qos '%s", qos); ++ xstrfmtcat(default_qos_str, ",%d", qos_id); ++ xfree(query); ++ xfree(qos); ++ } ++ FREE_NULL_LIST(char_list); ++ } else { ++ query = xstrdup_printf( ++ "INSERT INTO %s " ++ "(creation_time, mod_time, name, description) " ++ "VALUES (%ld, %ld, 'normal', " ++ "'Normal QOS default') " ++ "ON CONFLICT (substr(name, 1, 42)) DO UPDATE " ++ "SET deleted = 0;", ++ qos_table, now, now); ++ ++ DB_DEBUG(DB_QOS, pgsql_conn->conn, "%s", query); ++ qos_id = (int)pgsql_db_insert_ret_id(pgsql_conn, query); ++ if (!qos_id) ++ fatal("problem added qos 'normal"); ++ ++ xstrfmtcat(default_qos_str, ",%d", qos_id); ++ xfree(query); ++ } ++ ++ if (_set_qos_cnt(pgsql_conn) != SLURM_SUCCESS) ++ return SLURM_ERROR; ++ } ++ ++ /* This must be ran after create_cluster_tables() */ ++ if (pgsql_db_create_table(pgsql_conn, user_table, user_table_fields, ++ ", primary key (name))") == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ if (pgsql_db_create_table(pgsql_conn, federation_table, ++ federation_table_fields, ++ ", primary key (name))") == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ rc = as_pgsql_convert_non_cluster_tables_post_create(pgsql_conn); ++ ++ if (rc != SLURM_SUCCESS) { ++ error("issue converting non-cluster tables after create"); ++ return rc; ++ } ++ ++ rc2 = pgsql_db_query(pgsql_conn, get_parent_proc); ++ if (rc2 != SLURM_SUCCESS) ++ rc = rc2; ++ rc2 = pgsql_db_query(pgsql_conn, get_coord_qos); ++ if (rc2 != SLURM_SUCCESS) ++ rc = rc2; ++ ++ /* Add user root to be a user by default and have this default ++ * account be root. If already there just update ++ * name='root'. That way if the admins delete it it will ++ * remain deleted. Creation time will be 0 so it will never ++ * really be deleted. ++ */ ++ query = xstrdup_printf( ++ "INSERT INTO %s (creation_time, mod_time, name, admin_level) " ++ "VALUES (%ld, %ld, 'root', %d) " ++ "ON CONFLICT (name) DO UPDATE " ++ "SET name = 'root';", ++ user_table, (long)now, (long)now, SLURMDB_ADMIN_SUPER_USER); ++ ++ xstrfmtcat(query, ++ "INSERT INTO %s (creation_time, mod_time, name, description, organization) " ++ "VALUES (%ld, %ld, 'root', 'default root account', 'root') " ++ "ON CONFLICT (name) DO UPDATE " ++ "SET name = 'root';", ++ acct_table, (long)now, (long)now); ++ ++ ++ //DB_DEBUG(pgsql_conn->conn, "%s", query); ++ pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ return rc; ++} ++ ++/* This should be added to the beginning of each function to make sure ++ * we have a connection to the database before we try to use it. ++ */ ++extern int check_connection(pgsql_conn_t *pgsql_conn) ++{ ++ if (!pgsql_conn) { ++ error("We need a connection to run this"); ++ errno = ESLURM_DB_CONNECTION; ++ return ESLURM_DB_CONNECTION; ++ } else if (pgsql_db_ping(pgsql_conn) != 0) { ++ /* avoid memory leak and end thread */ ++ pgsql_db_close_db_connection(pgsql_conn); ++ if (pgsql_db_get_db_connection( ++ pgsql_conn, pgsql_db_name, pgsql_db_info) ++ != SLURM_SUCCESS) { ++ error("unable to re-connect to as_pgsql database"); ++ errno = ESLURM_DB_CONNECTION; ++ return ESLURM_DB_CONNECTION; ++ } ++ } ++ ++ if (pgsql_conn->cluster_deleted) { ++ errno = ESLURM_CLUSTER_DELETED; ++ return ESLURM_CLUSTER_DELETED; ++ } ++ ++ return SLURM_SUCCESS; ++} ++ ++extern void reset_pgsql_conn(pgsql_conn_t *pgsql_conn) ++{ ++ if (pgsql_conn->rollback) ++ pgsql_db_rollback(pgsql_conn); ++ xfree(pgsql_conn->pre_commit_query); ++ list_flush(pgsql_conn->update_list); ++} ++ ++extern int create_cluster_assoc_table( ++ pgsql_conn_t *pgsql_conn, char *cluster_name) ++{ ++ debug4("create cluster assoc table\n"); ++ storage_field_t assoc_table_fields[] = { ++ { "creation_time", "bigint", "not null" }, ++ { "mod_time", "bigint", "default 0|not null" }, ++ { "deleted", "boolean", "default false|not null" }, ++ { "is_def", "boolean", "default false|not null" }, ++ { "id_assoc", "serial", "not null" }, ++ { "user", "text", "not null|default ''" }, ++ { "acct", "text", "not null" }, ++ { "partition", "text", "not null|default ''" }, ++ { "parent_acct", "text", "not null|default ''" }, ++ { "lft", "integer", "not null" }, ++ { "rgt", "integer", "not null" }, ++ { "shares", "integer", "default 1|not null" }, ++ { "max_jobs", "integer", "default NULL" }, ++ { "max_jobs_accrue", "integer", "default NULL" }, ++ { "min_prio_thresh", "integer", "default NULL" }, ++ { "max_submit_jobs", "integer", "default NULL" }, ++ { "max_tres_pj", "text", "not null|default ''" }, ++ { "max_tres_pn", "text", "not null|default ''" }, ++ { "max_tres_mins_pj", "text", "not null|default ''" }, ++ { "max_tres_run_mins", "text", "not null|default ''" }, ++ { "max_wall_pj", "integer", "default NULL" }, ++ { "grp_jobs", "integer", "default NULL" }, ++ { "grp_jobs_accrue", "integer", "default NULL" }, ++ { "grp_submit_jobs", "integer", "default NULL" }, ++ { "grp_tres", "text", "not null|default ''" }, ++ { "grp_tres_mins", "text", "not null|default ''" }, ++ { "grp_tres_run_mins", "text", "not null|default ''" }, ++ { "grp_wall", "integer", "default NULL" }, ++ { "priority", "integer", "default NULL" }, ++ { "def_qos_id", "integer", "default NULL" }, ++ { "qos", "bytea", "not null|default ''" }, ++ { "delta_qos", "bytea", "not null|default ''" }, ++ { NULL, NULL} ++ }; ++ ++ char table_name[200]; ++ char* ending = NULL; ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, assoc_table); ++ ending = xstrdup_printf(", primary key (id_assoc));" ++ "CREATE UNIQUE INDEX IF NOT EXISTS %s_udex ON %s " ++ "(substr(\"user\", 1, 42), substr(acct, 1, 42), substr(\"partition\", 1, 42));" ++ "CREATE INDEX IF NOT EXISTS %s_lft_idx ON %s (lft);" ++ "CREATE INDEX IF NOT EXISTS %s_acct_idx ON %s (acct);", ++ table_name, table_name, table_name, table_name, table_name, table_name); ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ assoc_table_fields, ending) == SLURM_ERROR) { ++ xfree(ending); ++ return SLURM_ERROR; ++ } ++ xfree(ending); ++ ++ return SLURM_SUCCESS; ++} ++ ++extern int create_cluster_tables(pgsql_conn_t *pgsql_conn, char *cluster_name) ++{ ++ debug4("create cluster tables\n"); ++ storage_field_t cluster_usage_table_fields[] = { ++ { "creation_time", "bigint", "not null" }, ++ { "mod_time", "bigint", "default 0|not null" }, ++ { "deleted", "boolean", "default false|not null" }, ++ { "id_tres", "integer", "not null" }, ++ { "time_start", "bigint", "not null" }, ++ { "count", "bigint", "default 0|not null" }, ++ { "alloc_secs", "bigint", "default 0|not null" }, ++ { "down_secs", "bigint", "default 0|not null" }, ++ { "pdown_secs", "bigint", "default 0|not null" }, ++ { "idle_secs", "bigint", "default 0|not null" }, ++ { "plan_secs", "bigint", "default 0|not null" }, ++ { "over_secs", "bigint", "default 0|not null" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t event_table_fields[] = { ++ { "time_start", "bigint", "not null" }, ++ { "time_end", "bigint", "default 0|not null" }, ++ { "node_name", "varchar", "default ''|not null" }, ++ { "cluster_nodes", "text", "not null|default ''" }, ++ { "reason", "varchar", "not null" }, ++ { "reason_uid", "integer", "default 4294967294|not null" }, ++ { "state", "integer", "default 0|not null" }, ++ { "tres", "text", "not null|default ''" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t id_usage_table_fields[] = { ++ { "creation_time", "bigint", "not null" }, ++ { "mod_time", "bigint", "default 0|not null" }, ++ { "deleted", "boolean", "default false|not null" }, ++ { "id", "integer", "not null" }, ++ { "id_tres", "integer", "default 1|not null" }, ++ { "time_start", "bigint", "not null" }, ++ { "alloc_secs", "bigint", "default 0|not null" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t job_table_fields[] = { ++ { "job_db_inx", "bigserial", "not null" }, ++ { "mod_time", "bigint", "default 0|not null" }, ++ { "deleted", "boolean", "default false|not null" }, ++ { "account", "text", "" }, ++ { "admin_comment", "text", "" }, ++ { "array_task_str", "text", "" }, ++ { "array_max_tasks", "integer", "default 0|not null" }, ++ { "array_task_pending", "integer", "default 0|not null" }, ++ { "batch_script", "text", "" }, ++ { "constraints", "text", "default ''" }, ++ { "container", "text", "" }, ++ { "cpus_req", "integer", "not null" }, ++ { "derived_ec", "integer", "default 0|not null" }, ++ { "derived_es", "text", "" }, ++ { "env_vars", "text", "" }, ++ { "exit_code", "integer", "default 0|not null" }, ++ { "flags", "integer", "default 0|not null" }, ++ { "job_name", "varchar", "not null" }, ++ { "id_assoc", "integer", "not null" }, ++ { "id_array_job", "integer", "default 0|not null" }, ++ { "id_array_task", "integer", "default 4294967294|not null" }, ++ { "id_block", "varchar", "" }, ++ { "id_job", "integer", "not null" }, ++ { "id_qos", "integer", "default 0|not null" }, ++ { "id_resv", "integer", "not null" }, ++ { "id_wckey", "integer", "not null" }, ++ { "id_user", "integer", "not null" }, ++ { "id_group", "integer", "not null" }, ++ { "het_job_id", "integer", "not null" }, ++ { "het_job_offset", "integer", "not null" }, ++ { "kill_requid", "integer", "default -1|not null" }, ++ { "state_reason_prev", "integer", "not null" }, ++ { "mcs_label", "varchar", "default ''" }, ++ { "mem_req", "bigint", "default 0|not null" }, ++ { "nodelist", "text", "" }, ++ { "nodes_alloc", "integer", "not null" }, ++ { "node_inx", "text", "" }, ++ { "partition", "varchar", "not null" }, ++ { "priority", "integer", "not null" }, ++ { "state", "integer", "not null" }, ++ { "timelimit", "integer", "default 0|not null" }, ++ { "time_submit", "bigint", "default 0|not null" }, ++ { "time_eligible", "bigint", "default 0|not null" }, ++ { "time_start", "bigint", "default 0|not null" }, ++ { "time_end", "bigint", "default 0|not null" }, ++ { "time_suspended", "bigint", "default 0|not null" }, ++ { "gres_used", "text", "not null|default ''" }, ++ { "wckey", "varchar", "not null|default ''" }, ++ { "work_dir", "text", "not null|default ''" }, ++ { "submit_line", "text", "" }, ++ { "system_comment", "text", "" }, ++ { "track_steps", "smallint", "not null" }, ++ { "tres_alloc", "text", "not null|default ''" }, ++ { "tres_req", "text", "not null|default ''" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t last_ran_table_fields[] = { ++ { "hourly_rollup", "bigint", "default 0|not null" }, ++ { "daily_rollup", "bigint", "default 0|not null" }, ++ { "monthly_rollup", "bigint", "default 0|not null" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t resv_table_fields[] = { ++ { "id_resv", "integer", "default 0|not null" }, ++ { "deleted", "smallint", "default 0|not null" }, ++ { "assoclist", "text", "not null|default ''" }, ++ { "flags", "bigint", "default 0|not null" }, ++ { "nodelist", "text", "not null|default ''" }, ++ { "node_inx", "text", "not null|default ''" }, ++ { "resv_name", "text", "not null" }, ++ { "time_start", "bigint", "default 0|not null"}, ++ { "time_end", "bigint", "default 0|not null" }, ++ { "tres", "text", "not null|default ''" }, ++ { "unused_wall", "double precision", "default 0.0|not null" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t step_table_fields[] = { ++ { "job_db_inx", "bigint", "not null" }, ++ { "deleted", "smallint", "default 0|not null" }, ++ { "exit_code", "integer", "default 0|not null" }, ++ { "id_step", "integer", "not null" }, ++ { "step_het_comp", "integer", "default 4294967294|not null" }, ++ { "kill_requid", "integer", "default -1|not null" }, ++ { "nodelist", "text", "not null" }, ++ { "nodes_alloc", "integer", "not null" }, ++ { "node_inx", "text", "" }, ++ { "state", "smallint", "not null" }, ++ { "step_name", "text", "not null" }, ++ { "task_cnt", "integer", "not null" }, ++ { "task_dist", "integer", "default 0|not null" }, ++ { "time_start", "bigint", "default 0|not null" }, ++ { "time_end", "bigint", "default 0|not null" }, ++ { "time_suspended", "bigint", "default 0|not null" }, ++ { "user_sec", "bigint", "default 0|not null" }, ++ { "user_usec", "integer", "default 0|not null" }, ++ { "sys_sec", "bigint", "default 0|not null" }, ++ { "sys_usec", "integer", "default 0|not null" }, ++ { "act_cpufreq", "double precision", "default 0.0|not null" }, ++ { "consumed_energy", "bigint", "default 0|not null" }, ++ { "container", "text", "" }, ++ { "req_cpufreq_min", "integer", "default 0|not null" }, ++ { "req_cpufreq", "integer", "default 0|not null" }, ++ { "req_cpufreq_gov", "integer", "default 0|not null" }, ++ { "submit_line", "text", "" }, ++ { "tres_alloc", "text", "not null|default ''" }, ++ { "tres_usage_in_ave", "text", "not null|default ''" }, ++ { "tres_usage_in_max", "text", "not null|default ''" }, ++ { "tres_usage_in_max_taskid", "text", "not null|default ''" }, ++ { "tres_usage_in_max_nodeid", "text", "not null|default ''" }, ++ { "tres_usage_in_min", "text", "not null|default ''" }, ++ { "tres_usage_in_min_taskid", "text", "not null|default ''" }, ++ { "tres_usage_in_min_nodeid", "text", "not null|default ''" }, ++ { "tres_usage_in_tot", "text", "not null|default ''" }, ++ { "tres_usage_out_ave", "text", "not null|default ''" }, ++ { "tres_usage_out_max", "text", "not null|default ''" }, ++ { "tres_usage_out_max_taskid", "text", "not null|default ''" }, ++ { "tres_usage_out_max_nodeid", "text", "not null|default ''" }, ++ { "tres_usage_out_min", "text", "not null|default ''" }, ++ { "tres_usage_out_min_taskid", "text", "not null|default ''" }, ++ { "tres_usage_out_min_nodeid", "text", "not null|default ''" }, ++ { "tres_usage_out_tot", "text", "not null|default ''" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t suspend_table_fields[] = { ++ { "job_db_inx", "bigint", "not null" }, ++ { "id_assoc", "integer", "not null" }, ++ { "time_start", "bigint", "default 0|not null" }, ++ { "time_end", "bigint", "default 0|not null" }, ++ { NULL, NULL} ++ }; ++ ++ storage_field_t wckey_table_fields[] = { ++ { "creation_time", "bigint", "not null" }, ++ { "mod_time", "bigint", "default 0|not null" }, ++ { "deleted", "smallint", "default 0|not null" }, ++ { "is_def", "smallint", "default 0|not null" }, ++ { "id_wckey", "serial", "not null" }, ++ { "wckey_name", "text", "not null|default ''" }, ++ { "user", "text", "not null" }, ++ { NULL, NULL} ++ }; ++ ++ char table_name[200]; ++ char* ending = NULL; ++ ++ if (create_cluster_assoc_table(pgsql_conn, cluster_name) ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, assoc_day_table); ++ ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ id_usage_table_fields, ++ ", primary key (id, id_tres, time_start));") ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, assoc_hour_table); ++ ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ id_usage_table_fields, ++ ", primary key (id, id_tres, time_start));") ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, assoc_month_table); ++ ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ id_usage_table_fields, ++ ", primary key (id, id_tres, time_start));") ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, cluster_day_table); ++ ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ cluster_usage_table_fields, ++ ", primary key (id_tres, time_start));") ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, cluster_hour_table); ++ ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ cluster_usage_table_fields, ++ ", primary key (id_tres, time_start));") ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, cluster_month_table); ++ ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ cluster_usage_table_fields, ++ ", primary key (id_tres, time_start));") ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, event_table); ++ ++ ending = xstrdup_printf(", primary key (node_name, time_start));" ++ "CREATE INDEX IF NOT EXISTS %s_rollup_idx ON %s " ++ "(substr(node_name, 1, 42), time_start, time_end, state);", ++ table_name, table_name); ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ event_table_fields, ending) == SLURM_ERROR) { ++ xfree(ending); ++ return SLURM_ERROR; ++ } ++ xfree(ending); ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, job_table); ++ ++ /* ++ * sacct_def is the index for query's with state as time_start is used ++ * in these queries. sacct_def2 is for plain sacct queries. ++ */ ++ ending = xstrdup_printf(", primary key (job_db_inx));" ++ "CREATE UNIQUE INDEX IF NOT EXISTS %s_udx ON %s (id_job, time_submit);" ++ "CREATE INDEX IF NOT EXISTS %s_old_tuple_idx ON %s (id_job, id_assoc, time_submit);" ++ "CREATE INDEX IF NOT EXISTS %s_rollup_idx ON %s (time_eligible, time_end);" ++ "CREATE INDEX IF NOT EXISTS %s_rollup2_idx ON %s (time_end, time_eligible);" ++ "CREATE INDEX IF NOT EXISTS %s_nodes_alloc_idx ON %s (nodes_alloc);" ++ "CREATE INDEX IF NOT EXISTS %s_wckey_idx ON %s (id_wckey);" ++ "CREATE INDEX IF NOT EXISTS %s_qos_idx ON %s (id_qos);" ++ "CREATE INDEX IF NOT EXISTS %s_association_idx ON %s (id_assoc);" ++ "CREATE INDEX IF NOT EXISTS %s_array_job_idx ON %s (id_array_job);" ++ "CREATE INDEX IF NOT EXISTS %s_het_job_idx ON %s (het_job_id);" ++ "CREATE INDEX IF NOT EXISTS %s_reserv_idx ON %s (id_resv);" ++ "CREATE INDEX IF NOT EXISTS %s_sacct_def_idx ON %s (id_user, time_start, time_end);" ++ "CREATE INDEX IF NOT EXISTS %s_sacct_def2_idx ON %s (id_user, time_end, time_eligible);", ++ table_name, table_name, table_name, table_name, table_name, table_name, ++ table_name, table_name, table_name, table_name, table_name, table_name, ++ table_name, table_name, table_name, table_name, table_name, table_name, ++ table_name, table_name, table_name, table_name, table_name, table_name, ++ table_name, table_name); ++ ++ if (pgsql_db_create_table(pgsql_conn, table_name, job_table_fields, ending) ++ == SLURM_ERROR) { ++ xfree(ending); ++ return SLURM_ERROR; ++ } ++ xfree(ending); ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, last_ran_table); ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ last_ran_table_fields, ++ ", primary key (hourly_rollup, " ++ "daily_rollup, monthly_rollup));") ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, resv_table); ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ resv_table_fields, ++ ", primary key (id_resv, time_start));") ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, step_table); ++ ending = xstrdup_printf(", primary key (job_db_inx, id_step, step_het_comp));" ++ "CREATE INDEX IF NOT EXISTS %s_no_step_comp_idx ON %s (job_db_inx, id_step);" ++ "CREATE INDEX IF NOT EXISTS %s_time_start_end_idx ON %s (time_start, time_end);", ++ table_name, table_name, table_name, table_name); ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ step_table_fields, ending) == SLURM_ERROR) { ++ xfree(ending); ++ return SLURM_ERROR; ++ } ++ xfree(ending); ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, suspend_table); ++ ending = xstrdup_printf(", primary key (job_db_inx, time_start));" ++ "CREATE INDEX IF NOT EXISTS %s_job_db_inx_times_idx ON %s (job_db_inx, time_start, time_end);", ++ table_name, table_name); ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ suspend_table_fields, ending) == SLURM_ERROR) { ++ xfree(ending); ++ return SLURM_ERROR; ++ } ++ xfree(ending); ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, wckey_table); ++ ending = xstrdup_printf(", primary key (id_wckey));" ++ "CREATE UNIQUE INDEX IF NOT EXISTS %s_udex ON %s (substr(wckey_name, 1, 42), substr(user, 1, 42));", ++ table_name, table_name); ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ wckey_table_fields, ending) == SLURM_ERROR) { ++ xfree(ending); ++ return SLURM_ERROR; ++ } ++ xfree(ending); ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, wckey_day_table); ++ ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ id_usage_table_fields, ++ ", primary key (id, id_tres, time_start));") ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, wckey_hour_table); ++ ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ id_usage_table_fields, ++ ", primary key (id, id_tres, time_start));") ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ snprintf(table_name, sizeof(table_name), "\"%s_%s\"", ++ cluster_name, wckey_month_table); ++ ++ if (pgsql_db_create_table(pgsql_conn, table_name, ++ id_usage_table_fields, ++ ", primary key (id, id_tres, time_start));") ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ return SLURM_SUCCESS; ++} ++ ++extern int remove_cluster_tables(pgsql_conn_t *pgsql_conn, char *cluster_name) ++{ ++ char *query = NULL; ++ int rc = SLURM_SUCCESS; ++ pgsql_res_t* result = NULL; ++ ++ query = xstrdup_printf("select id_assoc from \"%s_%s\" limit 1;", ++ cluster_name, assoc_table); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ error("no result given when querying cluster %s", cluster_name); ++ return SLURM_ERROR; ++ } ++ ++ if (pgsql_num_rows(result)) { ++ pgsql_free_result(&result); ++ debug4("we still have associations, can't remove tables"); ++ return SLURM_SUCCESS; ++ } ++ pgsql_free_result(&result); ++ xstrfmtcat(pgsql_conn->pre_commit_query, ++ "drop table \"%s_%s\", \"%s_%s\", \"%s_%s\", " ++ "\"%s_%s\", \"%s_%s\", \"%s_%s\", \"%s_%s\", " ++ "\"%s_%s\", \"%s_%s\", \"%s_%s\", \"%s_%s\", " ++ "\"%s_%s\", \"%s_%s\", \"%s_%s\", \"%s_%s\", " ++ "\"%s_%s\", \"%s_%s\";", ++ cluster_name, assoc_table, ++ cluster_name, assoc_day_table, ++ cluster_name, assoc_hour_table, ++ cluster_name, assoc_month_table, ++ cluster_name, cluster_day_table, ++ cluster_name, cluster_hour_table, ++ cluster_name, cluster_month_table, ++ cluster_name, event_table, ++ cluster_name, job_table, ++ cluster_name, last_ran_table, ++ cluster_name, resv_table, ++ cluster_name, step_table, ++ cluster_name, suspend_table, ++ cluster_name, wckey_table, ++ cluster_name, wckey_day_table, ++ cluster_name, wckey_hour_table, ++ cluster_name, wckey_month_table); ++ /* Since we could possibly add this exact cluster after this ++ we will require a commit before doing anything else. This ++ flag will give us that. ++ */ ++ pgsql_conn->cluster_deleted = 1; ++ return rc; ++} ++ ++extern int setup_assoc_limits(slurmdb_assoc_rec_t *assoc, ++ char **cols, char **vals, ++ char **extra, qos_level_t qos_level, ++ bool for_add) ++{ ++ uint32_t tres_str_flags = TRES_STR_FLAG_REMOVE | ++ TRES_STR_FLAG_SORT_ID | TRES_STR_FLAG_SIMPLE | ++ TRES_STR_FLAG_NO_NULL; ++ ++ assoc_mgr_lock_t locks = { NO_LOCK, NO_LOCK, READ_LOCK, NO_LOCK, ++ NO_LOCK, NO_LOCK, NO_LOCK }; ++ if (!assoc) ++ return SLURM_ERROR; ++ ++ if (for_add) { ++ /* If we are adding we should make sure we don't get ++ old reside sitting around from a former life. ++ */ ++ if (assoc->shares_raw == NO_VAL) ++ assoc->shares_raw = INFINITE; ++ if (assoc->grp_jobs == NO_VAL) ++ assoc->grp_jobs = INFINITE; ++ if (assoc->grp_jobs_accrue == NO_VAL) ++ assoc->grp_jobs_accrue = INFINITE; ++ if (assoc->grp_submit_jobs == NO_VAL) ++ assoc->grp_submit_jobs = INFINITE; ++ if (assoc->grp_wall == NO_VAL) ++ assoc->grp_wall = INFINITE; ++ if (assoc->max_jobs == NO_VAL) ++ assoc->max_jobs = INFINITE; ++ if (assoc->max_jobs_accrue == NO_VAL) ++ assoc->max_jobs_accrue = INFINITE; ++ if (assoc->min_prio_thresh == NO_VAL) ++ assoc->min_prio_thresh = INFINITE; ++ if (assoc->max_submit_jobs == NO_VAL) ++ assoc->max_submit_jobs = INFINITE; ++ if (assoc->max_wall_pj == NO_VAL) ++ assoc->max_wall_pj = INFINITE; ++ if (assoc->priority == NO_VAL) ++ assoc->priority = INFINITE; ++ if (assoc->def_qos_id == NO_VAL) ++ assoc->def_qos_id = INFINITE; ++ } ++ ++ if (assoc->shares_raw == INFINITE) { ++ xstrcat(*cols, ", shares"); ++ xstrcat(*vals, ", 1"); ++ xstrcat(*extra, ", shares=1"); ++ assoc->shares_raw = 1; ++ } else if ((assoc->shares_raw != NO_VAL) ++ && (int32_t)assoc->shares_raw >= 0) { ++ xstrcat(*cols, ", shares"); ++ xstrfmtcat(*vals, ", %u", assoc->shares_raw); ++ xstrfmtcat(*extra, ", shares=%u", assoc->shares_raw); ++ } ++ ++ if (assoc->grp_jobs == INFINITE) { ++ xstrcat(*cols, ", grp_jobs"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", grp_jobs=NULL"); ++ } else if ((assoc->grp_jobs != NO_VAL) ++ && ((int32_t)assoc->grp_jobs >= 0)) { ++ xstrcat(*cols, ", grp_jobs"); ++ xstrfmtcat(*vals, ", %u", assoc->grp_jobs); ++ xstrfmtcat(*extra, ", grp_jobs=%u", assoc->grp_jobs); ++ } ++ ++ if (assoc->grp_jobs_accrue == INFINITE) { ++ xstrcat(*cols, ", grp_jobs_accrue"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", grp_jobs_accrue=NULL"); ++ } else if ((assoc->grp_jobs_accrue != NO_VAL) ++ && ((int32_t)assoc->grp_jobs_accrue >= 0)) { ++ xstrcat(*cols, ", grp_jobs_accrue"); ++ xstrfmtcat(*vals, ", %u", assoc->grp_jobs_accrue); ++ xstrfmtcat(*extra, ", grp_jobs_accrue=%u", ++ assoc->grp_jobs_accrue); ++ } ++ ++ if (assoc->grp_submit_jobs == INFINITE) { ++ xstrcat(*cols, ", grp_submit_jobs"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", grp_submit_jobs=NULL"); ++ } else if ((assoc->grp_submit_jobs != NO_VAL) ++ && ((int32_t)assoc->grp_submit_jobs >= 0)) { ++ xstrcat(*cols, ", grp_submit_jobs"); ++ xstrfmtcat(*vals, ", %u", assoc->grp_submit_jobs); ++ xstrfmtcat(*extra, ", grp_submit_jobs=%u", ++ assoc->grp_submit_jobs); ++ } ++ ++ if (assoc->grp_wall == INFINITE) { ++ xstrcat(*cols, ", grp_wall"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", grp_wall=NULL"); ++ } else if ((assoc->grp_wall != NO_VAL) ++ && ((int32_t)assoc->grp_wall >= 0)) { ++ xstrcat(*cols, ", grp_wall"); ++ xstrfmtcat(*vals, ", %u", assoc->grp_wall); ++ xstrfmtcat(*extra, ", grp_wall=%u", assoc->grp_wall); ++ } ++ ++ /* this only gets set on a user's association and is_def ++ * could be NO_VAL only 1 is accepted */ ++ if ((assoc->is_def == 1) ++ && ((qos_level == QOS_LEVEL_MODIFY) ++ || (assoc->user && assoc->cluster && assoc->acct))) { ++ xstrcat(*cols, ", is_def"); ++ xstrcat(*vals, ", 1"); ++ xstrcat(*extra, ", is_def=1"); ++ } ++ ++ if (assoc->max_jobs == INFINITE) { ++ xstrcat(*cols, ", max_jobs"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", max_jobs=NULL"); ++ } else if ((assoc->max_jobs != NO_VAL) ++ && ((int32_t)assoc->max_jobs >= 0)) { ++ xstrcat(*cols, ", max_jobs"); ++ xstrfmtcat(*vals, ", %u", assoc->max_jobs); ++ xstrfmtcat(*extra, ", max_jobs=%u", assoc->max_jobs); ++ } ++ ++ if (assoc->max_jobs_accrue == INFINITE) { ++ xstrcat(*cols, ", max_jobs_accrue"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", max_jobs_accrue=NULL"); ++ } else if ((assoc->max_jobs_accrue != NO_VAL) ++ && ((int32_t)assoc->max_jobs_accrue >= 0)) { ++ xstrcat(*cols, ", max_jobs_accrue"); ++ xstrfmtcat(*vals, ", %u", assoc->max_jobs_accrue); ++ xstrfmtcat(*extra, ", max_jobs_accrue=%u", ++ assoc->max_jobs_accrue); ++ } ++ ++ if (assoc->min_prio_thresh == INFINITE) { ++ xstrcat(*cols, ", min_prio_thresh"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", min_prio_thresh=NULL"); ++ } else if ((assoc->min_prio_thresh != NO_VAL) ++ && ((int32_t)assoc->min_prio_thresh >= 0)) { ++ xstrcat(*cols, ", min_prio_thresh"); ++ xstrfmtcat(*vals, ", %u", assoc->min_prio_thresh); ++ xstrfmtcat(*extra, ", min_prio_thresh=%u", ++ assoc->min_prio_thresh); ++ } ++ ++ if (assoc->max_submit_jobs == INFINITE) { ++ xstrcat(*cols, ", max_submit_jobs"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", max_submit_jobs=NULL"); ++ } else if ((assoc->max_submit_jobs != NO_VAL) ++ && ((int32_t)assoc->max_submit_jobs >= 0)) { ++ xstrcat(*cols, ", max_submit_jobs"); ++ xstrfmtcat(*vals, ", %u", assoc->max_submit_jobs); ++ xstrfmtcat(*extra, ", max_submit_jobs=%u", ++ assoc->max_submit_jobs); ++ } ++ ++ if (assoc->max_wall_pj == INFINITE) { ++ xstrcat(*cols, ", max_wall_pj"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", max_wall_pj=NULL"); ++ } else if ((assoc->max_wall_pj != NO_VAL) ++ && ((int32_t)assoc->max_wall_pj >= 0)) { ++ xstrcat(*cols, ", max_wall_pj"); ++ xstrfmtcat(*vals, ", %u", assoc->max_wall_pj); ++ xstrfmtcat(*extra, ", max_wall_pj=%u", assoc->max_wall_pj); ++ } ++ ++ if (assoc->priority == INFINITE) { ++ xstrcat(*cols, ", priority"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", priority=NULL"); ++ } else if ((assoc->priority != NO_VAL) ++ && ((int32_t)assoc->priority >= 0)) { ++ xstrcat(*cols, ", priority"); ++ xstrfmtcat(*vals, ", %u", assoc->priority); ++ xstrfmtcat(*extra, ", priority=%u", assoc->priority); ++ } ++ ++ if (assoc->def_qos_id == INFINITE) { ++ xstrcat(*cols, ", def_qos_id"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", def_qos_id=NULL"); ++ } else if ((assoc->def_qos_id != NO_VAL) ++ && ((int32_t)assoc->def_qos_id > 0)) { ++ assoc_mgr_lock(&locks); ++ if (!list_find_first(assoc_mgr_qos_list, ++ slurmdb_find_qos_in_list, &(assoc->def_qos_id))) { ++ assoc_mgr_unlock(&locks); ++ return ESLURM_INVALID_QOS; ++ } ++ assoc_mgr_unlock(&locks); ++ xstrcat(*cols, ", def_qos_id"); ++ xstrfmtcat(*vals, ", %u", assoc->def_qos_id); ++ xstrfmtcat(*extra, ", def_qos_id=%u", assoc->def_qos_id); ++ } ++ ++ /* When modifying anything below this comment it happens in ++ * the actual function since we have to wait until we hear ++ * about the parent first. ++ * What we do to make it known something needs to be changed ++ * is we cat "" onto extra which will inform the caller ++ * something needs changing. ++ */ ++ ++ if (assoc->grp_tres) { ++ if (qos_level == QOS_LEVEL_MODIFY) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", grp_tres"); ++ slurmdb_combine_tres_strings( ++ &assoc->grp_tres, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", assoc->grp_tres); ++ xstrfmtcat(*extra, ", grp_tres='%s'", assoc->grp_tres); ++ } ++ ++ if (assoc->grp_tres_mins) { ++ if (qos_level == QOS_LEVEL_MODIFY) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", grp_tres_mins"); ++ slurmdb_combine_tres_strings( ++ &assoc->grp_tres_mins, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", assoc->grp_tres_mins); ++ xstrfmtcat(*extra, ", grp_tres_mins='%s'", ++ assoc->grp_tres_mins); ++ } ++ ++ if (assoc->grp_tres_run_mins) { ++ if (qos_level == QOS_LEVEL_MODIFY) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", grp_tres_run_mins"); ++ slurmdb_combine_tres_strings( ++ &assoc->grp_tres_run_mins, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", assoc->grp_tres_run_mins); ++ xstrfmtcat(*extra, ", grp_tres_run_mins='%s'", ++ assoc->grp_tres_run_mins); ++ } ++ ++ if (assoc->max_tres_pj) { ++ if (qos_level == QOS_LEVEL_MODIFY) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", max_tres_pj"); ++ slurmdb_combine_tres_strings( ++ &assoc->max_tres_pj, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", assoc->max_tres_pj); ++ xstrfmtcat(*extra, ", max_tres_pj='%s'", assoc->max_tres_pj); ++ } ++ ++ if (assoc->max_tres_pn) { ++ if (qos_level == QOS_LEVEL_MODIFY) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", max_tres_pn"); ++ slurmdb_combine_tres_strings( ++ &assoc->max_tres_pn, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", assoc->max_tres_pn); ++ xstrfmtcat(*extra, ", max_tres_pn='%s'", assoc->max_tres_pn); ++ } ++ ++ if (assoc->max_tres_mins_pj) { ++ if (qos_level == QOS_LEVEL_MODIFY) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", max_tres_mins_pj"); ++ slurmdb_combine_tres_strings( ++ &assoc->max_tres_mins_pj, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", assoc->max_tres_mins_pj); ++ xstrfmtcat(*extra, ", max_tres_mins_pj='%s'", ++ assoc->max_tres_mins_pj); ++ } ++ ++ if (assoc->max_tres_run_mins) { ++ if (qos_level == QOS_LEVEL_MODIFY) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", max_tres_run_mins"); ++ slurmdb_combine_tres_strings( ++ &assoc->max_tres_run_mins, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", assoc->max_tres_run_mins); ++ xstrfmtcat(*extra, ", max_tres_run_mins='%s'", ++ assoc->max_tres_run_mins); ++ } ++ ++ if (assoc->qos_list && list_count(assoc->qos_list)) { ++ char *qos_type = "qos"; ++ char *qos_val = NULL; ++ char *tmp_char = NULL; ++ int set = 0; ++ ListIterator qos_itr; ++ ++ if (qos_level == QOS_LEVEL_MODIFY) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ ++ qos_itr = list_iterator_create(assoc->qos_list); ++ while ((tmp_char = list_next(qos_itr))) { ++ /* we don't want to include blank names */ ++ if (!tmp_char[0]) ++ continue; ++ if (!set) { ++ if (tmp_char[0] == '+' || tmp_char[0] == '-') ++ qos_type = "delta_qos"; ++ set = 1; ++ } ++ xstrfmtcat(qos_val, ",%s", tmp_char); ++ } ++ ++ list_iterator_destroy(qos_itr); ++ if (qos_val) { ++ xstrfmtcat(*cols, ", %s", qos_type); ++ xstrfmtcat(*vals, ", '%s,'", qos_val); ++ xstrfmtcat(*extra, ", %s='%s,'", qos_type, qos_val); ++ xfree(qos_val); ++ } ++ } else if ((qos_level == QOS_LEVEL_SET) && default_qos_str) { ++ /* Add default qos to the account */ ++ xstrcat(*cols, ", qos"); ++ xstrfmtcat(*vals, ", '%s,'", default_qos_str); ++ xstrfmtcat(*extra, ", qos='%s,'", default_qos_str); ++ if (!assoc->qos_list) ++ assoc->qos_list = list_create(xfree_ptr); ++ slurm_addto_char_list(assoc->qos_list, default_qos_str); ++ } else if (qos_level != QOS_LEVEL_MODIFY) { ++ /* clear the qos */ ++ xstrcat(*cols, ", qos, delta_qos"); ++ xstrcat(*vals, ", '', ''"); ++ xstrcat(*extra, ", qos='', delta_qos=''"); ++ } ++end_modify: ++ ++ return SLURM_SUCCESS; ++ ++} ++ ++/* This is called by most modify functions to alter the table and ++ * insert a new line in the transaction table. ++ */ ++extern int modify_common(pgsql_conn_t *pgsql_conn, ++ uint16_t type, ++ time_t now, ++ char *user_name, ++ char *table, ++ char *cond_char, ++ char *vals, ++ char *cluster_name) ++{ ++ char *query = NULL; ++ int rc = SLURM_SUCCESS; ++ char *tmp_cond_char = slurm_add_slash_to_quotes(cond_char); ++ char *tmp_vals = NULL; ++ bool cluster_centric = true; ++ ++ /* figure out which tables we need to append the cluster name to */ ++ if ((table == cluster_table) || (table == acct_coord_table) ++ || (table == acct_table) || (table == qos_table) ++ || (table == txn_table) || (table == user_table) ++ || (table == res_table) || (table == clus_res_table) ++ || (table == federation_table)) ++ cluster_centric = false; ++ ++ if (vals && vals[1]) ++ tmp_vals = slurm_add_slash_to_quotes(vals+2); ++ ++ if (cluster_centric) { ++ xassert(cluster_name); ++ xstrfmtcat(query, ++ "update \"%s_%s\" set mod_time=%ld%s " ++ "where deleted=0 && %s;", ++ cluster_name, table, now, vals, cond_char); ++ xstrfmtcat(query, ++ "insert into %s " ++ "(timestamp, action, name, cluster, actor, info) " ++ "values (%ld, %d, '%s', '%s', '%s', '%s');", ++ txn_table, ++ now, type, tmp_cond_char, cluster_name, ++ user_name, tmp_vals); ++ } else { ++ xstrfmtcat(query, ++ "update %s set mod_time=%ld%s " ++ "where deleted=0 && %s;", ++ table, now, vals, cond_char); ++ xstrfmtcat(query, ++ "insert into %s " ++ "(timestamp, action, name, actor, info) " ++ "values (%ld, %d, '%s', '%s', '%s');", ++ txn_table, ++ now, type, tmp_cond_char, user_name, tmp_vals); ++ } ++ xfree(tmp_cond_char); ++ xfree(tmp_vals); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ if (rc != SLURM_SUCCESS) { ++ reset_pgsql_conn(pgsql_conn); ++ return SLURM_ERROR; ++ } ++ ++ return SLURM_SUCCESS; ++} ++ ++/* Every option in assoc_char should have a 't1.' infront of it. */ ++extern int remove_common(pgsql_conn_t *pgsql_conn, ++ uint16_t type, ++ time_t now, ++ char *user_name, ++ char *table, ++ char *name_char, ++ char *assoc_char, ++ char *cluster_name, ++ List ret_list, ++ bool *jobs_running) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ char *loc_assoc_char = NULL, *loc_usage_id_char = NULL; ++ pgsql_res_t* result = NULL; ++ pgsql_row row; ++ time_t day_old = now - DELETE_SEC_BACK; ++ bool has_jobs = false; ++ char *tmp_name_char = NULL; ++ bool cluster_centric = true; ++ uint32_t smallest_lft = 0xFFFFFFFF; ++ ++ /* figure out which tables we need to append the cluster name to */ ++ if ((table == cluster_table) || (table == acct_coord_table) ++ || (table == acct_table) || (table == qos_table) ++ || (table == txn_table) || (table == user_table) ++ || (table == res_table) || (table == clus_res_table) ++ || (table == federation_table)) ++ cluster_centric = false; ++ ++ /* If we have jobs associated with this we do not want to ++ * really delete it for accounting purposes. This is for ++ * corner cases most of the time this won't matter. ++ */ ++ if ((table == acct_coord_table) || (table == res_table) ++ || (table == clus_res_table) || (table == federation_table)) { ++ /* This doesn't apply for these tables since we are ++ * only looking for association type tables. ++ */ ++ } else if ((table == qos_table) || (table == wckey_table)) { ++ if (cluster_name) ++ has_jobs = _check_jobs_before_remove_without_assoctable( ++ pgsql_conn, cluster_name, assoc_char); ++ } else if (table != assoc_table) { ++ /* first check to see if we are running jobs now */ ++ if (_check_jobs_before_remove( ++ pgsql_conn, cluster_name, assoc_char, ++ ret_list, jobs_running) || (*jobs_running)) ++ return SLURM_SUCCESS; ++ ++ has_jobs = _check_jobs_before_remove( ++ pgsql_conn, cluster_name, assoc_char, NULL, NULL); ++ } else { ++ /* first check to see if we are running jobs now */ ++ if (_check_jobs_before_remove_assoc( ++ pgsql_conn, cluster_name, name_char, ++ ret_list, jobs_running) || (*jobs_running)) ++ return SLURM_SUCCESS; ++ ++ /* now check to see if any jobs were ever run. */ ++ has_jobs = _check_jobs_before_remove_assoc( ++ pgsql_conn, cluster_name, name_char, ++ NULL, NULL); ++ } ++ /* we want to remove completely all that is less than a day old */ ++ if (!has_jobs && table != assoc_table) { ++ if (cluster_centric) { ++ query = xstrdup_printf("delete from \"%s_%s\" where " ++ "creation_time>%ld && (%s);", ++ cluster_name, table, day_old, ++ name_char); ++ } else { ++ query = xstrdup_printf("delete from %s where " ++ "creation_time>%ld && (%s);", ++ table, day_old, name_char); ++ } ++ } ++ ++ if (table != assoc_table) { ++ if (cluster_centric) { ++ xstrfmtcat(query, ++ "update \"%s_%s\" set mod_time=%ld, " ++ "deleted=1 where deleted=0 && (%s);", ++ cluster_name, table, now, name_char); ++ } else if (table == federation_table) { ++ xstrfmtcat(query, ++ "update %s set " ++ "mod_time=%ld, deleted=1, " ++ "flags=DEFAULT " ++ "where deleted=0 && (%s);", ++ federation_table, now, ++ name_char); ++ } else if (table == qos_table) { ++ xstrfmtcat(query, ++ "update %s set " ++ "mod_time=%ld, deleted=1, " ++ "grace_time=DEFAULT, " ++ "max_jobs_pa=DEFAULT, " ++ "max_jobs_per_user=DEFAULT, " ++ "max_jobs_accrue_pa=DEFAULT, " ++ "max_jobs_accrue_pu=DEFAULT, " ++ "min_prio_thresh=DEFAULT, " ++ "max_submit_jobs_pa=DEFAULT, " ++ "max_submit_jobs_per_user=DEFAULT, " ++ "max_tres_pa=DEFAULT, " ++ "max_tres_pj=DEFAULT, " ++ "max_tres_pn=DEFAULT, " ++ "max_tres_pu=DEFAULT, " ++ "max_tres_mins_pj=DEFAULT, " ++ "max_tres_run_mins_pa=DEFAULT, " ++ "max_tres_run_mins_pu=DEFAULT, " ++ "min_tres_pj=DEFAULT, " ++ "max_wall_duration_per_job=DEFAULT, " ++ "grp_jobs=DEFAULT, grp_submit_jobs=DEFAULT, " ++ "grp_jobs_accrue=DEFAULT, grp_tres=DEFAULT, " ++ "grp_tres_mins=DEFAULT, " ++ "grp_tres_run_mins=DEFAULT, " ++ "grp_wall=DEFAULT, " ++ "preempt=DEFAULT, " ++ "preempt_exempt_time=DEFAULT, " ++ "priority=DEFAULT, " ++ "usage_factor=DEFAULT, " ++ "usage_thres=DEFAULT, " ++ "limit_factor=DEFAULT " ++ "where deleted=0 && (%s);", ++ qos_table, now, name_char); ++ } else { ++ xstrfmtcat(query, ++ "update %s set mod_time=%ld, deleted=1 " ++ "where deleted=0 && (%s);", ++ table, now, name_char); ++ } ++ } ++ ++ /* If we are removing assocs use the assoc_char since the ++ name_char has lft between statements that can change over ++ time. The assoc_char has the actual ids of the assocs ++ which never change. ++ */ ++ if (type == DBD_REMOVE_ASSOCS && assoc_char) ++ tmp_name_char = slurm_add_slash_to_quotes(assoc_char); ++ else ++ tmp_name_char = slurm_add_slash_to_quotes(name_char); ++ ++ if (cluster_centric) ++ xstrfmtcat(query, ++ "insert into %s (timestamp, action, name, " ++ "actor, cluster) values " ++ "(%ld, %d, '%s', '%s', '%s');", ++ txn_table, ++ now, type, tmp_name_char, user_name, cluster_name); ++ else ++ xstrfmtcat(query, ++ "insert into %s (timestamp, action, name, actor) " ++ "values (%ld, %d, '%s', '%s');", ++ txn_table, ++ now, type, tmp_name_char, user_name); ++ ++ xfree(tmp_name_char); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ reset_pgsql_conn(pgsql_conn); ++ return SLURM_ERROR; ++ } else if ((table == acct_coord_table) ++ || (table == wckey_table) ++ || (table == clus_res_table) ++ || (table == res_table) ++ || (table == federation_table) ++ || (table == qos_table)) ++ return SLURM_SUCCESS; ++ ++ /* mark deleted=1 or remove completely the accounting tables ++ */ ++ if (table != assoc_table) { ++ if (!assoc_char) { ++ error("no assoc_char"); ++ if (pgsql_conn->rollback) { ++ pgsql_db_rollback(pgsql_conn); ++ } ++ list_flush(pgsql_conn->update_list); ++ return SLURM_ERROR; ++ } ++ ++ /* If we are doing this on an assoc_table we have ++ already done this, so don't */ ++ query = xstrdup_printf("select distinct t1.id_assoc " ++ "from \"%s_%s\" as t1, \"%s_%s\" as t2 " ++ "where (%s) AND t1.lft between " ++ "t2.lft and t2.rgt AND t1.deleted=0 " ++ "AND t2.deleted=0;", ++ cluster_name, assoc_table, ++ cluster_name, assoc_table, assoc_char); ++ ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ if (pgsql_conn->rollback) { ++ pgsql_db_rollback(pgsql_conn); ++ } ++ list_flush(pgsql_conn->update_list); ++ return SLURM_ERROR; ++ } ++ ++ rc = 0; ++ xfree(loc_assoc_char); ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_assoc_rec_t *rem_assoc = NULL; ++ if (loc_assoc_char) ++ xstrcat(loc_assoc_char, " || "); ++ xstrfmtcat(loc_assoc_char, "id_assoc=%s", row[0]); ++ ++ rem_assoc = xmalloc(sizeof(slurmdb_assoc_rec_t)); ++ rem_assoc->id = slurm_atoul(row[0]); ++ rem_assoc->cluster = xstrdup(cluster_name); ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_REMOVE_ASSOC, ++ rem_assoc) != SLURM_SUCCESS) ++ error("couldn't add to the update list"); ++ } ++ pgsql_free_result(&result); ++ } else ++ loc_assoc_char = assoc_char; ++ ++ if (!loc_assoc_char) { ++ debug2("No associations with object being deleted"); ++ return rc; ++ } ++ ++ loc_usage_id_char = xstrdup(loc_assoc_char); ++ xstrsubstituteall(loc_usage_id_char, "id_assoc", "id"); ++ ++ /* We should not have to delete from usage table, only flag since we ++ * only delete things that are typos. ++ */ ++ xstrfmtcat(query, ++ "update \"%s_%s\" set mod_time=%ld, deleted=1 where (%s);" ++ "update \"%s_%s\" set mod_time=%ld, deleted=1 where (%s);" ++ "update \"%s_%s\" set mod_time=%ld, deleted=1 where (%s);", ++ cluster_name, assoc_day_table, now, loc_usage_id_char, ++ cluster_name, assoc_hour_table, now, loc_usage_id_char, ++ cluster_name, assoc_month_table, now, loc_usage_id_char); ++ xfree(loc_usage_id_char); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s %zu", ++ query, strlen(query)); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ reset_pgsql_conn(pgsql_conn); ++ return SLURM_ERROR; ++ } ++ ++ /* If we have jobs that have ran don't go through the logic of ++ * removing the associations. Since we may want them for ++ * reports in the future since jobs had ran. ++ */ ++ if (has_jobs) ++ goto just_update; ++ ++ /* remove completely all the associations for this added in the last ++ * day, since they are most likely nothing we really wanted in ++ * the first place. ++ */ ++ query = xstrdup_printf("select id_assoc from \"%s_%s\" as t1 where " ++ "creation_time>%ld && (%s);", ++ cluster_name, assoc_table, ++ day_old, loc_assoc_char); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ reset_pgsql_conn(pgsql_conn); ++ return SLURM_ERROR; ++ } ++ ++ while ((row = pgsql_fetch_row(result))) { ++ pgsql_res_t* result2 = NULL; ++ pgsql_row row2; ++ uint32_t lft; ++ ++ /* we have to do this one at a time since the lft's and rgt's ++ change. If you think you need to remove this make ++ sure your new way can handle changing lft and rgt's ++ in the association. */ ++ xstrfmtcat(query, ++ "SELECT lft, rgt, (rgt - lft + 1) " ++ "FROM \"%s_%s\" WHERE id_assoc = %s;", ++ cluster_name, assoc_table, row[0]); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result2 = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result2), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result2); ++ rc = SLURM_ERROR; ++ break; ++ } ++ ++ if (!(row2 = pgsql_fetch_row(result2))) { ++ pgsql_free_result(&result2); ++ continue; ++ } ++ ++ xstrfmtcat(query, ++ "delete from \"%s_%s\" where " ++ "lft between %s AND %s;", ++ cluster_name, assoc_table, row2[0], row2[1]); ++ ++ xstrfmtcat(query, ++ "UPDATE \"%s_%s\" SET rgt = rgt - %s WHERE rgt > %s;" ++ "UPDATE \"%s_%s\" SET " ++ "lft = lft - %s WHERE lft > %s;", ++ cluster_name, assoc_table, row2[2], row2[1], ++ cluster_name, assoc_table, row2[2], row2[1]); ++ ++ lft = slurm_atoul(row2[0]); ++ if (lft < smallest_lft) ++ smallest_lft = lft; ++ ++ pgsql_free_result(&result2); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("couldn't remove assoc"); ++ break; ++ } ++ } ++ pgsql_free_result(&result); ++ /* This already happened before, but we need to run it again ++ since the first time we ran it we didn't know if we were ++ going to remove the above associations. ++ */ ++ if (rc == SLURM_SUCCESS) ++ rc = as_pgsql_get_modified_lfts(pgsql_conn, ++ cluster_name, smallest_lft); ++ ++ if (rc == SLURM_ERROR) { ++ reset_pgsql_conn(pgsql_conn); ++ return rc; ++ } ++ ++just_update: ++ /* now update the associations themselves that are still ++ * around clearing all the limits since if we add them back ++ * we don't want any residue from past associations lingering ++ * around. ++ */ ++ query = xstrdup_printf("update \"%s_%s\" as t1 set " ++ "mod_time=%ld, deleted=1, def_qos_id=DEFAULT, " ++ "shares=DEFAULT, max_jobs=DEFAULT, " ++ "max_jobs_accrue=DEFAULT, " ++ "min_prio_thresh=DEFAULT, " ++ "max_submit_jobs=DEFAULT, " ++ "max_wall_pj=DEFAULT, " ++ "max_tres_pj=DEFAULT, " ++ "max_tres_pn=DEFAULT, " ++ "max_tres_mins_pj=DEFAULT, " ++ "max_tres_run_mins=DEFAULT, " ++ "grp_jobs=DEFAULT, grp_submit_jobs=DEFAULT, " ++ "grp_jobs_accrue=DEFAULT, grp_wall=DEFAULT, " ++ "grp_tres=DEFAULT, " ++ "grp_tres_mins=DEFAULT, " ++ "grp_tres_run_mins=DEFAULT, " ++ "qos=DEFAULT, delta_qos=DEFAULT, " ++ "priority=DEFAULT " ++ "where (%s);", ++ cluster_name, assoc_table, now, ++ loc_assoc_char); ++ ++ if (table != assoc_table) ++ xfree(loc_assoc_char); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ reset_pgsql_conn(pgsql_conn); ++ } ++ ++ return rc; ++} ++ ++extern void mod_tres_str(char **out, char *mod, char *cur, ++ char *cur_par, char *name, char **vals, ++ uint32_t id, bool assoc) ++{ ++ uint32_t tres_str_flags = TRES_STR_FLAG_REMOVE | ++ TRES_STR_FLAG_SORT_ID | TRES_STR_FLAG_SIMPLE | ++ TRES_STR_FLAG_NO_NULL; ++ ++ xassert(out); ++ xassert(name); ++ ++ if (!mod) ++ return; ++ ++ /* We have to add strings in waves or we will not be able to ++ * get removes to work correctly. We want the string returned ++ * after the first slurmdb_combine_tres_strings to be put in ++ * the database. ++ */ ++ xfree(*out); /* just to make sure */ ++ *out = xstrdup(mod); ++ slurmdb_combine_tres_strings(out, cur, tres_str_flags); ++ ++ if (xstrcmp(*out, cur)) { ++ if (vals) { ++ /* This logic is here because while the change ++ * we are doing on the limit is the same for ++ * each limit the other limits on the ++ * associations might not be. What this does ++ * is only change the limit on the association ++ * given the id. I'm hoping someone in the ++ * future comes up with a better way to do ++ * this since this seems like a hack, but it ++ * does do the job. ++ */ ++ xstrfmtcat(*vals, ", %s = " ++ "if (%s=%u, '%s', %s)", ++ name, assoc ? "id_assoc" : "id", id, ++ *out, name); ++ /* xstrfmtcat(*vals, ", %s='%s%s')", */ ++ /* name, */ ++ /* *out[0] ? "," : "", */ ++ /* *out); */ ++ } ++ if (cur_par) ++ slurmdb_combine_tres_strings( ++ out, cur_par, tres_str_flags); ++ } else ++ xfree(*out); ++} ++ ++//static int _get_database_variable(pgsql_conn_t *pgsql_conn, ++// const char *variable_name, uint64_t *value) ++//{ ++// pgsql_row row = NULL; ++// pgsql_res_t* result = NULL; ++// char *err_check = NULL; ++// char *query; ++// ++// query = xstrdup_printf("show %s;", variable_name); ++// result = pgsql_db_query_ret(pgsql_conn, query, 0); ++// if (!result) { ++// error("%s: null result from query `%s`", __func__, query); ++// xfree(query); ++// return SLURM_ERROR; ++// } ++// ++// if (pgsql_num_rows(result) != 1) { ++// error("%s: invalid results from query `%s`", __func__, query); ++// xfree(query); ++// pgsql_free_result(&result); ++// return SLURM_ERROR; ++// } ++// ++// xfree(query); ++// ++// row = pgsql_fetch_row(result); ++// *value = (uint64_t) strtoll(row[1], &err_check, 10); ++// ++// if (*err_check) { ++// error("%s: error parsing string to int `%s`", __func__, row[1]); ++// pgsql_free_result(&result); ++// return SLURM_ERROR; ++// } ++// pgsql_free_result(&result); ++// ++// return SLURM_SUCCESS; ++//} ++ ++/* ++ * init() is called when the plugin is loaded, before any other functions ++ * are called. Put global initialization here. ++ */ ++extern int init(void) ++{ ++ int rc = SLURM_SUCCESS; ++ pgsql_conn_t *pgsql_conn = NULL; ++ ++ if (slurmdbd_conf->dbd_backup) { ++ char node_name_short[128]; ++ char node_name_long[128]; ++ if (gethostname(node_name_long, sizeof(node_name_long))) ++ fatal("getnodename: %m"); ++ if (gethostname_short(node_name_short, sizeof(node_name_short))) ++ fatal("getnodename_short: %m"); ++ if (!xstrcmp(node_name_short, slurmdbd_conf->dbd_backup) || ++ !xstrcmp(node_name_long, slurmdbd_conf->dbd_backup) || ++ !xstrcmp(slurmdbd_conf->dbd_backup, "localhost")) ++ backup_dbd = true; ++ } ++ ++ pgsql_db_info = create_pgsql_db_info(SLURM_PGSQL_PLUGIN_AS); ++ if (!pgsql_db_info) { ++ return SLURM_ERROR; ++ } ++ pgsql_db_name = acct_get_db_name(); ++ ++ debug2("create_pgsql_conn() called for db %s", pgsql_db_name); ++ pgsql_conn = create_pgsql_conn(0, 1, NULL); ++ while (pgsql_db_get_db_connection(pgsql_conn, pgsql_db_name, pgsql_db_info) ++ != SLURM_SUCCESS) { ++ error("The database must be up when starting " ++ "the pgsql plugin. Trying again in 5 seconds."); ++ sleep(5); ++ } ++ ++ rc = _as_pgsql_acct_check_tables(pgsql_conn); ++ ++ if (rc == SLURM_SUCCESS) { ++ if (pgsql_db_commit(pgsql_conn)) { ++ error("commit failed, meaning %s failed", plugin_name); ++ rc = SLURM_ERROR; ++ } else ++ verbose("%s loaded", plugin_name); ++ } else { ++ verbose("%s failed", plugin_name); ++ if (pgsql_db_rollback(pgsql_conn)) ++ error("rollback failed"); ++ } ++ ++ destroy_pgsql_conn(pgsql_conn); ++ ++ return rc; ++} ++ ++extern int fini ( void ) ++{ ++ slurm_rwlock_wrlock(&as_pgsql_cluster_list_lock); ++ FREE_NULL_LIST(as_pgsql_cluster_list); ++ FREE_NULL_LIST(as_pgsql_total_cluster_list); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ slurm_rwlock_destroy(&as_pgsql_cluster_list_lock); ++ destroy_pgsql_db_info(pgsql_db_info); ++ xfree(pgsql_db_name); ++ xfree(default_qos_str); ++ ++ return SLURM_SUCCESS; ++} ++ ++/* ++ * Get the dimensions of this cluster so we know how to deal with the hostlists. ++ * ++ * IN pgsql_conn - pgsql connection ++ * IN cluster_name - name of cluster to get dimensions for ++ * OUT dims - dimenions of cluster ++ * ++ * RET return SLURM_SUCCESS on success, SLURM_FAILURE otherwise. ++ */ ++extern int get_cluster_dims(pgsql_conn_t *pgsql_conn, char *cluster_name, ++ int *dims) ++{ ++ char *query; ++ pgsql_row row; ++ pgsql_res_t* result = NULL; ++ ++ query = xstrdup_printf("select dimensions from %s where name='%s'", ++ cluster_table, cluster_name); ++ ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ if (!(row = pgsql_fetch_row(result))) { ++ error("Couldn't get the dimensions of cluster '%s'.", ++ cluster_name); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ *dims = atoi(row[0]); ++ ++ pgsql_free_result(&result); ++ ++ return SLURM_SUCCESS; ++} ++ ++extern void *acct_storage_p_get_connection( ++ int conn_num, uint16_t *persist_conn_flags, ++ bool rollback, char *cluster_name) ++{ ++ pgsql_conn_t *pgsql_conn = NULL; ++ ++ debug2("acct_storage_p_get_connection: request new connection %d", ++ rollback); ++ ++ if (!(pgsql_conn = create_pgsql_conn( ++ conn_num, rollback, cluster_name))) { ++ fatal("couldn't get a pgsql_conn"); ++ return NULL; /* Fix CLANG false positive error */ ++ } ++ ++ errno = SLURM_SUCCESS; ++ pgsql_db_get_db_connection(pgsql_conn, pgsql_db_name, pgsql_db_info); ++ ++ if (pgsql_conn->db_conn) ++ errno = SLURM_SUCCESS; ++ ++ return (void *)pgsql_conn; ++} ++ ++extern int acct_storage_p_close_connection(pgsql_conn_t **pgsql_conn) ++{ ++ int rc; ++ ++ if (!pgsql_conn || !(*pgsql_conn)) ++ return SLURM_SUCCESS; ++ ++ acct_storage_p_commit((*pgsql_conn), 0); ++ rc = destroy_pgsql_conn(*pgsql_conn); ++ *pgsql_conn = NULL; ++ ++ return rc; ++} ++ ++extern int acct_storage_p_commit(pgsql_conn_t *pgsql_conn, bool commit) ++{ ++ int rc = check_connection(pgsql_conn); ++ List update_list = NULL; ++ ++ /* always reset this here */ ++ if (pgsql_conn) ++ pgsql_conn->cluster_deleted = 0; ++ ++ if ((rc != SLURM_SUCCESS) && (rc != ESLURM_CLUSTER_DELETED)) ++ return rc; ++ /* ++ * We should never get here since check_connection will return ++ * ESLURM_DB_CONNECTION when !pgsql_conn, but Coverity doesn't ++ * understand that. CID 44841. ++ */ ++ xassert(pgsql_conn); ++ ++ update_list = list_create(slurmdb_destroy_update_object); ++ list_transfer(update_list, pgsql_conn->update_list); ++ debug4("got %d commits", list_count(update_list)); ++ ++ if (pgsql_conn->rollback) { ++ if (!commit) { ++ if (pgsql_db_rollback(pgsql_conn)) ++ error("rollback failed"); ++ } else { ++ int rc = SLURM_SUCCESS; ++ /* ++ * Handle anything here we were unable to do ++ * because of rollback issues. ++ */ ++ if (pgsql_conn->pre_commit_query) { ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, ++ "query\n%s", ++ pgsql_conn->pre_commit_query); ++ rc = pgsql_db_query( ++ pgsql_conn, ++ pgsql_conn->pre_commit_query); ++ } ++ ++ if (rc != SLURM_SUCCESS) { ++ if (pgsql_db_rollback(pgsql_conn)) ++ error("rollback failed"); ++ } else { ++ if (pgsql_db_commit(pgsql_conn)) ++ error("commit failed"); ++ } ++ } ++ } ++ ++ if (commit && list_count(update_list)) { ++ char *query = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ListIterator itr = NULL; ++ slurmdb_update_object_t *object = NULL; ++ ++ xstrfmtcat(query, "select control_host, control_port, " ++ "name, rpc_version, flags " ++ "from %s where deleted=0 && control_port != 0", ++ cluster_table); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ goto skip; ++ } ++ ++ while ((row = pgsql_fetch_row(result))) { ++ if (slurm_atoul(row[4]) & CLUSTER_FLAG_EXT) ++ continue; ++ (void) slurmdb_send_accounting_update( ++ update_list, ++ row[2], row[0], ++ slurm_atoul(row[1]), ++ slurm_atoul(row[3])); ++ } ++ pgsql_free_result(&result); ++ skip: ++ (void) assoc_mgr_update(update_list, 0); ++ ++ slurm_rwlock_wrlock(&as_pgsql_cluster_list_lock); ++ itr = list_iterator_create(update_list); ++ while ((object = list_next(itr))) { ++ if (!object->objects || !list_count(object->objects)) ++ continue; ++ /* We only care about clusters removed here. */ ++ switch (object->type) { ++ case SLURMDB_REMOVE_CLUSTER: ++ { ++ ListIterator rem_itr = NULL; ++ char *rem_cluster = NULL; ++ rem_itr = list_iterator_create(object->objects); ++ while ((rem_cluster = list_next(rem_itr))) { ++ list_delete_all(as_pgsql_cluster_list, ++ slurm_find_char_in_list, ++ rem_cluster); ++ } ++ list_iterator_destroy(rem_itr); ++ break; ++ } ++ default: ++ break; ++ } ++ } ++ list_iterator_destroy(itr); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ xfree(pgsql_conn->pre_commit_query); ++ FREE_NULL_LIST(update_list); ++ ++ return SLURM_SUCCESS; ++} ++ ++extern int acct_storage_p_add_users(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List user_list) ++{ ++ return as_pgsql_add_users(pgsql_conn, uid, user_list); ++} ++ ++extern int acct_storage_p_add_coord(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List acct_list, ++ slurmdb_user_cond_t *user_cond) ++{ ++ return as_pgsql_add_coord(pgsql_conn, uid, acct_list, user_cond); ++} ++ ++extern int acct_storage_p_add_accts(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List acct_list) ++{ ++ return as_pgsql_add_accts(pgsql_conn, uid, acct_list); ++} ++ ++extern int acct_storage_p_add_clusters(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List cluster_list) ++{ ++ return as_pgsql_add_clusters(pgsql_conn, uid, cluster_list); ++} ++ ++extern int acct_storage_p_add_federations(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, List federation_list) ++{ ++ return as_pgsql_add_federations(pgsql_conn, uid, federation_list); ++} ++ ++extern int acct_storage_p_add_tres(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, List tres_list_in) ++{ ++ return as_pgsql_add_tres(pgsql_conn, uid, tres_list_in); ++} ++ ++extern int acct_storage_p_add_assocs(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ List assoc_list) ++{ ++ return as_pgsql_add_assocs(pgsql_conn, uid, assoc_list); ++} ++ ++extern int acct_storage_p_add_qos(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List qos_list) ++{ ++ return as_pgsql_add_qos(pgsql_conn, uid, qos_list); ++} ++ ++extern int acct_storage_p_add_res(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List res_list) ++{ ++ return as_pgsql_add_res(pgsql_conn, uid, res_list); ++} ++ ++extern int acct_storage_p_add_wckeys(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List wckey_list) ++{ ++ return as_pgsql_add_wckeys(pgsql_conn, uid, wckey_list); ++} ++ ++extern int acct_storage_p_add_reservation(pgsql_conn_t *pgsql_conn, ++ slurmdb_reservation_rec_t *resv) ++{ ++ return as_pgsql_add_resv(pgsql_conn, resv); ++} ++ ++extern List acct_storage_p_modify_users(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_user_cond_t *user_cond, ++ slurmdb_user_rec_t *user) ++{ ++ return as_pgsql_modify_users(pgsql_conn, uid, user_cond, user); ++} ++ ++extern List acct_storage_p_modify_accts(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_account_cond_t *acct_cond, ++ slurmdb_account_rec_t *acct) ++{ ++ return as_pgsql_modify_accts(pgsql_conn, uid, acct_cond, acct); ++} ++ ++extern List acct_storage_p_modify_clusters(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ slurmdb_cluster_cond_t *cluster_cond, ++ slurmdb_cluster_rec_t *cluster) ++{ ++ return as_pgsql_modify_clusters(pgsql_conn, uid, cluster_cond, cluster); ++} ++ ++extern List acct_storage_p_modify_assocs( ++ pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_assoc_cond_t *assoc_cond, ++ slurmdb_assoc_rec_t *assoc) ++{ ++ return as_pgsql_modify_assocs(pgsql_conn, uid, assoc_cond, assoc); ++} ++ ++extern List acct_storage_p_modify_federations( ++ pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_federation_cond_t *fed_cond, ++ slurmdb_federation_rec_t *fed) ++{ ++ return as_pgsql_modify_federations(pgsql_conn, uid, fed_cond, fed); ++} ++ ++extern List acct_storage_p_modify_job(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_job_cond_t *job_cond, ++ slurmdb_job_rec_t *job) ++{ ++ return as_pgsql_modify_job(pgsql_conn, uid, job_cond, job); ++} ++ ++extern List acct_storage_p_modify_qos(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_qos_cond_t *qos_cond, ++ slurmdb_qos_rec_t *qos) ++{ ++ return as_pgsql_modify_qos(pgsql_conn, uid, qos_cond, qos); ++} ++ ++extern List acct_storage_p_modify_res(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ slurmdb_res_cond_t *res_cond, ++ slurmdb_res_rec_t *res) ++{ ++ return as_pgsql_modify_res(pgsql_conn, uid, res_cond, res); ++} ++ ++extern List acct_storage_p_modify_wckeys(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ slurmdb_wckey_cond_t *wckey_cond, ++ slurmdb_wckey_rec_t *wckey) ++{ ++ return as_pgsql_modify_wckeys(pgsql_conn, uid, wckey_cond, wckey); ++} ++ ++extern int acct_storage_p_modify_reservation(pgsql_conn_t *pgsql_conn, ++ slurmdb_reservation_rec_t *resv) ++{ ++ return as_pgsql_modify_resv(pgsql_conn, resv); ++} ++ ++extern List acct_storage_p_remove_users(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_user_cond_t *user_cond) ++{ ++ return as_pgsql_remove_users(pgsql_conn, uid, user_cond); ++} ++ ++extern List acct_storage_p_remove_coord(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List acct_list, ++ slurmdb_user_cond_t *user_cond) ++{ ++ return as_pgsql_remove_coord(pgsql_conn, uid, acct_list, user_cond); ++} ++ ++extern List acct_storage_p_remove_accts(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_account_cond_t *acct_cond) ++{ ++ return as_pgsql_remove_accts(pgsql_conn, uid, acct_cond); ++} ++ ++extern List acct_storage_p_remove_clusters(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ slurmdb_cluster_cond_t *cluster_cond) ++{ ++ return as_pgsql_remove_clusters(pgsql_conn, uid, cluster_cond); ++} ++ ++extern List acct_storage_p_remove_assocs( ++ pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_assoc_cond_t *assoc_cond) ++{ ++ return as_pgsql_remove_assocs(pgsql_conn, uid, assoc_cond); ++} ++ ++extern List acct_storage_p_remove_federations( ++ pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_federation_cond_t *fed_cond) ++{ ++ return as_pgsql_remove_federations(pgsql_conn, uid, fed_cond); ++} ++ ++extern List acct_storage_p_remove_qos(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_qos_cond_t *qos_cond) ++{ ++ return as_pgsql_remove_qos(pgsql_conn, uid, qos_cond); ++} ++ ++extern List acct_storage_p_remove_res(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ slurmdb_res_cond_t *res_cond) ++{ ++ return as_pgsql_remove_res(pgsql_conn, uid, res_cond); ++} ++ ++extern List acct_storage_p_remove_wckeys(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ slurmdb_wckey_cond_t *wckey_cond) ++{ ++ return as_pgsql_remove_wckeys(pgsql_conn, uid, wckey_cond); ++} ++ ++extern int acct_storage_p_remove_reservation(pgsql_conn_t *pgsql_conn, ++ slurmdb_reservation_rec_t *resv) ++{ ++ return as_pgsql_remove_resv(pgsql_conn, resv); ++} ++ ++extern List acct_storage_p_get_users(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_user_cond_t *user_cond) ++{ ++ return as_pgsql_get_users(pgsql_conn, uid, user_cond); ++} ++ ++extern List acct_storage_p_get_accts(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_account_cond_t *acct_cond) ++{ ++ return as_pgsql_get_accts(pgsql_conn, uid, acct_cond); ++} ++ ++extern List acct_storage_p_get_clusters(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_cluster_cond_t *cluster_cond) ++{ ++ return as_pgsql_get_clusters(pgsql_conn, uid, cluster_cond); ++} ++ ++extern List acct_storage_p_get_federations(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_federation_cond_t *fed_cond) ++{ ++ return as_pgsql_get_federations(pgsql_conn, uid, fed_cond); ++} ++ ++extern List acct_storage_p_get_tres( ++ pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_tres_cond_t *tres_cond) ++{ ++ return as_pgsql_get_tres(pgsql_conn, uid, tres_cond); ++} ++ ++extern List acct_storage_p_get_assocs( ++ pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_assoc_cond_t *assoc_cond) ++{ ++ return as_pgsql_get_assocs(pgsql_conn, uid, assoc_cond); ++} ++ ++extern List acct_storage_p_get_events(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_event_cond_t *event_cond) ++{ ++ return as_pgsql_get_cluster_events(pgsql_conn, uid, event_cond); ++} ++ ++extern List acct_storage_p_get_problems(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_assoc_cond_t *assoc_cond) ++{ ++ List ret_list = NULL; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ ++ ret_list = list_create(slurmdb_destroy_assoc_rec); ++ ++ if (as_pgsql_acct_no_assocs(pgsql_conn, assoc_cond, ret_list) ++ != SLURM_SUCCESS) ++ goto end_it; ++ ++ if (as_pgsql_acct_no_users(pgsql_conn, assoc_cond, ret_list) ++ != SLURM_SUCCESS) ++ goto end_it; ++ ++ if (as_pgsql_user_no_assocs_or_no_uid(pgsql_conn, assoc_cond, ret_list) ++ != SLURM_SUCCESS) ++ goto end_it; ++ ++end_it: ++ ++ return ret_list; ++} ++ ++extern List acct_storage_p_get_config(void *db_conn, char *config_name) ++{ ++ return NULL; ++} ++ ++extern List acct_storage_p_get_qos(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_qos_cond_t *qos_cond) ++{ ++ return as_pgsql_get_qos(pgsql_conn, uid, qos_cond); ++} ++ ++extern List acct_storage_p_get_res(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_res_cond_t *res_cond) ++{ ++ return as_pgsql_get_res(pgsql_conn, uid, res_cond); ++} ++ ++extern List acct_storage_p_get_wckeys(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_wckey_cond_t *wckey_cond) ++{ ++ return as_pgsql_get_wckeys(pgsql_conn, uid, wckey_cond); ++} ++ ++extern List acct_storage_p_get_reservations( ++ pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_reservation_cond_t *resv_cond) ++{ ++ return as_pgsql_get_resvs(pgsql_conn, uid, resv_cond); ++} ++ ++extern List acct_storage_p_get_txn(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_txn_cond_t *txn_cond) ++{ ++ return as_pgsql_get_txn(pgsql_conn, uid, txn_cond); ++} ++ ++extern int acct_storage_p_get_usage(pgsql_conn_t *pgsql_conn, uid_t uid, ++ void *in, slurmdbd_msg_type_t type, ++ time_t start, time_t end) ++{ ++ return as_pgsql_get_usage(pgsql_conn, uid, in, type, start, end); ++} ++ ++extern int acct_storage_p_roll_usage(pgsql_conn_t *pgsql_conn, ++ time_t sent_start, time_t sent_end, ++ uint16_t archive_data, ++ List *rollup_stats_list_in) ++{ ++ return as_pgsql_roll_usage(pgsql_conn, sent_start, sent_end, ++ archive_data, rollup_stats_list_in); ++} ++ ++extern int acct_storage_p_fix_runaway_jobs(void *db_conn, uint32_t uid, ++ List jobs) ++{ ++ return as_pgsql_fix_runaway_jobs(db_conn, uid, jobs); ++} ++ ++extern int clusteracct_storage_p_node_down(pgsql_conn_t *pgsql_conn, ++ node_record_t *node_ptr, ++ time_t event_time, char *reason, ++ uint32_t reason_uid) ++{ ++ return as_pgsql_node_down(pgsql_conn, node_ptr, ++ event_time, reason, reason_uid); ++} ++ ++extern int clusteracct_storage_p_node_up(pgsql_conn_t *pgsql_conn, ++ node_record_t *node_ptr, ++ time_t event_time) ++{ ++ return as_pgsql_node_up(pgsql_conn, node_ptr, event_time); ++} ++ ++/* This is only called when not running from the slurmdbd so we can ++ * assumes some things like rpc_version. ++ */ ++extern int clusteracct_storage_p_register_ctld(pgsql_conn_t *pgsql_conn, ++ uint16_t port) ++{ ++ return as_pgsql_register_ctld( ++ pgsql_conn, pgsql_conn->cluster_name, port); ++} ++ ++extern uint16_t clusteracct_storage_p_register_disconn_ctld( ++ pgsql_conn_t *pgsql_conn, char *control_host) ++{ ++ uint16_t control_port = 0; ++ char *query = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!pgsql_conn->cluster_name) { ++ error("%s:%d no cluster name", THIS_FILE, __LINE__); ++ return control_port; ++ } else if (!control_host) { ++ error("%s:%d no control host for cluster %s", ++ THIS_FILE, __LINE__, pgsql_conn->cluster_name); ++ return control_port; ++ } ++ ++ query = xstrdup_printf("select last_port from %s where name='%s';", ++ cluster_table, pgsql_conn->cluster_name); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ error("register_disconn_ctld: no result given for cluster %s", ++ pgsql_conn->cluster_name); ++ return control_port; ++ } ++ ++ if ((row = pgsql_fetch_row(result))) { ++ control_port = slurm_atoul(row[0]); ++ /* If there is ever a network issue talking to the DBD, and ++ both the DBD and the ctrl stay up when the ctld goes to ++ talk to the DBD again it may not re-register (<=2.2). ++ Since the slurmctld didn't go down we can presume the port ++ is still the same and just use the last information as the ++ information we should use and go along our merry way. ++ */ ++ query = xstrdup_printf( ++ "update %s set control_host='%s', " ++ "control_port=%u where name='%s';", ++ cluster_table, control_host, control_port, ++ pgsql_conn->cluster_name); ++ DB_DEBUG(DB_EVENT, pgsql_conn->conn, "query\n%s", query); ++ if (pgsql_db_query(pgsql_conn, query) != SLURM_SUCCESS) ++ control_port = 0; ++ xfree(query); ++ } ++ pgsql_free_result(&result); ++ ++ return control_port; ++} ++ ++extern int clusteracct_storage_p_fini_ctld(pgsql_conn_t *pgsql_conn, ++ slurmdb_cluster_rec_t *cluster_rec) ++{ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!cluster_rec || (!pgsql_conn->cluster_name && !cluster_rec->name)) { ++ error("%s:%d no cluster name", THIS_FILE, __LINE__); ++ return SLURM_ERROR; ++ } ++ ++ if (!cluster_rec->name) ++ cluster_rec->name = pgsql_conn->cluster_name; ++ ++ return as_pgsql_fini_ctld(pgsql_conn, cluster_rec); ++} ++ ++extern int clusteracct_storage_p_cluster_tres(pgsql_conn_t *pgsql_conn, ++ char *cluster_nodes, ++ char *tres_str_in, ++ time_t event_time, ++ uint16_t rpc_version) ++{ ++ return as_pgsql_cluster_tres(pgsql_conn, ++ cluster_nodes, &tres_str_in, ++ event_time, rpc_version); ++} ++ ++/* ++ * load into the storage the start of a job ++ */ ++extern int jobacct_storage_p_job_start(pgsql_conn_t *pgsql_conn, ++ job_record_t *job_ptr) ++{ ++ return as_pgsql_job_start(pgsql_conn, job_ptr); ++} ++ ++/* ++ * load into the storage the end of a job ++ */ ++extern int jobacct_storage_p_job_complete(pgsql_conn_t *pgsql_conn, ++ job_record_t *job_ptr) ++{ ++ return as_pgsql_job_complete(pgsql_conn, job_ptr); ++} ++ ++/* ++ * load into the storage the start of a job step ++ */ ++extern int jobacct_storage_p_step_start(pgsql_conn_t *pgsql_conn, ++ step_record_t *step_ptr) ++{ ++ return as_pgsql_step_start(pgsql_conn, step_ptr); ++} ++ ++/* ++ * load into the storage the end of a job step ++ */ ++extern int jobacct_storage_p_step_complete(pgsql_conn_t *pgsql_conn, ++ step_record_t *step_ptr) ++{ ++ return as_pgsql_step_complete(pgsql_conn, step_ptr); ++} ++ ++/* ++ * load into the storage a suspension of a job ++ */ ++extern int jobacct_storage_p_suspend(pgsql_conn_t *pgsql_conn, ++ job_record_t *job_ptr) ++{ ++ return as_pgsql_suspend(pgsql_conn, 0, job_ptr); ++} ++ ++/* ++ * get info from the storage ++ * returns List of job_rec_t * ++ * note List needs to be freed when called ++ */ ++extern List jobacct_storage_p_get_jobs_cond(pgsql_conn_t *pgsql_conn, ++ uid_t uid, ++ slurmdb_job_cond_t *job_cond) ++{ ++ List job_list = NULL; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) { ++ return NULL; ++ } ++ job_list = as_pgsql_jobacct_process_get_jobs(pgsql_conn, uid, job_cond); ++ ++ return job_list; ++} ++ ++/* ++ * expire old info from the storage ++ */ ++extern int jobacct_storage_p_archive(pgsql_conn_t *pgsql_conn, ++ slurmdb_archive_cond_t *arch_cond) ++{ ++ int rc; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ /* Make sure only 1 archive is happening at a time. */ ++ slurm_mutex_lock(&usage_rollup_lock); ++ rc = as_pgsql_jobacct_process_archive(pgsql_conn, arch_cond); ++ slurm_mutex_unlock(&usage_rollup_lock); ++ ++ return rc; ++} ++ ++/* ++ * load old info into the storage ++ */ ++extern int jobacct_storage_p_archive_load(pgsql_conn_t *pgsql_conn, ++ slurmdb_archive_rec_t *arch_rec) ++{ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ return as_pgsql_jobacct_process_archive_load(pgsql_conn, arch_rec); ++} ++ ++extern int acct_storage_p_update_shares_used(pgsql_conn_t *pgsql_conn, ++ List shares_used) ++{ ++ /* No plans to have the database hold the used shares */ ++ return SLURM_SUCCESS; ++} ++ ++extern int acct_storage_p_flush_jobs_on_cluster( ++ pgsql_conn_t *pgsql_conn, time_t event_time) ++{ ++ return as_pgsql_flush_jobs_on_cluster(pgsql_conn, event_time); ++} ++ ++extern int acct_storage_p_reconfig(pgsql_conn_t *pgsql_conn, bool dbd) ++{ ++ return SLURM_SUCCESS; ++} ++ ++extern int acct_storage_p_reset_lft_rgt(pgsql_conn_t *pgsql_conn, uid_t uid, ++ List cluster_list) ++{ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ return as_pgsql_reset_lft_rgt(pgsql_conn, uid, cluster_list); ++} ++ ++extern int acct_storage_p_get_stats(void *db_conn, bool dbd) ++{ ++ return SLURM_SUCCESS; ++} ++ ++extern int acct_storage_p_clear_stats(void *db_conn, bool dbd) ++{ ++ return SLURM_SUCCESS; ++} ++ ++extern int acct_storage_p_get_data(void *db_conn, acct_storage_info_t dinfo, ++ void *data) ++{ ++ return SLURM_SUCCESS; ++} ++ ++extern int acct_storage_p_shutdown(void *db_conn, bool dbd) ++{ ++ return SLURM_SUCCESS; ++} +diff --git a/src/plugins/accounting_storage/pgsql/accounting_storage_pgsql.h b/src/plugins/accounting_storage/pgsql/accounting_storage_pgsql.h +new file mode 100755 +index 0000000..78b7da2 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/accounting_storage_pgsql.h +@@ -0,0 +1,207 @@ ++/*****************************************************************************\ ++ * accounting_storage_pgsql.h - accounting interface to as_pgsql header file. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_ACCOUNTING_STORAGE_PGSQL_H ++#define _HAVE_ACCOUNTING_STORAGE_PGSQL_H ++ ++#include ++#include ++ ++/* we can't include common/slurm_xlator.h here since it contains ++ * list_push and list_pop which are pgsql macros what for some reason ++ * are given to us. So if you need something from the header just ++ * copy it here. ++ */ ++#define fatal slurm_fatal ++#define error slurm_error ++ ++/* ++ * Allow up to 999 static TRES ++ * NOTE: If this changes for some reason you will also need to update the 1001 ++ * in accounting_storage_pgsql.c... ++ * ++ * if (pgsql_db_create_table(pgsql_conn, tres_table, ++ * tres_table_fields, ++ * ", primary key (id), " ++ * "unique index (type(20), name(20))) " ++ * "auto_increment=1001") ++ * ++ */ ++#define TRES_OFFSET 1000 ++ ++#include "src/common/assoc_mgr.h" ++#include "src/common/macros.h" ++#include "src/common/slurmdbd_defs.h" ++#include "src/common/slurm_auth.h" ++#include "src/common/uid.h" ++ ++#include "src/database/pgsql_common.h" ++ ++#include "src/slurmdbd/read_config.h" ++ ++#include "../common/common_as.h" ++ ++extern char *acct_coord_table; ++extern char *acct_table; ++extern char *tres_table; ++extern char *assoc_day_table; ++extern char *assoc_hour_table; ++extern char *assoc_month_table; ++extern char *assoc_table; ++extern char *clus_res_table; ++extern char *cluster_day_table; ++extern char *cluster_hour_table; ++extern char *cluster_month_table; ++extern char *cluster_table; ++extern char *convert_version_table; ++extern char *federation_table; ++extern char *event_table; ++extern char *job_table; ++extern char *last_ran_table; ++extern char *qos_table; ++extern char *resv_table; ++extern char *res_table; ++extern char *step_table; ++extern char *txn_table; ++extern char *user_table; ++extern char *suspend_table; ++extern char *wckey_day_table; ++extern char *wckey_hour_table; ++extern char *wckey_month_table; ++extern char *wckey_table; ++ ++/* Since tables are cluster centric we have a global cluster list to ++ * go off of. ++ */ ++extern List as_pgsql_cluster_list; ++extern List as_pgsql_total_cluster_list; ++extern pthread_rwlock_t as_pgsql_cluster_list_lock; ++ ++extern bool backup_dbd; ++ ++typedef enum { ++ QOS_LEVEL_NONE, ++ QOS_LEVEL_SET, ++ QOS_LEVEL_MODIFY ++} qos_level_t; ++ ++#define DB_DEBUG(flag, conn, fmt, ...) \ ++ log_flag(flag, "%d(%s:%d) "fmt, conn, THIS_FILE, __LINE__, ##__VA_ARGS__); ++ ++/*global functions */ ++extern int check_connection(pgsql_conn_t *pgsql_conn); ++extern char *fix_double_quotes(char *str); ++extern void reset_pgsql_conn(pgsql_conn_t *pgsql_conn); ++extern int create_cluster_assoc_table( ++ pgsql_conn_t *pgsql_conn, char *cluster_name); ++extern int create_cluster_tables(pgsql_conn_t *pgsql_conn, char *cluster_name); ++extern int remove_cluster_tables(pgsql_conn_t *pgsql_conn, char *cluster_name); ++extern int setup_assoc_limits(slurmdb_assoc_rec_t *assoc, ++ char **cols, char **vals, ++ char **extra, qos_level_t qos_level, ++ bool for_add); ++extern int modify_common(pgsql_conn_t *pgsql_conn, ++ uint16_t type, ++ time_t now, ++ char *user_name, ++ char *table, ++ char *cond_char, ++ char *vals, ++ char *cluster_name); ++extern int remove_common(pgsql_conn_t *pgsql_conn, ++ uint16_t type, ++ time_t now, ++ char *user_name, ++ char *table, ++ char *name_char, ++ char *assoc_char, ++ char *cluster_name, ++ List ret_list, ++ bool *jobs_running); ++ ++extern void mod_tres_str(char **out, char *mod, char *cur, ++ char *cur_par, char *name, char **vals, ++ uint32_t id, bool assoc); ++ ++/* ++ * Get the dimensions of this cluster so we know how to deal with the hostlists. ++ * ++ * IN pgsql_conn - pgsql connection ++ * IN cluster_name - name of cluster to get dimensions for ++ * OUT dims - dimenions of cluster ++ * ++ * RET return SLURM_SUCCESS on success, SLURM_FAILURE otherwise. ++ */ ++extern int get_cluster_dims(pgsql_conn_t *pgsql_conn, char *cluster_name, ++ int *dims); ++ ++/*local api functions */ ++extern int acct_storage_p_commit(pgsql_conn_t *pgsql_conn, bool commit); ++ ++extern int acct_storage_p_add_assocs(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ List assoc_list); ++ ++extern int acct_storage_p_add_wckeys(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List wckey_list); ++ ++extern List acct_storage_p_get_assocs( ++ pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_assoc_cond_t *assoc_cond); ++ ++extern List acct_storage_p_get_wckeys(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_wckey_cond_t *wckey_cond); ++ ++extern int acct_storage_p_get_usage(pgsql_conn_t *pgsql_conn, uid_t uid, ++ void *in, slurmdbd_msg_type_t type, ++ time_t start, time_t end); ++ ++extern int clusteracct_storage_p_get_usage( ++ pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_cluster_rec_t *cluster_rec, slurmdbd_msg_type_t type, ++ time_t start, time_t end); ++ ++extern List acct_storage_p_remove_coord(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List acct_list, ++ slurmdb_user_cond_t *user_cond); ++ ++extern List acct_storage_p_remove_wckeys(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ slurmdb_wckey_cond_t *wckey_cond); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_acct.c b/src/plugins/accounting_storage/pgsql/as_pgsql_acct.c +new file mode 100755 +index 0000000..0aadd11 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_acct.c +@@ -0,0 +1,795 @@ ++/*****************************************************************************\ ++ * as_pgsql_acct.c - functions dealing with accounts. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_assoc.h" ++#include "as_pgsql_acct.h" ++#include "as_pgsql_user.h" ++ ++/* Fill in all the users that are coordinator for this account. This ++ * will fill in if there are coordinators from a parent account also. ++ */ ++static int _get_account_coords(pgsql_conn_t *pgsql_conn, ++ slurmdb_account_rec_t *acct) ++{ ++ char *query = NULL, *cluster_name = NULL; ++ slurmdb_coord_rec_t *coord = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ListIterator itr; ++ ++ if (!acct) { ++ error("We need a account to fill in."); ++ return SLURM_ERROR; ++ } ++ ++ if (!acct->coordinators) ++ acct->coordinators = list_create(slurmdb_destroy_coord_rec); ++ ++ query = xstrdup_printf( ++ "select \"user\" from %s where acct='%s' AND deleted=0", ++ acct_coord_table, acct->name); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ coord = xmalloc(sizeof(slurmdb_coord_rec_t)); ++ list_append(acct->coordinators, coord); ++ coord->name = xstrdup(row[0]); ++ coord->direct = 1; ++ } ++ pgsql_free_result(&result); ++ ++ ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ itr = list_iterator_create(as_pgsql_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ if (query) ++ xstrcat(query, " union "); ++ xstrfmtcat(query, ++ "select distinct t0.\"user\" from %s as t0, " ++ "\"%s_%s\" as t1, \"%s_%s\" as t2 " ++ "where t0.acct=t1.acct AND " ++ "t1.lftt2.lft AND " ++ "t1.\"user\"='' AND t2.acct='%s' " ++// "AND t1.acct!='%s' AND !t0.deleted", ++ "AND t1.acct!='%s' AND t0.deleted=0", ++ acct_coord_table, cluster_name, assoc_table, ++ cluster_name, assoc_table, ++ acct->name, acct->name); ++ } ++ list_iterator_destroy(itr); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ ++ if (!query) { ++ error("No clusters defined? How could there be accts?"); ++ return SLURM_SUCCESS; ++ } ++ xstrcat(query, ";"); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ coord = xmalloc(sizeof(slurmdb_coord_rec_t)); ++ list_append(acct->coordinators, coord); ++ coord->name = xstrdup(row[0]); ++ coord->direct = 0; ++ } ++ pgsql_free_result(&result); ++ ++ return SLURM_SUCCESS; ++} ++ ++extern int as_pgsql_add_accts(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List acct_list) ++{ ++ ListIterator itr = NULL; ++ int rc = SLURM_SUCCESS; ++ slurmdb_account_rec_t *object = NULL; ++ char *cols = NULL, *vals = NULL, *query = NULL, *txn_query = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ char *extra = NULL, *tmp_extra = NULL; ++ ++ int affect_rows = 0; ++ List assoc_list; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { ++ slurmdb_user_rec_t user; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (!is_user_any_coord(pgsql_conn, &user)) { ++ error("Only admins/operators/coordinators " ++ "can add accounts"); ++ return ESLURM_ACCESS_DENIED; ++ } ++ /* If the user is a coord of any acct they can add ++ * accounts they are only able to make associations to ++ * these accounts if they are coordinators of the ++ * parent they are trying to add to ++ */ ++ } ++ ++ assoc_list = list_create(slurmdb_destroy_assoc_rec); ++ user_name = uid_to_string((uid_t) uid); ++ itr = list_iterator_create(acct_list); ++ while ((object = list_next(itr))) { ++ if (!object->name || !object->name[0] ++ || !object->description || !object->description[0] ++ || !object->organization || !object->organization[0]) { ++ error("We need an account name, description, and " ++ "organization to add. %s %s %s", ++ object->name, object->description, ++ object->organization); ++ rc = SLURM_ERROR; ++ continue; ++ } ++ xstrcat(cols, "creation_time, mod_time, name, " ++ "description, organization"); ++ xstrfmtcat(vals, "%ld, %ld, '%s', '%s', '%s'", ++ now, now, object->name, ++ object->description, object->organization); ++ xstrfmtcat(extra, ", description='%s', organization='%s'", ++ object->description, object->organization); ++ ++ query = xstrdup_printf( ++ "insert into %s (%s) values (%s) " ++ "on conflict (name) do update set deleted=0, mod_time=%ld %s;", ++ acct_table, cols, vals, ++ now, extra); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ ++// rc = pgsql_db_query(pgsql_conn, query); ++ SQLHSTMT stmt = NULL; ++ rc = pgsql_db_query_stmt(pgsql_conn, query, &stmt); ++ xfree(cols); ++ xfree(vals); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add acct"); ++ xfree(extra); ++ pgsql_db_free_statement(&stmt); ++ continue; ++ } ++ affect_rows = last_affected_rows(stmt); ++ pgsql_db_free_statement(&stmt); ++ //DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "affected %d", ++ // affect_rows); ++ ++ if (!affect_rows) { ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "nothing changed"); ++ xfree(extra); ++ continue; ++ } ++ ++ /* we always have a ', ' as the first 2 chars */ ++ tmp_extra = slurm_add_slash_to_quotes(extra+2); ++ ++ if (txn_query) ++ xstrfmtcat(txn_query, ++ ", (%ld, %u, '%s', '%s', '%s')", ++ now, DBD_ADD_ACCOUNTS, object->name, ++ user_name, tmp_extra); ++ else ++ xstrfmtcat(txn_query, ++ "insert into %s " ++ "(timestamp, action, name, actor, info) " ++ "values (%ld, %u, '%s', '%s', '%s')", ++ txn_table, ++ now, DBD_ADD_ACCOUNTS, object->name, ++ user_name, tmp_extra); ++ xfree(tmp_extra); ++ xfree(extra); ++ ++ if (!object->assoc_list) ++ continue; ++ ++ if (!assoc_list) ++ assoc_list = ++ list_create(slurmdb_destroy_assoc_rec); ++ list_transfer(assoc_list, object->assoc_list); ++ } ++ list_iterator_destroy(itr); ++ xfree(user_name); ++ ++ if (rc != SLURM_ERROR) { ++ if (txn_query) { ++ xstrcat(txn_query, ";"); ++ rc = pgsql_db_query(pgsql_conn, ++ txn_query); ++ xfree(txn_query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add txn"); ++ rc = SLURM_SUCCESS; ++ } ++ } ++ } else ++ xfree(txn_query); ++ ++ if (assoc_list && list_count(assoc_list)) { ++ if ((rc = as_pgsql_add_assocs(pgsql_conn, uid, assoc_list)) ++ != SLURM_SUCCESS) ++ error("Problem adding accounts associations"); ++ } ++ FREE_NULL_LIST(assoc_list); ++ ++ return rc; ++} ++ ++extern List as_pgsql_modify_accts(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_account_cond_t *acct_cond, ++ slurmdb_account_rec_t *acct) ++{ ++ ListIterator itr = NULL; ++ List ret_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *object = NULL; ++ char *vals = NULL, *extra = NULL, *query = NULL, *name_char = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ int set = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ ++ if (!acct_cond || !acct) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ ++ xstrcat(extra, "where deleted=0"); ++ if (acct_cond->assoc_cond ++ && acct_cond->assoc_cond->acct_list ++ && list_count(acct_cond->assoc_cond->acct_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(acct_cond->assoc_cond->acct_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (acct_cond->description_list ++ && list_count(acct_cond->description_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(acct_cond->description_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "description='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (acct_cond->organization_list ++ && list_count(acct_cond->organization_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(acct_cond->organization_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "organization='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (acct->description) ++ xstrfmtcat(vals, ", description='%s'", acct->description); ++ if (acct->organization) ++ xstrfmtcat(vals, ", organization='%s'", acct->organization); ++ ++ if (!extra || !vals) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ error("Nothing to change"); ++ return NULL; ++ } ++ ++ query = xstrdup_printf("select name from %s %s;", acct_table, extra); ++ xfree(extra); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ xfree(vals); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ rc = 0; ++ ret_list = list_create(xfree_ptr); ++ ++ ++ while ((row = pgsql_fetch_row(result))) { ++ object = xstrdup(row[0]); ++ list_append(ret_list, object); ++ if (!rc) { ++ xstrfmtcat(name_char, "(name='%s'", object); ++ rc = 1; ++ } else { ++ xstrfmtcat(name_char, " OR name='%s'", object); ++ } ++ ++ } ++ pgsql_free_result(&result); ++ ++ ++ if (!list_count(ret_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(query); ++ xfree(vals); ++ return ret_list; ++ } ++ xfree(query); ++ xstrcat(name_char, ")"); ++ ++ user_name = uid_to_string((uid_t) uid); ++ rc = modify_common(pgsql_conn, DBD_MODIFY_ACCOUNTS, now, ++ user_name, acct_table, name_char, vals, NULL); ++ xfree(user_name); ++ if (rc == SLURM_ERROR) { ++ error("Couldn't modify accounts"); ++ FREE_NULL_LIST(ret_list); ++ errno = SLURM_ERROR; ++ ret_list = NULL; ++ } ++ ++ xfree(name_char); ++ xfree(vals); ++ ++ return ret_list; ++} ++ ++extern List as_pgsql_remove_accts(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_account_cond_t *acct_cond) ++{ ++ ListIterator itr = NULL; ++ List ret_list = NULL; ++ List coord_list = NULL; ++ List cluster_list_tmp = NULL; ++ int rc = SLURM_SUCCESS; ++ char *object = NULL; ++ char *extra = NULL, *query = NULL, ++ *name_char = NULL, *assoc_char = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ int set = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ bool jobs_running = 0; ++ ++ if (!acct_cond) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ ++ xstrcat(extra, "where deleted=0"); ++ if (acct_cond->assoc_cond ++ && acct_cond->assoc_cond->acct_list ++ && list_count(acct_cond->assoc_cond->acct_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(acct_cond->assoc_cond->acct_list); ++ while ((object = list_next(itr))) { ++ if (!object[0]) ++ continue; ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (acct_cond->description_list ++ && list_count(acct_cond->description_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(acct_cond->description_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "description='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (acct_cond->organization_list ++ && list_count(acct_cond->organization_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(acct_cond->organization_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "organization='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (!extra) { ++ error("Nothing to remove"); ++ return NULL; ++ } ++ ++ query = xstrdup_printf("select name from %s %s;", acct_table, extra); ++ xfree(extra); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ rc = 0; ++ ret_list = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) { ++ char *object = xstrdup(row[0]); ++ list_append(ret_list, object); ++ if (!rc) { ++ xstrfmtcat(name_char, "name='%s'", object); ++ xstrfmtcat(assoc_char, "t2.acct='%s'", object); ++ rc = 1; ++ } else { ++ xstrfmtcat(name_char, " OR name='%s'", object); ++ xstrfmtcat(assoc_char, " OR t2.acct='%s'", object); ++ } ++ } ++ pgsql_free_result(&result); ++ ++ ++ if (!list_count(ret_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(query); ++ return ret_list; ++ } ++ xfree(query); ++ ++ /* We need to remove these accounts from the coord's that have it */ ++ coord_list = as_pgsql_remove_coord( ++ pgsql_conn, uid, ret_list, NULL); ++ FREE_NULL_LIST(coord_list); ++ ++ user_name = uid_to_string((uid_t) uid); ++ ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ cluster_list_tmp = list_shallow_copy(as_pgsql_cluster_list); ++ itr = list_iterator_create(cluster_list_tmp); ++ while ((object = list_next(itr))) { ++ if ((rc = remove_common(pgsql_conn, DBD_REMOVE_ACCOUNTS, now, ++ user_name, acct_table, name_char, ++ assoc_char, object, ret_list, ++ &jobs_running)) ++ != SLURM_SUCCESS) ++ break; ++ } ++ list_iterator_destroy(itr); ++ FREE_NULL_LIST(cluster_list_tmp); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ ++ xfree(user_name); ++ xfree(name_char); ++ xfree(assoc_char); ++ if (rc == SLURM_ERROR) { ++ FREE_NULL_LIST(ret_list); ++ return NULL; ++ } ++ ++ if (jobs_running) ++ errno = ESLURM_JOBS_RUNNING_ON_ASSOC; ++ else ++ errno = SLURM_SUCCESS; ++ return ret_list; ++} ++ ++extern List as_pgsql_get_accts(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_account_cond_t *acct_cond) ++{ ++ char *query = NULL; ++ char *extra = NULL; ++ char *tmp = NULL; ++ List acct_list = NULL; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ int set = 0; ++ int i=0, is_admin=1; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ slurmdb_user_rec_t user; ++ ++ /* if this changes you will need to edit the corresponding enum */ ++ char *acct_req_inx[] = { ++ "name", ++ "description", ++ "organization", ++ "deleted", ++ }; ++ enum { ++ SLURMDB_REQ_NAME, ++ SLURMDB_REQ_DESC, ++ SLURMDB_REQ_ORG, ++ SLURMDB_REQ_DELETED, ++ SLURMDB_REQ_COUNT ++ }; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (slurm_conf.private_data & PRIVATE_DATA_ACCOUNTS) { ++ if (!(is_admin = is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { ++ if (!is_user_any_coord(pgsql_conn, &user)) { ++ error("Only admins/coordinators " ++ "can look at account usage"); ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ } ++ } ++ ++ if (!acct_cond) { ++ xstrcat(extra, "where deleted=0"); ++ goto empty; ++ } ++ ++ if (acct_cond->with_deleted) ++ xstrcat(extra, "where (deleted=0 OR deleted=1)"); ++ else ++ xstrcat(extra, "where deleted=0"); ++ ++ if (acct_cond->assoc_cond ++ && acct_cond->assoc_cond->acct_list ++ && list_count(acct_cond->assoc_cond->acct_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(acct_cond->assoc_cond->acct_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (acct_cond->description_list ++ && list_count(acct_cond->description_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(acct_cond->description_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "description='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (acct_cond->organization_list ++ && list_count(acct_cond->organization_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(acct_cond->organization_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "organization='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++empty: ++ ++ xfree(tmp); ++ xstrfmtcat(tmp, "%s", acct_req_inx[i]); ++ for(i=1; iname); ++ } else { ++ set = 1; ++ xstrfmtcat(extra, " AND (name='%s'", ++ coord->name); ++ } ++ } ++ list_iterator_destroy(itr); ++ if (set) ++ xstrcat(extra,")"); ++ } ++ ++ query = xstrdup_printf("select %s from %s %s", tmp, acct_table, extra); ++ xfree(tmp); ++ xfree(extra); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ xfree(query); ++ ++ acct_list = list_create(slurmdb_destroy_account_rec); ++ ++ if (acct_cond && acct_cond->assoc_cond && acct_cond->with_assocs) { ++ /* We are going to be freeing the inners of ++ this list in the acct->name so we don't ++ free it here ++ */ ++ FREE_NULL_LIST(acct_cond->assoc_cond->acct_list); ++ acct_cond->assoc_cond->acct_list = list_create(NULL); ++ acct_cond->assoc_cond->with_deleted = acct_cond->with_deleted; ++ } ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_account_rec_t *acct = ++ xmalloc(sizeof(slurmdb_account_rec_t)); ++ list_append(acct_list, acct); ++ ++ acct->name = xstrdup(row[SLURMDB_REQ_NAME]); ++ acct->description = xstrdup(row[SLURMDB_REQ_DESC]); ++ acct->organization = xstrdup(row[SLURMDB_REQ_ORG]); ++ ++ if (slurm_atoul(row[SLURMDB_REQ_DELETED])) ++ acct->flags |= SLURMDB_ACCT_FLAG_DELETED; ++ ++ if (acct_cond && acct_cond->with_coords) { ++ _get_account_coords(pgsql_conn, acct); ++ } ++ ++ if (acct_cond && acct_cond->with_assocs) { ++ if (!acct_cond->assoc_cond) { ++ acct_cond->assoc_cond = xmalloc( ++ sizeof(slurmdb_assoc_cond_t)); ++ } ++ ++ list_append(acct_cond->assoc_cond->acct_list, ++ acct->name); ++ } ++ } ++ pgsql_free_result(&result); ++ ++ ++ if (acct_cond && acct_cond->with_assocs && acct_cond->assoc_cond ++ && list_count(acct_cond->assoc_cond->acct_list)) { ++ ListIterator assoc_itr = NULL; ++ slurmdb_account_rec_t *acct = NULL; ++ slurmdb_assoc_rec_t *assoc = NULL; ++ List assoc_list = as_pgsql_get_assocs( ++ pgsql_conn, uid, acct_cond->assoc_cond); ++ ++ if (!assoc_list) { ++ error("no associations"); ++ return acct_list; ++ } ++ ++ itr = list_iterator_create(acct_list); ++ assoc_itr = list_iterator_create(assoc_list); ++ while ((acct = list_next(itr))) { ++ while ((assoc = list_next(assoc_itr))) { ++ if (xstrcmp(assoc->acct, acct->name)) ++ continue; ++ ++ if (!acct->assoc_list) ++ acct->assoc_list = list_create( ++ slurmdb_destroy_assoc_rec); ++ list_append(acct->assoc_list, assoc); ++ list_remove(assoc_itr); ++ } ++ list_iterator_reset(assoc_itr); ++ } ++ list_iterator_destroy(itr); ++ list_iterator_destroy(assoc_itr); ++ ++ FREE_NULL_LIST(assoc_list); ++ } ++ ++ return acct_list; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_acct.h b/src/plugins/accounting_storage/pgsql/as_pgsql_acct.h +new file mode 100755 +index 0000000..c5bfe2b +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_acct.h +@@ -0,0 +1,56 @@ ++/*****************************************************************************\ ++ * as_pgsql_acct.h - functions dealing with accounts. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++#ifndef _HAVE_PGSQL_ACCT_H ++#define _HAVE_PGSQL_ACCT_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_add_accts(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List acct_list); ++ ++extern List as_pgsql_modify_accts(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_account_cond_t *acct_cond, ++ slurmdb_account_rec_t *acct); ++ ++extern List as_pgsql_remove_accts(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_account_cond_t *acct_cond); ++ ++extern List as_pgsql_get_accts(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_account_cond_t *acct_cond); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_archive.c b/src/plugins/accounting_storage/pgsql/as_pgsql_archive.c +new file mode 100755 +index 0000000..0899a8f +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_archive.c +@@ -0,0 +1,4737 @@ ++/*****************************************************************************\ ++ * as_pgsql_archive.c - functions dealing with the archiving. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 ++#include ++#include ++#include ++ ++#include "as_pgsql_archive.h" ++#include "src/common/env.h" ++#include "src/common/slurm_time.h" ++#include "src/common/slurmdbd_defs.h" ++ ++#define SLURM_19_05_PROTOCOL_VERSION ((34 << 8) | 0) ++#define SLURM_18_08_PROTOCOL_VERSION ((33 << 8) | 0) ++#define SLURM_17_11_PROTOCOL_VERSION ((32 << 8) | 0) ++#define SLURM_17_02_PROTOCOL_VERSION ((31 << 8) | 0) /* slurm version 17.02. */ ++#define SLURM_16_05_PROTOCOL_VERSION ((30 << 8) | 0) /* slurm version 16.05. */ ++#define SLURM_15_08_PROTOCOL_VERSION ((29 << 8) | 0) /* slurm version 15.08. */ ++#define SLURM_14_11_PROTOCOL_VERSION ((28 << 8) | 0) /* slurm version 14.11. */ ++#define SLURM_14_03_PROTOCOL_VERSION ((27 << 8) | 0) /* slurm version ++ * 14.03, not ++ * needed here ++ * but added for ++ * reference. */ ++/* Before 14.03 the DBD had it's own versioning, in 14.03 all daemons use the ++ * same version numbering. */ ++#define SLURMDBD_2_6_VERSION 12 /* slurm version 2.6 */ ++#define SLURMDBD_2_5_VERSION 11 /* slurm version 2.5 */ ++ ++#define MAX_PURGE_LIMIT 50000 /* Number of records that are purged at a time ++ so that locks can be periodically released. */ ++#define MAX_ARCHIVE_AGE (60 * 60 * 24 * 60) /* If archive data is older than ++ this then archive by month to ++ handle large datasets. */ ++ ++#ifndef RECORDS_PER_PASS ++#define RECORDS_PER_PASS 1000 /* Records per single sql statement. */ ++#endif /* RECORDS_PER_PASS */ ++ ++typedef struct { ++ char *cluster_nodes; ++ char *node_name; ++ char *period_end; ++ char *period_start; ++ char *reason; ++ char *reason_uid; ++ char *state; ++ char *tres_str; ++} local_event_t; ++ ++static void _free_local_event_members(local_event_t *object) ++{ ++ if (object) { ++ xfree(object->cluster_nodes); ++ xfree(object->node_name); ++ xfree(object->period_end); ++ xfree(object->period_start); ++ xfree(object->reason); ++ xfree(object->reason_uid); ++ xfree(object->state); ++ xfree(object->tres_str); ++ } ++} ++ ++typedef struct { ++ char *account; ++ char *admin_comment; ++ char *alloc_nodes; ++ char *associd; ++ char *array_jobid; ++ char *array_max_tasks; ++ char *array_taskid; ++ char *array_task_pending; ++ char *array_task_str; ++ char *blockid; ++ char *constraints; ++ char *deleted; ++ char *derived_ec; ++ char *derived_es; ++ char *env; ++ char *exit_code; ++ char *eligible; ++ char *end; ++ char *flags; ++ char *gid; ++ char *gres_used; ++ char *het_job_id; ++ char *het_job_offset; ++ char *job_db_inx; ++ char *jobid; ++ char *kill_requid; ++ char *mcs_label; ++ char *mod_time; ++ char *name; ++ char *nodelist; ++ char *node_inx; ++ char *partition; ++ char *priority; ++ char *qos; ++ char *req_cpus; ++ char *req_mem; ++ char *resvid; ++ char *script; ++ char *start; ++ char *state; ++ char *state_reason_prev; ++ char *submit; ++ char *suspended; ++ char *system_comment; ++ char *timelimit; ++ char *track_steps; ++ char *tres_alloc_str; ++ char *tres_req_str; ++ char *uid; ++ char *wckey; ++ char *wckey_id; ++ char *work_dir; ++} local_job_t; ++ ++static void _convert_old_step_id(char **step_id) ++{ ++ if (!step_id || !*step_id) ++ return; ++ ++ if (!xstrcmp(*step_id, "-2")) { ++ xfree(*step_id); ++ *step_id = xstrdup_printf("%d", SLURM_BATCH_SCRIPT); ++ } else if (!xstrcmp(*step_id, "-1")) { ++ xfree(*step_id); ++ *step_id = xstrdup_printf("%d", SLURM_EXTERN_CONT); ++ } ++} ++ ++static void _free_local_job_members(local_job_t *object) ++{ ++ if (object) { ++ xfree(object->account); ++ xfree(object->admin_comment); ++ xfree(object->alloc_nodes); ++ xfree(object->associd); ++ xfree(object->array_jobid); ++ xfree(object->array_max_tasks); ++ xfree(object->array_taskid); ++ xfree(object->array_task_pending); ++ xfree(object->array_task_str); ++ xfree(object->blockid); ++ xfree(object->constraints); ++ xfree(object->deleted); ++ xfree(object->derived_ec); ++ xfree(object->derived_es); ++ xfree(object->env); ++ xfree(object->exit_code); ++ xfree(object->eligible); ++ xfree(object->end); ++ xfree(object->flags); ++ xfree(object->gid); ++ xfree(object->gres_used); ++ xfree(object->het_job_id); ++ xfree(object->het_job_offset); ++ xfree(object->job_db_inx); ++ xfree(object->jobid); ++ xfree(object->kill_requid); ++ xfree(object->mcs_label); ++ xfree(object->mod_time); ++ xfree(object->name); ++ xfree(object->nodelist); ++ xfree(object->node_inx); ++ xfree(object->partition); ++ xfree(object->priority); ++ xfree(object->qos); ++ xfree(object->req_cpus); ++ xfree(object->req_mem); ++ xfree(object->resvid); ++ xfree(object->script); ++ xfree(object->start); ++ xfree(object->state); ++ xfree(object->state_reason_prev); ++ xfree(object->submit); ++ xfree(object->suspended); ++ xfree(object->system_comment); ++ xfree(object->timelimit); ++ xfree(object->track_steps); ++ xfree(object->tres_alloc_str); ++ xfree(object->tres_req_str); ++ xfree(object->uid); ++ xfree(object->wckey); ++ xfree(object->wckey_id); ++ xfree(object->work_dir); ++ } ++} ++ ++typedef struct { ++ char *assocs; ++ char *deleted; ++ char *flags; ++ char *id; ++ char *name; ++ char *nodes; ++ char *node_inx; ++ char *time_end; ++ char *time_start; ++ char *tres_str; ++ char *unused_wall; ++} local_resv_t; ++ ++static void _free_local_resv_members(local_resv_t *object) ++{ ++ if (object) { ++ xfree(object->assocs); ++ xfree(object->deleted); ++ xfree(object->flags); ++ xfree(object->id); ++ xfree(object->name); ++ xfree(object->nodes); ++ xfree(object->node_inx); ++ xfree(object->time_end); ++ xfree(object->time_start); ++ xfree(object->tres_str); ++ xfree(object->unused_wall); ++ } ++} ++ ++typedef struct { ++ char *act_cpufreq; ++ char *deleted; ++ char *exit_code; ++ char *consumed_energy; ++ char *job_db_inx; ++ char *kill_requid; ++ char *name; ++ char *nodelist; ++ char *nodes; ++ char *node_inx; ++ char *period_end; ++ char *period_start; ++ char *period_suspended; ++ char *req_cpufreq_min; ++ char *req_cpufreq_max; ++ char *req_cpufreq_gov; ++ char *state; ++ char *stepid; ++ char *step_het_comp; ++ char *submit_line; ++ char *sys_sec; ++ char *sys_usec; ++ char *tasks; ++ char *task_dist; ++ char *tres_alloc_str; ++ char *tres_usage_in_ave; ++ char *tres_usage_in_max; ++ char *tres_usage_in_max_nodeid; ++ char *tres_usage_in_max_taskid; ++ char *tres_usage_in_min; ++ char *tres_usage_in_min_nodeid; ++ char *tres_usage_in_min_taskid; ++ char *tres_usage_in_tot; ++ char *tres_usage_out_ave; ++ char *tres_usage_out_max; ++ char *tres_usage_out_max_nodeid; ++ char *tres_usage_out_max_taskid; ++ char *tres_usage_out_min; ++ char *tres_usage_out_min_nodeid; ++ char *tres_usage_out_min_taskid; ++ char *tres_usage_out_tot; ++ char *user_sec; ++ char *user_usec; ++} local_step_t; ++ ++static void _free_local_step_members(local_step_t *object) ++{ ++ if (object) { ++ xfree(object->act_cpufreq); ++ xfree(object->deleted); ++ xfree(object->exit_code); ++ xfree(object->consumed_energy); ++ xfree(object->job_db_inx); ++ xfree(object->kill_requid); ++ xfree(object->name); ++ xfree(object->nodelist); ++ xfree(object->nodes); ++ xfree(object->node_inx); ++ xfree(object->period_end); ++ xfree(object->period_start); ++ xfree(object->period_suspended); ++ xfree(object->req_cpufreq_min); ++ xfree(object->req_cpufreq_max); ++ xfree(object->req_cpufreq_gov); ++ xfree(object->state); ++ xfree(object->stepid); ++ xfree(object->step_het_comp); ++ xfree(object->submit_line); ++ xfree(object->sys_sec); ++ xfree(object->sys_usec); ++ xfree(object->tasks); ++ xfree(object->task_dist); ++ xfree(object->tres_alloc_str); ++ xfree(object->tres_usage_in_ave); ++ xfree(object->tres_usage_in_max); ++ xfree(object->tres_usage_in_max_nodeid); ++ xfree(object->tres_usage_in_max_taskid); ++ xfree(object->tres_usage_in_min); ++ xfree(object->tres_usage_in_min_nodeid); ++ xfree(object->tres_usage_in_min_taskid); ++ xfree(object->tres_usage_in_tot); ++ xfree(object->tres_usage_out_ave); ++ xfree(object->tres_usage_out_max); ++ xfree(object->tres_usage_out_max_nodeid); ++ xfree(object->tres_usage_out_max_taskid); ++ xfree(object->tres_usage_out_min); ++ xfree(object->tres_usage_out_min_nodeid); ++ xfree(object->tres_usage_out_min_taskid); ++ xfree(object->tres_usage_out_tot); ++ xfree(object->user_sec); ++ xfree(object->user_usec); ++ } ++} ++ ++typedef struct { ++ char *associd; ++ char *job_db_inx; ++ char *period_end; ++ char *period_start; ++} local_suspend_t; ++ ++static void _free_local_suspend_members(local_suspend_t *object) ++{ ++ if (object) { ++ xfree(object->associd); ++ xfree(object->job_db_inx); ++ xfree(object->period_end); ++ xfree(object->period_start); ++ } ++} ++ ++typedef struct { ++ char *id; ++ char *timestamp; ++ char *action; ++ char *name; ++ char *actor; ++ char *info; ++ char *cluster; ++} local_txn_t; ++ ++static void _free_local_txn_members(local_txn_t *object) ++{ ++ if (object) { ++ xfree(object->id); ++ xfree(object->timestamp); ++ xfree(object->action); ++ xfree(object->name); ++ xfree(object->actor); ++ xfree(object->info); ++ xfree(object->cluster); ++ } ++} ++ ++typedef struct { ++ char *alloc_secs; ++ char *id; ++ char *time_start; ++ char *tres_id; ++ char *creation_time; ++ char *mod_time; ++ char *deleted; ++} local_usage_t; ++ ++static void _free_local_usage_members(local_usage_t *object) ++{ ++ if (object) { ++ xfree(object->alloc_secs); ++ xfree(object->id); ++ xfree(object->time_start); ++ xfree(object->tres_id); ++ xfree(object->creation_time); ++ xfree(object->mod_time); ++ xfree(object->deleted); ++ } ++} ++ ++typedef struct { ++ char *alloc_secs; ++ char *down_secs; ++ char *idle_secs; ++ char *over_secs; ++ char *pdown_secs; ++ char *time_start; ++ char *plan_secs; ++ char *tres_id; ++ char *tres_cnt; ++ char *creation_time; ++ char *mod_time; ++ char *deleted; ++} local_cluster_usage_t; ++ ++static void _free_local_cluster_members(local_cluster_usage_t *object) ++{ ++ if (object) { ++ xfree(object->alloc_secs); ++ xfree(object->down_secs); ++ xfree(object->idle_secs); ++ xfree(object->over_secs); ++ xfree(object->pdown_secs); ++ xfree(object->time_start); ++ xfree(object->plan_secs); ++ xfree(object->tres_id); ++ xfree(object->tres_cnt); ++ xfree(object->creation_time); ++ xfree(object->mod_time); ++ xfree(object->deleted); ++ } ++} ++ ++/* if this changes you will need to edit the corresponding enum below */ ++char *event_req_inx[] = { ++ "time_start", ++ "time_end", ++ "node_name", ++ "cluster_nodes", ++ "reason", ++ "reason_uid", ++ "state", ++ "tres", ++}; ++ ++enum { ++ EVENT_REQ_START, ++ EVENT_REQ_END, ++ EVENT_REQ_NODE, ++ EVENT_REQ_CNODES, ++ EVENT_REQ_REASON, ++ EVENT_REQ_REASON_UID, ++ EVENT_REQ_STATE, ++ EVENT_REQ_TRES, ++ EVENT_REQ_COUNT ++}; ++ ++/* if this changes you will need to edit the corresponding enum below */ ++static char *job_req_inx[] = { ++ "account", ++ "admin_comment", ++ "array_max_tasks", ++ "array_task_pending", ++ "array_task_str", ++ "nodes_alloc", ++ "id_assoc", ++ "id_array_job", ++ "id_array_task", ++ "batch_script", ++ "id_block", ++ "constraints", ++ "deleted", ++ "derived_ec", ++ "derived_es", ++ "env_vars", ++ "exit_code", ++ "flags", ++ "timelimit", ++ "time_eligible", ++ "time_end", ++ "id_group", ++ "gres_used", ++ "het_job_id", ++ "het_job_offset", ++ "job_db_inx", ++ "id_job", ++ "kill_requid", ++ "mcs_label", ++ "mod_time", ++ "job_name", ++ "nodelist", ++ "node_inx", ++ "`partition`", ++ "priority", ++ "id_qos", ++ "cpus_req", ++ "mem_req", ++ "id_resv", ++ "time_start", ++ "state", ++ "state_reason_prev", ++ "system_comment", ++ "time_submit", ++ "time_suspended", ++ "track_steps", ++ "id_user", ++ "wckey", ++ "id_wckey", ++ "work_dir", ++ "tres_alloc", ++ "tres_req", ++}; ++ ++enum { ++ JOB_REQ_ACCOUNT, ++ JOB_REQ_ADMIN_COMMENT, ++ JOB_REQ_ARRAY_MAX, ++ JOB_REQ_ARRAY_TASK_PENDING, ++ JOB_REQ_ARRAY_TASK_STR, ++ JOB_REQ_ALLOC_NODES, ++ JOB_REQ_ASSOCID, ++ JOB_REQ_ARRAYJOBID, ++ JOB_REQ_ARRAYTASKID, ++ JOB_REQ_SCRIPT, ++ JOB_REQ_BLOCKID, ++ JOB_REQ_CONSTRAINTS, ++ JOB_REQ_DELETED, ++ JOB_REQ_DERIVED_EC, ++ JOB_REQ_DERIVED_ES, ++ JOB_REQ_ENV, ++ JOB_REQ_EXIT_CODE, ++ JOB_REQ_FLAGS, ++ JOB_REQ_TIMELIMIT, ++ JOB_REQ_ELIGIBLE, ++ JOB_REQ_END, ++ JOB_REQ_GID, ++ JOB_REQ_GRES_USED, ++ JOB_REQ_HET_JOB_ID, ++ JOB_REQ_HET_JOB_OFFSET, ++ JOB_REQ_DB_INX, ++ JOB_REQ_JOBID, ++ JOB_REQ_KILL_REQUID, ++ JOB_REQ_MCS_LABEL, ++ JOB_REQ_MOD_TIME, ++ JOB_REQ_NAME, ++ JOB_REQ_NODELIST, ++ JOB_REQ_NODE_INX, ++ JOB_REQ_PARTITION, ++ JOB_REQ_PRIORITY, ++ JOB_REQ_QOS, ++ JOB_REQ_REQ_CPUS, ++ JOB_REQ_REQ_MEM, ++ JOB_REQ_RESVID, ++ JOB_REQ_START, ++ JOB_REQ_STATE, ++ JOB_REQ_STATE_REASON, ++ JOB_REQ_SYSTEM_COMMENT, ++ JOB_REQ_SUBMIT, ++ JOB_REQ_SUSPENDED, ++ JOB_REQ_TRACKSTEPS, ++ JOB_REQ_UID, ++ JOB_REQ_WCKEY, ++ JOB_REQ_WCKEYID, ++ JOB_REQ_WORK_DIR, ++ JOB_REQ_TRESA, ++ JOB_REQ_TRESR, ++ JOB_REQ_COUNT ++}; ++ ++/* if this changes you will need to edit the corresponding enum */ ++char *resv_req_inx[] = { ++ "id_resv", ++ "assoclist", ++ "deleted", ++ "flags", ++ "tres", ++ "nodelist", ++ "node_inx", ++ "resv_name", ++ "time_start", ++ "time_end", ++ "unused_wall", ++}; ++ ++enum { ++ RESV_REQ_ID, ++ RESV_REQ_ASSOCS, ++ RESV_REQ_DELETED, ++ RESV_REQ_FLAGS, ++ RESV_REQ_TRES, ++ RESV_REQ_NODES, ++ RESV_REQ_NODE_INX, ++ RESV_REQ_NAME, ++ RESV_REQ_START, ++ RESV_REQ_END, ++ RESV_REQ_UNUSED, ++ RESV_REQ_COUNT ++}; ++ ++/* if this changes you will need to edit the corresponding enum below */ ++static char *step_req_inx[] = { ++ "job_db_inx", ++ "id_step", ++ "step_het_comp", ++ "deleted", ++ "time_start", ++ "time_end", ++ "time_suspended", ++ "step_name", ++ "nodelist", ++ "node_inx", ++ "state", ++ "kill_requid", ++ "exit_code", ++ "nodes_alloc", ++ "task_cnt", ++ "task_dist", ++ "user_sec", ++ "user_usec", ++ "sys_sec", ++ "sys_usec", ++ "act_cpufreq", ++ "consumed_energy", ++ "req_cpufreq_min", ++ "req_cpufreq", ++ "req_cpufreq_gov", ++ "submit_line", ++ "tres_alloc", ++ "tres_usage_in_ave", ++ "tres_usage_in_max", ++ "tres_usage_in_max_nodeid", ++ "tres_usage_in_max_taskid", ++ "tres_usage_in_min", ++ "tres_usage_in_min_nodeid", ++ "tres_usage_in_min_taskid", ++ "tres_usage_in_tot", ++ "tres_usage_out_ave", ++ "tres_usage_out_max", ++ "tres_usage_out_max_nodeid", ++ "tres_usage_out_max_taskid", ++ "tres_usage_out_min", ++ "tres_usage_out_min_nodeid", ++ "tres_usage_out_min_taskid", ++ "tres_usage_out_tot", ++}; ++ ++ ++enum { ++ STEP_REQ_DB_INX, ++ STEP_REQ_STEPID, ++ STEP_REQ_STEP_HET_COMP, ++ STEP_REQ_DELETED, ++ STEP_REQ_START, ++ STEP_REQ_END, ++ STEP_REQ_SUSPENDED, ++ STEP_REQ_NAME, ++ STEP_REQ_NODELIST, ++ STEP_REQ_NODE_INX, ++ STEP_REQ_STATE, ++ STEP_REQ_KILL_REQUID, ++ STEP_REQ_EXIT_CODE, ++ STEP_REQ_NODES, ++ STEP_REQ_TASKS, ++ STEP_REQ_TASKDIST, ++ STEP_REQ_USER_SEC, ++ STEP_REQ_USER_USEC, ++ STEP_REQ_SYS_SEC, ++ STEP_REQ_SYS_USEC, ++ STEP_REQ_ACT_CPUFREQ, ++ STEP_REQ_CONSUMED_ENERGY, ++ STEP_REQ_REQ_CPUFREQ_MIN, ++ STEP_REQ_REQ_CPUFREQ_MAX, ++ STEP_REQ_REQ_CPUFREQ_GOV, ++ STEP_REQ_SUBMIT_LINE, ++ STEP_REQ_TRES, ++ STEP_TRES_USAGE_IN_AVE, ++ STEP_TRES_USAGE_IN_MAX, ++ STEP_TRES_USAGE_IN_MAX_NODEID, ++ STEP_TRES_USAGE_IN_MAX_TASKID, ++ STEP_TRES_USAGE_IN_MIN, ++ STEP_TRES_USAGE_IN_MIN_NODEID, ++ STEP_TRES_USAGE_IN_MIN_TASKID, ++ STEP_TRES_USAGE_IN_TOT, ++ STEP_TRES_USAGE_OUT_AVE, ++ STEP_TRES_USAGE_OUT_MAX, ++ STEP_TRES_USAGE_OUT_MAX_NODEID, ++ STEP_TRES_USAGE_OUT_MAX_TASKID, ++ STEP_TRES_USAGE_OUT_MIN, ++ STEP_TRES_USAGE_OUT_MIN_NODEID, ++ STEP_TRES_USAGE_OUT_MIN_TASKID, ++ STEP_TRES_USAGE_OUT_TOT, ++ STEP_REQ_COUNT, ++}; ++ ++/* if this changes you will need to edit the corresponding enum below */ ++static char *suspend_req_inx[] = { ++ "job_db_inx", ++ "id_assoc", ++ "time_start", ++ "time_end", ++}; ++ ++enum { ++ SUSPEND_REQ_DB_INX, ++ SUSPEND_REQ_ASSOCID, ++ SUSPEND_REQ_START, ++ SUSPEND_REQ_END, ++ SUSPEND_REQ_COUNT ++}; ++ ++/* if this changes you will need to edit the corresponding enum below */ ++static char *txn_req_inx[] = { ++ "id", ++ "timestamp", ++ "action", ++ "name", ++ "actor", ++ "info", ++ "cluster" ++}; ++ ++enum { ++ TXN_REQ_ID, ++ TXN_REQ_TS, ++ TXN_REQ_ACTION, ++ TXN_REQ_NAME, ++ TXN_REQ_ACTOR, ++ TXN_REQ_INFO, ++ TXN_REQ_CLUSTER, ++ TXN_REQ_COUNT ++}; ++ ++/* if this changes you will need to edit the corresponding enum below */ ++char *usage_req_inx[] = { ++ "id", ++ "id_tres", ++ "time_start", ++ "alloc_secs", ++ "creation_time", ++ "mod_time", ++ "deleted" ++}; ++ ++enum { ++ USAGE_ID, ++ USAGE_TRES, ++ USAGE_START, ++ USAGE_ALLOC, ++ USAGE_CREATION_TIME, ++ USAGE_MOD_TIME, ++ USAGE_DELETED, ++ USAGE_COUNT ++}; ++ ++/* if this changes you will need to edit the corresponding enum below */ ++char *cluster_req_inx[] = { ++ "id_tres", ++ "time_start", ++ "count", ++ "alloc_secs", ++ "down_secs", ++ "pdown_secs", ++ "idle_secs", ++ "plan_secs", ++ "over_secs", ++ "creation_time", ++ "mod_time", ++ "deleted" ++}; ++ ++enum { ++ CLUSTER_TRES, ++ CLUSTER_START, ++ CLUSTER_CNT, ++ CLUSTER_ACPU, ++ CLUSTER_DCPU, ++ CLUSTER_PDCPU, ++ CLUSTER_ICPU, ++ CLUSTER_PCPU, ++ CLUSTER_OCPU, ++ CLUSTER_CREATION_TIME, ++ CLUSTER_MOD_TIME, ++ CLUSTER_DELETED, ++ CLUSTER_COUNT ++}; ++ ++typedef enum { ++ PURGE_EVENT, ++ PURGE_SUSPEND, ++ PURGE_RESV, ++ PURGE_JOB, ++ PURGE_STEP, ++ PURGE_TXN, ++ PURGE_USAGE, ++ PURGE_CLUSTER_USAGE ++} purge_type_t; ++ ++static uint32_t _archive_table(purge_type_t type, pgsql_conn_t *pgsql_conn, ++ char *cluster_name, time_t period_end, ++ char *arch_dir, uint32_t archive_period, ++ char *sql_table, uint32_t usage_info); ++ ++static uint32_t high_buffer_size = (1024 * 1024); ++ ++static void _pack_local_event(local_event_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ packstr(object->cluster_nodes, buffer); ++ packstr(object->node_name, buffer); ++ packstr(object->period_end, buffer); ++ packstr(object->period_start, buffer); ++ packstr(object->reason, buffer); ++ packstr(object->reason_uid, buffer); ++ packstr(object->state, buffer); ++ packstr(object->tres_str, buffer); ++} ++ ++/* this needs to be allocated before calling, and since we aren't ++ * doing any copying it needs to be used before destroying buffer */ ++static int _unpack_local_event(local_event_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ uint32_t tmp32; ++ char *tmp_char; ++ ++ if (rpc_version >= SLURM_15_08_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->cluster_nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->reason, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->reason_uid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_str, &tmp32, buffer); ++ } else { ++ safe_unpackstr_xmalloc(&object->cluster_nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&tmp_char, &tmp32, buffer); ++ object->tres_str = xstrdup_printf("%d=%s", TRES_CPU, tmp_char); ++ xfree(tmp_char); ++ safe_unpackstr_xmalloc(&object->node_name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->reason, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->reason_uid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ } ++ ++ return SLURM_SUCCESS; ++ ++unpack_error: ++ _free_local_event_members(object); ++ return SLURM_ERROR; ++} ++ ++static void _pack_local_job(local_job_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ packstr(object->account, buffer); ++ packstr(object->admin_comment, buffer); ++ packstr(object->alloc_nodes, buffer); ++ packstr(object->associd, buffer); ++ packstr(object->array_jobid, buffer); ++ packstr(object->array_max_tasks, buffer); ++ packstr(object->array_taskid, buffer); ++ packstr(object->array_task_pending, buffer); ++ packstr(object->array_task_str, buffer); ++ packstr(object->script, buffer); ++ packstr(object->blockid, buffer); ++ packstr(object->constraints, buffer); ++ packstr(object->deleted, buffer); ++ packstr(object->derived_ec, buffer); ++ packstr(object->derived_es, buffer); ++ packstr(object->env, buffer); ++ packstr(object->exit_code, buffer); ++ packstr(object->flags, buffer); ++ packstr(object->timelimit, buffer); ++ packstr(object->eligible, buffer); ++ packstr(object->end, buffer); ++ packstr(object->gid, buffer); ++ packstr(object->gres_used, buffer); ++ packstr(object->job_db_inx, buffer); ++ packstr(object->jobid, buffer); ++ packstr(object->kill_requid, buffer); ++ packstr(object->mcs_label, buffer); ++ packstr(object->mod_time, buffer); ++ packstr(object->name, buffer); ++ packstr(object->nodelist, buffer); ++ packstr(object->node_inx, buffer); ++ packstr(object->het_job_id, buffer); ++ packstr(object->het_job_offset, buffer); ++ packstr(object->partition, buffer); ++ packstr(object->priority, buffer); ++ packstr(object->qos, buffer); ++ packstr(object->req_cpus, buffer); ++ packstr(object->req_mem, buffer); ++ packstr(object->resvid, buffer); ++ packstr(object->start, buffer); ++ packstr(object->state, buffer); ++ packstr(object->state_reason_prev, buffer); ++ packstr(object->submit, buffer); ++ packstr(object->suspended, buffer); ++ packstr(object->system_comment, buffer); ++ packstr(object->track_steps, buffer); ++ packstr(object->tres_alloc_str, buffer); ++ packstr(object->tres_req_str, buffer); ++ packstr(object->uid, buffer); ++ packstr(object->wckey, buffer); ++ packstr(object->wckey_id, buffer); ++ packstr(object->work_dir, buffer); ++} ++ ++/* this needs to be allocated before calling, and since we aren't ++ * doing any copying it needs to be used before destroying buffer */ ++static int _unpack_local_job(local_job_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ uint32_t tmp32; ++ char *tmp_char = NULL; ++ ++ memset(object, 0, sizeof(local_job_t)); ++ ++ /* For protocols <= 14_11, job_req_inx and it's corresponding enum, ++ * were out of sync. This caused the following variables to have the ++ * corresponding values: ++ * job->partition = priority ++ * job->priority = qos ++ * job->qos = req_cpus ++ * job->req_cpus = req_mem ++ * job->req_mem = resvid ++ * job->resvid = partition ++ * ++ * The values were packed in the above order. To unpack the values ++ * into the correct variables, the unpacking order is changed to ++ * accomodate the shift in values. job->partition is unpacked before ++ * job->start instead of after job->node_inx. ++ * ++ * 15.08: job_req_inx and the it's corresponding enum were synced up ++ * and it unpacks in the expected order. ++ */ ++ ++ if (rpc_version >= SLURM_21_08_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->account, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->admin_comment, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->alloc_nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->associd, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_max_tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_taskid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_task_pending, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_task_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->script, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->blockid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->constraints, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->deleted, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_ec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_es, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->env, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->flags, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->timelimit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->eligible, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->gid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->gres_used, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->mcs_label, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->mod_time, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->het_job_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->het_job_offset, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->partition, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->priority, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->qos, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpus, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_mem, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->resvid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state_reason_prev, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->submit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->system_comment, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->track_steps, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_alloc_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_req_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->uid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->work_dir, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_20_02_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->account, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->admin_comment, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->alloc_nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->associd, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_max_tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_taskid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_task_pending, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_task_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->blockid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->constraints, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->deleted, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_ec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_es, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->flags, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->timelimit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->eligible, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->gid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->gres_used, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->mcs_label, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->mod_time, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->het_job_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->het_job_offset, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->partition, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->priority, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->qos, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpus, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_mem, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->resvid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state_reason_prev, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->submit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->system_comment, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->track_steps, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_alloc_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_req_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->uid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->work_dir, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_19_05_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->account, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->admin_comment, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->alloc_nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->associd, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_max_tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_taskid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->blockid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->constraints, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_ec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_es, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->flags, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->timelimit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->eligible, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->gid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->mcs_label, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->het_job_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->het_job_offset, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->partition, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->priority, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->qos, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpus, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_mem, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->resvid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state_reason_prev, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->submit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->system_comment, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->track_steps, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_alloc_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_req_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->uid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->work_dir, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_18_08_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->account, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->admin_comment, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->alloc_nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->associd, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_max_tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_taskid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->blockid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_ec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_es, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->timelimit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->eligible, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->gid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->mcs_label, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->het_job_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->het_job_offset, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->partition, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->priority, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->qos, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpus, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_mem, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->resvid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->submit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->system_comment, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->track_steps, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_alloc_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_req_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->uid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->work_dir, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_17_11_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->account, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->admin_comment, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->alloc_nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->associd, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_max_tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_taskid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->blockid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_ec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_es, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->timelimit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->eligible, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->gid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->mcs_label, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->het_job_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->het_job_offset, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->partition, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->priority, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->qos, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpus, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_mem, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->resvid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->submit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->track_steps, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_alloc_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_req_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->uid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->work_dir, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_17_02_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->account, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->admin_comment, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->alloc_nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->associd, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_max_tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_taskid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->blockid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_ec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_es, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->timelimit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->eligible, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->gid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ object->het_job_id = xstrdup("0"); ++ object->het_job_offset = xstrdup("4294967294"); ++ safe_unpackstr_xmalloc(&object->partition, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->priority, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->qos, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpus, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&tmp_char, &tmp32, buffer); ++ if (tmp_char) { ++ uint64_t tmp_uint64 = slurm_atoull(tmp_char); ++ if ((tmp_uint64 & 0x80000000) && ++ (tmp_uint64 < 0x100000000)) { ++ /* ++ * Handle old conversion of memory ++ * stored incorrectly in the database. ++ * This will be fixed in 17.11 and we ++ * can remove this check. 0x80000000 ++ * was the old value of MEM_PER_CPU ++ */ ++ tmp_uint64 &= (~0x80000000); ++ tmp_uint64 |= MEM_PER_CPU; ++ object->req_mem = xstrdup_printf("%"PRIu64, ++ tmp_uint64); ++ xfree(tmp_char); ++ } else { ++ object->req_mem = tmp_char; ++ tmp_char = NULL; ++ } ++ } ++ safe_unpackstr_xmalloc(&object->resvid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->submit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->track_steps, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_alloc_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_req_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->uid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey_id, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_15_08_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->account, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->alloc_nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->associd, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_max_tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_taskid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->blockid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_ec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_es, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->timelimit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->eligible, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->gid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ object->het_job_id = xstrdup("0"); ++ object->het_job_offset = xstrdup("4294967294"); ++ safe_unpackstr_xmalloc(&object->partition, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->priority, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->qos, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpus, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&tmp_char, &tmp32, buffer); ++ if (tmp_char) { ++ uint64_t tmp_uint64 = slurm_atoull(tmp_char); ++ if ((tmp_uint64 & 0x80000000) && ++ (tmp_uint64 < 0x100000000)) { ++ /* ++ * Handle old conversion of memory ++ * stored incorrectly in the database. ++ * This will be fixed in 17.11 and we ++ * can remove this check. 0x80000000 ++ * was the old value of MEM_PER_CPU ++ */ ++ tmp_uint64 &= (~0x80000000); ++ tmp_uint64 |= MEM_PER_CPU; ++ object->req_mem = xstrdup_printf("%"PRIu64, ++ tmp_uint64); ++ xfree(tmp_char); ++ } else { ++ object->req_mem = tmp_char; ++ tmp_char = NULL; ++ } ++ } ++ safe_unpackstr_xmalloc(&object->resvid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->submit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->track_steps, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_alloc_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_req_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->uid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey_id, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_14_11_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->account, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&tmp_char, &tmp32, buffer); ++ object->tres_alloc_str = xstrdup_printf( ++ "%d=%s", TRES_CPU, tmp_char); ++ xfree(tmp_char); ++ safe_unpackstr_xmalloc(&object->alloc_nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->associd, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_max_tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->array_taskid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->blockid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_ec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_es, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->timelimit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->eligible, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->gid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->priority, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->qos, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpus, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&tmp_char, &tmp32, buffer); ++ if (tmp_char) { ++ uint64_t tmp_uint64 = slurm_atoull(tmp_char); ++ if ((tmp_uint64 & 0x80000000) && ++ (tmp_uint64 < 0x100000000)) { ++ /* ++ * Handle old conversion of memory ++ * stored incorrectly in the database. ++ * This will be fixed in 17.11 and we ++ * can remove this check. 0x80000000 ++ * was the old value of MEM_PER_CPU ++ */ ++ tmp_uint64 &= (~0x80000000); ++ tmp_uint64 |= MEM_PER_CPU; ++ object->req_mem = xstrdup_printf("%"PRIu64, ++ tmp_uint64); ++ xfree(tmp_char); ++ } else { ++ object->req_mem = tmp_char; ++ tmp_char = NULL; ++ } ++ } ++ safe_unpackstr_xmalloc(&object->resvid, &tmp32, buffer); ++ object->het_job_id = xstrdup("0"); ++ object->het_job_offset = xstrdup("4294967294"); ++ safe_unpackstr_xmalloc(&object->partition, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->submit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->track_steps, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->uid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey_id, &tmp32, buffer); ++ } else if (rpc_version >= SLURMDBD_2_6_VERSION) { ++ safe_unpackstr_xmalloc(&object->account, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&tmp_char, &tmp32, buffer); ++ object->tres_alloc_str = xstrdup_printf( ++ "%d=%s", TRES_CPU, tmp_char); ++ xfree(tmp_char); ++ safe_unpackstr_xmalloc(&object->alloc_nodes, &tmp32, buffer); ++ object->array_taskid = xstrdup("4294967294"); ++ safe_unpackstr_xmalloc(&object->associd, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->blockid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_ec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_es, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->timelimit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->eligible, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->gid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->priority, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->qos, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpus, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&tmp_char, &tmp32, buffer); ++ if (tmp_char) { ++ uint64_t tmp_uint64 = slurm_atoull(tmp_char); ++ if ((tmp_uint64 & 0x80000000) && ++ (tmp_uint64 < 0x100000000)) { ++ /* ++ * Handle old conversion of memory ++ * stored incorrectly in the database. ++ * This will be fixed in 17.11 and we ++ * can remove this check. 0x80000000 ++ * was the old value of MEM_PER_CPU ++ */ ++ tmp_uint64 &= (~0x80000000); ++ tmp_uint64 |= MEM_PER_CPU; ++ object->req_mem = xstrdup_printf("%"PRIu64, ++ tmp_uint64); ++ xfree(tmp_char); ++ } else { ++ object->req_mem = tmp_char; ++ tmp_char = NULL; ++ } ++ } ++ safe_unpackstr_xmalloc(&object->resvid, &tmp32, buffer); ++ object->het_job_id = xstrdup("0"); ++ object->het_job_offset = xstrdup("4294967294"); ++ safe_unpackstr_xmalloc(&object->partition, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->submit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->track_steps, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->uid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey_id, &tmp32, buffer); ++ } else { ++ safe_unpackstr_xmalloc(&object->account, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&tmp_char, &tmp32, buffer); ++ object->tres_alloc_str = xstrdup_printf( ++ "%d=%s", TRES_CPU, tmp_char); ++ xfree(tmp_char); ++ safe_unpackstr_xmalloc(&object->alloc_nodes, &tmp32, buffer); ++ object->array_taskid = xstrdup("4294967294"); ++ safe_unpackstr_xmalloc(&object->associd, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->blockid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_ec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->derived_es, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->timelimit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->eligible, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->gid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->jobid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->priority, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->qos, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpus, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->resvid, &tmp32, buffer); ++ object->het_job_id = xstrdup("0"); ++ object->het_job_offset = xstrdup("4294967294"); ++ safe_unpackstr_xmalloc(&object->partition, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->submit, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->track_steps, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->uid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->wckey_id, &tmp32, buffer); ++ } ++ return SLURM_SUCCESS; ++ ++unpack_error: ++ _free_local_job_members(object); ++ return SLURM_ERROR; ++} ++ ++static void _pack_local_resv(local_resv_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ packstr(object->assocs, buffer); ++ packstr(object->deleted, buffer); ++ packstr(object->flags, buffer); ++ packstr(object->id, buffer); ++ packstr(object->name, buffer); ++ packstr(object->nodes, buffer); ++ packstr(object->node_inx, buffer); ++ packstr(object->time_end, buffer); ++ packstr(object->time_start, buffer); ++ packstr(object->tres_str, buffer); ++ packstr(object->unused_wall, buffer); ++} ++ ++/* this needs to be allocated before calling, and since we aren't ++ * doing any copying it needs to be used before destroying buffer */ ++static int _unpack_local_resv(local_resv_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ uint32_t tmp32; ++ char *tmp_char; ++ ++ if (rpc_version >= SLURM_20_02_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->assocs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->deleted, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->flags, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->time_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->time_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->unused_wall, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_17_11_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->assocs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->flags, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->time_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->time_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->unused_wall, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_15_08_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->assocs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->flags, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->time_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->time_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_str, &tmp32, buffer); ++ } else { ++ safe_unpackstr_xmalloc(&object->assocs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&tmp_char, &tmp32, buffer); ++ object->tres_str = xstrdup_printf("%d=%s", TRES_CPU, tmp_char); ++ xfree(tmp_char); ++ safe_unpackstr_xmalloc(&object->flags, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->time_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->time_start, &tmp32, buffer); ++ } ++ ++ return SLURM_SUCCESS; ++ ++unpack_error: ++ _free_local_resv_members(object); ++ return SLURM_ERROR; ++} ++ ++static void _pack_local_step(local_step_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ packstr(object->act_cpufreq, buffer); ++ packstr(object->deleted, buffer); ++ packstr(object->exit_code, buffer); ++ packstr(object->consumed_energy, buffer); ++ packstr(object->job_db_inx, buffer); ++ packstr(object->kill_requid, buffer); ++ packstr(object->name, buffer); ++ packstr(object->nodelist, buffer); ++ packstr(object->nodes, buffer); ++ packstr(object->node_inx, buffer); ++ packstr(object->period_end, buffer); ++ packstr(object->period_start, buffer); ++ packstr(object->period_suspended, buffer); ++ packstr(object->req_cpufreq_min, buffer); ++ packstr(object->req_cpufreq_max, buffer); ++ packstr(object->req_cpufreq_gov, buffer); ++ packstr(object->state, buffer); ++ packstr(object->stepid, buffer); ++ packstr(object->step_het_comp, buffer); ++ packstr(object->submit_line, buffer); ++ packstr(object->sys_sec, buffer); ++ packstr(object->sys_usec, buffer); ++ packstr(object->tasks, buffer); ++ packstr(object->task_dist, buffer); ++ packstr(object->tres_alloc_str, buffer); ++ packstr(object->tres_usage_in_ave, buffer); ++ packstr(object->tres_usage_in_max, buffer); ++ packstr(object->tres_usage_in_max_nodeid, buffer); ++ packstr(object->tres_usage_in_max_taskid, buffer); ++ packstr(object->tres_usage_in_min, buffer); ++ packstr(object->tres_usage_in_min_nodeid, buffer); ++ packstr(object->tres_usage_in_min_taskid, buffer); ++ packstr(object->tres_usage_in_tot, buffer); ++ packstr(object->tres_usage_out_ave, buffer); ++ packstr(object->tres_usage_out_max, buffer); ++ packstr(object->tres_usage_out_max_nodeid, buffer); ++ packstr(object->tres_usage_out_max_taskid, buffer); ++ packstr(object->tres_usage_out_min, buffer); ++ packstr(object->tres_usage_out_min_nodeid, buffer); ++ packstr(object->tres_usage_out_min_taskid, buffer); ++ packstr(object->tres_usage_out_tot, buffer); ++ packstr(object->user_sec, buffer); ++ packstr(object->user_usec, buffer); ++} ++ ++/* this needs to be allocated before calling, and since we aren't ++ * doing any copying it needs to be used before destroying buffer */ ++static int _unpack_local_step(local_step_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ uint32_t tmp32; ++ char *tmp_char; ++ ++ if (rpc_version >= SLURM_21_08_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->act_cpufreq, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->deleted, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->consumed_energy, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_suspended, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_min, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_max, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_gov, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->stepid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->step_het_comp, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->submit_line, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->sys_sec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->sys_usec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->task_dist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_alloc_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_ave, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_max, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_max_nodeid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_max_taskid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_min, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_min_nodeid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_min_taskid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_tot, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_ave, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_max, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_max_nodeid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_max_taskid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_min, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_min_nodeid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_min_taskid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_tot, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->user_sec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->user_usec, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_20_11_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->act_cpufreq, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->deleted, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->consumed_energy, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_suspended, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_min, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_max, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_gov, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->stepid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->step_het_comp, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->sys_sec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->sys_usec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->task_dist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_alloc_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_ave, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_max, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_max_nodeid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_max_taskid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_min, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_min_nodeid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_min_taskid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_tot, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_ave, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_max, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_max_nodeid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_max_taskid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_min, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_min_nodeid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_min_taskid, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_tot, ++ &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->user_sec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->user_usec, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_20_02_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->act_cpufreq, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->deleted, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->consumed_energy, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_min, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_max, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_gov, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->stepid, &tmp32, buffer); ++ _convert_old_step_id(&object->stepid); ++ safe_unpackstr_xmalloc(&object->sys_sec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->sys_usec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->task_dist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_alloc_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_ave, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_max, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_max_nodeid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_max_taskid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_min, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_min_nodeid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_min_taskid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_tot, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_ave, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_max, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_max_nodeid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_max_taskid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_min, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_min_nodeid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_min_taskid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_tot, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->user_sec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->user_usec, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_18_08_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->act_cpufreq, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->consumed_energy, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_min, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_max, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_gov, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->stepid, &tmp32, buffer); ++ _convert_old_step_id(&object->stepid); ++ safe_unpackstr_xmalloc(&object->sys_sec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->sys_usec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->task_dist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_alloc_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_ave, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_max, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_max_nodeid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_max_taskid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_min, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_min_nodeid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_min_taskid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_in_tot, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_ave, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_max, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_max_nodeid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_max_taskid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_min, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_min_nodeid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_min_taskid, &tmp32, ++ buffer); ++ safe_unpackstr_xmalloc(&object->tres_usage_out_tot, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->user_sec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->user_usec, &tmp32, buffer); ++ } else if (rpc_version >= SLURM_15_08_PROTOCOL_VERSION) { ++ char *ave_cpu; ++ char *ave_disk_read; ++ char *ave_disk_write; ++ char *ave_pages; ++ char *ave_rss; ++ char *ave_vsize; ++ char *max_disk_read; ++ char *max_disk_read_node; ++ char *max_disk_read_task; ++ char *max_disk_write; ++ char *max_disk_write_node; ++ char *max_disk_write_task; ++ char *max_pages; ++ char *max_pages_node; ++ char *max_pages_task; ++ char *max_rss; ++ char *max_rss_node; ++ char *max_rss_task; ++ char *max_vsize; ++ char *max_vsize_node; ++ char *max_vsize_task; ++ char *min_cpu; ++ char *min_cpu_node; ++ char *min_cpu_task; ++ ++ safe_unpackstr_xmalloc(&object->act_cpufreq, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&ave_cpu, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&ave_disk_read, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&ave_disk_write, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&ave_pages, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&ave_rss, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&ave_vsize, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->consumed_energy, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_disk_read, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_disk_read_node, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_disk_read_task, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_disk_write, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_disk_write_node, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_disk_write_task, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_pages, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_pages_node, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_pages_task, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_rss, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_rss_node, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_rss_task, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_vsize, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_vsize_node, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_vsize_task, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&min_cpu, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&min_cpu_node, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&min_cpu_task, &tmp32, buffer); ++ ++ if (atol(min_cpu) != NO_VAL) { ++ object->tres_usage_in_ave = xstrdup_printf( ++ "%d=%s,%d=%s,%d=%s,%d=%s,%d=%s", ++ TRES_CPU, ave_cpu, ++ TRES_MEM, ave_rss, ++ TRES_VMEM, ave_vsize, ++ TRES_PAGES, ave_pages, ++ TRES_FS_DISK, ave_disk_read); ++ object->tres_usage_out_ave = xstrdup_printf( ++ "%d=%s", ++ TRES_FS_DISK, ave_disk_write); ++ ++ object->tres_usage_in_max = xstrdup_printf( ++ "%d=%s,%d=%s,%d=%s,%d=%s", ++ TRES_MEM, max_rss, ++ TRES_VMEM, max_vsize, ++ TRES_PAGES, max_pages, ++ TRES_FS_DISK, max_disk_read); ++ object->tres_usage_out_max = xstrdup_printf( ++ "%d=%s", ++ TRES_FS_DISK, max_disk_write); ++ ++ object->tres_usage_in_max_nodeid = xstrdup_printf( ++ "%d=%s,%d=%s,%d=%s,%d=%s", ++ TRES_MEM, max_rss_node, ++ TRES_VMEM, max_vsize_node, ++ TRES_PAGES, max_pages_node, ++ TRES_FS_DISK, max_disk_read_node); ++ object->tres_usage_out_max_nodeid = xstrdup_printf( ++ "%d=%s", ++ TRES_FS_DISK, max_disk_write_node); ++ ++ object->tres_usage_in_max_taskid = xstrdup_printf( ++ "%d=%s,%d=%s,%d=%s,%d=%s,%d=%s", ++ TRES_CPU, min_cpu_task, ++ TRES_MEM, max_rss_task, ++ TRES_VMEM, max_vsize_task, ++ TRES_PAGES, max_pages_task, ++ TRES_FS_DISK, max_disk_read_task); ++ object->tres_usage_out_max_taskid = xstrdup_printf( ++ "%d=%s", ++ TRES_FS_DISK, max_disk_write_task); ++ ++ object->tres_usage_in_min = xstrdup_printf( ++ "%d=%s", ++ TRES_CPU, min_cpu); ++ object->tres_usage_in_min_nodeid = xstrdup_printf( ++ "%d=%s", ++ TRES_CPU, min_cpu_node); ++ object->tres_usage_in_min_taskid = xstrdup_printf( ++ "%d=%s", ++ TRES_CPU, min_cpu_task); ++ } ++ ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_min, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_max, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_gov, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->stepid, &tmp32, buffer); ++ _convert_old_step_id(&object->stepid); ++ safe_unpackstr_xmalloc(&object->sys_sec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->sys_usec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->task_dist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_alloc_str, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->user_sec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->user_usec, &tmp32, buffer); ++ ++ xfree(ave_cpu); ++ xfree(ave_disk_read); ++ xfree(ave_disk_write); ++ xfree(ave_pages); ++ xfree(ave_rss); ++ xfree(ave_vsize); ++ xfree(max_disk_read); ++ xfree(max_disk_read_node); ++ xfree(max_disk_read_task); ++ xfree(max_disk_write); ++ xfree(max_disk_write_node); ++ xfree(max_disk_write_task); ++ xfree(max_pages); ++ xfree(max_pages_node); ++ xfree(max_pages_task); ++ xfree(max_rss); ++ xfree(max_rss_node); ++ xfree(max_rss_task); ++ xfree(max_vsize); ++ xfree(max_vsize_node); ++ xfree(max_vsize_task); ++ xfree(min_cpu); ++ xfree(min_cpu_node); ++ xfree(min_cpu_task); ++ } else if (rpc_version >= SLURMDBD_2_6_VERSION) { ++ char *ave_cpu; ++ char *ave_disk_read; ++ char *ave_disk_write; ++ char *ave_pages; ++ char *ave_rss; ++ char *ave_vsize; ++ char *max_disk_read; ++ char *max_disk_read_node; ++ char *max_disk_read_task; ++ char *max_disk_write; ++ char *max_disk_write_node; ++ char *max_disk_write_task; ++ char *max_pages; ++ char *max_pages_node; ++ char *max_pages_task; ++ char *max_rss; ++ char *max_rss_node; ++ char *max_rss_task; ++ char *max_vsize; ++ char *max_vsize_node; ++ char *max_vsize_task; ++ char *min_cpu; ++ char *min_cpu_node; ++ char *min_cpu_task; ++ ++ safe_unpackstr_xmalloc(&object->act_cpufreq, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&ave_cpu, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&ave_disk_read, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&ave_disk_write, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&ave_pages, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&ave_rss, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&ave_vsize, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->exit_code, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->consumed_energy, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&tmp_char, &tmp32, buffer); ++ object->tres_alloc_str = xstrdup_printf( ++ "%d=%s", TRES_CPU, tmp_char); ++ xfree(tmp_char); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->kill_requid, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_disk_read, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_disk_read_node, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_disk_read_task, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_disk_write, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_disk_write_node, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_disk_write_task, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_pages, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_pages_node, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_pages_task, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_rss, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_rss_node, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_rss_task, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_vsize, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_vsize_node, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&max_vsize_task, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&min_cpu, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&min_cpu_node, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&min_cpu_task, &tmp32, buffer); ++ ++ if (atol(min_cpu) != NO_VAL) { ++ object->tres_usage_in_ave = xstrdup_printf( ++ "%d=%s,%d=%s,%d=%s,%d=%s,%d=%s", ++ TRES_CPU, ave_cpu, ++ TRES_MEM, ave_rss, ++ TRES_VMEM, ave_vsize, ++ TRES_PAGES, ave_pages, ++ TRES_FS_DISK, ave_disk_read); ++ object->tres_usage_out_ave = xstrdup_printf( ++ "%d=%s", ++ TRES_FS_DISK, ave_disk_write); ++ ++ object->tres_usage_in_max = xstrdup_printf( ++ "%d=%s,%d=%s,%d=%s,%d=%s", ++ TRES_MEM, max_rss, ++ TRES_VMEM, max_vsize, ++ TRES_PAGES, max_pages, ++ TRES_FS_DISK, max_disk_read); ++ object->tres_usage_out_max = xstrdup_printf( ++ "%d=%s", ++ TRES_FS_DISK, max_disk_write); ++ ++ object->tres_usage_in_max_nodeid = xstrdup_printf( ++ "%d=%s,%d=%s,%d=%s,%d=%s", ++ TRES_MEM, max_rss_node, ++ TRES_VMEM, max_vsize_node, ++ TRES_PAGES, max_pages_node, ++ TRES_FS_DISK, max_disk_read_node); ++ object->tres_usage_out_max_nodeid = xstrdup_printf( ++ "%d=%s", ++ TRES_FS_DISK, max_disk_write_node); ++ ++ object->tres_usage_in_max_taskid = xstrdup_printf( ++ "%d=%s,%d=%s,%d=%s,%d=%s,%d=%s", ++ TRES_CPU, min_cpu_task, ++ TRES_MEM, max_rss_task, ++ TRES_VMEM, max_vsize_task, ++ TRES_PAGES, max_pages_task, ++ TRES_FS_DISK, max_disk_read_task); ++ object->tres_usage_out_max_taskid = xstrdup_printf( ++ "%d=%s", ++ TRES_FS_DISK, max_disk_write_task); ++ ++ object->tres_usage_in_min = xstrdup_printf( ++ "%d=%s", ++ TRES_CPU, min_cpu); ++ object->tres_usage_in_min_nodeid = xstrdup_printf( ++ "%d=%s", ++ TRES_CPU, min_cpu_node); ++ object->tres_usage_in_min_taskid = xstrdup_printf( ++ "%d=%s", ++ TRES_CPU, min_cpu_task); ++ } ++ ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodelist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->nodes, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->node_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_suspended, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->req_cpufreq_max, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->state, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->stepid, &tmp32, buffer); ++ _convert_old_step_id(&object->stepid); ++ safe_unpackstr_xmalloc(&object->sys_sec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->sys_usec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tasks, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->task_dist, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->user_sec, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->user_usec, &tmp32, buffer); ++ ++ xfree(ave_cpu); ++ xfree(ave_disk_read); ++ xfree(ave_disk_write); ++ xfree(ave_pages); ++ xfree(ave_rss); ++ xfree(ave_vsize); ++ xfree(max_disk_read); ++ xfree(max_disk_read_node); ++ xfree(max_disk_read_task); ++ xfree(max_disk_write); ++ xfree(max_disk_write_node); ++ xfree(max_disk_write_task); ++ xfree(max_pages); ++ xfree(max_pages_node); ++ xfree(max_pages_task); ++ xfree(max_rss); ++ xfree(max_rss_node); ++ xfree(max_rss_task); ++ xfree(max_vsize); ++ xfree(max_vsize_node); ++ xfree(max_vsize_task); ++ xfree(min_cpu); ++ xfree(min_cpu_node); ++ xfree(min_cpu_task); ++ } else { ++ goto unpack_error; ++ } ++ ++ return SLURM_SUCCESS; ++ ++unpack_error: ++ _free_local_step_members(object); ++ return SLURM_ERROR; ++} ++ ++static void _pack_local_suspend(local_suspend_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ packstr(object->associd, buffer); ++ packstr(object->job_db_inx, buffer); ++ packstr(object->period_end, buffer); ++ packstr(object->period_start, buffer); ++} ++ ++/* this needs to be allocated before calling, and since we aren't ++ * doing any copying it needs to be used before destroying buffer */ ++static int _unpack_local_suspend(local_suspend_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ uint32_t tmp32; ++ ++ safe_unpackstr_xmalloc(&object->associd, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->job_db_inx, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_end, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->period_start, &tmp32, buffer); ++ ++ return SLURM_SUCCESS; ++ ++unpack_error: ++ _free_local_suspend_members(object); ++ return SLURM_ERROR; ++} ++ ++static void _pack_local_txn(local_txn_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ packstr(object->id, buffer); ++ packstr(object->timestamp, buffer); ++ packstr(object->action, buffer); ++ packstr(object->name, buffer); ++ packstr(object->actor, buffer); ++ packstr(object->info, buffer); ++ packstr(object->cluster, buffer); ++} ++ ++/* this needs to be allocated before calling, and since we aren't ++ * doing any copying it needs to be used before destroying buffer */ ++static int _unpack_local_txn(local_txn_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ uint32_t tmp32; ++ ++ safe_unpackstr_xmalloc(&object->id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->timestamp, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->action, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->name, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->actor, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->info, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->cluster, &tmp32, buffer); ++ ++ return SLURM_SUCCESS; ++ ++unpack_error: ++ _free_local_txn_members(object); ++ return SLURM_ERROR; ++} ++ ++static void _pack_local_usage(local_usage_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ packstr(object->id, buffer); ++ packstr(object->tres_id, buffer); ++ packstr(object->time_start, buffer); ++ packstr(object->alloc_secs, buffer); ++ packstr(object->creation_time, buffer); ++ packstr(object->mod_time, buffer); ++ packstr(object->deleted, buffer); ++} ++ ++/* this needs to be allocated before calling, and since we aren't ++ * doing any copying it needs to be used before destroying buffer */ ++static int _unpack_local_usage(local_usage_t *object, uint16_t rpc_version, ++ buf_t *buffer) ++{ ++ uint32_t tmp32; ++ ++ if (rpc_version >= SLURM_20_02_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->time_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->alloc_secs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->creation_time, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->mod_time, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->deleted, &tmp32, buffer); ++ } else { ++ safe_unpackstr_xmalloc(&object->id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->time_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->alloc_secs, &tmp32, buffer); ++ } ++ ++ return SLURM_SUCCESS; ++ ++unpack_error: ++ _free_local_usage_members(object); ++ return SLURM_ERROR; ++} ++ ++static void _pack_local_cluster_usage(local_cluster_usage_t *object, ++ uint16_t rpc_version, buf_t *buffer) ++{ ++ packstr(object->tres_id, buffer); ++ packstr(object->time_start, buffer); ++ packstr(object->tres_cnt, buffer); ++ packstr(object->alloc_secs, buffer); ++ packstr(object->down_secs, buffer); ++ packstr(object->pdown_secs, buffer); ++ packstr(object->idle_secs, buffer); ++ packstr(object->plan_secs, buffer); ++ packstr(object->over_secs, buffer); ++ packstr(object->creation_time, buffer); ++ packstr(object->mod_time, buffer); ++ packstr(object->deleted, buffer); ++} ++ ++/* this needs to be allocated before calling, and since we aren't ++ * doing any copying it needs to be used before destroying buffer */ ++static int _unpack_local_cluster_usage(local_cluster_usage_t *object, ++ uint16_t rpc_version, buf_t *buffer) ++{ ++ uint32_t tmp32; ++ ++ if (rpc_version >= SLURM_20_02_PROTOCOL_VERSION) { ++ safe_unpackstr_xmalloc(&object->tres_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->time_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_cnt, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->alloc_secs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->down_secs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->pdown_secs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->idle_secs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->plan_secs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->over_secs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->creation_time, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->mod_time, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->deleted, &tmp32, buffer); ++ } else { ++ safe_unpackstr_xmalloc(&object->tres_id, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->time_start, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->tres_cnt, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->alloc_secs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->down_secs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->idle_secs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->plan_secs, &tmp32, buffer); ++ safe_unpackstr_xmalloc(&object->over_secs, &tmp32, buffer); ++ } ++ ++ return SLURM_SUCCESS; ++ ++unpack_error: ++ _free_local_cluster_members(object); ++ return SLURM_ERROR; ++} ++ ++static int _process_old_sql_line(const char *data_in, ++ char **cluster_name, char **data_full_out) ++{ ++ int start = 0, i = 0; ++ char *beginning = NULL; ++ char *ending = NULL; ++ char *data_out = *data_full_out; ++ char *table = NULL; ++ char *fields = NULL; ++ char *new_vals = NULL; ++ char *vals = NULL; ++ char *new_cluster_name = NULL; ++ int rc = SLURM_SUCCESS; ++ int cnt = 0, cluster_inx = -1, ending_start = 0, ending_end = 0; ++ bool delete = 0; ++ bool new_cluster = 0; ++ ++ while (data_in[i]) { ++ if (!xstrncasecmp("INSERT INTO ", data_in+i, 12)) { ++ beginning = xstrndup(data_in+i, 11); ++ i+=12; ++ break; ++ } else if (!xstrncasecmp("DELETE FROM ", data_in+i, 12)) { ++ beginning = xstrndup(data_in+i, 11); ++ i+=12; ++ delete = 1; ++ break; ++ } else if (!xstrncasecmp("DROP TABLE ", data_in+i, 11)) { ++ start = i; ++ i+=11; ++ while (data_in[i] && data_in[i-1] != ';') ++ i++; ++ xstrncat(data_out, data_in+start, i-start); ++ goto end_it; ++ } else if (!xstrncasecmp("TRUNCATE TABLE ", data_in+i, 15)) { ++ start = i; ++ i+=15; ++ while (data_in[i] && data_in[i-1] != ';') ++ i++; ++ xstrncat(data_out, data_in+start, i-start); ++ goto end_it; ++ } ++ i++; ++ } ++ ++ if (!data_in[i]) ++ goto end_it; ++ ++ //info("processing %s", data_in); ++ /* get table name */ ++ if (!xstrncmp("cluster_event_table", data_in+i, 19)) { ++ i+=19; ++ table = event_table; ++ } else if (!xstrncmp("job_table", data_in+i, 9)) { ++ i+=9; ++ table = job_table; ++ } else if (!xstrncmp("step_table", data_in+i, 10)) { ++ i+=10; ++ table = step_table; ++ } else if (!xstrncmp("suspend_table", data_in+i, 13)) { ++ i+=13; ++ table = suspend_table; ++ } else if (!xstrncmp("txn_table", data_in+i, 9)) { ++ i+=9; ++ table = txn_table; ++ } else if (!xstrncmp("cluster_day_usage_table", data_in+i, 23)) { ++ i+=23; ++ table = cluster_day_table; ++ } else if (!xstrncmp("cluster_hour_usage_table", data_in+i, 24)) { ++ i+=24; ++ table = cluster_hour_table; ++ } else if (!xstrncmp("cluster_month_usage_table", data_in+i, 25)) { ++ i+=25; ++ table = cluster_month_table; ++ } else if (!xstrncmp("assoc_day_usage_table", data_in+i, 21)) { ++ i+=21; ++ table = assoc_day_table; ++ } else if (!xstrncmp("assoc_hour_usage_table", data_in+i, 22)) { ++ i+=22; ++ table = assoc_hour_table; ++ } else if (!xstrncmp("assoc_month_usage_table", data_in+i, 23)) { ++ i+=23; ++ table = assoc_month_table; ++ } else if (!xstrncmp("wckey_day_usage_table", data_in+i, 21)) { ++ i+=21; ++ table = wckey_day_table; ++ } else if (!xstrncmp("wckey_hour_usage_table", data_in+i, 22)) { ++ i+=22; ++ table = wckey_hour_table; ++ } else if (!xstrncmp("wckey_month_usage_table", data_in+i, 23)) { ++ i+=23; ++ table = wckey_month_table; ++ } else { ++ error("unknown table in sql '%s'", data_in); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ /* get to the columns */ ++ if (!delete) ++ while (data_in[i] && data_in[i-1] != '(' && data_in[i-1] != ';') ++ i++; ++ else ++ while (data_in[i] && data_in[i-1] != ' ') ++ i++; ++ //info("table is %s '%s'", table, data_in+i); ++ while (data_in[i] && data_in[i] != ')') { ++ if (delete && !xstrncasecmp("WHERE ", data_in+i, 6)) { ++ i+=6; ++ continue; ++ } else if (!xstrncmp("period_start", data_in+i, 12)) { ++ xstrcat(fields, "time_start"); ++ i+=12; ++ } else if (!xstrncmp("period_end", data_in+i, 10)) { ++ xstrcat(fields, "time_end"); ++ i+=10; ++ } else if (!xstrncmp("cpu_count", data_in+i, 9)) { ++ xstrcat(fields, "count"); ++ i+=9; ++ } else if (!xstrncmp("jobid", data_in+i, 5)) { ++ xstrcat(fields, "id_job"); ++ i+=5; ++ } else if (!xstrncmp("stepid", data_in+i, 6)) { ++ xstrcat(fields, "id_step"); ++ i+=6; ++ } else if (!xstrncmp("associd", data_in+i, 7)) { ++ xstrcat(fields, "id_assoc"); ++ i+=7; ++ } else if (!xstrncmp("blockid", data_in+i, 7)) { ++ xstrcat(fields, "id_block"); ++ i+=7; ++ } else if (!xstrncmp("wckeyid", data_in+i, 7)) { ++ xstrcat(fields, "id_wckey"); ++ i+=7; ++ } else if (!xstrncmp("qos", data_in+i, 3)) { ++ xstrcat(fields, "id_qos"); ++ i+=3; ++ } else if (!xstrncmp("uid", data_in+i, 3)) { ++ xstrcat(fields, "id_user"); ++ i+=3; ++ } else if (!xstrncmp("gid", data_in+i, 3)) { ++ xstrcat(fields, "id_group"); ++ i+=3; ++ } else if (!xstrncmp("submit", data_in+i, 6)) { ++ xstrcat(fields, "time_submit"); ++ i+=6; ++ } else if (!xstrncmp("eligible", data_in+i, 8)) { ++ xstrcat(fields, "time_eligible"); ++ i+=8; ++ } else if (!xstrncmp("start", data_in+i, 5)) { ++ xstrcat(fields, "time_start"); ++ i+=5; ++ } else if (!xstrncmp("suspended", data_in+i, 9)) { ++ xstrcat(fields, "time_suspended"); ++ i+=9; ++ } else if (!xstrncmp("end", data_in+i, 3)) { ++ xstrcat(fields, "time_end"); ++ i+=3; ++ } else if (!xstrncmp("comp_code", data_in+i, 9)) { ++ xstrcat(fields, "exit_code"); ++ i+=9; ++ } else if (!xstrncmp("alloc_cpus", data_in+i, 10)) { ++ xstrcat(fields, "cpus_alloc"); ++ i+=10; ++ } else if (!xstrncmp("req_cpus", data_in+i, 8)) { ++ xstrcat(fields, "cpus_req"); ++ i+=8; ++ } else if (!xstrncmp("alloc_nodes", data_in+i, 11)) { ++ i+=11; ++ if (!delete) { ++ xstrcat(fields, "nodes_alloc"); ++ } else { ++ char *nodes = NULL; ++ while (data_in[i] && data_in[i-1] != '\'') ++ i++; ++ start = i; ++ while (data_in[i] && data_in[i] != '\'') ++ i++; ++ if (!data_in[i]) { ++ error("returning here nodes_alloc"); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ xstrncat(nodes, data_in+start, (i-start)); ++ if (!fields) ++ xstrcat(fields, "WHERE "); ++ xstrfmtcat(fields, "nodes_alloc='%s'", nodes); ++ xfree(nodes); ++ i++; ++ } ++ } else if (!xstrncmp("name", data_in+i, 4)) { ++ if (table == job_table) ++ xstrcat(fields, "job_name"); ++ else if (table == step_table) ++ xstrcat(fields, "step_name"); ++ i+=4; ++ } else if (!xstrncmp("tres_id", data_in+i, 7)) { ++ start = i; ++ while (data_in[i] ++ && data_in[i] != ',' && data_in[i] != ')') { ++ i++; ++ } ++ if (!data_in[i]) { ++ error("returning here end"); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ xstrncat(fields, data_in+start, (i-start)); ++ } else if (!xstrncmp("id", data_in+i, 2)) { ++ i+=2; ++ if ((table == assoc_day_table) ++ || (table == assoc_hour_table) ++ || (table == assoc_month_table)) { ++ char *id_assoc = NULL; ++ while (data_in[i] && data_in[i-1] != '=') { ++ i++; ++ } ++ start = i; ++ while (data_in[i] ++ && data_in[i] != ' ' ++ && data_in[i] != ';') { ++ i++; ++ } ++ if (!data_in[i]) { ++ error("returning at id_assoc"); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ if (data_in[i] == ' ') { ++ while (data_in[i] && data_in[i] == ' ') ++ i++; ++ while (data_in[i] && data_in[i] == '|') ++ i++; ++ while (data_in[i] && data_in[i] == ' ') ++ i++; ++ } ++ xstrncat(id_assoc, data_in+start, (i-start)); ++ if (!fields) ++ xstrcat(fields, "WHERE "); ++ xstrfmtcat(fields, "id_assoc=%s", id_assoc); ++ xfree(id_assoc); ++ } else ++ xstrcat(fields, "job_db_inx"); ++ } else if (!xstrncmp("cluster_nodes", data_in+i, 13)) { ++ /* this is here just to make it easier to ++ handle the cluster field. */ ++ xstrcat(fields, "cluster_nodes"); ++ i+=13; ++ } else if (!xstrncmp("cluster", data_in+i, 7)) { ++ i+=7; ++ if (!delete) { ++ cluster_inx = cnt; ++ if (cnt) ++ fields[strlen(fields)-2] = '\0'; ++ } else { ++ while (data_in[i] && data_in[i-1] != '\'') ++ i++; ++ start = i; ++ while (data_in[i] && data_in[i] != '\'') ++ i++; ++ if (!data_in[i]) { ++ error("returning here cluster"); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ xfree(*cluster_name); ++ *cluster_name = xstrndup(data_in+start, ++ (i-start)); ++ i++; ++ } ++ } else { ++ start = i; ++ while (data_in[i] ++ && data_in[i] != ',' && data_in[i] != ')') { ++ i++; ++ } ++ if (!data_in[i]) { ++ error("returning here end"); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ xstrncat(fields, data_in+start, (i-start)); ++ } ++ if (data_in[i]) { ++ if (!delete || ((table != assoc_day_table) ++ && (table != assoc_hour_table) ++ && (table != assoc_month_table) ++ && (table != job_table))) { ++ if (data_in[i] == ',') ++ xstrcat(fields, ", "); ++ else if (data_in[i] == ')' ++ || data_in[i] == ';') { ++ break; ++ } else { ++ error("unknown char '%s'", data_in+i); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ i++; ++ } else { ++ if (data_in[i] == ';') ++ break; ++ } ++ while (data_in[i] && data_in[i] == ' ') ++ i++; ++ } ++ cnt++; ++ } ++ ++ if (data_in[i] && data_in[i] == ')') { ++ ending_end = i; ++ ending_start = 0; ++ while (data_in[ending_end] && data_in[ending_end-1] != ';') { ++ if (!xstrncmp(data_in+ending_end, ++ "on duplicate key", 16)) { ++ ending_start = ending_end; ++ } ++ if (ending_start) { ++ if (!xstrncmp("period_start", ++ data_in+ending_end, 12)) { ++ xstrcat(ending, "time_start"); ++ ending_end+=12; ++ } else if (!xstrncmp("period_end", ++ data_in+ending_end, 10)) { ++ xstrcat(ending, "time_end"); ++ ending_end+=10; ++ } else if (!xstrncmp("jobid", ++ data_in+ending_end, 5)) { ++ xstrcat(ending, "id_job"); ++ ending_end+=5; ++ } else if (!xstrncmp("stepid", ++ data_in+ending_end, 6)) { ++ xstrcat(ending, "id_step"); ++ ending_end+=6; ++ } else if (!xstrncmp("associd", ++ data_in+ending_end, 7)) { ++ xstrcat(ending, "id_assoc"); ++ ending_end+=7; ++ } else if (!xstrncmp("blockid", ++ data_in+ending_end, 7)) { ++ xstrcat(ending, "id_block"); ++ ending_end+=7; ++ } else if (!xstrncmp("wckeyid", ++ data_in+ending_end, 7)) { ++ xstrcat(ending, "id_wckey"); ++ ending_end+=7; ++ } else if (!xstrncmp("uid", ++ data_in+ending_end, 3)) { ++ xstrcat(ending, "id_user"); ++ ending_end+=3; ++ } else if (!xstrncmp("gid", ++ data_in+ending_end, 3)) { ++ xstrcat(ending, "id_group"); ++ ending_end+=3; ++ } else if (!xstrncmp("submit", ++ data_in+ending_end, 6)) { ++ xstrcat(ending, "time_submit"); ++ ending_end+=6; ++ } else if (!xstrncmp("eligible", ++ data_in+ending_end, 8)) { ++ xstrcat(ending, "time_eligible"); ++ ending_end+=8; ++ } else if (!xstrncmp("start", ++ data_in+ending_end, 5)) { ++ xstrcat(ending, "time_start"); ++ ending_end+=5; ++ } else if (!xstrncmp("suspended", ++ data_in+ending_end, 9)) { ++ xstrcat(ending, "time_suspended"); ++ ending_end+=9; ++ } else if (!xstrncmp("end", ++ data_in+ending_end, 3)) { ++ xstrcat(ending, "time_end"); ++ ending_end+=3; ++ } else if (!xstrncmp("comp_code", ++ data_in+ending_end, 9)) { ++ xstrcat(ending, "exit_code"); ++ ending_end+=9; ++ } else if (!xstrncmp("alloc_cpus", ++ data_in+ending_end, 10)) { ++ xstrcat(ending, "cpus_alloc"); ++ ending_end+=10; ++ } else if (!xstrncmp("req_cpus", ++ data_in+ending_end, 8)) { ++ xstrcat(ending, "cpus_req"); ++ ending_end+=8; ++ } else if (!xstrncmp("alloc_nodes", ++ data_in+ending_end, 11)) { ++ xstrcat(ending, "nodes_alloc"); ++ ending_end+=11; ++ } else if (!xstrncmp("name", ++ data_in+ending_end, 4)) { ++ if (table == job_table) ++ xstrcat(ending, "job_name"); ++ else if (table == step_table) ++ xstrcat(ending, "step_name"); ++ ending_end+=4; ++ } else if (!xstrncmp("id", ++ data_in+ending_end, 2)) { ++ if ((table == assoc_day_table) ++ || (table == assoc_hour_table) ++ || (table == assoc_month_table)) ++ xstrcat(ending, "id_assoc"); ++ else ++ xstrcat(ending, "job_db_inx"); ++ ending_end+=2; ++ } ++ ++ if (data_in[ending_end]) ++ xstrcatchar(ending, ++ data_in[ending_end]); ++ } ++ ending_end++; ++ } ++ ++ /* get values */ ++ while (i < ending_start) { ++ /* get to the start of the values */ ++ while ((i < ending_start) && data_in[i-1] != '(') ++ i++; ++ ++ /* find the values */ ++ cnt = 0; ++ while ((i < ending_start) && data_in[i] != ')') { ++ start = i; ++ while (i < ending_start) { ++ if (data_in[i] == ',' || ++ (data_in[i] == ')' && ++ data_in[i-1] != '(')) ++ break; ++ i++; ++ } ++ if (!data_in[i]) { ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ if (cnt == cluster_inx) { ++ /* get the cluster name and remove the ++ ticks */ ++ xstrncat(new_cluster_name, ++ data_in+start+1, (i-start-2)); ++ if (*cluster_name) { ++ if (xstrcmp(*cluster_name, ++ new_cluster_name)) ++ new_cluster = 1; ++ else ++ xfree(new_cluster_name); ++ } else { ++ xfree(*cluster_name); ++ *cluster_name = ++ new_cluster_name; ++ new_cluster_name = NULL; ++ } ++ } else { ++ xstrncat(new_vals, data_in+start, ++ (i-start)); ++ ++ if (data_in[i]) { ++ if (data_in[i] == ',') ++ xstrcat(new_vals, ", "); ++ else if (data_in[i] == ')' ++ || data_in[i] == ';') { ++ i++; ++ break; ++ } else { ++ error("unknown char " ++ "'%s'", ++ data_in+i); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ } ++ } ++ i++; ++ while ((i < ending_start) && data_in[i] == ' ') ++ i++; ++ cnt++; ++ } ++ if (new_cluster) { ++ /* info("new cluster, adding insert\n%s " */ ++ /* "\"%s_%s\" (%s) values %s %s", */ ++ /* beginning, cluster_name, table, */ ++ /* fields, vals, ending); */ ++ xstrfmtcat(data_out, ++ "%s \"%s_%s\" (%s) values %s %s", ++ beginning, *cluster_name, ++ table, fields, vals, ending); ++ new_cluster = 0; ++ xfree(vals); ++ xfree(*cluster_name); ++ *cluster_name = new_cluster_name; ++ new_cluster_name = NULL; ++ } ++ ++ if (new_vals) { ++ if (vals) ++ xstrfmtcat(vals, ", (%s)", new_vals); ++ else ++ xstrfmtcat(vals, "(%s)", new_vals); ++ xfree(new_vals); ++ } ++ } ++ i = ending_end; ++ } ++ ++ if (!*cluster_name) { ++ error("No cluster given for %s", table); ++ goto end_it; ++ } ++ ++ if (!delete) { ++ /* info("adding insert\n%s \"%s_%s\" (%s) values %s %s", ++ beginning, cluster_name, table, fields, vals, ending); */ ++ xstrfmtcat(data_out, "%s \"%s_%s\" (%s) values %s %s", ++ beginning, *cluster_name, table, fields, ++ vals, ending); ++ } else { ++ if (fields) { ++ /* info("adding delete\n%s \"%s_%s\" %s", */ ++ /* beginning, cluster_name, table, fields); */ ++ xstrfmtcat(data_out, "%s \"%s_%s\" %s", ++ beginning, *cluster_name, table, fields); ++ } else { ++ /* info("adding drop\ndrop table \"%s_%s\";", */ ++ /* cluster_name, table); */ ++ xstrfmtcat(data_out, "drop table \"%s_%s\";", ++ *cluster_name, table); ++ } ++ } ++ ++end_it: ++ xfree(beginning); ++ xfree(ending); ++ xfree(fields); ++ xfree(vals); ++ *data_full_out = data_out; ++ //info("returning\n%s", data_out); ++ if (rc == SLURM_ERROR) ++ return -1; ++ return i; ++} ++ ++static int _process_old_sql(char **data) ++{ ++ int i = 0; ++ char *data_in = *data; ++ char *data_out = NULL; ++ int rc = SLURM_SUCCESS; ++ char *cluster_name = NULL; ++ ++ while (data_in[i]) { ++ if ((rc = _process_old_sql_line( ++ data_in+i, &cluster_name, &data_out)) == -1) ++ break; ++ i += rc; ++ } ++ //rc = -1; ++ xfree(cluster_name); ++ xfree(data_in); ++ if (rc == -1) ++ xfree(data_out); ++ //info("returning\n%s", data_out); ++ *data = data_out; ++ return rc; ++} ++ ++static char *_get_archive_columns(purge_type_t type) ++{ ++ char **cols = NULL; ++ char *tmp = NULL; ++ int col_count = 0, i = 0; ++ ++ xfree(cols); ++ ++ switch (type) { ++ case PURGE_EVENT: ++ cols = event_req_inx; ++ col_count = EVENT_REQ_COUNT; ++ break; ++ case PURGE_SUSPEND: ++ cols = suspend_req_inx; ++ col_count = SUSPEND_REQ_COUNT; ++ break; ++ case PURGE_RESV: ++ cols = resv_req_inx; ++ col_count = RESV_REQ_COUNT; ++ break; ++ case PURGE_JOB: ++ cols = job_req_inx; ++ col_count = JOB_REQ_COUNT; ++ break; ++ case PURGE_STEP: ++ cols = step_req_inx; ++ col_count = STEP_REQ_COUNT; ++ break; ++ case PURGE_TXN: ++ cols = txn_req_inx; ++ col_count = TXN_REQ_COUNT; ++ break; ++ case PURGE_USAGE: ++ cols = usage_req_inx; ++ col_count = USAGE_COUNT; ++ break; ++ case PURGE_CLUSTER_USAGE: ++ cols = cluster_req_inx; ++ col_count = CLUSTER_COUNT; ++ break; ++ default: ++ xassert(0); ++ return NULL; ++ } ++ ++ xstrfmtcat(tmp, "%s", cols[0]); ++ for (i=1; i> 16; ++ ++ buffer = init_buf(high_buffer_size); ++ pack16(SLURM_PROTOCOL_VERSION, buffer); ++ pack_time(time(NULL), buffer); ++ pack16(type, buffer); ++ packstr(cluster_name, buffer); ++ pack32(cnt, buffer); ++ pack16(period, buffer); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ if (period_start && !*period_start) ++ *period_start = slurm_atoul(row[USAGE_START]); ++ ++ memset(&usage, 0, sizeof(local_usage_t)); ++ ++ usage.id = row[USAGE_ID]; ++ usage.tres_id = row[USAGE_TRES]; ++ usage.time_start = row[USAGE_START]; ++ usage.alloc_secs = row[USAGE_ALLOC]; ++ usage.creation_time = row[USAGE_CREATION_TIME]; ++ usage.mod_time = row[USAGE_MOD_TIME]; ++ usage.deleted = row[USAGE_DELETED]; ++ ++ _pack_local_usage(&usage, SLURM_PROTOCOL_VERSION, buffer); ++ } ++ ++ return buffer; ++} ++ ++/* returns sql statement from archived data or NULL on error */ ++static char *_load_usage(uint16_t rpc_version, buf_t *buffer, ++ char *cluster_name, uint16_t type, uint16_t period, ++ uint32_t rec_cnt) ++{ ++ char *insert = NULL, *format = NULL, *my_usage_table = NULL; ++ local_usage_t object; ++ int i = 0; ++ ++ switch (type) { ++ case DBD_GOT_ASSOC_USAGE: ++ switch (period) { ++ case DBD_ROLLUP_HOUR: ++ my_usage_table = assoc_hour_table; ++ break; ++ case DBD_ROLLUP_DAY: ++ my_usage_table = assoc_day_table; ++ break; ++ case DBD_ROLLUP_MONTH: ++ my_usage_table = assoc_month_table; ++ break; ++ default: ++ error("Unknown period"); ++ return NULL; ++ break; ++ } ++ break; ++ case DBD_GOT_WCKEY_USAGE: ++ switch (period) { ++ case DBD_ROLLUP_HOUR: ++ my_usage_table = wckey_hour_table; ++ break; ++ case DBD_ROLLUP_DAY: ++ my_usage_table = wckey_day_table; ++ break; ++ case DBD_ROLLUP_MONTH: ++ my_usage_table = wckey_month_table; ++ break; ++ default: ++ error("Unknown period"); ++ return NULL; ++ break; ++ } ++ break; ++ default: ++ error("Unknown usage type %d", type); ++ return NULL; ++ break; ++ } ++ ++ xstrfmtcat(insert, "INSERT INTO \"%s_%s\" (%s", ++ cluster_name, my_usage_table, usage_req_inx[0]); ++ xstrcat(format, "('%s'"); ++ for(i=1; i> 16; ++ ++ buffer = init_buf(high_buffer_size); ++ pack16(SLURM_PROTOCOL_VERSION, buffer); ++ pack_time(time(NULL), buffer); ++ pack16(DBD_GOT_CLUSTER_USAGE, buffer); ++ packstr(cluster_name, buffer); ++ pack32(cnt, buffer); ++ pack16(period, buffer); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ if (period_start && !*period_start) ++ *period_start = slurm_atoul(row[CLUSTER_START]); ++ ++ memset(&usage, 0, sizeof(local_cluster_usage_t)); ++ ++ usage.tres_id = row[CLUSTER_TRES]; ++ usage.time_start = row[CLUSTER_START]; ++ usage.tres_cnt = row[CLUSTER_CNT]; ++ usage.alloc_secs = row[CLUSTER_ACPU]; ++ usage.down_secs = row[CLUSTER_DCPU]; ++ usage.pdown_secs = row[CLUSTER_PDCPU]; ++ usage.idle_secs = row[CLUSTER_ICPU]; ++ usage.plan_secs = row[CLUSTER_PCPU]; ++ usage.over_secs = row[CLUSTER_OCPU]; ++ usage.creation_time = row[CLUSTER_CREATION_TIME]; ++ usage.mod_time = row[CLUSTER_MOD_TIME]; ++ usage.deleted = row[CLUSTER_DELETED]; ++ ++ _pack_local_cluster_usage( ++ &usage, SLURM_PROTOCOL_VERSION, buffer); ++ } ++ ++ return buffer; ++} ++ ++/* returns sql statement from archived data or NULL on error */ ++static char *_load_cluster_usage(uint16_t rpc_version, buf_t *buffer, ++ char *cluster_name, uint16_t period, ++ uint32_t rec_cnt) ++{ ++ char *insert = NULL, *format = NULL, *my_usage_table = NULL; ++ local_cluster_usage_t object; ++ int i = 0; ++ ++ switch (period) { ++ case DBD_ROLLUP_HOUR: ++ my_usage_table = cluster_hour_table; ++ break; ++ case DBD_ROLLUP_DAY: ++ my_usage_table = cluster_day_table; ++ break; ++ case DBD_ROLLUP_MONTH: ++ my_usage_table = cluster_month_table; ++ break; ++ default: ++ error("Unknown period"); ++ return NULL; ++ break; ++ } ++ ++ xstrfmtcat(insert, "INSERT INTO \"%s_%s\" (%s", ++ cluster_name, my_usage_table, cluster_req_inx[0]); ++ xstrcat(format, "('%s'"); ++ for(i=1; iconn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ if (!(cnt = pgsql_num_rows(result))) { ++ pgsql_free_result(&result); ++ return 0; ++ } ++ ++ buffer = (*pack_func)(result, cluster_name, cnt, usage_info, ++ &period_start); ++ pgsql_free_result(&result); ++ ++ error_code = archive_write_file(buffer, cluster_name, ++ period_start, period_end, ++ arch_dir, sql_table, ++ archive_period); ++ free_buf(buffer); ++ ++ if (error_code != SLURM_SUCCESS) ++ return error_code; ++ ++ return cnt; ++} ++ ++uint32_t _get_begin_next_month(time_t start) ++{ ++ struct tm parts; ++ ++ localtime_r(&start, &parts); ++ ++ parts.tm_mon++; ++ parts.tm_mday = 1; ++ parts.tm_hour = 0; ++ parts.tm_min = 0; ++ parts.tm_sec = 0; ++ ++ if (parts.tm_mon > 11) { ++ parts.tm_year++; ++ parts.tm_mon = 0; ++ } ++ ++ return slurm_mktime(&parts); ++} ++ ++/* Get the oldest purge'able record. ++ * Returns SLURM_ERROR for pgsql error, 0 no purge'able records found, ++ * 1 found purgeable record. ++ */ ++static int _get_oldest_record(pgsql_conn_t *pgsql_conn, char *cluster, ++ char *table, purge_type_t type, char *col_name, ++ time_t period_end, time_t *record_start) ++{ ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *query = NULL; ++ ++ if (record_start == NULL) ++ return SLURM_ERROR; ++ ++ /* get oldest record */ ++ switch (type) { ++ case PURGE_TXN: ++ query = xstrdup_printf( ++ "SELECT %s FROM \"%s\" WHERE %s <= %ld " ++ "AND cluster='%s' ORDER BY %s asc LIMIT 1", ++ col_name, table, col_name, period_end, cluster, ++ col_name); ++ break; ++ case PURGE_USAGE: ++ case PURGE_CLUSTER_USAGE: ++ query = xstrdup_printf( ++ "SELCET %s FROM \"%s_%s\" WHERE %s <= %ld " ++ "ORDER BY %s asc LIMIT 1", ++ col_name, cluster, table, col_name, period_end, ++ col_name); ++ break; ++ default: ++ query = xstrdup_printf( ++ "SELECT %s FROM \"%s_%s\" WHERE %s <= %ld " ++ "AND time_end != 0 ORDER BY %s asc LIMIT 1", ++ col_name, cluster, table, col_name, period_end, ++ col_name); ++ break; ++ } ++ ++ DB_DEBUG(DB_ARCHIVE, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ if (!(pgsql_num_rows(result))) { ++ pgsql_free_result(&result); ++ return 0; ++ } ++ row = pgsql_fetch_row(result); ++ *record_start = slurm_atoul(row[0]); ++ pgsql_free_result(&result); ++ ++ return 1; /* found one record */ ++} ++ ++/* Archive and purge a table. ++ * ++ * Returns SLURM_ERROR on error and SLURM_SUCCESS on success. ++ */ ++static int _archive_purge_table(purge_type_t purge_type, uint32_t usage_info, ++ pgsql_conn_t *pgsql_conn, char *cluster_name, ++ slurmdb_archive_cond_t *arch_cond) ++{ ++ int rc = SLURM_SUCCESS; ++ uint32_t purge_attr = 0; ++ uint16_t type, period; ++ time_t last_submit = time(NULL); ++ time_t curr_end = 0, tmp_end = 0, record_start = 0; ++ char *query = NULL, *sql_table = NULL, ++ *col_name = NULL; ++ uint32_t tmp_archive_period; ++ ++ switch (purge_type) { ++ case PURGE_EVENT: ++ purge_attr = arch_cond->purge_event; ++ sql_table = event_table; ++ col_name = event_req_inx[EVENT_REQ_START]; ++ break; ++ case PURGE_SUSPEND: ++ purge_attr = arch_cond->purge_suspend; ++ sql_table = suspend_table; ++ col_name = suspend_req_inx[SUSPEND_REQ_START]; ++ break; ++ case PURGE_RESV: ++ purge_attr = arch_cond->purge_resv; ++ sql_table = resv_table; ++ col_name = step_req_inx[STEP_REQ_START]; ++ break; ++ case PURGE_JOB: ++ purge_attr = arch_cond->purge_job; ++ sql_table = job_table; ++ col_name = job_req_inx[JOB_REQ_SUBMIT]; ++ break; ++ case PURGE_STEP: ++ purge_attr = arch_cond->purge_step; ++ sql_table = step_table; ++ col_name = step_req_inx[STEP_REQ_START]; ++ break; ++ case PURGE_TXN: ++ purge_attr = arch_cond->purge_txn; ++ sql_table = txn_table; ++ col_name = txn_req_inx[TXN_REQ_TS]; ++ break; ++ case PURGE_USAGE: ++ type = usage_info & 0x0000ffff; ++ period = usage_info >> 16; ++ ++ switch (type) { ++ case DBD_GOT_ASSOC_USAGE: ++ switch (period) { ++ case DBD_ROLLUP_HOUR: ++ sql_table = assoc_hour_table; ++ break; ++ case DBD_ROLLUP_DAY: ++ sql_table = assoc_day_table; ++ break; ++ case DBD_ROLLUP_MONTH: ++ sql_table = assoc_month_table; ++ break; ++ default: ++ error("Unknown period"); ++ return SLURM_ERROR; ++ break; ++ } ++ break; ++ case DBD_GOT_WCKEY_USAGE: ++ switch (period) { ++ case DBD_ROLLUP_HOUR: ++ sql_table = wckey_hour_table; ++ break; ++ case DBD_ROLLUP_DAY: ++ sql_table = wckey_day_table; ++ break; ++ case DBD_ROLLUP_MONTH: ++ sql_table = wckey_month_table; ++ break; ++ default: ++ error("Unknown period"); ++ return SLURM_ERROR; ++ break; ++ } ++ break; ++ default: ++ error("Unknown usage type %d", type); ++ return SLURM_ERROR; ++ break; ++ } ++ ++ purge_attr = arch_cond->purge_usage; ++ col_name = usage_req_inx[USAGE_START]; ++ break; ++ case PURGE_CLUSTER_USAGE: ++ period = usage_info >> 16; ++ ++ switch (period) { ++ case DBD_ROLLUP_HOUR: ++ sql_table = cluster_hour_table; ++ break; ++ case DBD_ROLLUP_DAY: ++ sql_table = cluster_day_table; ++ break; ++ case DBD_ROLLUP_MONTH: ++ sql_table = cluster_month_table; ++ break; ++ default: ++ error("Unknown period"); ++ return SLURM_ERROR; ++ break; ++ } ++ ++ purge_attr = arch_cond->purge_usage; ++ col_name = cluster_req_inx[CLUSTER_START]; ++ break; ++ default: ++ fatal("Unknown purge type: %d", purge_type); ++ return SLURM_ERROR; ++ } ++ ++ if (!(curr_end = archive_setup_end_time(last_submit, purge_attr))) { ++ error("Parsing purge %s_%s", cluster_name, sql_table); ++ return SLURM_ERROR; ++ } ++ ++ /* continue archive/purge until no records in the period are found */ ++ while (1) { ++ rc = _get_oldest_record(pgsql_conn, cluster_name, sql_table, ++ purge_type, col_name, ++ curr_end, &record_start); ++ if (!rc) /* no purgeable records found - base case */ ++ break; ++ else if (rc == SLURM_ERROR) ++ return rc; ++ ++ tmp_archive_period = purge_attr; ++ ++ if (curr_end - record_start > MAX_ARCHIVE_AGE) { ++ time_t begin_next = _get_begin_next_month(record_start); ++ /* old stuff, catch up by archiving by month */ ++ tmp_archive_period = SLURMDB_PURGE_MONTHS; ++ tmp_end = MIN(curr_end, begin_next); ++ } else ++ tmp_end = curr_end; ++ ++ log_flag(DB_ARCHIVE, "Purging %s_%s before %ld", ++ cluster_name, sql_table, tmp_end); ++ ++ /* Do archive */ ++ if (SLURMDB_PURGE_ARCHIVE_SET(purge_attr)) { ++ rc = _archive_table(purge_type, pgsql_conn, ++ cluster_name, tmp_end, ++ arch_cond->archive_dir, ++ tmp_archive_period, ++ sql_table, usage_info); ++ if (!rc) { /* no records archived */ ++ error("%s: No records archived for %s before %ld but we found some records", ++ __func__, sql_table, tmp_end); ++ return SLURM_ERROR; ++ } else if (rc == SLURM_ERROR) ++ return rc; ++ } ++ ++ /* ++ * The purge query should have the same where clause as the ++ * archive query. The order by is very important so we get ++ * records in the same order as we do when archiving, since we ++ * only want to delete records that have been archived (if ++ * archiving is enabled). ++ */ ++ switch (purge_type) { ++ case PURGE_TXN: ++ query = xstrdup_printf( ++ "DELETE FROM \"%s\" WHERE " ++ "%s <= %ld AND cluster='%s' ORDER BY %s asc LIMIT %d", ++ sql_table, col_name, tmp_end, cluster_name, ++ col_name, MAX_PURGE_LIMIT); ++ break; ++ case PURGE_USAGE: ++ case PURGE_CLUSTER_USAGE: ++ query = xstrdup_printf( ++ "DELETE FROM \"%s_%s\" WHERE " ++ "%s <= %ld ORDER BY %s asc LIMIT %d", ++ cluster_name, sql_table, col_name, ++ tmp_end, col_name, MAX_PURGE_LIMIT); ++ break; ++ default: ++ query = xstrdup_printf( ++ "DELETE FROM \"%s_%s\" WHERE " ++ "%s <= %ld AND time_end != 0 ORDER BY %s asc LIMIT %d", ++ cluster_name, sql_table, col_name, ++ tmp_end, col_name, MAX_PURGE_LIMIT); ++ break; ++ } ++ DB_DEBUG(DB_ARCHIVE, pgsql_conn->conn, "query\n%s", query); ++ ++ /* ++ * Don't loop this query, just do it once, since we are only ++ * archiving and purging MAX_PURGE_LIMIT rows at a time. ++ * pgsql_db_delete_affected_rows will return < 0 on failure or ++ * 0 if no records are affected. ++ */ ++ if ((rc = pgsql_db_sql_affected_rows( ++ pgsql_conn, query)) > 0) { ++ /* Commit here every time since this could create a huge ++ * transaction. ++ */ ++ if ((rc = pgsql_db_commit(pgsql_conn))) ++ error("Couldn't commit cluster (%s) purge", ++ cluster_name); ++ } ++ ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't remove old data from %s table", ++ sql_table); ++ return SLURM_ERROR; ++ } else if (pgsql_db_commit(pgsql_conn)) { ++ error("Couldn't commit cluster (%s) purge", ++ cluster_name); ++ break; ++ } ++ } ++ ++ return SLURM_SUCCESS; ++} ++ ++static int _execute_archive(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, ++ slurmdb_archive_cond_t *arch_cond) ++{ ++ int rc = SLURM_SUCCESS; ++ time_t last_submit = time(NULL); ++ ++ if (arch_cond->archive_script) ++ return archive_run_script(arch_cond, cluster_name, last_submit); ++ else if (!arch_cond->archive_dir) { ++ error("No archive dir given, can't process"); ++ return SLURM_ERROR; ++ } ++ ++ if (arch_cond->purge_event != NO_VAL) { ++ if ((rc = _archive_purge_table(PURGE_EVENT, 0, pgsql_conn, ++ cluster_name, arch_cond))) ++ return rc; ++ } ++ ++ if (arch_cond->purge_suspend != NO_VAL) { ++ if ((rc = _archive_purge_table(PURGE_SUSPEND, 0, pgsql_conn, ++ cluster_name, arch_cond))) ++ return rc; ++ } ++ ++ if (arch_cond->purge_step != NO_VAL) { ++ if ((rc = _archive_purge_table(PURGE_STEP, 0, pgsql_conn, ++ cluster_name, arch_cond))) ++ return rc; ++ } ++ ++ if (arch_cond->purge_job != NO_VAL) { ++ if ((rc = _archive_purge_table(PURGE_JOB, 0, pgsql_conn, ++ cluster_name, arch_cond))) ++ return rc; ++ } ++ ++ if (arch_cond->purge_resv != NO_VAL) { ++ if ((rc = _archive_purge_table(PURGE_RESV, 0, pgsql_conn, ++ cluster_name, arch_cond))) ++ return rc; ++ } ++ ++ if (arch_cond->purge_txn != NO_VAL) { ++ if ((rc = _archive_purge_table(PURGE_TXN, 0, pgsql_conn, ++ cluster_name, arch_cond))) ++ return rc; ++ } ++ ++ if (arch_cond->purge_usage != NO_VAL) { ++ int i; ++ for (i = 0; i < DBD_ROLLUP_COUNT; i++) { ++ uint32_t usage_info = i << 16; ++ if ((rc = _archive_purge_table( ++ PURGE_USAGE, ++ usage_info + DBD_GOT_ASSOC_USAGE, ++ pgsql_conn, cluster_name, arch_cond))) ++ return rc; ++ ++ if ((rc = _archive_purge_table( ++ PURGE_USAGE, ++ usage_info + DBD_GOT_WCKEY_USAGE, ++ pgsql_conn, cluster_name, arch_cond))) ++ return rc; ++ ++ if ((rc = _archive_purge_table( ++ PURGE_CLUSTER_USAGE, ++ usage_info + DBD_GOT_CLUSTER_USAGE, ++ pgsql_conn, cluster_name, arch_cond))) ++ return rc; ++ } ++ } ++ ++ return SLURM_SUCCESS; ++} ++ ++extern int as_pgsql_jobacct_process_archive(pgsql_conn_t *pgsql_conn, ++ slurmdb_archive_cond_t *arch_cond) ++{ ++ int rc = SLURM_SUCCESS; ++ char *cluster_name = NULL; ++ List use_cluster_list; ++ bool new_cluster_list = false; ++ ListIterator itr = NULL; ++ ++ if (!arch_cond) { ++ error("No arch_cond was given to archive from. returning"); ++ return SLURM_ERROR; ++ } ++ ++ if (arch_cond->job_cond && arch_cond->job_cond->cluster_list ++ && list_count(arch_cond->job_cond->cluster_list)) { ++ use_cluster_list = arch_cond->job_cond->cluster_list; ++ } else { ++ /* execute_archive may take a long time to run, so ++ * don't keep the as_pgsql_cluster_list_lock locked ++ * the whole time, just copy the list and work off ++ * that. ++ */ ++ new_cluster_list = true; ++ use_cluster_list = list_create(xfree_ptr); ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ itr = list_iterator_create(as_pgsql_cluster_list); ++ while ((cluster_name = list_next(itr))) ++ list_append(use_cluster_list, xstrdup(cluster_name)); ++ list_iterator_destroy(itr); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ ++ itr = list_iterator_create(use_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ if ((rc = _execute_archive(pgsql_conn, cluster_name, arch_cond)) ++ != SLURM_SUCCESS) ++ break; ++ } ++ list_iterator_destroy(itr); ++ ++ if (new_cluster_list) ++ FREE_NULL_LIST(use_cluster_list); ++ ++ return rc; ++} ++ ++extern int as_pgsql_jobacct_process_archive_load( ++ pgsql_conn_t *pgsql_conn, slurmdb_archive_rec_t *arch_rec) ++{ ++ char *data = NULL, *cluster_name = NULL; ++ int error_code = SLURM_SUCCESS; ++ buf_t *buffer = NULL; ++ time_t buf_time; ++ uint16_t type = 0, ver = 0, period = 0; ++ uint32_t data_size = 0, rec_cnt = 0, tmp32 = 0; ++ uint32_t rec_cnt_total = 0, rec_cnt_left = 0, pass_cnt = 0; ++ ++ /* Ensure that the connection is not set in autocommit mode. */ ++ xassert(pgsql_conn->rollback); ++ ++ if (!arch_rec) { ++ error("We need a slurmdb_archive_rec to load anything."); ++ return SLURM_ERROR; ++ } ++ ++ if (arch_rec->insert) { ++ data = xstrdup(arch_rec->insert); ++ } else if (arch_rec->archive_file) { ++ int data_allocated, data_read = 0; ++ int state_fd = open(arch_rec->archive_file, O_RDONLY); ++ if (state_fd < 0) { ++ info("Could not open archive file `%s`: %m", ++ arch_rec->archive_file); ++ error_code = errno; ++ } else { ++ data_allocated = BUF_SIZE + 1; ++ data = xmalloc_nz(data_allocated); ++ while (1) { ++ data_read = read(state_fd, &data[data_size], ++ BUF_SIZE); ++ if (data_read < 0) { ++ data[data_size] = '\0'; ++ if (errno == EINTR) ++ continue; ++ else { ++ error("Read error on %s: %m", ++ arch_rec->archive_file); ++ break; ++ } ++ } ++ data[data_size + data_read] = '\0'; ++ if (data_read == 0) /* eof */ ++ break; ++ data_size += data_read; ++ data_allocated += data_read; ++ xrealloc_nz(data, data_allocated); ++ } ++ close(state_fd); ++ } ++ if (error_code != SLURM_SUCCESS) { ++ xfree(data); ++ return error_code; ++ } ++ } else { ++ error("Nothing was set in your " ++ "slurmdb_archive_rec so I am unable to process."); ++ xfree(data); ++ return SLURM_ERROR; ++ } ++ ++ if (!data) { ++ error("It doesn't appear we have anything to load."); ++ return SLURM_ERROR; ++ } ++ ++ /* ++ * this is the old version of an archive file where the file ++ * was straight sql. ++ */ ++ if ((strlen(data) >= 12) ++ && (!xstrncasecmp("INSERT INTO ", data, 12) ++ || !xstrncasecmp("DELETE FROM ", data, 12) ++ || !xstrncasecmp("DROP TABLE ", data, 11) ++ || !xstrncasecmp("TRUNCATE TABLE ", data, 15))) { ++ _process_old_sql(&data); ++ goto got_sql; ++ } ++ ++ buffer = create_buf(data, data_size); ++ data = NULL; /* Moved to "buffer" */ ++ ++ safe_unpack16(&ver, buffer); ++ DB_DEBUG(DB_ARCHIVE, pgsql_conn->conn, ++ "Version in archive header is %u", ver); ++ /* ++ * Don't verify the lower limit as we should be keeping all ++ * older versions around here just to support super old ++ * archive files since they don't get regenerated all the time. ++ */ ++ if (ver > SLURM_PROTOCOL_VERSION) { ++ error("***********************************************"); ++ error("Can not recover archive file, incompatible version, " ++ "got %u need <= %u", ver, ++ SLURM_PROTOCOL_VERSION); ++ error("***********************************************"); ++ FREE_NULL_BUFFER(buffer); ++ return EFAULT; ++ } ++ safe_unpack_time(&buf_time, buffer); ++ safe_unpack16(&type, buffer); ++ safe_unpackstr_xmalloc(&cluster_name, &tmp32, buffer); ++ safe_unpack32(&rec_cnt, buffer); ++ ++ if (!rec_cnt) { ++ error("we didn't get any records from this file of type '%s'", ++ slurmdbd_msg_type_2_str(type, 0)); ++ goto got_sql; ++ } ++ ++ rec_cnt_left = rec_cnt; ++ rec_cnt_total = rec_cnt; ++pass: ++ rec_cnt = MIN(rec_cnt_left, RECORDS_PER_PASS); ++ ++ DB_DEBUG(DB_ARCHIVE, pgsql_conn->conn, ++ "%s: Pass %u: loaded %u/%u records. Attempting partial load %u.", ++ __func__, pass_cnt, rec_cnt_total - rec_cnt_left, ++ rec_cnt_total, rec_cnt); ++ ++ rec_cnt_left -= rec_cnt; ++ ++ switch (type) { ++ case DBD_GOT_EVENTS: ++ data = _load_events(ver, buffer, cluster_name, rec_cnt); ++ break; ++ case DBD_GOT_JOBS: ++ data = _load_jobs(ver, buffer, cluster_name, rec_cnt); ++ break; ++ case DBD_GOT_RESVS: ++ data = _load_resvs(ver, buffer, cluster_name, rec_cnt); ++ break; ++ case DBD_STEP_START: ++ data = _load_steps(ver, buffer, cluster_name, rec_cnt); ++ break; ++ case DBD_JOB_SUSPEND: ++ data = _load_suspend(ver, buffer, cluster_name, rec_cnt); ++ break; ++ case DBD_GOT_TXN: ++ data = _load_txn(ver, buffer, cluster_name, rec_cnt); ++ break; ++ case DBD_GOT_ASSOC_USAGE: ++ case DBD_GOT_WCKEY_USAGE: ++ if (pass_cnt == 0) ++ safe_unpack16(&period, buffer); ++ data = _load_usage(ver, buffer, cluster_name, type, period, ++ rec_cnt); ++ break; ++ case DBD_GOT_CLUSTER_USAGE: ++ if (pass_cnt == 0) ++ safe_unpack16(&period, buffer); ++ data = _load_cluster_usage(ver, buffer, cluster_name, period, ++ rec_cnt); ++ break; ++ default: ++ error("Unknown type '%u' to load from archive", type); ++ break; ++ } ++ ++got_sql: ++ if (!data) { ++ error("No data to load"); ++ error_code = SLURM_ERROR; ++ goto cleanup; ++ } ++ if (slurm_conf.debug_flags & DEBUG_FLAG_DB_ARCHIVE) ++ DB_DEBUG(DB_QUERY, pgsql_conn->conn, "query\n%s", data); ++ error_code = pgsql_db_query_check_after(pgsql_conn, data); ++ xfree(data); ++ if (error_code != SLURM_SUCCESS) { ++unpack_error: ++ error("Couldn't load old data"); ++ if (error_code == SLURM_SUCCESS) { ++ /* This happens on unpack_error */ ++ error_code = SLURM_ERROR; ++ } ++ goto cleanup; ++ } ++ ++ if (rec_cnt_left) { ++ pass_cnt++; ++ goto pass; ++ } ++ ++cleanup: ++ xfree(cluster_name); ++ FREE_NULL_BUFFER(buffer); ++ ++ if (error_code) ++ error("%s: failure loading archive: %s", __func__, ++ slurm_strerror(error_code)); ++ else ++ DB_DEBUG(DB_ARCHIVE, pgsql_conn->conn, ++ "%s: archive loaded successfully.", __func__); ++ ++ return error_code; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_archive.h b/src/plugins/accounting_storage/pgsql/as_pgsql_archive.h +new file mode 100755 +index 0000000..a11a968 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_archive.h +@@ -0,0 +1,50 @@ ++/*****************************************************************************\ ++ * as_pgsql_archive.h - functions dealing with the archiving. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_ARCHIVE_H ++#define _HAVE_PGSQL_ARCHIVE_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_jobacct_process_archive(pgsql_conn_t *pgsql_conn, ++ slurmdb_archive_cond_t *arch_cond); ++ ++extern int as_pgsql_jobacct_process_archive_load(pgsql_conn_t *pgsql_conn, ++ slurmdb_archive_rec_t *arch_rec); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_assoc.c b/src/plugins/accounting_storage/pgsql/as_pgsql_assoc.c +new file mode 100755 +index 0000000..f846b39 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_assoc.c +@@ -0,0 +1,3779 @@ ++/*****************************************************************************\ ++ * as_pgsql_assoc.c - functions dealing with associations. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_assoc.h" ++#include "as_pgsql_usage.h" ++ ++static char *tmp_cluster_name = "slurmredolftrgttemp"; ++ ++ ++/* if this changes you will need to edit the corresponding enum */ ++char *assoc_req_inx[] = { ++ "id_assoc", ++ "lft", ++ "rgt", ++ "user", ++ "acct", ++ "`partition`", ++ "shares", ++ "grp_tres_mins", ++ "grp_tres_run_mins", ++ "grp_tres", ++ "grp_jobs", ++ "grp_jobs_accrue", ++ "grp_submit_jobs", ++ "grp_wall", ++ "max_tres_mins_pj", ++ "max_tres_run_mins", ++ "max_tres_pj", ++ "max_tres_pn", ++ "max_jobs", ++ "max_jobs_accrue", ++ "min_prio_thresh", ++ "max_submit_jobs", ++ "max_wall_pj", ++ "parent_acct", ++ "priority", ++ "def_qos_id", ++ "qos", ++ "delta_qos", ++ "is_def", ++ "deleted", ++}; ++enum { ++ ASSOC_REQ_ID, ++ ASSOC_REQ_LFT, ++ ASSOC_REQ_RGT, ++ ASSOC_REQ_USER, ++ ASSOC_REQ_ACCT, ++ ASSOC_REQ_PART, ++ ASSOC_REQ_FS, ++ ASSOC_REQ_GTM, ++ ASSOC_REQ_GTRM, ++ ASSOC_REQ_GT, ++ ASSOC_REQ_GJ, ++ ASSOC_REQ_GJA, ++ ASSOC_REQ_GSJ, ++ ASSOC_REQ_GW, ++ ASSOC_REQ_MTMPJ, ++ ASSOC_REQ_MTRM, ++ ASSOC_REQ_MTPJ, ++ ASSOC_REQ_MTPN, ++ ASSOC_REQ_MJ, ++ ASSOC_REQ_MJA, ++ ASSOC_REQ_MPT, ++ ASSOC_REQ_MSJ, ++ ASSOC_REQ_MWPJ, ++ ASSOC_REQ_PARENT, ++ ASSOC_REQ_PRIO, ++ ASSOC_REQ_DEF_QOS, ++ ASSOC_REQ_QOS, ++ ASSOC_REQ_DELTA_QOS, ++ ASSOC_REQ_DEFAULT, ++ ASSOC_REQ_DELETED, ++ ASSOC_REQ_COUNT ++}; ++ ++static char *get_parent_limits_select = ++ "select @par_id, @mj, @mja, @mpt, @msj, " ++ "@mwpj, @mtpj, @mtpn, @mtmpj, @mtrm, " ++ "@def_qos_id, @qos, @delta_qos, @prio;"; ++ ++enum { ++ ASSOC2_REQ_PARENT_ID, ++ ASSOC2_REQ_MJ, ++ ASSOC2_REQ_MJA, ++ ASSOC2_REQ_MPT, ++ ASSOC2_REQ_MSJ, ++ ASSOC2_REQ_MWPJ, ++ ASSOC2_REQ_MTPJ, ++ ASSOC2_REQ_MTPN, ++ ASSOC2_REQ_MTMPJ, ++ ASSOC2_REQ_MTRM, ++ ASSOC2_REQ_DEF_QOS, ++ ASSOC2_REQ_QOS, ++ ASSOC2_REQ_DELTA_QOS, ++ ASSOC2_REQ_PRIO, ++}; ++ ++static char *aassoc_req_inx[] = { ++ "id_assoc", ++ "parent_acct", ++ "lft", ++ "rgt", ++ "deleted" ++}; ++ ++enum { ++ AASSOC_ID, ++ AASSOC_PACCT, ++ AASSOC_LFT, ++ AASSOC_RGT, ++ AASSOC_DELETED, ++ AASSOC_COUNT ++}; ++ ++static char *massoc_req_inx[] = { ++ "id_assoc", ++ "acct", ++ "parent_acct", ++ "user", ++ "`partition`", ++ "lft", ++ "rgt", ++ "qos", ++ "grp_tres_mins", ++ "grp_tres_run_mins", ++ "grp_tres", ++ "max_tres_mins_pj", ++ "max_tres_run_mins", ++ "max_tres_pj", ++ "max_tres_pn", ++}; ++ ++enum { ++ MASSOC_ID, ++ MASSOC_ACCT, ++ MASSOC_PACCT, ++ MASSOC_USER, ++ MASSOC_PART, ++ MASSOC_LFT, ++ MASSOC_RGT, ++ MASSOC_QOS, ++ MASSOC_GTM, ++ MASSOC_GTRM, ++ MASSOC_GT, ++ MASSOC_MTMPJ, ++ MASSOC_MTRM, ++ MASSOC_MTPJ, ++ MASSOC_MTPN, ++ MASSOC_COUNT ++}; ++ ++/* if this changes you will need to edit the corresponding ++ * enum below also t1 is step_table */ ++static char *rassoc_req_inx[] = { ++ "id_assoc", ++ "lft", ++ "acct", ++ "parent_acct", ++ "user", ++ "`partition`" ++}; ++ ++enum { ++ RASSOC_ID, ++ RASSOC_LFT, ++ RASSOC_ACCT, ++ RASSOC_PACCT, ++ RASSOC_USER, ++ RASSOC_PART, ++ RASSOC_COUNT ++}; ++ ++static void _move_assoc_list_to_update_list(List update_list, List assoc_list) ++{ ++ slurmdb_assoc_rec_t *assoc; ++ ++ if (!assoc_list) ++ return; ++ ++ /* ++ * NOTE: You have to use slurm_list_pop here, since ++ * pgsql is exporting something of the same type as a ++ * macro, which messes everything up ++ * (my_list.h is the bad boy). ++ */ ++ while ((assoc = slurm_list_pop(assoc_list))) { ++ /* ++ * Only free the pointer on error as success will have ++ * moved it to update_list. ++ */ ++ if (addto_update_list(update_list, ++ SLURMDB_MODIFY_ASSOC, ++ assoc) != SLURM_SUCCESS) ++ slurmdb_destroy_assoc_rec(assoc); ++ } ++} ++ ++static int _assoc_sort_cluster(void *r1, void *r2) ++{ ++ slurmdb_assoc_rec_t *rec_a = *(slurmdb_assoc_rec_t **)r1; ++ slurmdb_assoc_rec_t *rec_b = *(slurmdb_assoc_rec_t **)r2; ++ int diff; ++ ++ diff = xstrcmp(rec_a->cluster, rec_b->cluster); ++ if (diff < 0) ++ return -1; ++ else if (diff > 0) ++ return 1; ++ ++ return 0; ++} ++ ++/* Caller responsible for freeing query being sent in as it could be ++ * changed while running the function. ++ */ ++static int _reset_default_assoc(pgsql_conn_t *pgsql_conn, ++ slurmdb_assoc_rec_t *assoc, ++ char **query, ++ bool add_to_update) ++{ ++ time_t now = time(NULL); ++ int rc = SLURM_SUCCESS; ++ ++ xassert(query); ++ ++ if ((assoc->is_def != 1) || !assoc->cluster ++ || !assoc->acct || !assoc->user) ++ return SLURM_ERROR; ++ ++ xstrfmtcat(*query, "update \"%s_%s\" set is_def=0, mod_time=%ld " ++ "where (\"user\"='%s' AND acct!='%s' AND is_def=1);", ++ assoc->cluster, assoc_table, (long)now, ++ assoc->user, assoc->acct); ++ if (add_to_update) { ++ char *sel_query = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ /* If moved parent all the associations will be sent ++ so no need to do this extra step. Else, this has ++ to be done one at a time so we can send ++ the updated assocs back to the slurmctlds ++ */ ++ xstrfmtcat(sel_query, "select id_assoc from \"%s_%s\" " ++ "where (\"user\"='%s' AND acct!='%s' AND is_def=1);", ++ assoc->cluster, assoc_table, ++ assoc->user, assoc->acct); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", sel_query); ++ result = pgsql_db_query_ret(pgsql_conn, sel_query, 1); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(sel_query); ++ pgsql_free_result(&result); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ xfree(sel_query); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_assoc_rec_t *mod_assoc = xmalloc( ++ sizeof(slurmdb_assoc_rec_t)); ++ slurmdb_init_assoc_rec(mod_assoc, 0); ++ ++ mod_assoc->cluster = xstrdup(assoc->cluster); ++ mod_assoc->id = slurm_atoul(row[0]); ++ mod_assoc->is_def = 0; ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_MODIFY_ASSOC, ++ mod_assoc) ++ != SLURM_SUCCESS) { ++ slurmdb_destroy_assoc_rec(mod_assoc); ++ error("couldn't add to the update list"); ++ rc = SLURM_ERROR; ++ break; ++ } ++ } ++ pgsql_free_result(&result); ++ } ++end_it: ++ return rc; ++} ++ ++/* assoc_mgr_lock_t should be clear before coming in here. */ ++static int _check_coord_qos(pgsql_conn_t *pgsql_conn, char *cluster_name, ++ char *account, char *coord_name, List qos_list) ++{ ++ char *query; ++ bitstr_t *request_qos, *valid_qos; ++ pgsql_res_t *result; ++ pgsql_row row; ++ int rc = SLURM_SUCCESS; ++ assoc_mgr_lock_t locks = { NO_LOCK, NO_LOCK, READ_LOCK, NO_LOCK, ++ NO_LOCK, NO_LOCK, NO_LOCK }; ++ ++ if (!qos_list || !list_count(qos_list)) ++ return SLURM_SUCCESS; ++ ++ /* If there is a variable cleared here we need to make ++ sure we get the parent's information, if any. */ ++ query = xstrdup_printf( ++ "call get_coord_qos('%s', '%s', '%s', '%s');", ++ assoc_table, account, ++ cluster_name, coord_name); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 1); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ if (!(row = pgsql_fetch_row(result)) || !row[0]) { ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ /* First set the values of the valid ones this coordinator has ++ access to. ++ */ ++ ++ assoc_mgr_lock(&locks); ++ valid_qos = bit_alloc(g_qos_count); ++ request_qos = bit_alloc(g_qos_count); ++ assoc_mgr_unlock(&locks); ++ ++ set_qos_bitstr_from_string(valid_qos, row[0]); ++ ++ pgsql_free_result(&result); ++ ++ /* Now set the ones they are requesting */ ++ set_qos_bitstr_from_list(request_qos, qos_list); ++ ++ /* If they are authorized their list should be in the super set */ ++ if (!bit_super_set(request_qos, valid_qos)) ++ rc = SLURM_ERROR; ++ ++ FREE_NULL_BITMAP(valid_qos); ++ FREE_NULL_BITMAP(request_qos); ++ ++ return rc; ++} ++ ++/* This needs to happen to make since 2.1 code doesn't have enough ++ * smarts to figure out it isn't adding a default account if just ++ * adding an association to the mix. ++ */ ++static int _make_sure_users_have_default( ++ pgsql_conn_t *pgsql_conn, List user_list, List cluster_list) ++{ ++ char *query = NULL, *cluster = NULL, *user = NULL; ++ ListIterator itr = NULL, clus_itr = NULL; ++ int rc = SLURM_SUCCESS; ++ ++ if (!user_list) ++ return SLURM_SUCCESS; ++ ++ clus_itr = list_iterator_create(cluster_list); ++ itr = list_iterator_create(user_list); ++ ++ while ((user = list_next(itr))) { ++ while ((cluster = list_next(clus_itr))) { ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ query = xstrdup_printf( ++ "select distinct is_def, acct from " ++ "\"%s_%s\" where \"user\"='%s' and deleted=0 " ++ "ORDER BY is_def desc, creation_time desc " ++ "LIMIT 1;", ++ cluster, assoc_table, user); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ error("couldn't query the database"); ++ pgsql_free_result(&result); ++ rc = SLURM_ERROR; ++ break; ++ } ++ xfree(query); ++ /* Check to see if the user is even added to ++ the cluster. ++ */ ++ if (!pgsql_num_rows(result)) { ++ pgsql_free_result(&result); ++ continue; ++ } ++ ++ /* check if the row is default */ ++ row = pgsql_fetch_row(result); ++ if (row[0][0] == '1') { ++ /* default found, continue */ ++ pgsql_free_result(&result); ++ continue; ++ } ++ ++ /* if we made it here, there is no default */ ++ query = xstrdup_printf( ++ "update \"%s_%s\" set is_def=1 where " ++ "\"user\"='%s' and acct='%s';", ++ cluster, assoc_table, user, row[1]); ++ pgsql_free_result(&result); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", ++ query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("problem with update query"); ++ rc = SLURM_ERROR; ++ break; ++ } ++ } ++ if (rc != SLURM_SUCCESS) ++ break; ++ list_iterator_reset(clus_itr); ++ } ++ list_iterator_destroy(itr); ++ list_iterator_destroy(clus_itr); ++ ++ return rc; ++} ++ ++/* This should take care of all the lft and rgts when you move an ++ * account. This handles deleted associations also. ++ */ ++static int _move_account(pgsql_conn_t *pgsql_conn, uint32_t *lft, uint32_t *rgt, ++ char *cluster, char *id, char *parent, time_t now) ++{ ++ int rc = SLURM_SUCCESS; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ uint32_t par_left = 0; ++ uint32_t diff = 0; ++ uint32_t width = 0; ++ char *query = xstrdup_printf( ++ "SELECT lft from \"%s_%s\" where acct='%s' AND \"user\"='';", ++ cluster, assoc_table, parent); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ if (!(row = pgsql_fetch_row(result))) { ++ debug4("Can't move a none existent association"); ++ pgsql_free_result(&result); ++ return ESLURM_INVALID_PARENT_ACCOUNT; ++ } ++ par_left = slurm_atoul(row[0]); ++ pgsql_free_result(&result); ++ ++ diff = ((par_left + 1) - *lft); ++ ++ if (diff == 0) { ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, ++ "Trying to move association to the same position? Nothing to do."); ++ return ESLURM_SAME_PARENT_ACCOUNT; ++ } ++ ++ width = (*rgt - *lft + 1); ++ ++ /* every thing below needs to be a %d not a %u because we are ++ looking for -1 */ ++ xstrfmtcat(query, ++ "update \"%s_%s\" set mod_time=%ld, deleted = deleted + 2, " ++ "lft = lft + %d, rgt = rgt + %d " ++ "WHERE lft BETWEEN %d AND %d;", ++ cluster, assoc_table, now, diff, diff, *lft, *rgt); ++ ++ xstrfmtcat(query, ++ "UPDATE \"%s_%s\" SET mod_time=%ld, rgt = rgt + %d WHERE " ++ "rgt > %d AND deleted < 2;" ++ "UPDATE \"%s_%s\" SET mod_time=%ld, lft = lft + %d WHERE " ++ "lft > %d AND deleted < 2;", ++ cluster, assoc_table, now, width, ++ par_left, ++ cluster, assoc_table, now, width, ++ par_left); ++ ++ xstrfmtcat(query, ++ "UPDATE \"%s_%s\" SET mod_time=%ld, rgt = rgt - %d WHERE " ++ "(%d < 0 AND rgt > %d AND deleted < 2) " ++ "OR (%d > 0 AND rgt > %d);" ++ "UPDATE \"%s_%s\" SET mod_time=%ld, lft = lft - %d WHERE " ++ "(%d < 0 AND lft > %d AND deleted < 2) " ++ "OR (%d > 0 AND lft > %d);", ++ cluster, assoc_table, now, width, ++ diff, *rgt, ++ diff, *lft, ++ cluster, assoc_table, now, width, ++ diff, *rgt, ++ diff, *lft); ++ ++ xstrfmtcat(query, ++ "update \"%s_%s\" set mod_time=%ld, " ++ "deleted = deleted - 2 WHERE deleted > 1;", ++ cluster, assoc_table, now); ++ xstrfmtcat(query, ++ "update \"%s_%s\" set mod_time=%ld, " ++ "parent_acct='%s' where id_assoc = %s;", ++ cluster, assoc_table, now, parent, id); ++ /* get the new lft and rgt if changed */ ++ xstrfmtcat(query, ++ "select lft, rgt from \"%s_%s\" where id_assoc = %s", ++ cluster, assoc_table, id); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 1); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ if ((row = pgsql_fetch_row(result))) { ++ debug4("lft and rgt were %u %u and now is %s %s", ++ *lft, *rgt, row[0], row[1]); ++ *lft = slurm_atoul(row[0]); ++ *rgt = slurm_atoul(row[1]); ++ } ++ pgsql_free_result(&result); ++ ++ return rc; ++} ++ ++ ++/* This code will move an account from one parent to another. This ++ * should work either way in the tree. (i.e. move child to be parent ++ * of current parent, and parent to be child of child.) ++ */ ++static int _move_parent(pgsql_conn_t *pgsql_conn, uid_t uid, ++ uint32_t *lft, uint32_t *rgt, ++ char *cluster, ++ char *id, char *old_parent, char *new_parent, ++ time_t now) ++{ ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *query = NULL; ++ int rc = SLURM_SUCCESS; ++ ++ /* first we need to see if we are going to make a child of this ++ * account the new parent. If so we need to move that child to this ++ * accounts parent and then do the move. ++ */ ++ query = xstrdup_printf( ++ "select id_assoc, lft, rgt from \"%s_%s\" " ++ "where lft between %d and %d " ++ "AND acct='%s' AND \"user\"='' order by lft;", ++ cluster, assoc_table, *lft, *rgt, ++ new_parent); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ if ((row = pgsql_fetch_row(result))) { ++ uint32_t child_lft = slurm_atoul(row[1]), ++ child_rgt = slurm_atoul(row[2]); ++ ++ debug4("%s(%s) %s,%s is a child of %s", ++ new_parent, row[0], row[1], row[2], id); ++ rc = _move_account(pgsql_conn, &child_lft, &child_rgt, ++ cluster, row[0], old_parent, now); ++ } ++ ++ pgsql_free_result(&result); ++ ++ if (rc != SLURM_SUCCESS) ++ return rc; ++ ++ /* now move the one we wanted to move in the first place ++ * We need to get the new lft and rgts though since they may ++ * have changed. ++ */ ++ query = xstrdup_printf( ++ "select lft, rgt from \"%s_%s\" where id_assoc=%s;", ++ cluster, assoc_table, id); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ if ((row = pgsql_fetch_row(result))) { ++ *lft = slurm_atoul(row[0]); ++ *rgt = slurm_atoul(row[1]); ++ rc = _move_account(pgsql_conn, lft, rgt, ++ cluster, id, new_parent, now); ++ } else { ++ error("can't find parent? we were able to a second ago."); ++ rc = SLURM_ERROR; ++ } ++ pgsql_free_result(&result); ++ ++ return rc; ++} ++ ++static uint32_t _get_parent_id( ++ pgsql_conn_t *pgsql_conn, char *parent, char *cluster) ++{ ++ uint32_t parent_id = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *query = NULL; ++ ++ xassert(parent); ++ xassert(cluster); ++ ++ query = xstrdup_printf("select id_assoc from \"%s_%s\" where \"user\"='' " ++ "and deleted = 0 and acct='%s';", ++ cluster, assoc_table, parent); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 1); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return 0; ++ } ++ xfree(query); ++ ++ if ((row = pgsql_fetch_row(result))) { ++ if (row[0]) ++ parent_id = slurm_atoul(row[0]); ++ } else ++ error("no association for parent %s on cluster %s", ++ parent, cluster); ++ pgsql_free_result(&result); ++ ++ return parent_id; ++} ++ ++static int _set_assoc_lft_rgt( ++ pgsql_conn_t *pgsql_conn, slurmdb_assoc_rec_t *assoc) ++{ ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *query = NULL; ++ int rc = SLURM_ERROR; ++ ++ xassert(assoc->cluster); ++ xassert(assoc->id); ++ ++ query = xstrdup_printf("select lft, rgt from \"%s_%s\" " ++ "where id_assoc=%u;", ++ assoc->cluster, assoc_table, assoc->id); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 1); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return 0; ++ } ++ xfree(query); ++ ++ if ((row = pgsql_fetch_row(result))) { ++ if (row[0]) ++ assoc->lft = slurm_atoul(row[0]); ++ if (row[1]) ++ assoc->rgt = slurm_atoul(row[1]); ++ rc = SLURM_SUCCESS; ++ } else ++ error("no association (%u)", assoc->id); ++ pgsql_free_result(&result); ++ ++ return rc; ++} ++ ++static int _set_assoc_limits_for_add( ++ pgsql_conn_t *pgsql_conn, slurmdb_assoc_rec_t *assoc) ++{ ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *query = NULL; ++ char *parent = NULL; ++ char *qos_delta = NULL; ++ uint32_t tres_str_flags = TRES_STR_FLAG_REMOVE; ++ ++ xassert(assoc); ++ ++ if (assoc->parent_acct) ++ parent = assoc->parent_acct; ++ else if (assoc->user) ++ parent = assoc->acct; ++ else ++ return SLURM_SUCCESS; ++ ++ query = xstrdup_printf("call get_parent_limits('%s', " ++ "'%s', '%s', %u); %s", ++ assoc_table, parent, assoc->cluster, 0, ++ get_parent_limits_select); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 1); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ if (!(row = pgsql_fetch_row(result))) ++ goto end_it; ++ ++ if (row[ASSOC2_REQ_DEF_QOS] && assoc->def_qos_id == INFINITE) ++ assoc->def_qos_id = slurm_atoul(row[ASSOC2_REQ_DEF_QOS]); ++ else if (assoc->def_qos_id == INFINITE) ++ assoc->def_qos_id = 0; ++ ++ if (row[ASSOC2_REQ_MJ] && assoc->max_jobs == INFINITE) ++ assoc->max_jobs = slurm_atoul(row[ASSOC2_REQ_MJ]); ++ if (row[ASSOC2_REQ_MJA] && assoc->max_jobs_accrue == INFINITE) ++ assoc->max_jobs_accrue = slurm_atoul(row[ASSOC2_REQ_MJA]); ++ if (row[ASSOC2_REQ_MPT] && assoc->min_prio_thresh == INFINITE) ++ assoc->min_prio_thresh = slurm_atoul(row[ASSOC2_REQ_MPT]); ++ if (row[ASSOC2_REQ_MSJ] && assoc->max_submit_jobs == INFINITE) ++ assoc->max_submit_jobs = slurm_atoul(row[ASSOC2_REQ_MSJ]); ++ if (row[ASSOC2_REQ_MWPJ] && assoc->max_wall_pj == INFINITE) ++ assoc->max_wall_pj = slurm_atoul(row[ASSOC2_REQ_MWPJ]); ++ if (row[ASSOC2_REQ_PRIO] && assoc->priority == INFINITE) ++ assoc->priority = slurm_atoul(row[ASSOC2_REQ_PRIO]); ++ ++ /* For the tres limits we just concatted the limits going up ++ * the hierarchy slurmdb_tres_list_from_string will just skip ++ * over any reoccuring limit to give us the first one per ++ * TRES. ++ */ ++ slurmdb_combine_tres_strings( ++ &assoc->max_tres_pj, row[ASSOC2_REQ_MTPJ], ++ tres_str_flags); ++ slurmdb_combine_tres_strings( ++ &assoc->max_tres_pn, row[ASSOC2_REQ_MTPN], ++ tres_str_flags); ++ slurmdb_combine_tres_strings( ++ &assoc->max_tres_mins_pj, row[ASSOC2_REQ_MTMPJ], ++ tres_str_flags); ++ slurmdb_combine_tres_strings( ++ &assoc->max_tres_run_mins, row[ASSOC2_REQ_MTRM], ++ tres_str_flags); ++ ++ if (assoc->qos_list) { ++ int set = 0; ++ char *tmp_char = NULL; ++ ListIterator qos_itr = list_iterator_create(assoc->qos_list); ++ while ((tmp_char = list_next(qos_itr))) { ++ /* we don't want to include blank names */ ++ if (!tmp_char[0]) ++ continue; ++ ++ if (!set) { ++ if (tmp_char[0] != '+' && tmp_char[0] != '-') ++ break; ++ set = 1; ++ } ++ xstrfmtcat(qos_delta, ",%s", tmp_char); ++ } ++ list_iterator_destroy(qos_itr); ++ ++ if (tmp_char) { ++ /* we have the qos here nothing from parents ++ needed */ ++ goto end_it; ++ } ++ list_flush(assoc->qos_list); ++ } else ++ assoc->qos_list = list_create(xfree_ptr); ++ ++ if (row[ASSOC2_REQ_QOS][0]) ++ slurm_addto_char_list(assoc->qos_list, row[ASSOC2_REQ_QOS]+1); ++ ++ if (row[ASSOC2_REQ_DELTA_QOS][0]) ++ slurm_addto_char_list(assoc->qos_list, ++ row[ASSOC2_REQ_DELTA_QOS]+1); ++ if (qos_delta) { ++ slurm_addto_char_list(assoc->qos_list, qos_delta+1); ++ xfree(qos_delta); ++ } ++ ++end_it: ++ pgsql_free_result(&result); ++ ++ return SLURM_SUCCESS; ++} ++ ++/* Used to get all the users inside a lft and rgt set. This is just ++ * to send the user all the associations that are being modified from ++ * a previous change to it's parent. ++ */ ++static int _modify_unset_users(pgsql_conn_t *pgsql_conn, ++ slurmdb_assoc_rec_t *assoc, ++ char *acct, ++ uint32_t lft, uint32_t rgt, ++ List ret_list, int moved_parent) ++{ ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *query = NULL, *object = NULL; ++ int i; ++ uint32_t tres_str_flags = TRES_STR_FLAG_REMOVE | TRES_STR_FLAG_NO_NULL; ++ ++ char *assoc_inx[] = { ++ "id_assoc", ++ "user", ++ "acct", ++ "`partition`", ++ "max_jobs", ++ "max_jobs_accrue", ++ "min_prio_thresh", ++ "max_submit_jobs", ++ "max_tres_pj", ++ "max_tres_pn", ++ "max_wall_pj", ++ "max_tres_mins_pj", ++ "max_tres_run_mins", ++ "priority", ++ "def_qos_id", ++ "qos", ++ "delta_qos", ++ "lft", ++ "rgt" ++ }; ++ ++ enum { ++ ASSOC_ID, ++ ASSOC_USER, ++ ASSOC_ACCT, ++ ASSOC_PART, ++ ASSOC_MJ, ++ ASSOC_MJA, ++ ASSOC_MPT, ++ ASSOC_MSJ, ++ ASSOC_MTPJ, ++ ASSOC_MTPN, ++ ASSOC_MWPJ, ++ ASSOC_MTMPJ, ++ ASSOC_MTRM, ++ ASSOC_PRIO, ++ ASSOC_DEF_QOS, ++ ASSOC_QOS, ++ ASSOC_DELTA_QOS, ++ ASSOC_LFT, ++ ASSOC_RGT, ++ ASSOC_COUNT ++ }; ++ ++ xassert(assoc); ++ xassert(assoc->cluster); ++ ++ if (!ret_list || !acct) ++ return SLURM_ERROR; ++ ++ xstrcat(object, assoc_inx[0]); ++ for(i=1; icluster, assoc_table, ++ lft, rgt, acct, acct); ++ xfree(object); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_assoc_rec_t *mod_assoc = NULL; ++ int modified = 0; ++ char *tmp_char = NULL; ++ ++ mod_assoc = xmalloc(sizeof(slurmdb_assoc_rec_t)); ++ slurmdb_init_assoc_rec(mod_assoc, 0); ++ mod_assoc->id = slurm_atoul(row[ASSOC_ID]); ++ mod_assoc->cluster = xstrdup(assoc->cluster); ++ ++ if (!row[ASSOC_DEF_QOS] && assoc->def_qos_id != NO_VAL) { ++ mod_assoc->def_qos_id = assoc->def_qos_id; ++ modified = 1; ++ } ++ ++ if (!row[ASSOC_MJ] && assoc->max_jobs != NO_VAL) { ++ mod_assoc->max_jobs = assoc->max_jobs; ++ modified = 1; ++ } ++ ++ if (!row[ASSOC_MJA] && assoc->max_jobs_accrue != NO_VAL) { ++ mod_assoc->max_jobs_accrue = assoc->max_jobs_accrue; ++ modified = 1; ++ } ++ ++ if (!row[ASSOC_MPT] && assoc->min_prio_thresh != NO_VAL) { ++ mod_assoc->min_prio_thresh = assoc->min_prio_thresh; ++ modified = 1; ++ } ++ ++ if (!row[ASSOC_MSJ] && assoc->max_submit_jobs != NO_VAL) { ++ mod_assoc->max_submit_jobs = assoc->max_submit_jobs; ++ modified = 1; ++ } ++ ++ if (!row[ASSOC_MWPJ] && assoc->max_wall_pj != NO_VAL) { ++ mod_assoc->max_wall_pj = assoc->max_wall_pj; ++ modified = 1; ++ } ++ ++ if (!row[ASSOC_PRIO] && assoc->priority != NO_VAL) { ++ mod_assoc->priority = assoc->priority; ++ modified = 1; ++ } ++ ++ if (assoc->max_tres_pj) { ++ tmp_char = xstrdup(row[ASSOC_MTPJ]); ++ slurmdb_combine_tres_strings( ++ &tmp_char, assoc->max_tres_pj, ++ tres_str_flags); ++ mod_assoc->max_tres_pj = tmp_char; ++ tmp_char = NULL; ++ modified = 1; ++ } ++ ++ if (assoc->max_tres_pn) { ++ tmp_char = xstrdup(row[ASSOC_MTPN]); ++ slurmdb_combine_tres_strings( ++ &tmp_char, assoc->max_tres_pn, ++ tres_str_flags); ++ mod_assoc->max_tres_pn = tmp_char; ++ tmp_char = NULL; ++ modified = 1; ++ } ++ ++ if (assoc->max_tres_mins_pj) { ++ tmp_char = xstrdup(row[ASSOC_MTMPJ]); ++ slurmdb_combine_tres_strings( ++ &tmp_char, assoc->max_tres_mins_pj, ++ tres_str_flags); ++ mod_assoc->max_tres_mins_pj = tmp_char; ++ tmp_char = NULL; ++ modified = 1; ++ } ++ ++ if (assoc->max_tres_run_mins) { ++ tmp_char = xstrdup(row[ASSOC_MTRM]); ++ slurmdb_combine_tres_strings( ++ &tmp_char, assoc->max_tres_run_mins, ++ tres_str_flags); ++ mod_assoc->max_tres_run_mins = tmp_char; ++ tmp_char = NULL; ++ modified = 1; ++ } ++ if (!row[ASSOC_QOS][0] && assoc->qos_list) { ++ List delta_qos_list = NULL; ++ char *qos_char = NULL, *delta_char = NULL; ++ ListIterator delta_itr = NULL; ++ ListIterator qos_itr = ++ list_iterator_create(assoc->qos_list); ++ if (row[ASSOC_DELTA_QOS][0]) { ++ delta_qos_list = list_create(xfree_ptr); ++ slurm_addto_char_list(delta_qos_list, ++ row[ASSOC_DELTA_QOS]+1); ++ delta_itr = ++ list_iterator_create(delta_qos_list); ++ } ++ ++ mod_assoc->qos_list = list_create(xfree_ptr); ++ /* here we are making sure a child does not ++ have the qos added or removed before we add ++ it to the parent. ++ */ ++ while ((qos_char = list_next(qos_itr))) { ++ if (delta_itr && qos_char[0] != '=') { ++ while ((delta_char = ++ list_next(delta_itr))) { ++ ++ if ((qos_char[0] ++ != delta_char[0]) ++ && (!xstrcmp(qos_char+1, ++ delta_char+1))) ++ break; ++ } ++ list_iterator_reset(delta_itr); ++ if (delta_char) ++ continue; ++ } ++ list_append(mod_assoc->qos_list, ++ xstrdup(qos_char)); ++ } ++ list_iterator_destroy(qos_itr); ++ if (delta_itr) ++ list_iterator_destroy(delta_itr); ++ FREE_NULL_LIST(delta_qos_list); ++ if (list_count(mod_assoc->qos_list) ++ || !list_count(assoc->qos_list)) ++ modified = 1; ++ else { ++ FREE_NULL_LIST(mod_assoc->qos_list); ++ mod_assoc->qos_list = NULL; ++ } ++ } ++ ++ /* We only want to add those that are modified here */ ++ if (modified) { ++ /* Since we aren't really changing this non ++ * user association we don't want to send it. ++ */ ++ if (!row[ASSOC_USER][0]) { ++ /* This is a sub account so run it ++ * through as if it is a parent. ++ */ ++ _modify_unset_users(pgsql_conn, ++ mod_assoc, ++ row[ASSOC_ACCT], ++ slurm_atoul(row[ASSOC_LFT]), ++ slurm_atoul(row[ASSOC_RGT]), ++ ret_list, moved_parent); ++ slurmdb_destroy_assoc_rec(mod_assoc); ++ continue; ++ } ++ /* We do want to send all user accounts though */ ++ mod_assoc->shares_raw = NO_VAL; ++ if (row[ASSOC_PART][0]) { ++ // see if there is a partition name ++ object = xstrdup_printf( ++ "C = %-10s A = %-20s U = %-9s P = %s", ++ assoc->cluster, row[ASSOC_ACCT], ++ row[ASSOC_USER], row[ASSOC_PART]); ++ } else { ++ object = xstrdup_printf( ++ "C = %-10s A = %-20s U = %-9s", ++ assoc->cluster, ++ row[ASSOC_ACCT], ++ row[ASSOC_USER]); ++ } ++ ++ list_append(ret_list, object); ++ object = NULL; ++ ++ if (moved_parent) ++ slurmdb_destroy_assoc_rec(mod_assoc); ++ else ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_MODIFY_ASSOC, ++ mod_assoc) ++ != SLURM_SUCCESS) { ++ slurmdb_destroy_assoc_rec( ++ mod_assoc); ++ error("couldn't add to " ++ "the update list"); ++ } ++ } else ++ slurmdb_destroy_assoc_rec(mod_assoc); ++ ++ } ++ pgsql_free_result(&result); ++ ++ return SLURM_SUCCESS; ++} ++ ++/* when doing a select on this all the select should have a prefix of ++ * t1. Returns "where" clause which needs to be xfreed. */ ++static char *_setup_assoc_cond_qos(slurmdb_assoc_cond_t *assoc_cond, ++ char *cluster_name) ++{ ++ int set = 0; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ char *prefix = "t1"; ++ char *extra = NULL; ++ ++ /* Since this gets put in an SQL query we don't want it to be ++ * NULL since it would print (null) instead of nothing. ++ */ ++ if (!assoc_cond) ++ return xstrdup(""); ++ ++ /* we need to check this first so we can update the ++ with_sub_accts if needed since this the qos_list is a ++ parent thing ++ */ ++ if (assoc_cond->qos_list && list_count(assoc_cond->qos_list)) { ++ /* we have to do the same thing as with_sub_accts does ++ first since we are looking for something that is ++ really most likely a parent thing */ ++ assoc_cond->with_sub_accts = 1; ++ prefix = "t2"; ++ xstrfmtcat(extra, ", \"%s_%s\" as t2 where " ++ "(t1.lft between t2.lft and t2.rgt) AND (", ++ cluster_name, assoc_table); ++ set = 0; ++ itr = list_iterator_create(assoc_cond->qos_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, ++ "(%s.qos like '%%,%s' " ++ "OR %s.qos like '%%,%s,%%' " ++ "OR %s.delta_qos like '%%,+%s' " ++ "OR %s.delta_qos like '%%,+%s,%%')", ++ prefix, object, prefix, object, ++ prefix, object, prefix, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ") AND"); ++ } else if (assoc_cond->with_sub_accts) { ++ xstrfmtcat(extra, ", \"%s_%s\" as t2 where " ++ "(t1.lft between t2.lft and t2.rgt) AND", ++ cluster_name, assoc_table); ++ } else ++ xstrcat(extra, " where"); ++ return extra; ++} ++ ++/* When doing a select on this all the select should have a prefix of t1. */ ++static int _setup_assoc_cond_limits( ++ slurmdb_assoc_cond_t *assoc_cond, ++ const char *prefix, char **extra) ++{ ++ int set = 0; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ ++ if (!assoc_cond) ++ return 0; ++ ++ /* ++ * Don't use prefix here, always use t1 or we could get extra "deleted" ++ * entries we don't want. ++ */ ++ if (assoc_cond->with_deleted) ++ xstrfmtcat(*extra, " (t1.deleted=0 OR t1.deleted=1)"); ++ else ++ xstrfmtcat(*extra, " t1.deleted=0"); ++ ++ if (assoc_cond->only_defs) { ++ set = 1; ++ xstrfmtcat(*extra, " AND (%s.is_def=1)", prefix); ++ } ++ ++ if (assoc_cond->acct_list && list_count(assoc_cond->acct_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(assoc_cond->acct_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "%s.acct='%s'", prefix, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (assoc_cond->def_qos_id_list ++ && list_count(assoc_cond->def_qos_id_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(assoc_cond->def_qos_id_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "%s.def_qos_id='%s'", ++ prefix, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (assoc_cond->user_list && list_count(assoc_cond->user_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(assoc_cond->user_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "%s.\"user\"='%s'", prefix, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } else if (assoc_cond->user_list) { ++ /* we want all the users, but no non-user associations */ ++ set = 1; ++ xstrfmtcat(*extra, " AND (%s.\"user\"!='')", prefix); ++ } ++ ++ if (assoc_cond->partition_list ++ && list_count(assoc_cond->partition_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(assoc_cond->partition_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "%s.partition='%s'", ++ prefix, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (assoc_cond->id_list && list_count(assoc_cond->id_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(assoc_cond->id_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "%s.id_assoc=%s", prefix, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (assoc_cond->parent_acct_list ++ && list_count(assoc_cond->parent_acct_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(assoc_cond->parent_acct_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "%s.parent_acct='%s'", ++ prefix, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ return set; ++} ++ ++static int _process_modify_assoc_results(pgsql_conn_t *pgsql_conn, ++ pgsql_res_t *result, ++ slurmdb_assoc_rec_t *assoc, ++ slurmdb_user_rec_t *user, ++ char *cluster_name, char *sent_vals, ++ bool is_admin, bool same_user, ++ List ret_list) ++{ ++ ListIterator itr = NULL; ++ pgsql_row row; ++ int added = 0; ++ int rc = SLURM_SUCCESS; ++ int set_qos_vals = 0; ++ int moved_parent = 0; ++ char *query = NULL, *vals = NULL, *object = NULL, *name_char = NULL; ++ char *reset_query = NULL; ++ time_t now = time(NULL); ++ ++ xassert(result); ++ ++ if (!pgsql_num_rows(result)) ++ return SLURM_SUCCESS; ++ ++ vals = xstrdup(sent_vals); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ pgsql_res_t *result2 = NULL; ++ slurmdb_assoc_rec_t *mod_assoc = NULL, alt_assoc; ++ int account_type=0; ++ /* If parent changes these also could change ++ so we need to keep track of the latest ++ ones. ++ */ ++ uint32_t lft = slurm_atoul(row[MASSOC_LFT]); ++ uint32_t rgt = slurm_atoul(row[MASSOC_RGT]); ++ char *orig_acct, *account; ++ ++ orig_acct = account = row[MASSOC_ACCT]; ++ ++ slurmdb_init_assoc_rec(&alt_assoc, 0); ++ ++ /* Here we want to see if the person ++ * is a coord of the parent account ++ * since we don't want him to be able ++ * to alter the limits of the account ++ * he is directly coord of. They ++ * should be able to alter the ++ * sub-accounts though. If no parent account ++ * that means we are talking about a user ++ * association so account is really the parent ++ * of the user a coord can change that all day long. ++ */ ++ if (row[MASSOC_PACCT][0]) ++ account = row[MASSOC_PACCT]; ++ ++ /* If this is the same user all has been done ++ previously to make sure the user is only changing ++ things they are allowed to change. ++ */ ++ if (!is_admin && !same_user) { ++ slurmdb_coord_rec_t *coord = NULL; ++ ++ if (!user->coord_accts) { // This should never ++ // happen ++ error("We are here with no coord accts."); ++ rc = ESLURM_ACCESS_DENIED; ++ goto end_it; ++ } ++ itr = list_iterator_create(user->coord_accts); ++ while ((coord = list_next(itr))) { ++ if (!xstrcasecmp(coord->name, account)) ++ break; ++ } ++ list_iterator_destroy(itr); ++ ++ if (!coord) { ++ if (row[MASSOC_PACCT][0]) ++ error("User %s(%d) can not modify " ++ "account (%s) because they " ++ "are not coordinators of " ++ "parent account '%s'.", ++ user->name, user->uid, ++ row[MASSOC_ACCT], ++ row[MASSOC_PACCT]); ++ else ++ error("User %s(%d) does not have the " ++ "ability to modify the account " ++ "(%s).", ++ user->name, user->uid, ++ row[MASSOC_ACCT]); ++ ++ rc = ESLURM_ACCESS_DENIED; ++ goto end_it; ++ } else if (_check_coord_qos(pgsql_conn, cluster_name, ++ account, user->name, ++ assoc->qos_list) ++ == SLURM_ERROR) { ++ assoc_mgr_lock_t locks = { ++ NO_LOCK, NO_LOCK, READ_LOCK, NO_LOCK, ++ NO_LOCK, NO_LOCK, NO_LOCK }; ++ char *requested_qos; ++ ++ assoc_mgr_lock(&locks); ++ requested_qos = get_qos_complete_str( ++ assoc_mgr_qos_list, assoc->qos_list); ++ assoc_mgr_unlock(&locks); ++ error("Coordinator %s(%d) does not have the " ++ "access to all the qos requested (%s), " ++ "so they can't modify account " ++ "%s with it.", ++ user->name, user->uid, requested_qos, ++ account); ++ xfree(requested_qos); ++ rc = ESLURM_ACCESS_DENIED; ++ goto end_it; ++ } ++ } ++ ++ if (row[MASSOC_PART][0]) { ++ // see if there is a partition name ++ object = xstrdup_printf( ++ "C = %-10s A = %-20s U = %-9s P = %s", ++ cluster_name, row[MASSOC_ACCT], ++ row[MASSOC_USER], row[MASSOC_PART]); ++ } else if (row[MASSOC_USER][0]){ ++ object = xstrdup_printf( ++ "C = %-10s A = %-20s U = %-9s", ++ cluster_name, row[MASSOC_ACCT], ++ row[MASSOC_USER]); ++ } else { ++ if (assoc->parent_acct) { ++ if (!xstrcasecmp(row[MASSOC_ACCT], ++ assoc->parent_acct)) { ++ error("You can't make an account be a " ++ "child of it's self"); ++ continue; ++ } ++ rc = _move_parent(pgsql_conn, user->uid, ++ &lft, &rgt, ++ cluster_name, ++ row[MASSOC_ID], ++ row[MASSOC_PACCT], ++ assoc->parent_acct, ++ now); ++ ++ if ((rc == ESLURM_INVALID_PARENT_ACCOUNT) ++ || (rc == ESLURM_SAME_PARENT_ACCOUNT)) { ++ continue; ++ } else if (rc != SLURM_SUCCESS) ++ break; ++ ++ moved_parent = 1; ++ } ++ if (row[MASSOC_PACCT][0]) { ++ object = xstrdup_printf( ++ "C = %-10s A = %s of %s", ++ cluster_name, row[MASSOC_ACCT], ++ row[MASSOC_PACCT]); ++ } else { ++ object = xstrdup_printf( ++ "C = %-10s A = %s", ++ cluster_name, row[MASSOC_ACCT]); ++ } ++ account_type = 1; ++ } ++ list_append(ret_list, object); ++ object = NULL; ++ added++; ++ ++ if (name_char) ++ xstrfmtcat(name_char, " OR id_assoc=%s", ++ row[MASSOC_ID]); ++ else ++ xstrfmtcat(name_char, "(id_assoc=%s", row[MASSOC_ID]); ++ ++ /* Only do this when not dealing with the root association. */ ++ if (xstrcmp(orig_acct, "root") || row[MASSOC_USER][0]) { ++ pgsql_row row2; ++ /* If there is a variable cleared here we need to make ++ sure we get the parent's information, if any. */ ++ query = xstrdup_printf( ++ "call get_parent_limits('%s', " ++ "'%s', '%s', %u); %s", ++ assoc_table, account, ++ cluster_name, 0, ++ get_parent_limits_select); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ result2 = pgsql_db_query_ret(pgsql_conn, query, 1); ++ if(!pgsql_db_match_state(pgsql_errno(result2), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result2); ++ break; ++ } ++ xfree(query); ++ ++ if ((row2 = pgsql_fetch_row(result2))) { ++ if (assoc->def_qos_id == INFINITE ++ && row2[ASSOC2_REQ_DEF_QOS]) ++ alt_assoc.def_qos_id = slurm_atoul( ++ row2[ASSOC2_REQ_DEF_QOS]); ++ ++ if ((assoc->max_jobs == INFINITE) ++ && row2[ASSOC2_REQ_MJ]) ++ alt_assoc.max_jobs = slurm_atoul( ++ row2[ASSOC2_REQ_MJ]); ++ if ((assoc->max_jobs_accrue == INFINITE) ++ && row2[ASSOC2_REQ_MJA]) ++ alt_assoc.max_jobs_accrue = slurm_atoul( ++ row2[ASSOC2_REQ_MJA]); ++ if ((assoc->min_prio_thresh == INFINITE) ++ && row2[ASSOC2_REQ_MPT]) ++ alt_assoc.min_prio_thresh = slurm_atoul( ++ row2[ASSOC2_REQ_MPT]); ++ if ((assoc->max_submit_jobs == INFINITE) ++ && row2[ASSOC2_REQ_MSJ]) ++ alt_assoc.max_submit_jobs = slurm_atoul( ++ row2[ASSOC2_REQ_MSJ]); ++ if ((assoc->max_wall_pj == INFINITE) ++ && row2[ASSOC2_REQ_MWPJ]) ++ alt_assoc.max_wall_pj = slurm_atoul( ++ row2[ASSOC2_REQ_MWPJ]); ++ if ((assoc->priority == INFINITE) ++ && row2[ASSOC2_REQ_PRIO]) ++ alt_assoc.priority = slurm_atoul( ++ row2[ASSOC2_REQ_PRIO]); ++ ++ /* We don't have to copy these strings ++ * or check for there existance, ++ * slurmdb_combine_tres_strings will ++ * do this for us below. ++ */ ++ if (row2[ASSOC2_REQ_MTPJ][0]) ++ alt_assoc.max_tres_pj = ++ row2[ASSOC2_REQ_MTPJ]; ++ if (row2[ASSOC2_REQ_MTPN][0]) ++ alt_assoc.max_tres_pn = ++ row2[ASSOC2_REQ_MTPN]; ++ if (row2[ASSOC2_REQ_MTMPJ][0]) ++ alt_assoc.max_tres_mins_pj = ++ row2[ASSOC2_REQ_MTMPJ]; ++ if (row2[ASSOC2_REQ_MTRM][0]) ++ alt_assoc.max_tres_run_mins = ++ row2[ASSOC2_REQ_MTRM]; ++ } ++ } ++ mod_assoc = xmalloc(sizeof(slurmdb_assoc_rec_t)); ++ slurmdb_init_assoc_rec(mod_assoc, 0); ++ mod_assoc->id = slurm_atoul(row[MASSOC_ID]); ++ mod_assoc->cluster = xstrdup(cluster_name); ++ ++ if (alt_assoc.def_qos_id != NO_VAL) ++ mod_assoc->def_qos_id = alt_assoc.def_qos_id; ++ else ++ mod_assoc->def_qos_id = assoc->def_qos_id; ++ ++ mod_assoc->is_def = assoc->is_def; ++ ++ mod_assoc->shares_raw = assoc->shares_raw; ++ ++ mod_tres_str(&mod_assoc->grp_tres, ++ assoc->grp_tres, row[MASSOC_GT], ++ NULL, "grp_tres", &vals, mod_assoc->id, 1); ++ mod_tres_str(&mod_assoc->grp_tres_mins, ++ assoc->grp_tres_mins, row[MASSOC_GTM], ++ NULL, "grp_tres_mins", &vals, mod_assoc->id, 1); ++ mod_tres_str(&mod_assoc->grp_tres_run_mins, ++ assoc->grp_tres_run_mins, row[MASSOC_GTRM], ++ NULL, "grp_tres_run_mins", &vals, ++ mod_assoc->id, 1); ++ ++ mod_assoc->grp_jobs = assoc->grp_jobs; ++ mod_assoc->grp_jobs_accrue = assoc->grp_jobs_accrue; ++ mod_assoc->grp_submit_jobs = assoc->grp_submit_jobs; ++ mod_assoc->grp_wall = assoc->grp_wall; ++ ++ mod_tres_str(&mod_assoc->max_tres_pj, ++ assoc->max_tres_pj, row[MASSOC_MTPJ], ++ alt_assoc.max_tres_pj, "max_tres_pj", ++ &vals, mod_assoc->id, 1); ++ mod_tres_str(&mod_assoc->max_tres_pn, ++ assoc->max_tres_pn, row[MASSOC_MTPN], ++ alt_assoc.max_tres_pn, "max_tres_pn", ++ &vals, mod_assoc->id, 1); ++ mod_tres_str(&mod_assoc->max_tres_mins_pj, ++ assoc->max_tres_mins_pj, row[MASSOC_MTMPJ], ++ alt_assoc.max_tres_mins_pj, "max_tres_mins_pj", ++ &vals, mod_assoc->id, 1); ++ mod_tres_str(&mod_assoc->max_tres_run_mins, ++ assoc->max_tres_run_mins, row[MASSOC_MTRM], ++ alt_assoc.max_tres_run_mins, "max_tres_run_mins", ++ &vals, mod_assoc->id, 1); ++ ++ if (result2) ++ pgsql_free_result(&result2); ++ ++ if (alt_assoc.max_jobs != NO_VAL) ++ mod_assoc->max_jobs = alt_assoc.max_jobs; ++ else ++ mod_assoc->max_jobs = assoc->max_jobs; ++ if (alt_assoc.max_jobs_accrue != NO_VAL) ++ mod_assoc->max_jobs_accrue = alt_assoc.max_jobs_accrue; ++ else ++ mod_assoc->max_jobs_accrue = assoc->max_jobs_accrue; ++ if (alt_assoc.min_prio_thresh != NO_VAL) ++ mod_assoc->min_prio_thresh = alt_assoc.min_prio_thresh; ++ else ++ mod_assoc->min_prio_thresh = assoc->min_prio_thresh; ++ if (alt_assoc.max_submit_jobs != NO_VAL) ++ mod_assoc->max_submit_jobs = alt_assoc.max_submit_jobs; ++ else ++ mod_assoc->max_submit_jobs = assoc->max_submit_jobs; ++ if (alt_assoc.max_wall_pj != NO_VAL) ++ mod_assoc->max_wall_pj = alt_assoc.max_wall_pj; ++ else ++ mod_assoc->max_wall_pj = assoc->max_wall_pj; ++ if (alt_assoc.priority != NO_VAL) ++ mod_assoc->priority = alt_assoc.priority; ++ else ++ mod_assoc->priority = assoc->priority; ++ ++ /* no need to get the parent id since if we moved ++ * parent id's we will get it when we send the total list */ ++ ++ if (!row[MASSOC_USER][0]) ++ mod_assoc->parent_acct = xstrdup(assoc->parent_acct); ++ if (assoc->qos_list && list_count(assoc->qos_list)) { ++ ListIterator new_qos_itr = ++ list_iterator_create(assoc->qos_list); ++ char *new_qos = NULL, *tmp_qos = NULL; ++ bool adding_straight = 0; ++ ++ mod_assoc->qos_list = list_create(xfree_ptr); ++ ++ while ((new_qos = list_next(new_qos_itr))) { ++ if (new_qos[0] == '-' || new_qos[0] == '+') { ++ list_append(mod_assoc->qos_list, ++ xstrdup(new_qos)); ++ } else if (new_qos[0]) { ++ list_append(mod_assoc->qos_list, ++ xstrdup_printf("=%s", ++ new_qos)); ++ } ++ ++ if (set_qos_vals) ++ continue; ++ /* Now we can set up the values and ++ make sure we aren't over writing ++ things that are really from the ++ parent ++ */ ++ if (new_qos[0] == '-') { ++ xstrfmtcat(vals, ++ ", qos=if (qos='', '', " ++ "replace(replace(" ++ "qos, ',%s,', ','), " ++ "',,', ','))" ++ ", qos=if (qos=',', '', qos)" ++ ", delta_qos=if (qos='', " ++ "replace(concat(replace(" ++ "replace(" ++ "delta_qos, ',+%s,', ','), " ++ "',-%s,', ','), " ++ "',%s,'), ',,', ','), '')", ++ new_qos+1, new_qos+1, ++ new_qos+1, new_qos); ++ } else if (new_qos[0] == '+') { ++ xstrfmtcat(vals, ++ ", qos=if (qos='', '', " ++ "replace(concat(" ++ "replace(qos, ',%s,', ','), " ++ "',%s,'), ',,', ',')), " ++ "delta_qos=if (" ++ "qos='', replace(concat(" ++ "replace(replace(" ++ "delta_qos, ',+%s,', ','), " ++ "',-%s,', ','), " ++ "',%s,'), ',,', ','), '')", ++ new_qos+1, new_qos+1, ++ new_qos+1, new_qos+1, ++ new_qos); ++ } else if (new_qos[0]) { ++ xstrfmtcat(tmp_qos, ",%s", new_qos); ++ adding_straight = 1; ++ } else ++ xstrcat(tmp_qos, ""); ++ ++ } ++ list_iterator_destroy(new_qos_itr); ++ ++ if (!set_qos_vals && tmp_qos) { ++ xstrfmtcat(vals, ", qos='%s%s', delta_qos=''", ++ tmp_qos, adding_straight ? "," : ""); ++ } ++ xfree(tmp_qos); ++ ++ set_qos_vals = 1; ++ } ++ ++ ++ if (account_type) { ++ _modify_unset_users(pgsql_conn, ++ mod_assoc, ++ row[MASSOC_ACCT], ++ lft, rgt, ++ ret_list, ++ moved_parent); ++ } else if ((assoc->is_def == 1) && row[MASSOC_USER][0]) { ++ /* Use fresh one here so we don't have to ++ worry about dealing with bad values. ++ */ ++ slurmdb_assoc_rec_t tmp_assoc; ++ slurmdb_init_assoc_rec(&tmp_assoc, 0); ++ tmp_assoc.is_def = 1; ++ tmp_assoc.cluster = cluster_name; ++ tmp_assoc.acct = row[MASSOC_ACCT]; ++ tmp_assoc.user = row[MASSOC_USER]; ++ if ((rc = _reset_default_assoc( ++ pgsql_conn, &tmp_assoc, &reset_query, ++ moved_parent ? 0 : 1)) ++ != SLURM_SUCCESS) { ++ slurmdb_destroy_assoc_rec(mod_assoc); ++ xfree(reset_query); ++ goto end_it; ++ } ++ } ++ ++ if (!vals || !vals[0] || moved_parent) ++ slurmdb_destroy_assoc_rec(mod_assoc); ++ else if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_MODIFY_ASSOC, ++ mod_assoc) != SLURM_SUCCESS) { ++ error("couldn't add to the update list"); ++ slurmdb_destroy_assoc_rec(mod_assoc); ++ } ++ } ++ ++ xstrcat(name_char, ")"); ++ ++ if (assoc->parent_acct) { ++ if (((rc == ESLURM_INVALID_PARENT_ACCOUNT) ++ || (rc == ESLURM_SAME_PARENT_ACCOUNT)) ++ && added) ++ rc = SLURM_SUCCESS; ++ } ++ ++ if (rc != SLURM_SUCCESS) ++ goto end_it; ++ ++ if (vals && vals[0]) { ++ char *user_name = uid_to_string((uid_t) user->uid); ++ rc = modify_common(pgsql_conn, DBD_MODIFY_ASSOCS, now, ++ user_name, assoc_table, name_char, vals, ++ cluster_name); ++ xfree(user_name); ++ if (rc == SLURM_ERROR) { ++ error("Couldn't modify associations"); ++ goto end_it; ++ } ++ } ++ ++ if (moved_parent) { ++ List local_assoc_list = NULL; ++ slurmdb_assoc_cond_t local_assoc_cond; ++ /* now we need to send the update of the new parents and ++ * limits, so just to be safe, send the whole ++ * tree because we could have some limits that ++ * were affected but not noticed. ++ */ ++ /* we can probably just look at the mod time now but ++ * we will have to wait for the next revision number ++ * since you can't query on mod time here and I don't ++ * want to rewrite code to make it happen ++ */ ++ ++ memset(&local_assoc_cond, 0, ++ sizeof(slurmdb_assoc_cond_t)); ++ local_assoc_cond.cluster_list = list_create(NULL); ++ list_append(local_assoc_cond.cluster_list, cluster_name); ++ local_assoc_list = as_pgsql_get_assocs( ++ pgsql_conn, user->uid, &local_assoc_cond); ++ FREE_NULL_LIST(local_assoc_cond.cluster_list); ++ if (!local_assoc_list) ++ goto end_it; ++ ++ ++ _move_assoc_list_to_update_list(pgsql_conn->update_list, ++ local_assoc_list); ++ FREE_NULL_LIST(local_assoc_list); ++ } ++ ++ if (reset_query) { ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", reset_query); ++ if ((rc = pgsql_db_query(pgsql_conn, reset_query)) ++ != SLURM_SUCCESS) ++ error("Couldn't update defaults"); ++ } ++end_it: ++ xfree(reset_query); ++ xfree(name_char); ++ xfree(vals); ++ ++ return rc; ++} ++ ++static int _process_remove_assoc_results(pgsql_conn_t *pgsql_conn, ++ pgsql_res_t *result, ++ slurmdb_user_rec_t *user, ++ char *cluster_name, ++ char *name_char, ++ bool is_admin, List ret_list, ++ bool *jobs_running) ++{ ++ ListIterator itr = NULL; ++ pgsql_row row; ++ int rc = SLURM_SUCCESS; ++ char *assoc_char = NULL, *object = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ uint32_t smallest_lft = 0xFFFFFFFF; ++ ++ xassert(result); ++ if (*jobs_running) ++ goto skip_process; ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_assoc_rec_t *rem_assoc = NULL; ++ uint32_t lft; ++ ++ if (!is_admin) { ++ slurmdb_coord_rec_t *coord = NULL; ++ if (!user->coord_accts) { // This should never ++ // happen ++ error("We are here with no coord accts"); ++ rc = ESLURM_ACCESS_DENIED; ++ goto end_it; ++ } ++ itr = list_iterator_create(user->coord_accts); ++ while ((coord = list_next(itr))) { ++ if (!xstrcasecmp(coord->name, ++ row[RASSOC_ACCT])) ++ break; ++ } ++ list_iterator_destroy(itr); ++ ++ if (!coord) { ++ error("User %s(%d) does not have the " ++ "ability to change this account (%s)", ++ user->name, user->uid, row[RASSOC_ACCT]); ++ rc = ESLURM_ACCESS_DENIED; ++ goto end_it; ++ } ++ } ++ if (row[RASSOC_PART][0]) { ++ // see if there is a partition name ++ object = xstrdup_printf( ++ "C = %-10s A = %-10s U = %-9s P = %s", ++ cluster_name, row[RASSOC_ACCT], ++ row[RASSOC_USER], row[RASSOC_PART]); ++ } else if (row[RASSOC_USER][0]){ ++ object = xstrdup_printf( ++ "C = %-10s A = %-10s U = %-9s", ++ cluster_name, row[RASSOC_ACCT], ++ row[RASSOC_USER]); ++ } else { ++ if (row[RASSOC_PACCT][0]) { ++ object = xstrdup_printf( ++ "C = %-10s A = %s of %s", ++ cluster_name, row[RASSOC_ACCT], ++ row[RASSOC_PACCT]); ++ } else { ++ object = xstrdup_printf( ++ "C = %-10s A = %s", ++ cluster_name, row[RASSOC_ACCT]); ++ } ++ } ++ list_append(ret_list, object); ++ if (assoc_char) ++ xstrfmtcat(assoc_char, " OR id_assoc=%s", ++ row[RASSOC_ID]); ++ else ++ xstrfmtcat(assoc_char, "id_assoc=%s", row[RASSOC_ID]); ++ ++ /* get the smallest lft here to be able to send all ++ the modified lfts after it. ++ */ ++ lft = slurm_atoul(row[RASSOC_LFT]); ++ if (lft < smallest_lft) ++ smallest_lft = lft; ++ ++ rem_assoc = xmalloc(sizeof(slurmdb_assoc_rec_t)); ++ slurmdb_init_assoc_rec(rem_assoc, 0); ++ rem_assoc->flags |= ASSOC_FLAG_DELETED; ++ rem_assoc->id = slurm_atoul(row[RASSOC_ID]); ++ rem_assoc->cluster = xstrdup(cluster_name); ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_REMOVE_ASSOC, ++ rem_assoc) != SLURM_SUCCESS) { ++ slurmdb_destroy_assoc_rec(rem_assoc); ++ error("couldn't add to the update list"); ++ } ++ ++ } ++ ++ if ((rc = as_pgsql_get_modified_lfts( ++ pgsql_conn, cluster_name, smallest_lft)) != SLURM_SUCCESS) ++ goto end_it; ++ ++ ++skip_process: ++ user_name = uid_to_string((uid_t) user->uid); ++ ++ rc = remove_common(pgsql_conn, DBD_REMOVE_ASSOCS, now, ++ user_name, assoc_table, name_char, ++ assoc_char, cluster_name, ret_list, jobs_running); ++end_it: ++ xfree(user_name); ++ xfree(assoc_char); ++ ++ return rc; ++} ++ ++static int _cluster_get_assocs(pgsql_conn_t *pgsql_conn, ++ slurmdb_user_rec_t *user, ++ slurmdb_assoc_cond_t *assoc_cond, ++ char *cluster_name, ++ char *fields, char *sent_extra, ++ bool is_admin, List sent_list) ++{ ++ List assoc_list; ++ List delta_qos_list = NULL; ++ ListIterator itr = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ uint32_t parent_def_qos_id = 0; ++ uint32_t parent_mj = INFINITE; ++ uint32_t parent_mja = INFINITE; ++ uint32_t parent_mpt = INFINITE; ++ uint32_t parent_msj = INFINITE; ++ uint32_t parent_mwpj = INFINITE; ++ uint32_t parent_prio = INFINITE; ++ char *parent_mtpj = NULL; ++ char *parent_mtpn = NULL; ++ char *parent_mtmpj = NULL; ++ char *parent_mtrm = NULL; ++ char *parent_acct = NULL; ++ char *parent_qos = NULL; ++ char *parent_delta_qos = NULL; ++ char *last_acct = NULL; ++ char *last_cluster = NULL; ++ uint32_t parent_id = 0; ++ char *query = NULL; ++ char *extra = xstrdup(sent_extra); ++ char *qos_extra = NULL; ++ ++ /* needed if we don't have an assoc_cond */ ++ uint16_t without_parent_info = 0; ++ uint16_t without_parent_limits = 0; ++ uint16_t with_usage = 0; ++ uint16_t with_raw_qos = 0; ++ ++ if (assoc_cond) { ++ with_raw_qos = assoc_cond->with_raw_qos; ++ with_usage = assoc_cond->with_usage; ++ without_parent_limits = assoc_cond->without_parent_limits; ++ without_parent_info = assoc_cond->without_parent_info; ++ } ++ ++ /* this is here to make sure we are looking at only this user ++ * if this flag is set. We also include any accounts they may be ++ * coordinator of. ++ */ ++ if (!is_admin && (slurm_conf.private_data & PRIVATE_DATA_USERS)) { ++ int set = 0; ++ query = xstrdup_printf("select lft from \"%s_%s\" where \"user\"='%s'", ++ cluster_name, assoc_table, user->name); ++ if (user->coord_accts) { ++ slurmdb_coord_rec_t *coord = NULL; ++ itr = list_iterator_create(user->coord_accts); ++ while ((coord = list_next(itr))) { ++ xstrfmtcat(query, " OR acct='%s'", ++ coord->name); ++ } ++ list_iterator_destroy(itr); ++ } ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(extra); ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ set = 0; ++ while ((row = pgsql_fetch_row(result))) { ++ if (set) { ++ xstrfmtcat(extra, ++ " OR (%s between t1.lft and t1.rgt)", ++ row[0]); ++ } else { ++ set = 1; ++ xstrfmtcat(extra, ++ " AND ((%s between t1.lft and t1.rgt)", ++ row[0]); ++ } ++ } ++ ++ pgsql_free_result(&result); ++ ++ if (set) ++ xstrcat(extra, ")"); ++ else { ++ xfree(extra); ++ debug("User %s has no associations, and is not admin, " ++ "so not returning any.", user->name); ++ /* This user has no valid associations, so ++ * end. */ ++ return SLURM_SUCCESS; ++ } ++ } ++ ++ qos_extra = _setup_assoc_cond_qos(assoc_cond, cluster_name); ++ ++ ++ //START_TIMER; ++ query = xstrdup_printf("select distinct %s from \"%s_%s\" as t1%s%s " ++ "order by lft;", ++ fields, cluster_name, assoc_table, ++ qos_extra, extra); ++ xfree(qos_extra); ++ xfree(extra); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++// if (!(result = pgsql_db_query_ret( ++// pgsql_conn, query, 0))) { ++// xfree(query); ++// ++// if (mysql_errno(pgsql_conn->db_conn) == ER_NO_SUCH_TABLE) ++// return SLURM_SUCCESS; ++// else ++// return SLURM_ERROR; ++// } ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ bool ret_success = pgsql_db_match_state(pgsql_errno(result), PGSQL_UNDEFINE_TABLE); ++ xfree(query); ++ pgsql_free_result(&result); ++ ++ if (ret_success){ ++ return SLURM_SUCCESS; ++ }else{ ++ return SLURM_ERROR; ++ } ++ } ++ ++ xfree(query); ++ ++ if (!pgsql_num_rows(result)) { ++ pgsql_free_result(&result); ++ return SLURM_SUCCESS; ++ } ++ ++ assoc_list = list_create(slurmdb_destroy_assoc_rec); ++ delta_qos_list = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_assoc_rec_t *assoc = ++ xmalloc(sizeof(slurmdb_assoc_rec_t)); ++ pgsql_res_t *result2 = NULL; ++ pgsql_row row2; ++ uint16_t deleted = slurm_atoul(row[ASSOC_REQ_DELETED]); ++ list_append(assoc_list, assoc); ++ assoc->id = slurm_atoul(row[ASSOC_REQ_ID]); ++ assoc->is_def = slurm_atoul(row[ASSOC_REQ_DEFAULT]); ++ ++ if (deleted) ++ assoc->flags |= ASSOC_FLAG_DELETED; ++ ++ assoc->lft = slurm_atoul(row[ASSOC_REQ_LFT]); ++ assoc->rgt = slurm_atoul(row[ASSOC_REQ_RGT]); ++ ++ if (row[ASSOC_REQ_USER][0]) ++ assoc->user = xstrdup(row[ASSOC_REQ_USER]); ++ assoc->acct = xstrdup(row[ASSOC_REQ_ACCT]); ++ assoc->cluster = xstrdup(cluster_name); ++ ++ if (row[ASSOC_REQ_GJ]) ++ assoc->grp_jobs = slurm_atoul(row[ASSOC_REQ_GJ]); ++ else ++ assoc->grp_jobs = INFINITE; ++ ++ if (row[ASSOC_REQ_GJA]) ++ assoc->grp_jobs_accrue = ++ slurm_atoul(row[ASSOC_REQ_GJA]); ++ else ++ assoc->grp_jobs_accrue = INFINITE; ++ ++ if (row[ASSOC_REQ_GSJ]) ++ assoc->grp_submit_jobs = ++ slurm_atoul(row[ASSOC_REQ_GSJ]); ++ else ++ assoc->grp_submit_jobs = INFINITE; ++ ++ if (row[ASSOC_REQ_GW]) ++ assoc->grp_wall = slurm_atoul(row[ASSOC_REQ_GW]); ++ else ++ assoc->grp_wall = INFINITE; ++ ++ if (row[ASSOC_REQ_GT][0]) ++ assoc->grp_tres = xstrdup(row[ASSOC_REQ_GT]); ++ if (row[ASSOC_REQ_GTM][0]) ++ assoc->grp_tres_mins = xstrdup(row[ASSOC_REQ_GTM]); ++ if (row[ASSOC_REQ_GTRM][0]) ++ assoc->grp_tres_run_mins = xstrdup(row[ASSOC_REQ_GTRM]); ++ ++ parent_acct = row[ASSOC_REQ_ACCT]; ++ if (!without_parent_info ++ && row[ASSOC_REQ_PARENT][0]) { ++ assoc->parent_acct = xstrdup(row[ASSOC_REQ_PARENT]); ++ parent_acct = row[ASSOC_REQ_PARENT]; ++ } else if (!assoc->user) { ++ /* This is the root association so we have no ++ parent id */ ++ parent_acct = NULL; ++ parent_id = 0; ++ } ++ ++ if (row[ASSOC_REQ_PART][0]) ++ assoc->partition = xstrdup(row[ASSOC_REQ_PART]); ++ if (row[ASSOC_REQ_FS]) ++ assoc->shares_raw = slurm_atoul(row[ASSOC_REQ_FS]); ++ else ++ assoc->shares_raw = 1; ++ ++ if (!without_parent_info && parent_acct && ++ (!last_acct || !last_cluster ++ || xstrcmp(parent_acct, last_acct) ++ || xstrcmp(cluster_name, last_cluster))) { ++ query = xstrdup_printf( ++ "call get_parent_limits('%s', " ++ "'%s', '%s', %u); %s", ++ assoc_table, parent_acct, ++ cluster_name, ++ without_parent_limits, ++ get_parent_limits_select); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ result2 = pgsql_db_query_ret(pgsql_conn, query, 1); ++ if(!pgsql_db_match_state(pgsql_errno(result2), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result2); ++ break; ++ } ++ xfree(query); ++ ++ if (!(row2 = pgsql_fetch_row(result2))) { ++ parent_id = 0; ++ goto no_parent_limits; ++ } ++ ++ parent_id = slurm_atoul(row2[ASSOC2_REQ_PARENT_ID]); ++ if (!without_parent_limits) { ++ if (row2[ASSOC2_REQ_DEF_QOS]) ++ parent_def_qos_id = slurm_atoul( ++ row2[ASSOC2_REQ_DEF_QOS]); ++ else ++ parent_def_qos_id = 0; ++ ++ if (row2[ASSOC2_REQ_MJ]) ++ parent_mj = slurm_atoul( ++ row2[ASSOC2_REQ_MJ]); ++ else ++ parent_mj = INFINITE; ++ ++ if (row2[ASSOC2_REQ_MJA]) ++ parent_mja = slurm_atoul( ++ row2[ASSOC2_REQ_MJA]); ++ else ++ parent_mja = INFINITE; ++ ++ if (row2[ASSOC2_REQ_MPT]) ++ parent_mpt = slurm_atoul( ++ row2[ASSOC2_REQ_MPT]); ++ else ++ parent_mpt = INFINITE; ++ ++ if (row2[ASSOC2_REQ_MSJ]) ++ parent_msj = slurm_atoul( ++ row2[ASSOC2_REQ_MSJ]); ++ else ++ parent_msj = INFINITE; ++ ++ if (row2[ASSOC2_REQ_MWPJ]) ++ parent_mwpj = slurm_atoul( ++ row2[ASSOC2_REQ_MWPJ]); ++ else ++ parent_mwpj = INFINITE; ++ ++ ++ if (row2[ASSOC2_REQ_PRIO]) ++ parent_prio = slurm_atoul( ++ row2[ASSOC2_REQ_PRIO]); ++ else ++ parent_prio = INFINITE; ++ ++ xfree(parent_mtpj); ++ if (row2[ASSOC2_REQ_MTPJ][0]) ++ parent_mtpj = xstrdup( ++ row2[ASSOC2_REQ_MTPJ]); ++ ++ xfree(parent_mtpn); ++ if (row2[ASSOC2_REQ_MTPN][0]) ++ parent_mtpn = xstrdup( ++ row2[ASSOC2_REQ_MTPN]); ++ ++ xfree(parent_mtmpj); ++ if (row2[ASSOC2_REQ_MTMPJ][0]) ++ parent_mtmpj = xstrdup( ++ row2[ASSOC2_REQ_MTMPJ]); ++ ++ xfree(parent_mtrm); ++ if (row2[ASSOC2_REQ_MTRM][0]) ++ parent_mtrm = xstrdup( ++ row2[ASSOC2_REQ_MTRM]); ++ ++ xfree(parent_qos); ++ if (row2[ASSOC2_REQ_QOS][0]) ++ parent_qos = ++ xstrdup(row2[ASSOC2_REQ_QOS]); ++ ++ xfree(parent_delta_qos); ++ if (row2[ASSOC2_REQ_DELTA_QOS][0]) ++ parent_delta_qos = xstrdup( ++ row2[ASSOC2_REQ_DELTA_QOS]); ++ } ++ last_acct = parent_acct; ++ last_cluster = cluster_name; ++ no_parent_limits: ++ pgsql_free_result(&result2); ++ } ++ ++ if (row[ASSOC_REQ_DEF_QOS]) ++ assoc->def_qos_id = slurm_atoul(row[ASSOC_REQ_DEF_QOS]); ++ else ++ assoc->def_qos_id = parent_def_qos_id; ++ ++ if (row[ASSOC_REQ_MJ]) ++ assoc->max_jobs = slurm_atoul(row[ASSOC_REQ_MJ]); ++ else ++ assoc->max_jobs = parent_mj; ++ ++ if (row[ASSOC_REQ_MJA]) ++ assoc->max_jobs_accrue = ++ slurm_atoul(row[ASSOC_REQ_MJA]); ++ else ++ assoc->max_jobs_accrue = parent_mja; ++ ++ if (row[ASSOC_REQ_MPT]) ++ assoc->min_prio_thresh = slurm_atoul( ++ row[ASSOC_REQ_MPT]); ++ else ++ assoc->min_prio_thresh = parent_mpt; ++ ++ if (row[ASSOC_REQ_MSJ]) ++ assoc->max_submit_jobs = slurm_atoul( ++ row[ASSOC_REQ_MSJ]); ++ else ++ assoc->max_submit_jobs = parent_msj; ++ ++ if (row[ASSOC_REQ_MWPJ]) ++ assoc->max_wall_pj = slurm_atoul(row[ASSOC_REQ_MWPJ]); ++ else ++ assoc->max_wall_pj = parent_mwpj; ++ ++ if (row[ASSOC_REQ_PRIO]) ++ assoc->priority = slurm_atoul(row[ASSOC_REQ_PRIO]); ++ else ++ assoc->priority = parent_prio; ++ ++ if (row[ASSOC_REQ_MTPJ][0]) ++ assoc->max_tres_pj = xstrdup(row[ASSOC_REQ_MTPJ]); ++ ++ if (row[ASSOC_REQ_MTPN][0]) ++ assoc->max_tres_pn = xstrdup(row[ASSOC_REQ_MTPN]); ++ ++ if (row[ASSOC_REQ_MTMPJ][0]) ++ assoc->max_tres_mins_pj = xstrdup(row[ASSOC_REQ_MTMPJ]); ++ ++ if (row[ASSOC_REQ_MTRM][0]) ++ assoc->max_tres_run_mins = xstrdup(row[ASSOC_REQ_MTRM]); ++ ++ /* For the tres limits we just concatted the limits going up ++ * the hierarchy slurmdb_tres_list_from_string will just skip ++ * over any reoccuring limit to give us the first one per ++ * TRES. ++ */ ++ slurmdb_combine_tres_strings( ++ &assoc->max_tres_pj, parent_mtpj, ++ TRES_STR_FLAG_SORT_ID); ++ slurmdb_combine_tres_strings( ++ &assoc->max_tres_pn, parent_mtpn, ++ TRES_STR_FLAG_SORT_ID); ++ slurmdb_combine_tres_strings( ++ &assoc->max_tres_mins_pj, parent_mtmpj, ++ TRES_STR_FLAG_SORT_ID); ++ slurmdb_combine_tres_strings( ++ &assoc->max_tres_run_mins, parent_mtrm, ++ TRES_STR_FLAG_SORT_ID); ++ ++ assoc->qos_list = list_create(xfree_ptr); ++ ++ /* do a plus 1 since a comma is the first thing there ++ * in the list. Also you can never have both a qos ++ * and a delta qos so if you have a qos don't worry ++ * about the delta. ++ */ ++ ++ if (row[ASSOC_REQ_QOS][0]) ++ slurm_addto_char_list(assoc->qos_list, ++ row[ASSOC_REQ_QOS]+1); ++ else { ++ /* if qos is set on the association itself do ++ not worry about the deltas ++ */ ++ ++ /* add the parents first */ ++ if (parent_qos) ++ slurm_addto_char_list(assoc->qos_list, ++ parent_qos+1); ++ ++ /* then add the parents delta */ ++ if (parent_delta_qos) ++ slurm_addto_char_list(delta_qos_list, ++ parent_delta_qos+1); ++ ++ /* now add the associations */ ++ if (row[ASSOC_REQ_DELTA_QOS][0]) ++ slurm_addto_char_list( ++ delta_qos_list, ++ row[ASSOC_REQ_DELTA_QOS]+1); ++ } ++ ++ /* Sometimes we want to see exactly what is here in ++ the database instead of a complete list. This will ++ give it to us. ++ */ ++ if (with_raw_qos && list_count(delta_qos_list)) { ++ list_transfer(assoc->qos_list, delta_qos_list); ++ list_flush(delta_qos_list); ++ } else if (list_count(delta_qos_list)) { ++ ListIterator curr_qos_itr = ++ list_iterator_create(assoc->qos_list); ++ ListIterator new_qos_itr = ++ list_iterator_create(delta_qos_list); ++ char *new_qos = NULL, *curr_qos = NULL; ++ ++ while ((new_qos = list_next(new_qos_itr))) { ++ if (new_qos[0] == '-') { ++ while ((curr_qos = ++ list_next(curr_qos_itr))) { ++ if (!xstrcmp(curr_qos, ++ new_qos+1)) { ++ list_delete_item( ++ curr_qos_itr); ++ break; ++ } ++ } ++ list_iterator_reset(curr_qos_itr); ++ } else if (new_qos[0] == '+') { ++ while ((curr_qos = ++ list_next(curr_qos_itr))) { ++ if (!xstrcmp(curr_qos, ++ new_qos+1)) { ++ break; ++ } ++ } ++ if (!curr_qos) { ++ list_append(assoc->qos_list, ++ xstrdup(new_qos+1)); ++ } ++ list_iterator_reset(curr_qos_itr); ++ } ++ } ++ ++ list_iterator_destroy(new_qos_itr); ++ list_iterator_destroy(curr_qos_itr); ++ list_flush(delta_qos_list); ++ } ++ ++ assoc->parent_id = parent_id; ++ ++ //info("parent id is %d", assoc->parent_id); ++ //log_assoc_rec(assoc); ++ } ++ xfree(parent_mtpj); ++ xfree(parent_mtpn); ++ xfree(parent_mtmpj); ++ xfree(parent_mtrm); ++ pgsql_free_result(&result); ++ ++ FREE_NULL_LIST(delta_qos_list); ++ ++ xfree(parent_delta_qos); ++ xfree(parent_qos); ++ ++ if (with_usage && assoc_list && list_count(assoc_list)) ++ get_usage_for_list(pgsql_conn, DBD_GET_ASSOC_USAGE, ++ assoc_list, cluster_name, ++ assoc_cond->usage_start, ++ assoc_cond->usage_end); ++ ++ list_transfer(sent_list, assoc_list); ++ FREE_NULL_LIST(assoc_list); ++ return SLURM_SUCCESS; ++} ++ ++extern int as_pgsql_get_modified_lfts(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, uint32_t start_lft) ++{ ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *query = xstrdup_printf( ++ "select id_assoc, lft from \"%s_%s\" where lft > %u " ++ "AND deleted = 0", ++ cluster_name, assoc_table, start_lft); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ error("couldn't query the database for modified lfts"); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_assoc_rec_t *assoc = ++ xmalloc(sizeof(slurmdb_assoc_rec_t)); ++ slurmdb_init_assoc_rec(assoc, 0); ++ assoc->id = slurm_atoul(row[0]); ++ assoc->lft = slurm_atoul(row[1]); ++ assoc->cluster = xstrdup(cluster_name); ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_MODIFY_ASSOC, ++ assoc) != SLURM_SUCCESS) ++ slurmdb_destroy_assoc_rec(assoc); ++ } ++ pgsql_free_result(&result); ++ ++ return SLURM_SUCCESS; ++} ++ ++extern int as_pgsql_add_assocs(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List assoc_list) ++{ ++ ListIterator itr = NULL; ++ int rc = SLURM_SUCCESS; ++ int i=0; ++ slurmdb_assoc_rec_t *object = NULL; ++ char *cols = NULL, *vals = NULL, *txn_query = NULL; ++ char *extra = NULL, *query = NULL, *update = NULL, *tmp_extra = NULL; ++ char *parent = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ char *tmp_char = NULL; ++ int assoc_id = 0; ++ int incr = 0, my_left = 0, my_par_id = 0; ++ int moved_parent = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *old_parent = NULL, *old_cluster = NULL; ++ char *last_parent = NULL, *last_cluster = NULL; ++ List local_cluster_list = NULL; ++ List added_user_list = NULL; ++ bool is_coord = false; ++ slurmdb_update_object_t *update_object = NULL; ++ List assoc_list_tmp = NULL; ++ bool acct_added = false; ++ ++ if (!assoc_list) { ++ error("No association list given"); ++ return SLURM_ERROR; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, ++ SLURMDB_ADMIN_OPERATOR)) { ++ ListIterator itr2 = NULL; ++ slurmdb_user_rec_t user; ++ slurmdb_coord_rec_t *coord = NULL; ++ slurmdb_assoc_rec_t *object = NULL; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (!is_user_any_coord(pgsql_conn, &user)) { ++ error("Only admins/operators/coordinators " ++ "can add associations"); ++ return ESLURM_ACCESS_DENIED; ++ } ++ ++ itr = list_iterator_create(assoc_list); ++ itr2 = list_iterator_create(user.coord_accts); ++ while ((object = list_next(itr))) { ++ char *account = "root"; ++ if (object->user) ++ account = object->acct; ++ else if (object->parent_acct) ++ account = object->parent_acct; ++ list_iterator_reset(itr2); ++ while ((coord = list_next(itr2))) { ++ if (!xstrcasecmp(coord->name, account)) ++ break; ++ } ++ if (!coord) ++ break; ++ } ++ list_iterator_destroy(itr2); ++ list_iterator_destroy(itr); ++ if (!coord) { ++ error("Coordinator %s(%d) tried to add associations " ++ "where they were not allowed", ++ user.name, user.uid); ++ return ESLURM_ACCESS_DENIED; ++ } ++ is_coord = true; ++ } ++ ++ local_cluster_list = list_create(NULL); ++ user_name = uid_to_string((uid_t) uid); ++ /* these need to be in a specific order */ ++ list_sort(assoc_list, (ListCmpF)_assoc_sort_cluster); ++ ++ itr = list_iterator_create(assoc_list); ++ while ((object = list_next(itr))) { ++ if (!object->cluster || !object->cluster[0] ++ || !object->acct || !object->acct[0]) { ++ error("We need a association cluster and " ++ "acct to add one."); ++ rc = SLURM_ERROR; ++ continue; ++ } ++ ++ /* ++ * If the user issuing the command is a coordinator, ++ * do not allow changing the default account ++ */ ++ if (is_coord && (object->is_def == 1)) { ++ char *query = NULL; ++ int has_def_acct = 0; ++ /* Check if there is already a default account. */ ++ xstrfmtcat(query, "select id_assoc from \"%s_%s\" " ++ "where \"user\"='%s' AND acct!='%s' AND is_def=1 " ++ "AND deleted=0;", ++ object->cluster, assoc_table, ++ object->user, object->acct); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", ++ query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 1); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ rc = SLURM_ERROR; ++ pgsql_free_result(&result); ++ break; ++ } ++ ++ xfree(query); ++ has_def_acct = pgsql_num_rows(result); ++ pgsql_free_result(&result); ++ ++ if (has_def_acct) { ++ debug("Coordinator %s(%d) tried to change the default account of user %s to account %s. This is only allowed on initial user creation. Ignoring default account.", ++ user_name, uid, object->user, ++ object->acct); ++ object->is_def = 0; ++ } ++ } ++ ++ if (is_coord && _check_coord_qos(pgsql_conn, object->cluster, ++ object->acct, user_name, ++ object->qos_list) ++ == SLURM_ERROR) { ++ assoc_mgr_lock_t locks = { ++ NO_LOCK, NO_LOCK, READ_LOCK, NO_LOCK, ++ NO_LOCK, NO_LOCK, NO_LOCK }; ++ char *requested_qos; ++ ++ assoc_mgr_lock(&locks); ++ requested_qos = get_qos_complete_str( ++ assoc_mgr_qos_list, object->qos_list); ++ assoc_mgr_unlock(&locks); ++ error("Coordinator %s(%d) does not have the " ++ "access to all the qos requested (%s), " ++ "so they can't add to account " ++ "%s with it.", ++ user_name, uid, requested_qos, ++ object->acct); ++ xfree(requested_qos); ++ rc = ESLURM_ACCESS_DENIED; ++ break; ++ } ++ ++ /* When adding if this isn't a default might as well ++ force it to be 0 to avoid confusion since ++ uninitialized it is NO_VAL. ++ */ ++ if (object->is_def != 1) ++ object->is_def = 0; ++ ++ if (!list_find_first(local_cluster_list, ++ slurm_find_char_in_list, ++ object->cluster)) ++ list_append(local_cluster_list, object->cluster); ++ ++ if (object->parent_acct) { ++ parent = object->parent_acct; ++ } else if (object->user) { ++ parent = object->acct; ++ } else { ++ parent = "root"; ++ } ++ ++ xstrcat(cols, "creation_time, mod_time, acct"); ++ xstrfmtcat(vals, "%ld, %ld, '%s'", ++ now, now, object->acct); ++ xstrfmtcat(update, "where acct='%s'", object->acct); ++ ++ xstrfmtcat(extra, ", mod_time=%ld, acct='%s'", ++ now, object->acct); ++ if (!object->user) { ++ acct_added = true; ++ xstrcat(cols, ", parent_acct"); ++ xstrfmtcat(vals, ", '%s'", parent); ++ xstrfmtcat(extra, ", parent_acct='%s', \"user\"=''", ++ parent); ++ xstrfmtcat(update, " AND \"user\"=''"); ++ } else { ++ char *part = object->partition; ++ xstrcat(cols, ", \"user\""); ++ xstrfmtcat(vals, ", '%s'", object->user); ++ xstrfmtcat(update, " AND \"user\"='%s'", object->user); ++ xstrfmtcat(extra, ", \"user\"='%s'", object->user); ++ ++ /* We need to give a partition whether it be ++ * '' or the actual partition name given ++ */ ++ if (!part) ++ part = ""; ++ xstrcat(cols, ", `partition`"); ++ xstrfmtcat(vals, ", '%s'", part); ++ xstrfmtcat(update, " AND `partition`='%s'", part); ++ xstrfmtcat(extra, ", `partition`='%s'", part); ++ if (!added_user_list) ++ added_user_list = list_create(NULL); ++ if (!list_find_first(added_user_list, ++ slurm_find_char_in_list, ++ object->user)) ++ list_append(added_user_list, object->user); ++ } ++ ++ if (object->id) { ++ xstrcat(cols, ", id_assoc"); ++ xstrfmtcat(vals, ", '%u'", object->id); ++ xstrfmtcat(update, " AND id_assoc='%u'", object->id); ++ xstrfmtcat(extra, ", id_assoc='%u'", object->id); ++ } ++ ++ if ((rc = setup_assoc_limits(object, &cols, &vals, &extra, ++ QOS_LEVEL_NONE, 1))) { ++ error("%s: Failed, setup_assoc_limits functions returned error", ++ __func__); ++ xfree(query); ++ xfree(cols); ++ xfree(vals); ++ xfree(extra); ++ xfree(update); ++ break; ++ } ++ ++ ++ xstrcat(tmp_char, aassoc_req_inx[0]); ++ for(i=1; icluster, assoc_table, update); ++ xfree(tmp_char); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ xfree(cols); ++ xfree(vals); ++ xfree(extra); ++ xfree(update); ++ error("couldn't query the database"); ++ rc = SLURM_ERROR; ++ pgsql_free_result(&result); ++ break; ++ } ++ xfree(query); ++ ++ assoc_id = 0; ++ if (!(row = pgsql_fetch_row(result))) { ++ /* This code speeds up the add process quite a bit ++ * here we are only doing an update when we are done ++ * adding to a specific group (cluster/account) other ++ * than that we are adding right behind what we were ++ * so just total them up and then do one update ++ * instead of the slow ones that require an update ++ * every time. There is a incr check outside of the ++ * loop to catch everything on the last spin of the ++ * while. ++ */ ++ if (!old_parent || !old_cluster ++ || xstrcasecmp(parent, old_parent) ++ || xstrcasecmp(object->cluster, old_cluster)) { ++ char *sel_query = xstrdup_printf( ++ "SELECT lft FROM \"%s_%s\" WHERE " ++ "acct = '%s' and \"user\" = '' " ++ "order by lft;", ++ object->cluster, assoc_table, ++ parent); ++ pgsql_res_t *sel_result = NULL; ++ ++ if (incr) { ++ char *up_query = xstrdup_printf( ++ "UPDATE \"%s_%s\" SET " ++ "rgt = rgt+%d " ++ "WHERE rgt > %d AND deleted < 2;" ++ "UPDATE \"%s_%s\" SET " ++ "lft = lft+%d " ++ "WHERE lft > %d " ++ "AND deleted < 2;" ++ "UPDATE \"%s_%s\" SET " ++ "deleted = 0 " ++ "WHERE deleted = 2;", ++ old_cluster, assoc_table, ++ incr, my_left, ++ old_cluster, assoc_table, ++ incr, my_left, ++ old_cluster, assoc_table); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, ++ "query\n%s", up_query); ++ rc = pgsql_db_query( ++ pgsql_conn, ++ up_query); ++ xfree(up_query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't do update"); ++ xfree(cols); ++ xfree(vals); ++ xfree(update); ++ xfree(extra); ++ xfree(sel_query); ++ break; ++ } ++ } ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, ++ "query\n%s", sel_query); ++ sel_result = pgsql_db_query_ret(pgsql_conn, sel_query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(sel_result), SUCCESSFUL_COMPLETION)){ ++ xfree(cols); ++ xfree(vals); ++ xfree(update); ++ xfree(extra); ++ xfree(sel_query); ++ rc = SLURM_ERROR; ++ pgsql_free_result(&sel_result); ++ break; ++ } ++ ++ if (!(row = pgsql_fetch_row(sel_result))) { ++ error("Couldn't get left from " ++ "query\n%s", ++ sel_query); ++ pgsql_free_result(&sel_result); ++ xfree(cols); ++ xfree(vals); ++ xfree(update); ++ xfree(extra); ++ xfree(sel_query); ++ rc = SLURM_ERROR; ++ break; ++ } ++ xfree(sel_query); ++ ++ my_left = slurm_atoul(row[0]); ++ pgsql_free_result(&sel_result); ++ //info("left is %d", my_left); ++ old_parent = parent; ++ old_cluster = object->cluster; ++ incr = 0; ++ } ++ incr += 2; ++ xstrfmtcat(query, ++ "insert into \"%s_%s\" " ++ "(%s, lft, rgt, deleted) " ++ "values (%s, %d, %d, 2);", ++ object->cluster, assoc_table, cols, ++ vals, my_left+(incr-1), my_left+incr); ++ ++ /* definantly works but slow */ ++/* xstrfmtcat(query, */ ++/* "SELECT @myLeft := lft FROM %s WHERE " */ ++/* "acct = '%s' " */ ++/* "and cluster = '%s' and user = '';", */ ++/* assoc_table, */ ++/* parent, */ ++/* object->cluster); */ ++/* xstrfmtcat(query, */ ++/* "UPDATE %s SET rgt = rgt+2 " */ ++/* "WHERE rgt > @myLeft;" */ ++/* "UPDATE %s SET lft = lft+2 " */ ++/* "WHERE lft > @myLeft;", */ ++/* assoc_table, */ ++/* assoc_table); */ ++/* xstrfmtcat(query, */ ++/* "insert into %s (%s, lft, rgt) " */ ++/* "values (%s, @myLeft+1, @myLeft+2);", */ ++/* assoc_table, cols, */ ++/* vals); */ ++ } else if (!slurm_atoul(row[AASSOC_DELETED])) { ++ /* We don't need to do anything here */ ++ debug("This account %s was added already", ++ object->acct); ++ xfree(cols); ++ xfree(vals); ++ xfree(update); ++ pgsql_free_result(&result); ++ xfree(extra); ++ continue; ++ } else { ++ uint32_t lft = slurm_atoul(row[AASSOC_LFT]); ++ uint32_t rgt = slurm_atoul(row[AASSOC_RGT]); ++ ++ /* If it was once deleted we have kept the lft ++ * and rgt's consant while it was deleted and ++ * so we can just unset the deleted flag, ++ * check for the parent and move if needed. ++ */ ++ assoc_id = slurm_atoul(row[AASSOC_ID]); ++ if (object->parent_acct ++ && xstrcasecmp(object->parent_acct, ++ row[AASSOC_PACCT])) { ++ ++ /* We need to move the parent! */ ++ if (_move_parent(pgsql_conn, uid, ++ &lft, &rgt, ++ object->cluster, ++ row[AASSOC_ID], ++ row[AASSOC_PACCT], ++ object->parent_acct, now) ++ == SLURM_ERROR) ++ continue; ++ moved_parent = 1; ++ } else { ++ object->lft = lft; ++ object->rgt = rgt; ++ } ++ ++ xstrfmtcat(query, ++ "update \"%s_%s\" set deleted=0, " ++ //"id_assoc=LAST_INSERT_ID(id_assoc)%s %s;", ++ "id_assoc = currval(pg_get_serial_sequence('%s_%s', 'id_assoc')) %s %s;", ++ object->cluster, assoc_table, object->cluster, assoc_table, ++ extra, update); ++ } ++ pgsql_free_result(&result); ++ ++ xfree(cols); ++ xfree(vals); ++ xfree(update); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++// rc = pgsql_db_query(pgsql_conn, query); ++ SQLHSTMT stmt = NULL; ++ rc = pgsql_db_query_stmt(pgsql_conn, query, &stmt); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add assoc"); ++ xfree(extra); ++ pgsql_db_free_statement(&stmt); ++ break; ++ } ++ /* see if this was an insert or update. On an update ++ * the assoc_id will already be set ++ */ ++ if (!assoc_id) { ++// (void) last_affected_rows(pgsql_conn); ++// assoc_id = mysql_insert_id(pgsql_conn->db_conn); ++ (void) last_affected_rows(stmt); ++ assoc_id = pgsql_insert_id(stmt); ++ //info("last id was %d", assoc_id); ++ } ++ pgsql_db_free_statement(&stmt); ++ ++ object->id = assoc_id; ++ ++ /* get the parent id only if we haven't moved the ++ * parent since we get the total list if that has ++ * happened */ ++ if (!moved_parent && ++ (!last_parent || !last_cluster ++ || xstrcmp(parent, last_parent) ++ || xstrcmp(object->cluster, last_cluster))) { ++ uint32_t tmp32 = 0; ++ if ((tmp32 = _get_parent_id(pgsql_conn, ++ parent, ++ object->cluster))) { ++ my_par_id = tmp32; ++ ++ last_parent = parent; ++ last_cluster = object->cluster; ++ } ++ } ++ object->parent_id = my_par_id; ++ ++ if (!moved_parent) { ++ _set_assoc_limits_for_add(pgsql_conn, object); ++ if (object->lft == NO_VAL) ++ _set_assoc_lft_rgt(pgsql_conn, object); ++ } ++ ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_ADD_ASSOC, ++ object) == SLURM_SUCCESS) { ++ list_remove(itr); ++ } ++ ++ /* We don't want to record the transactions of the ++ * tmp_cluster. ++ */ ++ ++ if (xstrcmp(object->cluster, tmp_cluster_name)) { ++ /* we always have a ', ' as the first 2 chars */ ++ tmp_extra = slurm_add_slash_to_quotes(extra+2); ++ if (txn_query) ++ xstrfmtcat(txn_query, ++ ", (%ld, %d, 'id_assoc=%d', " ++ "'%s', '%s', '%s')", ++ now, DBD_ADD_ASSOCS, assoc_id, ++ user_name, ++ tmp_extra, object->cluster); ++ else ++ xstrfmtcat(txn_query, ++ "insert into %s " ++ "(timestamp, action, name, actor, " ++ "info, cluster) values (%ld, %d, " ++ "'id_assoc=%d', '%s', '%s', '%s')", ++ txn_table, ++ now, DBD_ADD_ASSOCS, assoc_id, ++ user_name, ++ tmp_extra, object->cluster); ++ xfree(tmp_extra); ++ } ++ xfree(extra); ++ } ++ list_iterator_destroy(itr); ++ xfree(user_name); ++ ++ if (rc != SLURM_SUCCESS) ++ goto end_it; ++ ++ if (incr) { ++ char *up_query = xstrdup_printf( ++ "UPDATE \"%s_%s\" SET rgt = rgt+%d " ++ "WHERE rgt > %d AND deleted < 2;" ++ "UPDATE \"%s_%s\" SET lft = lft+%d " ++ "WHERE lft > %d " ++ "AND deleted < 2;" ++ "UPDATE \"%s_%s\" SET deleted = 0 " ++ "WHERE deleted = 2;", ++ old_cluster, assoc_table, incr, ++ my_left, ++ old_cluster, assoc_table, incr, ++ my_left, ++ old_cluster, assoc_table); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", up_query); ++ rc = pgsql_db_query(pgsql_conn, up_query); ++ xfree(up_query); ++ if (rc != SLURM_SUCCESS) ++ error("Couldn't do update 2"); ++ ++ } ++ ++ /* Since we are already removed all the items from assoc_list ++ * we need to work off the update_list from here on out. ++ */ ++ itr = list_iterator_create(pgsql_conn->update_list);; ++ while ((update_object = list_next(itr))) { ++ if (!update_object->objects || ++ !list_count(update_object->objects)) ++ continue; ++ if (update_object->type == SLURMDB_ADD_ASSOC) ++ break; ++ } ++ list_iterator_destroy(itr); ++ ++ if (update_object && update_object->objects ++ && list_count(update_object->objects)) ++ assoc_list_tmp = update_object->objects; ++ ++ if (assoc_list_tmp) { ++ ListIterator itr2 = list_iterator_create(assoc_list_tmp); ++ ++ if (!moved_parent) { ++ char *cluster_name; ++ ++ itr = list_iterator_create(local_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ uint32_t smallest_lft = 0xFFFFFFFF; ++ while ((object = list_next(itr2))) { ++ if (object->lft < smallest_lft ++ && !xstrcmp(object->cluster, ++ cluster_name)) ++ smallest_lft = object->lft; ++ } ++ list_iterator_reset(itr2); ++ /* now get the lowest lft from the ++ added files by cluster */ ++ if (smallest_lft != 0xFFFFFFFF) ++ rc = as_pgsql_get_modified_lfts( ++ pgsql_conn, cluster_name, ++ smallest_lft); ++ } ++ list_iterator_destroy(itr); ++ } ++ ++ /* make sure we don't have any other default accounts */ ++ list_iterator_reset(itr2); ++ while ((object = list_next(itr2))) { ++ if ((object->is_def != 1) || !object->cluster ++ || !object->acct || !object->user) ++ continue; ++ ++ if ((rc = _reset_default_assoc( ++ pgsql_conn, object, ++ &query, moved_parent ? 0 : 1)) ++ != SLURM_SUCCESS) { ++ xfree(query); ++ goto end_it; ++ } ++ } ++ list_iterator_destroy(itr2); ++ /* This temp list is no longer needed */ ++ assoc_list_tmp = NULL; ++ } ++ ++ if (query) { ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) ++ error("Couldn't update defaults"); ++ } ++end_it: ++ ++ if (rc == SLURM_SUCCESS) { ++ _make_sure_users_have_default(pgsql_conn, added_user_list, ++ local_cluster_list); ++ FREE_NULL_LIST(added_user_list); ++ ++ if (txn_query) { ++ xstrcat(txn_query, ";"); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, ++ __LINE__, txn_query); ++ rc = pgsql_db_query(pgsql_conn, ++ txn_query); ++ xfree(txn_query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add txn"); ++ rc = SLURM_SUCCESS; ++ } ++ } ++ if (moved_parent) { ++ slurmdb_assoc_cond_t assoc_cond; ++ /* now we need to send the update of the new parents and ++ * limits, so just to be safe, send the whole ++ * tree because we could have some limits that ++ * were affected but not noticed. ++ */ ++ /* we can probably just look at the mod time now but ++ * we will have to wait for the next revision number ++ * since you can't query on mod time here and I don't ++ * want to rewrite code to make it happen ++ */ ++ memset(&assoc_cond, 0, sizeof(assoc_cond)); ++ assoc_cond.cluster_list = local_cluster_list; ++ if (!(assoc_list_tmp = ++ as_pgsql_get_assocs(pgsql_conn, uid, ++ &assoc_cond))) { ++ FREE_NULL_LIST(local_cluster_list); ++ return rc; ++ } ++ ++ _move_assoc_list_to_update_list(pgsql_conn->update_list, ++ assoc_list_tmp); ++ FREE_NULL_LIST(assoc_list_tmp); ++ } ++ ++ /* ++ * We need to refresh the assoc_mgr_user_list to ensure that ++ * coordinators of parent accounts are also assigned to ++ * subaccounts potentially added here. ++ */ ++ if (acct_added) { ++ if (assoc_mgr_refresh_lists((void *)pgsql_conn, ++ ASSOC_MGR_CACHE_USER)) { ++ error ("Cannot refresh users/coordinators cache after new ccount was added"); ++ rc = SLURM_ERROR; ++ } ++ } ++ } else { ++ FREE_NULL_LIST(added_user_list); ++ xfree(txn_query); ++ reset_pgsql_conn(pgsql_conn); ++ } ++ FREE_NULL_LIST(local_cluster_list); ++ return rc; ++} ++ ++extern List as_pgsql_modify_assocs(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_assoc_cond_t *assoc_cond, ++ slurmdb_assoc_rec_t *assoc) ++{ ++ ListIterator itr = NULL; ++ List ret_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *object = NULL; ++ char *vals = NULL, *extra = NULL, *query = NULL; ++ int i = 0; ++ bool is_admin=0, same_user=0; ++ pgsql_res_t *result = NULL; ++ slurmdb_user_rec_t user; ++ char *tmp_char1=NULL, *tmp_char2=NULL; ++ char *cluster_name = NULL; ++ char *prefix = "t1"; ++ List use_cluster_list = NULL; ++ bool locked = false; ++ ++ if (!assoc_cond || !assoc) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (!(is_admin = is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { ++ if (is_user_any_coord(pgsql_conn, &user)) { ++ goto is_same_user; ++ } else if (assoc_cond->user_list ++ && (list_count(assoc_cond->user_list) == 1)) { ++ uid_t pw_uid; ++ char *name; ++ name = list_peek(assoc_cond->user_list); ++ if ((uid_from_string (name, &pw_uid) >= 0) ++ && (pw_uid == uid)) { ++ uint16_t is_def = assoc->is_def; ++ uint32_t def_qos_id = assoc->def_qos_id; ++ /* Make sure they aren't trying to ++ change something they aren't ++ allowed to. Currently they are ++ only allowed to change the default ++ account, and default QOS. ++ */ ++ slurmdb_init_assoc_rec(assoc, 1); ++ ++ assoc->is_def = is_def; ++ assoc->def_qos_id = def_qos_id; ++ same_user = 1; ++ ++ goto is_same_user; ++ } ++ } ++ ++ error("Only admins/coordinators can modify associations"); ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++is_same_user: ++ ++ if ((assoc_cond->qos_list && list_count(assoc_cond->qos_list)) ++ || assoc_cond->with_sub_accts) ++ prefix = "t2"; ++ ++ (void) _setup_assoc_cond_limits(assoc_cond, prefix, &extra); ++ ++ /* This needs to be here to make sure we only modify the ++ correct set of assocs The first clause was already ++ taken care of above. */ ++ if (assoc_cond->user_list && !list_count(assoc_cond->user_list)) { ++ debug4("no user specified looking at users"); ++ xstrcat(extra, " AND \"user\" != '' "); ++ } else if (!assoc_cond->user_list) { ++ debug4("no user specified looking at accounts"); ++ xstrcat(extra, " AND \"user\" = '' "); ++ } ++ ++ if ((rc = setup_assoc_limits(assoc, &tmp_char1, &tmp_char2, ++ &vals, QOS_LEVEL_MODIFY, 0))) { ++ xfree(tmp_char1); ++ xfree(tmp_char2); ++ xfree(vals); ++ xfree(extra); ++ errno = rc; ++ error("%s: Failed, setup_assoc_limits functions returned error", ++ __func__); ++ return NULL; ++ } ++ xfree(tmp_char1); ++ xfree(tmp_char2); ++ ++ if (!extra || (!vals && !assoc->parent_acct)) { ++ xfree(vals); ++ xfree(extra); ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ error("Nothing to change"); ++ return NULL; ++ } ++ ++ xstrfmtcat(object, "t1.%s", massoc_req_inx[0]); ++ for(i=1; icluster_list && list_count(assoc_cond->cluster_list)) ++ use_cluster_list = assoc_cond->cluster_list; ++ else { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = list_shallow_copy(as_pgsql_cluster_list); ++ locked = true; ++ } ++ ++ itr = list_iterator_create(use_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ char *qos_extra = _setup_assoc_cond_qos( ++ assoc_cond, cluster_name); ++ ++ xstrfmtcat(query, "select distinct %s " ++ "from \"%s_%s\" as t1%s%s " ++ "order by lft FOR UPDATE;", ++ object, cluster_name, ++ assoc_table, qos_extra, extra); ++ xfree(qos_extra); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++// if (!(result = pgsql_db_query_ret( ++// pgsql_conn, query, 0))) { ++// xfree(query); ++// if (mysql_errno(pgsql_conn->db_conn) ++// != ER_NO_SUCH_TABLE) { ++// FREE_NULL_LIST(ret_list); ++// ret_list = NULL; ++// } ++// break; ++// } ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ if(!pgsql_db_match_state(pgsql_errno(result), PGSQL_UNDEFINE_TABLE)){ ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ } ++ xfree(query); ++ pgsql_free_result(&result); ++ break; ++ } ++ ++ xfree(query); ++ rc = _process_modify_assoc_results(pgsql_conn, result, assoc, ++ &user, cluster_name, vals, ++ is_admin, same_user, ++ ret_list); ++ pgsql_free_result(&result); ++ ++ if ((rc == ESLURM_INVALID_PARENT_ACCOUNT) ++ || (rc == ESLURM_SAME_PARENT_ACCOUNT)) { ++ continue; ++ } else if (rc != SLURM_SUCCESS) { ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ break; ++ } ++ } ++ list_iterator_destroy(itr); ++ if (locked) { ++ FREE_NULL_LIST(use_cluster_list); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ xfree(vals); ++ xfree(object); ++ xfree(extra); ++ ++ if (!ret_list) { ++ reset_pgsql_conn(pgsql_conn); ++ errno = rc; ++ return NULL; ++ } else if (!list_count(ret_list)) { ++ reset_pgsql_conn(pgsql_conn); ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "didn't affect anything"); ++ return ret_list; ++ } ++ ++ return ret_list; ++} ++ ++extern List as_pgsql_remove_assocs(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_assoc_cond_t *assoc_cond) ++{ ++ ListIterator itr = NULL; ++ List ret_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *object = NULL, *cluster_name = NULL; ++ char *extra = NULL, *query = NULL, *name_char = NULL; ++ int i = 0, is_admin = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ slurmdb_user_rec_t user; ++ char *prefix = "t1"; ++ List use_cluster_list = NULL; ++ bool jobs_running = 0, locked = false;; ++ ++ if (!assoc_cond) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (!(is_admin = is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { ++ if (!is_user_any_coord(pgsql_conn, &user)) { ++ error("Only admins/coordinators can " ++ "remove associations"); ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ } ++ ++ if ((assoc_cond->qos_list && list_count(assoc_cond->qos_list)) ++ || assoc_cond->with_sub_accts) ++ prefix = "t2"; ++ ++ (void)_setup_assoc_cond_limits(assoc_cond, prefix, &extra); ++ ++ xstrcat(object, rassoc_req_inx[0]); ++ for(i=1; icluster_list && list_count(assoc_cond->cluster_list)) ++ use_cluster_list = assoc_cond->cluster_list; ++ else { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = list_shallow_copy(as_pgsql_cluster_list); ++ locked = true; ++ } ++ ++ itr = list_iterator_create(use_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ char *qos_extra = _setup_assoc_cond_qos( ++ assoc_cond, cluster_name); ++ ++ query = xstrdup_printf("select distinct t1.lft, t1.rgt from " ++ "\"%s_%s\" as t1%s%s order by " ++ "lft FOR UPDATE;", ++ cluster_name, assoc_table, ++ qos_extra, extra); ++ xfree(qos_extra); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++// if (!(result = pgsql_db_query_ret( ++// pgsql_conn, query, 0))) { ++// xfree(query); ++// if (mysql_errno(pgsql_conn->db_conn) ++// != ER_NO_SUCH_TABLE) { ++// FREE_NULL_LIST(ret_list); ++// ret_list = NULL; ++// } ++// break; ++// } ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ if(!pgsql_db_match_state(pgsql_errno(result), PGSQL_UNDEFINE_TABLE)){ ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ } ++ xfree(query); ++ pgsql_free_result(&result); ++ break; ++ } ++ ++ xfree(query); ++ ++ if (!pgsql_num_rows(result)) { ++ pgsql_free_result(&result); ++ continue; ++ } ++ ++ while ((row = pgsql_fetch_row(result))) { ++ if (name_char) ++ xstrfmtcat(name_char, ++ " OR lft between %s and %s", ++ row[0], row[1]); ++ else ++ xstrfmtcat(name_char, "lft between %s and %s", ++ row[0], row[1]); ++ } ++ pgsql_free_result(&result); ++ ++ query = xstrdup_printf("select distinct %s " ++ "from \"%s_%s\" where (%s) " ++ "and deleted = 0 order by lft;", ++ object, ++ cluster_name, assoc_table, name_char); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ xfree(name_char); ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ pgsql_free_result(&result); ++ break; ++ } ++ xfree(query); ++ ++ rc = _process_remove_assoc_results(pgsql_conn, result, ++ &user, cluster_name, ++ name_char, is_admin, ++ ret_list, &jobs_running); ++ xfree(name_char); ++ pgsql_free_result(&result); ++ ++ if (rc != SLURM_SUCCESS) { ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ break; ++ } ++ } ++ list_iterator_destroy(itr); ++ if (locked) { ++ FREE_NULL_LIST(use_cluster_list); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ ++ xfree(object); ++ xfree(extra); ++ ++ if (!ret_list) { ++ reset_pgsql_conn(pgsql_conn); ++ return NULL; ++ } else if (!list_count(ret_list)) { ++ reset_pgsql_conn(pgsql_conn); ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "didn't affect anything"); ++ return ret_list; ++ } ++ if (jobs_running) ++ errno = ESLURM_JOBS_RUNNING_ON_ASSOC; ++ else ++ errno = SLURM_SUCCESS; ++ return ret_list; ++} ++ ++extern List as_pgsql_get_assocs(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_assoc_cond_t *assoc_cond) ++{ ++ //DEF_TIMERS; ++ char *extra = NULL; ++ char *tmp = NULL; ++ List assoc_list = NULL; ++ ListIterator itr = NULL; ++ int i=0, is_admin=1; ++ slurmdb_user_rec_t user; ++ char *prefix = "t1"; ++ List use_cluster_list = NULL; ++ char *cluster_name = NULL; ++ bool locked = false; ++ ++ if (!assoc_cond) { ++ xstrcat(extra, " where deleted=0"); ++ goto empty; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (slurm_conf.private_data & PRIVATE_DATA_USERS) { ++ if (!(is_admin = is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { ++ /* Fill in the user with any accounts they may ++ be coordinator of, which is checked inside ++ _cluster_get_assocs. ++ */ ++ assoc_mgr_fill_in_user( ++ pgsql_conn, &user, 1, NULL, false); ++ } ++ if (!is_admin && !user.name) { ++ debug("User %u has no associations, and is not admin, " ++ "so not returning any.", user.uid); ++ return NULL; ++ } ++ } ++ ++ if ((assoc_cond->qos_list && list_count(assoc_cond->qos_list)) ++ || assoc_cond->with_sub_accts) ++ prefix = "t2"; ++ ++ (void) _setup_assoc_cond_limits(assoc_cond, prefix, &extra); ++ ++empty: ++ xfree(tmp); ++ xstrfmtcat(tmp, "t1.%s", assoc_req_inx[i]); ++ for(i=1; icluster_list && ++ list_count(assoc_cond->cluster_list)) { ++ use_cluster_list = assoc_cond->cluster_list; ++ } else { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = list_shallow_copy(as_pgsql_cluster_list); ++ locked = true; ++ } ++ itr = list_iterator_create(use_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ int rc; ++ if ((rc = _cluster_get_assocs(pgsql_conn, &user, assoc_cond, ++ cluster_name, tmp, extra, ++ is_admin, assoc_list)) ++ != SLURM_SUCCESS) { ++ FREE_NULL_LIST(assoc_list); ++ assoc_list = NULL; ++ break; ++ } ++ } ++ list_iterator_destroy(itr); ++ if (locked) { ++ FREE_NULL_LIST(use_cluster_list); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ xfree(tmp); ++ xfree(extra); ++ ++ //END_TIMER2("get_assocs"); ++ return assoc_list; ++} ++ ++extern int as_pgsql_reset_lft_rgt(pgsql_conn_t *pgsql_conn, uid_t uid, ++ List cluster_list) ++{ ++ List assoc_list = NULL; ++ ListIterator itr = NULL, assoc_itr; ++ int i=0, is_admin=1; ++ slurmdb_user_rec_t user; ++ char *query = NULL, *tmp = NULL, *cluster_name = NULL; ++ slurmdb_assoc_cond_t assoc_cond; ++ slurmdb_assoc_rec_t *assoc_rec; ++ int rc = SLURM_SUCCESS; ++ slurmdb_update_object_t *update_object; ++ slurmdb_update_type_t type; ++ List use_cluster_list = as_pgsql_cluster_list; ++ ++ info("Resetting lft and rgt's called"); ++ /* This is not safe if ran during the middle of a slurmdbd ++ * run since we can not lock as_pgsql_cluster_list_lock when ++ * no list is given. At the time of this writing the function ++ * was only called at the beginning of the DBD. If this ever ++ * changes please make the appropriate changes to allow empty lists. ++ */ ++ if (cluster_list && list_count(cluster_list)) ++ use_cluster_list = cluster_list; ++ /* else */ ++ /* slurm_mutex_lock(&as_pgsql_cluster_list_lock); */ ++ ++ memset(&assoc_cond, 0, sizeof(slurmdb_assoc_cond_t)); ++ ++ xfree(tmp); ++ xstrfmtcat(tmp, "t1.%s", assoc_req_inx[i]); ++ for (i=1; iid == 1) { ++ /* Remove root association as we will make it ++ * manually in the next step. ++ */ ++ list_delete_item(assoc_itr); ++ continue; ++ } ++ xfree(assoc_rec->cluster); ++ assoc_rec->cluster = xstrdup(tmp_cluster_name); ++ /* Remove the qos_list just to simplify things ++ * since we really want to just simplify ++ * things. ++ */ ++ FREE_NULL_LIST(assoc_rec->qos_list); ++ } ++ list_iterator_destroy(assoc_itr); ++ ++ /* What we are going to do now is add all the ++ * associations to a tmp cluster this will recreate ++ * the lft and rgt hierarchy. Then when this is done ++ * we will move those lft/rgts back over to the ++ * original cluster based on the same id_assoc's. ++ * After that we will send these new objects out to ++ * the slurmctld to use. ++ */ ++ ++ create_cluster_assoc_table(pgsql_conn, tmp_cluster_name); ++ ++ /* We must add the root association here to make it so ++ * the adds work afterwards. ++ */ ++// xstrfmtcat(query, ++// "insert into \"%s_%s\" " ++// "(creation_time, mod_time, acct, lft, rgt) " ++// "values (%ld, %ld, 'root', 1, 2) " ++// "on duplicate key update deleted=0, " ++// "id_assoc=LAST_INSERT_ID(id_assoc), mod_time=%ld;", ++// tmp_cluster_name, assoc_table, now, now, now); ++ ++ xstrfmtcat(query, ++ "insert into \"%s_%s\" " ++ "(creation_time, mod_time, acct, lft, rgt) " ++ "values (%ld, %ld, 'root', 1, 2) " ++ "on conflict(id_assoc) do update set deleted=0, " ++ "id_assoc=currval(pg_get_serial_sequence('%s_%s', 'id_assoc')), mod_time=%ld;", ++ tmp_cluster_name, assoc_table, now, now, tmp_cluster_name, assoc_table, now); ++ ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add cluster root assoc"); ++ break; ++ } ++ ++ info("Redoing the hierarchy in a temporary table"); ++ if (as_pgsql_add_assocs(pgsql_conn, uid, assoc_list) != ++ SLURM_SUCCESS) ++ goto endit; ++ ++ /* Since the list we now have has deleted ++ * items in it we need to get a list with just ++ * the current items instead ++ */ ++ list_flush(assoc_list); ++ ++ info("Resetting cluster with correct lft and rgt's"); ++ /* Update the normal table with the correct lft and rgts */ ++ query = xstrdup_printf( ++ "update \"%s_%s\" t1 left outer join \"%s_%s\" t2 on " ++ "t1.id_assoc = t2.id_assoc set t1.lft = t2.lft, " ++ "t1.rgt = t2.rgt, t1.mod_time = t2.mod_time;", ++ cluster_name, assoc_table, ++ tmp_cluster_name, assoc_table); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) ++ error("Couldn't fix assocs"); ++ ++ /* Now we will remove the adds that happened and ++ * replace them with mod's so we can push the new ++ * lft's to the cluster. ++ */ ++ type = SLURMDB_ADD_ASSOC; ++ assoc_itr = list_iterator_create(pgsql_conn->update_list); ++ while ((update_object = list_next(assoc_itr))) { ++ if (update_object->type == type) { ++ list_delete_item(assoc_itr); ++ break; ++ } ++ } ++ list_iterator_destroy(assoc_itr); ++ ++ /* Make the mod assoc update_object if it doesn't exist */ ++ type = SLURMDB_MODIFY_ASSOC; ++ if (!(update_object = list_find_first( ++ pgsql_conn->update_list, ++ slurmdb_find_update_object_in_list, ++ &type))) { ++ update_object = xmalloc( ++ sizeof(slurmdb_update_object_t)); ++ list_append(pgsql_conn->update_list, update_object); ++ update_object->type = type; ++ update_object->objects = list_create( ++ slurmdb_destroy_assoc_rec); ++ } ++ ++ /* set this up to get the associations with parent_limits */ ++ assoc_cond.without_parent_limits = 0; ++ if ((rc = _cluster_get_assocs(pgsql_conn, &user, &assoc_cond, ++ cluster_name, tmp, ++ " deleted=0", ++ is_admin, assoc_list)) ++ != SLURM_SUCCESS) { ++ goto endit; ++ } ++ list_transfer(update_object->objects, assoc_list); ++ endit: ++ FREE_NULL_LIST(assoc_list); ++ /* Get rid of the temporary table. */ ++ query = xstrdup_printf("drop table \"%s_%s\";", ++ tmp_cluster_name, assoc_table); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("problem with update query"); ++ rc = SLURM_ERROR; ++ } ++ END_TIMER; ++ info("resetting took %s", TIME_STR); ++ } ++ list_iterator_destroy(itr); ++ ++ xfree(tmp); ++ ++ /* if (use_cluster_list == as_pgsql_cluster_list) */ ++ /* slurm_mutex_unlock(&as_pgsql_cluster_list_lock); */ ++ ++ return rc; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_assoc.h b/src/plugins/accounting_storage/pgsql/as_pgsql_assoc.h +new file mode 100755 +index 0000000..26d4936 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_assoc.h +@@ -0,0 +1,64 @@ ++/*****************************************************************************\ ++ * as_pgsql_assoc.h - functions dealing with associations. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_ASSOC_H ++#define _HAVE_PGSQL_ASSOC_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_get_modified_lfts(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, uint32_t start_lft); ++ ++extern int as_pgsql_add_assocs(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ List assoc_list); ++ ++extern List as_pgsql_modify_assocs(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_assoc_cond_t *assoc_cond, ++ slurmdb_assoc_rec_t *assoc); ++ ++extern List as_pgsql_remove_assocs(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_assoc_cond_t *assoc_cond); ++ ++extern List as_pgsql_get_assocs(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_assoc_cond_t *assoc_cond); ++ ++extern int as_pgsql_reset_lft_rgt(pgsql_conn_t *pgsql_conn, uid_t uid, ++ List cluster_list); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_cluster.c b/src/plugins/accounting_storage/pgsql/as_pgsql_cluster.c +new file mode 100755 +index 0000000..c93c355 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_cluster.c +@@ -0,0 +1,1750 @@ ++/*****************************************************************************\ ++ * as_pgsql_cluster.c - functions dealing with clusters. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2011 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_tres.h" ++#include "as_pgsql_assoc.h" ++#include "as_pgsql_cluster.h" ++#include "as_pgsql_federation.h" ++#include "as_pgsql_usage.h" ++#include "as_pgsql_wckey.h" ++#include "src/common/node_select.h" ++ ++extern int as_pgsql_get_fed_cluster_id(pgsql_conn_t *pgsql_conn, ++ const char *cluster, ++ const char *federation, ++ int last_id, int *ret_id) ++{ ++ /* find id for cluster in federation. ++ * don't do anything if cluster is already part of federation ++ * get list of clusters that are part of the federration. ++ * loop through each cluster and find the first id available. ++ * report error if all are full in 63 slots. */ ++ ++ int id = 1; ++ char *query = NULL; ++ pgsql_row row; ++ pgsql_res_t *result = NULL; ++ ++ xassert(cluster); ++ xassert(federation); ++ xassert(ret_id); ++ ++ /* See if cluster is already part of federation */ ++ xstrfmtcat(query, "SELECT name, fed_id " ++ "FROM %s " ++ "WHERE deleted=0 AND name='%s' AND federation='%s';", ++ cluster_table, cluster, federation); ++ DB_DEBUG(FEDR, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ error("no result given for %s", query); ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ int tmp_id = slurm_atoul(row[1]); ++ log_flag(FEDR, "cluster '%s' already part of federation '%s', using existing id %d", ++ cluster, federation, tmp_id); ++ pgsql_free_result(&result); ++ *ret_id = tmp_id; ++ return SLURM_SUCCESS; ++ } ++ pgsql_free_result(&result); ++ ++ /* Get all other clusters in the federation and find an open id. */ ++ xstrfmtcat(query, "SELECT name, federation, fed_id " ++ "FROM %s " ++ "WHERE name!='%s' AND federation='%s' " ++ "AND fed_id > %d AND deleted=0 ORDER BY fed_id;", ++ cluster_table, cluster, federation, last_id); ++ DB_DEBUG(FEDR, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ error("no result given for %s", query); ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ if (last_id >= id) ++ id = last_id + 1; ++ while ((row = pgsql_fetch_row(result))) { ++ if (id != slurm_atoul(row[2])) ++ break; ++ id++; ++ } ++ pgsql_free_result(&result); ++ ++ if (id > MAX_FED_CLUSTERS) { ++ error("Too many clusters in this federation."); ++ errno = ESLURM_FED_CLUSTER_MAX_CNT; ++ return ESLURM_FED_CLUSTER_MAX_CNT; ++ } ++ ++ *ret_id = id; ++ return SLURM_SUCCESS; ++} ++ ++static int _setup_cluster_cond_limits(slurmdb_cluster_cond_t *cluster_cond, ++ char **extra) ++{ ++ int set = 0; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ ++ if (!cluster_cond) ++ return 0; ++ ++ if (cluster_cond->with_deleted) ++ xstrcat(*extra, " WHERE (deleted=0 OR deleted=1)"); ++ else ++ xstrcat(*extra, " WHERE deleted=0"); ++ ++ if (cluster_cond->cluster_list ++ && list_count(cluster_cond->cluster_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(cluster_cond->cluster_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (cluster_cond->federation_list ++ && list_count(cluster_cond->federation_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(cluster_cond->federation_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "federation='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (cluster_cond->plugin_id_select_list ++ && list_count(cluster_cond->plugin_id_select_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(cluster_cond->plugin_id_select_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "plugin_id_select='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (cluster_cond->rpc_version_list ++ && list_count(cluster_cond->rpc_version_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(cluster_cond->rpc_version_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "rpc_version='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (cluster_cond->classification) { ++ xstrfmtcat(*extra, " AND (classification & %u)", ++ cluster_cond->classification); ++ } ++ ++ if (cluster_cond->flags != NO_VAL) { ++ xstrfmtcat(*extra, " AND (flags & %u)", ++ cluster_cond->flags); ++ } ++ ++ return set; ++} ++ ++extern int as_pgsql_add_clusters(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List cluster_list) ++{ ++ ListIterator itr = NULL; ++ int rc = SLURM_SUCCESS; ++ slurmdb_cluster_rec_t *object = NULL; ++ char *cols = NULL, *vals = NULL, *extra = NULL, ++ *query = NULL, *tmp_extra = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ int affect_rows = 0; ++ int added = 0; ++ bool has_feds = false; ++ List assoc_list = NULL; ++ slurmdb_assoc_rec_t *assoc = NULL; ++ bool external_cluster = false; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_SUPER_USER)) ++ return ESLURM_ACCESS_DENIED; ++ ++ assoc_list = list_create(slurmdb_destroy_assoc_rec); ++ ++ user_name = uid_to_string((uid_t) uid); ++ /* Since adding tables make it so you can't roll back, if ++ there is an error there is no way to easily remove entries ++ in the database, so we will create the tables first and ++ then after that works out then add them to the mix. ++ */ ++ itr = list_iterator_create(cluster_list); ++ while ((object = list_next(itr))) { ++ if (!object->name || !object->name[0]) { ++ error("We need a cluster name to add."); ++ rc = SLURM_ERROR; ++ list_remove(itr); ++ continue; ++ } ++ if ((object->flags != NO_VAL) && ++ (object->flags & CLUSTER_FLAG_EXT)) ++ external_cluster = true; ++ if ((rc = create_cluster_tables(pgsql_conn, ++ object->name)) ++ != SLURM_SUCCESS) { ++ added = 0; ++ goto end_it; ++ } ++ } ++ ++ /* Now that all the tables were created successfully lets go ++ ahead and add it to the system. ++ */ ++ list_iterator_reset(itr); ++ while ((object = list_next(itr))) { ++ int fed_id = 0; ++ uint16_t fed_state = CLUSTER_FED_STATE_NA; ++ char *features = NULL; ++ xstrcat(cols, "creation_time, mod_time, acct"); ++ xstrfmtcat(vals, "%ld, %ld, 'root'", now, now); ++ xstrfmtcat(extra, ", mod_time=%ld", now); ++ if (object->root_assoc) { ++ rc = setup_assoc_limits(object->root_assoc, &cols, ++ &vals, &extra, ++ QOS_LEVEL_SET, 1); ++ if (rc) { ++ xfree(extra); ++ xfree(cols); ++ xfree(vals); ++ added=0; ++ error("%s: Failed, setup_assoc_limits functions returned error", ++ __func__); ++ goto end_it; ++ ++ } ++ } ++ ++ if (object->fed.name) { ++ has_feds = 1; ++ rc = as_pgsql_get_fed_cluster_id(pgsql_conn, ++ object->name, ++ object->fed.name, -1, ++ &fed_id); ++ if (rc) { ++ error("failed to get cluster id for " ++ "federation"); ++ xfree(extra); ++ xfree(cols); ++ xfree(vals); ++ added=0; ++ goto end_it; ++ } ++ ++ if (object->fed.state != NO_VAL) ++ fed_state = object->fed.state; ++ else ++ fed_state = CLUSTER_FED_STATE_ACTIVE; ++ } ++ ++ if (object->fed.feature_list) { ++ features = ++ slurm_char_list_to_xstr( ++ object->fed.feature_list); ++ has_feds = 1; ++ } ++ ++ xstrfmtcat(query, ++ "INSERT INTO %s (creation_time, mod_time, " ++ "name, classification, federation, fed_id, " ++ "fed_state, features) " ++ "VALUES (%ld, %ld, '%s', %u, '%s', %d, %u, '%s') " ++ "ON CONFLICT (name) DO UPDATE SET deleted=0, mod_time=%ld, " ++ "control_host='', control_port=0, " ++ "classification=%u, flags=0, federation='%s', " ++ "fed_id=%d, fed_state=%u, features='%s'", ++ cluster_table, ++ now, now, object->name, object->classification, ++ (object->fed.name) ? object->fed.name : "", ++ fed_id, fed_state, (features) ? features : "", ++ now, object->classification, ++ (object->fed.name) ? object->fed.name : "", ++ fed_id, fed_state, (features) ? features : ""); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ SQLHSTMT stmt_ptr = SQL_NULL_HSTMT; ++ rc = pgsql_db_query_stmt(pgsql_conn, query, &stmt_ptr); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add cluster %s", object->name); ++ xfree(extra); ++ xfree(cols); ++ xfree(vals); ++ xfree(features); ++ added=0; ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ break; ++ } ++ ++ affect_rows = last_affected_rows(stmt_ptr); ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ if (!affect_rows) { ++ debug2("nothing changed %d", affect_rows); ++ xfree(extra); ++ xfree(cols); ++ xfree(vals); ++ xfree(features); ++ continue; ++ } ++ ++ if (!external_cluster) { ++ /* Add root account */ ++ xstrfmtcat(query, ++ "INSERT INTO \"%s_%s\" (%s, lft, rgt) " ++ "VALUES (%s, 1, 2) " ++ "ON CONFLICT (id_assoc) DO UPDATE SET deleted=0, " ++ "id_assoc=currval(pg_get_serial_sequence('%s_%s', 'id_assoc'))%s;", ++ object->name, assoc_table, cols, vals, object->name, assoc_table, extra); ++ xfree(cols); ++ xfree(vals); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add cluster root assoc"); ++ xfree(extra); ++ xfree(features); ++ added=0; ++ break; ++ } ++ } else { ++ xfree(cols); ++ xfree(vals); ++ } ++ ++ /* Build up extra with cluster specfic values for txn table */ ++ xstrfmtcat(extra, ", federation='%s', fed_id=%d, fed_state=%u, " ++ "features='%s'", ++ (object->fed.name) ? object->fed.name : "", ++ fed_id, fed_state, (features) ? features : ""); ++ xfree(features); ++ ++ /* we always have a ', ' as the first 2 chars */ ++ tmp_extra = slurm_add_slash_to_quotes(extra+2); ++ ++ xstrfmtcat(query, ++ "INSERT INTO %s " ++ "(timestamp, action, name, actor, info) " ++ "VALUES (%ld, %u, '%s', '%s', '%s');", ++ txn_table, now, DBD_ADD_CLUSTERS, ++ object->name, user_name, tmp_extra); ++ xfree(tmp_extra); ++ xfree(extra); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add txn"); ++ } else { ++ ListIterator check_itr; ++ char *tmp_name; ++ ++ added++; ++ /* add it to the list and sort */ ++ slurm_rwlock_wrlock(&as_pgsql_cluster_list_lock); ++ check_itr = list_iterator_create(as_pgsql_cluster_list); ++ while ((tmp_name = list_next(check_itr))) { ++ if (!xstrcmp(tmp_name, object->name)) ++ break; ++ } ++ list_iterator_destroy(check_itr); ++ if (!tmp_name) { ++ list_append(as_pgsql_cluster_list, ++ xstrdup(object->name)); ++ list_sort(as_pgsql_cluster_list, ++ (ListCmpF)slurm_sort_char_list_asc); ++ } else ++ error("Cluster %s(%s) appears to already be in " ++ "our cache list, not adding.", tmp_name, ++ object->name); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ ++ if (!external_cluster) { ++ /* Add user root by default to run from the root ++ * association. This gets popped off so we need to ++ * read it every time here. ++ */ ++ assoc = xmalloc(sizeof(slurmdb_assoc_rec_t)); ++ slurmdb_init_assoc_rec(assoc, 0); ++ list_append(assoc_list, assoc); ++ ++ assoc->cluster = xstrdup(object->name); ++ assoc->user = xstrdup("root"); ++ assoc->acct = xstrdup("root"); ++ assoc->is_def = 1; ++ ++ if (as_pgsql_add_assocs(pgsql_conn, uid, assoc_list) ++ == SLURM_ERROR) { ++ error("Problem adding root user association"); ++ rc = SLURM_ERROR; ++ } ++ } ++ } ++end_it: ++ list_iterator_destroy(itr); ++ xfree(user_name); ++ ++ FREE_NULL_LIST(assoc_list); ++ ++ if (!added) ++ reset_pgsql_conn(pgsql_conn); ++ else if (has_feds) ++ as_pgsql_add_feds_to_update_list(pgsql_conn); ++ ++ return rc; ++} ++ ++static int _reconcile_existing_features(void *object, void *arg) ++{ ++ char *new_feature = (char *)object; ++ List existing_features = (List)arg; ++ ++ if (new_feature[0] == '-') ++ list_delete_all(existing_features, slurm_find_char_in_list, ++ new_feature + 1); ++ else if (new_feature[0] == '+') ++ list_append(existing_features, xstrdup(new_feature + 1)); ++ else ++ list_append(existing_features, xstrdup(new_feature)); ++ ++ return SLURM_SUCCESS; ++} ++ ++extern List as_pgsql_modify_clusters(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_cluster_cond_t *cluster_cond, ++ slurmdb_cluster_rec_t *cluster) ++{ ++ List ret_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *object = NULL; ++ char *vals = NULL, *extra = NULL, *query = NULL, *name_char = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ int set = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ bool clust_reg = false, fed_update = false; ++ ++ /* If you need to alter the default values of the cluster use ++ * modify_assocs since this is used only for registering ++ * the controller when it loads ++ */ ++ ++ if (!cluster_cond || !cluster) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, ++ SLURMDB_ADMIN_SUPER_USER)) { ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ ++ /* force to only do non-deleted clusters */ ++ cluster_cond->with_deleted = 0; ++ _setup_cluster_cond_limits(cluster_cond, &extra); ++ ++ /* Needed if talking to older Slurm versions < 2.2 */ ++ if (!pgsql_conn->cluster_name && cluster_cond->cluster_list ++ && list_count(cluster_cond->cluster_list)) ++ pgsql_conn->cluster_name = ++ xstrdup(list_peek(cluster_cond->cluster_list)); ++ ++ set = 0; ++ if (cluster->control_host) { ++ xstrfmtcat(vals, ", control_host='%s'", cluster->control_host); ++ set++; ++ clust_reg = true; ++ } ++ ++ if (cluster->control_port) { ++ xstrfmtcat(vals, ", control_port=%u, last_port=%u", ++ cluster->control_port, cluster->control_port); ++ set++; ++ clust_reg = true; ++ } ++ ++ if (cluster->rpc_version) { ++ xstrfmtcat(vals, ", rpc_version=%u", cluster->rpc_version); ++ set++; ++ clust_reg = true; ++ } ++ ++ if (cluster->dimensions) { ++ xstrfmtcat(vals, ", dimensions=%u", cluster->dimensions); ++ clust_reg = true; ++ } ++ ++ if (cluster->plugin_id_select) { ++ xstrfmtcat(vals, ", plugin_id_select=%u", ++ cluster->plugin_id_select); ++ clust_reg = true; ++ } ++ if (cluster->flags != NO_VAL) { ++ xstrfmtcat(vals, ", flags=%u", cluster->flags); ++ clust_reg = true; ++ } ++ ++ if (cluster->classification) { ++ xstrfmtcat(vals, ", classification=%u", ++ cluster->classification); ++ } ++ ++ if (cluster->fed.name) { ++ xstrfmtcat(vals, ", federation='%s'", cluster->fed.name); ++ fed_update = true; ++ } ++ ++ if (cluster->fed.state != NO_VAL) { ++ xstrfmtcat(vals, ", fed_state=%u", cluster->fed.state); ++ fed_update = true; ++ } ++ ++ if (!vals && !cluster->fed.feature_list) { ++ xfree(extra); ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ error("Nothing to change"); ++ return NULL; ++ } else if (clust_reg && (set != 3)) { ++ xfree(vals); ++ xfree(extra); ++ errno = EFAULT; ++ error("Need control host, port and rpc version " ++ "to register a cluster"); ++ return NULL; ++ } ++ ++ xstrfmtcat(query, "SELECT name, control_port, federation, features FROM %s%s;", ++ cluster_table, extra); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ error("no result given for %s", extra); ++ xfree(query); ++ xfree(vals); ++ xfree(extra); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ xfree(extra); ++ ++ ret_list = list_create(xfree_ptr); ++ user_name = uid_to_string((uid_t) uid); ++ while ((row = pgsql_fetch_row(result))) { ++ char *tmp_vals = xstrdup(vals); ++ ++ object = xstrdup(row[0]); ++ ++ if (cluster->fed.name) { ++ int id = 0; ++ char *curr_fed = NULL; ++ uint32_t set_state = NO_VAL; ++ ++ if (cluster->fed.name[0] != '\0') { ++ rc = as_pgsql_get_fed_cluster_id( ++ pgsql_conn, object, ++ cluster->fed.name, -1, ++ &id); ++ if (rc) { ++ error("failed to get cluster id for " ++ "federation"); ++ xfree(tmp_vals); ++ xfree(object); ++ FREE_NULL_LIST(ret_list); ++ pgsql_free_result(&result); ++ goto end_it; ++ } ++ } ++ /* will set fed_id=0 if being removed from fed. */ ++ xstrfmtcat(tmp_vals, ", fed_id=%d", id); ++ ++ curr_fed = xstrdup(row[2]); ++ if (cluster->fed.name[0] == '\0') ++ /* clear fed_state when leaving federation */ ++ set_state = CLUSTER_FED_STATE_NA; ++ else if (cluster->fed.state != NO_VAL) { ++ /* NOOP: fed_state already set in vals */ ++ } else if (xstrcmp(curr_fed, cluster->fed.name)) ++ /* set state to active when joining fed * */ ++ set_state = CLUSTER_FED_STATE_ACTIVE; ++ /* else use existing state */ ++ ++ if (set_state != NO_VAL) ++ xstrfmtcat(tmp_vals, ", fed_state=%u", ++ set_state); ++ ++ xfree(curr_fed); ++ } ++ ++ if (cluster->fed.feature_list) { ++ if (!list_count(cluster->fed.feature_list)) { ++ /* clear all existing features */ ++ xstrfmtcat(tmp_vals, ", features=''"); ++ } else { ++ char *features = NULL, *feature = NULL; ++ List existing_features = list_create(xfree_ptr); ++ ++ if ((feature = ++ list_peek(cluster->fed.feature_list)) && ++ (feature[0] == '+' || feature[0] == '-')) ++ slurm_addto_char_list(existing_features, ++ row[3]); ++ ++ list_for_each(cluster->fed.feature_list, ++ _reconcile_existing_features, ++ existing_features); ++ ++ features = ++ slurm_char_list_to_xstr( ++ existing_features); ++ xstrfmtcat(tmp_vals, ", features='%s'", ++ features ? features : ""); ++ ++ xfree(features); ++ FREE_NULL_LIST(existing_features); ++ } ++ ++ fed_update = true; ++ } ++ ++ list_append(ret_list, object); ++ xstrfmtcat(name_char, "name='%s'", object); ++ ++ rc = modify_common(pgsql_conn, DBD_MODIFY_CLUSTERS, now, ++ user_name, cluster_table, ++ name_char, tmp_vals, NULL); ++ xfree(name_char); ++ xfree(tmp_vals); ++ if (rc == SLURM_ERROR) { ++ error("Couldn't modify cluster 1"); ++ FREE_NULL_LIST(ret_list); ++ pgsql_free_result(&result); ++ goto end_it; ++ } ++ } ++ pgsql_free_result(&result); ++ xfree(user_name); ++ ++ if (!list_count(ret_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(name_char); ++ xfree(vals); ++ xfree(query); ++ return ret_list; ++ } ++ ++ if (fed_update) ++ as_pgsql_add_feds_to_update_list(pgsql_conn); ++ ++end_it: ++ xfree(query); ++ xfree(vals); ++ xfree(user_name); ++ ++ return ret_list; ++} ++ ++extern List as_pgsql_remove_clusters(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_cluster_cond_t *cluster_cond) ++{ ++ ListIterator itr = NULL; ++ List ret_list = NULL; ++ List tmp_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *object = NULL; ++ char *extra = NULL, *query = NULL, *cluster_name = NULL, ++ *name_char = NULL, *assoc_char = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ slurmdb_wckey_cond_t wckey_cond; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ bool jobs_running = 0, fed_update = false; ++ ++ if (!cluster_cond) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_SUPER_USER)) { ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ ++ /* force to only do non-deleted clusters */ ++ cluster_cond->with_deleted = 0; ++ _setup_cluster_cond_limits(cluster_cond, &extra); ++ ++ if (!extra) { ++ error("Nothing to remove"); ++ return NULL; ++ } ++ ++ query = xstrdup_printf("SELECT name,federation FROM %s%s;", ++ cluster_table, extra); ++ xfree(extra); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ rc = 0; ++ ret_list = list_create(xfree_ptr); ++ ++ if (!pgsql_num_rows(result)) { ++ pgsql_free_result(&result); ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(query); ++ return ret_list; ++ } ++ xfree(query); ++ ++ assoc_char = xstrdup_printf("t2.acct='root'"); ++ ++ user_name = uid_to_string((uid_t) uid); ++ while ((row = pgsql_fetch_row(result))) { ++ char *object = xstrdup(row[0]); ++ if (!jobs_running) { ++ /* strdup the cluster name because ret_list will be ++ * flushed if there are running jobs. This will cause an ++ * invalid read because _check_jobs_before_remove() will ++ * still try to access "cluster_name" which was ++ * "object". */ ++ list_append(ret_list, xstrdup(object)); ++ } ++ ++ if (row[1] && (*row[1] != '\0')) ++ fed_update = true; ++ ++ xfree(name_char); ++ xstrfmtcat(name_char, "name='%s'", object); ++ /* We should not need to delete any cluster usage just set it ++ * to deleted */ ++ xstrfmtcat(query, ++ "UPDATE \"%s_%s\" SET time_end=%ld WHERE time_end=0;" ++ "UPDATE \"%s_%s\" SET mod_time=%ld, deleted=1;" ++ "UPDATE \"%s_%s\" SET mod_time=%ld, deleted=1;" ++ "UPDATE \"%s_%s\" SET mod_time=%ld, deleted=1;", ++ object, event_table, now, ++ object, cluster_day_table, now, ++ object, cluster_hour_table, now, ++ object, cluster_month_table, now); ++ rc = remove_common(pgsql_conn, DBD_REMOVE_CLUSTERS, now, ++ user_name, cluster_table, name_char, ++ assoc_char, object, ret_list, &jobs_running); ++ xfree(object); ++ if (rc != SLURM_SUCCESS) ++ break; ++ } ++ pgsql_free_result(&result); ++ xfree(user_name); ++ xfree(name_char); ++ xfree(assoc_char); ++ ++ if (rc != SLURM_SUCCESS) { ++ FREE_NULL_LIST(ret_list); ++ xfree(query); ++ return NULL; ++ } ++ if (!jobs_running) { ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ reset_pgsql_conn(pgsql_conn); ++ FREE_NULL_LIST(ret_list); ++ return NULL; ++ } ++ ++ /* We need to remove these clusters from the wckey table */ ++ memset(&wckey_cond, 0, sizeof(slurmdb_wckey_cond_t)); ++ wckey_cond.cluster_list = ret_list; ++ tmp_list = as_pgsql_remove_wckeys(pgsql_conn, uid, &wckey_cond); ++ FREE_NULL_LIST(tmp_list); ++ ++ itr = list_iterator_create(ret_list); ++ while ((object = list_next(itr))) { ++ if ((rc = remove_cluster_tables(pgsql_conn, object)) ++ != SLURM_SUCCESS) ++ break; ++ cluster_name = xstrdup(object); ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_REMOVE_CLUSTER, ++ cluster_name) != SLURM_SUCCESS) ++ xfree(cluster_name); ++ } ++ list_iterator_destroy(itr); ++ ++ if (rc != SLURM_SUCCESS) { ++ reset_pgsql_conn(pgsql_conn); ++ FREE_NULL_LIST(ret_list); ++ errno = rc; ++ return NULL; ++ } ++ ++ if (fed_update) ++ as_pgsql_add_feds_to_update_list(pgsql_conn); ++ ++ errno = SLURM_SUCCESS; ++ } else ++ errno = ESLURM_JOBS_RUNNING_ON_ASSOC; ++ ++ xfree(query); ++ ++ return ret_list; ++} ++ ++extern List as_pgsql_get_clusters(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_cluster_cond_t *cluster_cond) ++{ ++ char *query = NULL; ++ char *extra = NULL; ++ char *tmp = NULL; ++ List cluster_list = NULL; ++ ListIterator itr = NULL; ++ int i=0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ slurmdb_assoc_cond_t assoc_cond; ++ ListIterator assoc_itr = NULL; ++ slurmdb_cluster_rec_t *cluster = NULL; ++ slurmdb_assoc_rec_t *assoc = NULL; ++ List assoc_list = NULL; ++ ++ /* if this changes you will need to edit the corresponding enum */ ++ char *cluster_req_inx[] = { ++ "name", ++ "classification", ++ "control_host", ++ "control_port", ++ "features", ++ "federation", ++ "fed_id", ++ "fed_state", ++ "rpc_version", ++ "dimensions", ++ "flags", ++ "plugin_id_select" ++ }; ++ enum { ++ CLUSTER_REQ_NAME, ++ CLUSTER_REQ_CLASS, ++ CLUSTER_REQ_CH, ++ CLUSTER_REQ_CP, ++ CLUSTER_REQ_FEATURES, ++ CLUSTER_REQ_FEDR, ++ CLUSTER_REQ_FEDID, ++ CLUSTER_REQ_FEDSTATE, ++ CLUSTER_REQ_VERSION, ++ CLUSTER_REQ_DIMS, ++ CLUSTER_REQ_FLAGS, ++ CLUSTER_REQ_PI_SELECT, ++ CLUSTER_REQ_COUNT ++ }; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ ++ if (!cluster_cond) { ++ xstrcat(extra, " WHERE deleted=0"); ++ goto empty; ++ } ++ ++ _setup_cluster_cond_limits(cluster_cond, &extra); ++ ++empty: ++ ++ xfree(tmp); ++ i=0; ++ xstrfmtcat(tmp, "%s", cluster_req_inx[i]); ++ for(i=1; iconn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ cluster_list = list_create(slurmdb_destroy_cluster_rec); ++ ++ memset(&assoc_cond, 0, sizeof(slurmdb_assoc_cond_t)); ++ ++ if (cluster_cond) { ++ /* I don't think we want the with_usage flag here. ++ * We do need the with_deleted though. */ ++ //assoc_cond.with_usage = cluster_cond->with_usage; ++ assoc_cond.with_deleted = cluster_cond->with_deleted; ++ } ++ assoc_cond.cluster_list = list_create(NULL); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ pgsql_res_t *result2 = NULL; ++ pgsql_row row2; ++ char *features = NULL; ++ cluster = xmalloc(sizeof(slurmdb_cluster_rec_t)); ++ slurmdb_init_cluster_rec(cluster, 0); ++ list_append(cluster_list, cluster); ++ ++ cluster->name = xstrdup(row[CLUSTER_REQ_NAME]); ++ ++ list_append(assoc_cond.cluster_list, cluster->name); ++ ++ cluster->classification = slurm_atoul(row[CLUSTER_REQ_CLASS]); ++ cluster->control_host = xstrdup(row[CLUSTER_REQ_CH]); ++ cluster->control_port = slurm_atoul(row[CLUSTER_REQ_CP]); ++ cluster->fed.name = xstrdup(row[CLUSTER_REQ_FEDR]); ++ features = row[CLUSTER_REQ_FEATURES]; ++ if (features && *features) { ++ cluster->fed.feature_list = list_create(xfree_ptr); ++ slurm_addto_char_list(cluster->fed.feature_list, ++ features); ++ } ++ cluster->fed.id = slurm_atoul(row[CLUSTER_REQ_FEDID]); ++ cluster->fed.state = slurm_atoul(row[CLUSTER_REQ_FEDSTATE]); ++ cluster->rpc_version = slurm_atoul(row[CLUSTER_REQ_VERSION]); ++ cluster->dimensions = slurm_atoul(row[CLUSTER_REQ_DIMS]); ++ cluster->flags = slurm_atoul(row[CLUSTER_REQ_FLAGS]); ++ cluster->plugin_id_select = ++ slurm_atoul(row[CLUSTER_REQ_PI_SELECT]); ++ ++ query = xstrdup_printf( ++ "SELECT tres, cluster_nodes FROM " ++ "\"%s_%s\" WHERE time_end=0 AND node_name='' limit 1", ++ cluster->name, event_table); ++ DB_DEBUG(DB_TRES, pgsql_conn->conn, "query\n%s", query); ++ result2 = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result2), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result2); ++ continue; ++ } ++ ++ if ((row2 = pgsql_fetch_row(result2))) { ++ cluster->tres_str = xstrdup(row2[0]); ++ if (row2[1] && row2[1][0]) ++ cluster->nodes = xstrdup(row2[1]); ++ } ++ pgsql_free_result(&result2); ++ ++ /* get the usage if requested */ ++ if (cluster_cond && cluster_cond->with_usage) { ++ as_pgsql_get_usage( ++ pgsql_conn, uid, cluster, ++ DBD_GET_CLUSTER_USAGE, ++ cluster_cond->usage_start, ++ cluster_cond->usage_end); ++ } ++ ++ } ++ pgsql_free_result(&result); ++ ++ if (!list_count(assoc_cond.cluster_list)) { ++ FREE_NULL_LIST(assoc_cond.cluster_list); ++ return cluster_list; ++ } ++ ++ assoc_cond.acct_list = list_create(NULL); ++ list_append(assoc_cond.acct_list, "root"); ++ ++ assoc_cond.user_list = list_create(NULL); ++ list_append(assoc_cond.user_list, ""); ++ ++ assoc_list = as_pgsql_get_assocs(pgsql_conn, uid, &assoc_cond); ++ FREE_NULL_LIST(assoc_cond.cluster_list); ++ FREE_NULL_LIST(assoc_cond.acct_list); ++ FREE_NULL_LIST(assoc_cond.user_list); ++ ++ if (!assoc_list) ++ return cluster_list; ++ ++ itr = list_iterator_create(cluster_list); ++ assoc_itr = list_iterator_create(assoc_list); ++ while ((cluster = list_next(itr))) { ++ while ((assoc = list_next(assoc_itr))) { ++ if (xstrcmp(assoc->cluster, cluster->name)) ++ continue; ++ ++ if (cluster->root_assoc) { ++ debug("This cluster %s already has " ++ "an association.", cluster->name); ++ continue; ++ } ++ cluster->root_assoc = assoc; ++ list_remove(assoc_itr); ++ } ++ list_iterator_reset(assoc_itr); ++ } ++ list_iterator_destroy(itr); ++ list_iterator_destroy(assoc_itr); ++ if (list_count(assoc_list)) ++ error("I have %d left over associations", ++ list_count(assoc_list)); ++ FREE_NULL_LIST(assoc_list); ++ ++ return cluster_list; ++} ++ ++extern List as_pgsql_get_cluster_events(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_event_cond_t *event_cond) ++{ ++ char *query = NULL; ++ char *extra = NULL; ++ char *tmp = NULL; ++ List ret_list = NULL; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ int set = 0; ++ int i=0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ time_t now = time(NULL); ++ List use_cluster_list = NULL; ++ slurmdb_user_rec_t user; ++ bool locked = false; ++ ++ /* if this changes you will need to edit the corresponding enum */ ++ char *event_req_inx[] = { ++ "cluster_nodes", ++ "node_name", ++ "state", ++ "time_start", ++ "time_end", ++ "reason", ++ "reason_uid", ++ "tres", ++ }; ++ ++ enum { ++ EVENT_REQ_CNODES, ++ EVENT_REQ_NODE, ++ EVENT_REQ_STATE, ++ EVENT_REQ_START, ++ EVENT_REQ_END, ++ EVENT_REQ_REASON, ++ EVENT_REQ_REASON_UID, ++ EVENT_REQ_TRES, ++ EVENT_REQ_COUNT ++ }; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (slurm_conf.private_data & PRIVATE_DATA_EVENTS) { ++ if (!is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { ++ error("UID %u tried to access events, only administrators can look at events", ++ uid); ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ } ++ ++ if (!event_cond) ++ goto empty; ++ ++ if (event_cond->cpus_min) { ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " WHERE ("); ++ ++ if (event_cond->cpus_max) { ++ xstrfmtcat(extra, "count BETWEEN %u AND %u)", ++ event_cond->cpus_min, event_cond->cpus_max); ++ ++ } else { ++ xstrfmtcat(extra, "count='%u')", ++ event_cond->cpus_min); ++ ++ } ++ } ++ ++ switch(event_cond->event_type) { ++ case SLURMDB_EVENT_ALL: ++ break; ++ case SLURMDB_EVENT_CLUSTER: ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " WHERE ("); ++ xstrcat(extra, "node_name = '')"); ++ ++ break; ++ case SLURMDB_EVENT_NODE: ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " WHERE ("); ++ xstrcat(extra, "node_name != '')"); ++ ++ break; ++ default: ++ error("Unknown event %u doing all", event_cond->event_type); ++ break; ++ } ++ ++ if (event_cond->node_list) { ++ int dims = 0; ++ hostlist_t temp_hl = NULL; ++ ++ if (get_cluster_dims(pgsql_conn, ++ (char *)list_peek(event_cond->cluster_list), ++ &dims)) ++ return NULL; ++ ++ temp_hl = hostlist_create_dims(event_cond->node_list, dims); ++ if (hostlist_count(temp_hl) <= 0) { ++ error("we didn't get any real hosts to look for."); ++ return NULL; ++ } ++ ++ set = 0; ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " WHERE ("); ++ ++ while ((object = hostlist_shift(temp_hl))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "node_name='%s'", object); ++ set = 1; ++ free(object); ++ } ++ xstrcat(extra, ")"); ++ hostlist_destroy(temp_hl); ++ } ++ ++ if (event_cond->period_start) { ++ if (!event_cond->period_end) ++ event_cond->period_end = now; ++ ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " WHERE ("); ++ ++ xstrfmtcat(extra, ++ "(time_start < %ld) " ++ "AND (time_end >= %ld OR time_end = 0))", ++ event_cond->period_end, event_cond->period_start); ++ } ++ ++ if (event_cond->reason_list ++ && list_count(event_cond->reason_list)) { ++ set = 0; ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " WHERE ("); ++ itr = list_iterator_create(event_cond->reason_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "reason like '%%%s%%'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (event_cond->reason_uid_list ++ && list_count(event_cond->reason_uid_list)) { ++ set = 0; ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " WHERE ("); ++ itr = list_iterator_create(event_cond->reason_uid_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "reason_uid='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (event_cond->state_list ++ && list_count(event_cond->state_list)) { ++ set = 0; ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " WHERE ("); ++ itr = list_iterator_create(event_cond->state_list); ++ while ((object = list_next(itr))) { ++ uint32_t tmp_state = strtol(object, NULL, 10); ++ if (set) ++ xstrcat(extra, " OR "); ++ if (tmp_state & NODE_STATE_BASE) ++ xstrfmtcat(extra, "(state&%u)=%u", ++ NODE_STATE_BASE, ++ tmp_state & NODE_STATE_BASE); ++ else ++ xstrfmtcat(extra, "state&%u", tmp_state); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++empty: ++ xfree(tmp); ++ xstrfmtcat(tmp, "%s", event_req_inx[0]); ++ for(i=1; icluster_list && ++ list_count(event_cond->cluster_list)) { ++ use_cluster_list = event_cond->cluster_list; ++ } else { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = list_shallow_copy(as_pgsql_cluster_list); ++ locked = true; ++ } ++ ++ ret_list = list_create(slurmdb_destroy_event_rec); ++ ++ itr = list_iterator_create(use_cluster_list); ++ while ((object = list_next(itr))) { ++ query = xstrdup_printf("SELECT %s FROM \"%s_%s\"", ++ tmp, object, event_table); ++ if (extra) ++ xstrfmtcat(query, " %s", extra); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ if (pgsql_db_match_state(pgsql_errno(result), PGSQL_UNDEFINE_TABLE)) { ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ } ++ pgsql_free_result(&result); ++ break; ++ } ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_event_rec_t *event = ++ xmalloc(sizeof(slurmdb_event_rec_t)); ++ ++ list_append(ret_list, event); ++ ++ event->cluster = xstrdup(object); ++ ++ if (row[EVENT_REQ_NODE] && row[EVENT_REQ_NODE][0]) { ++ event->node_name = xstrdup(row[EVENT_REQ_NODE]); ++ event->event_type = SLURMDB_EVENT_NODE; ++ } else ++ event->event_type = SLURMDB_EVENT_CLUSTER; ++ ++ event->state = slurm_atoul(row[EVENT_REQ_STATE]); ++ event->period_start = slurm_atoul(row[EVENT_REQ_START]); ++ event->period_end = slurm_atoul(row[EVENT_REQ_END]); ++ ++ if (row[EVENT_REQ_REASON] && row[EVENT_REQ_REASON][0]) ++ event->reason = xstrdup(row[EVENT_REQ_REASON]); ++ event->reason_uid = ++ slurm_atoul(row[EVENT_REQ_REASON_UID]); ++ ++ if (row[EVENT_REQ_CNODES] && row[EVENT_REQ_CNODES][0]) ++ event->cluster_nodes = ++ xstrdup(row[EVENT_REQ_CNODES]); ++ ++ if (row[EVENT_REQ_TRES] && row[EVENT_REQ_TRES][0]) ++ event->tres_str = xstrdup(row[EVENT_REQ_TRES]); ++ } ++ pgsql_free_result(&result); ++ } ++ list_iterator_destroy(itr); ++ xfree(tmp); ++ xfree(extra); ++ ++ if (locked) { ++ FREE_NULL_LIST(use_cluster_list); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ ++ return ret_list; ++} ++ ++extern int as_pgsql_node_down(pgsql_conn_t *pgsql_conn, ++ node_record_t *node_ptr, ++ time_t event_time, char *reason, ++ uint32_t reason_uid) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ char *my_reason; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!pgsql_conn->cluster_name) { ++ error("%s:%d no cluster name", THIS_FILE, __LINE__); ++ return SLURM_ERROR; ++ } ++ ++ if (!node_ptr) { ++ error("No node_ptr given!"); ++ return SLURM_ERROR; ++ } ++ ++ if (!node_ptr->tres_str) { ++ error("node ptr has no tres_list!"); ++ return SLURM_ERROR; ++ } ++ ++ query = xstrdup_printf("SELECT state, reason, time_start FROM \"%s_%s\" WHERE " ++ "time_end=0 AND node_name='%s';", ++ pgsql_conn->cluster_name, event_table, ++ node_ptr->name); ++ /* info("%d(%s:%d) query\n%s", */ ++ /* pgsql_conn->conn, THIS_FILE, __LINE__, query); */ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ if (reason) ++ my_reason = reason; ++ else ++ my_reason = node_ptr->reason; ++ ++ if (!my_reason) ++ my_reason = ""; ++ ++ row = pgsql_fetch_row(result); ++ if (row && (node_ptr->node_state == slurm_atoul(row[0])) && ++ !xstrcasecmp(my_reason, row[1])) { ++ DB_DEBUG(DB_EVENT, pgsql_conn->conn, ++ "no change to %s(%s) needed %u == %s and %s == %s", ++ node_ptr->name, pgsql_conn->cluster_name, ++ node_ptr->node_state, row[0], my_reason, row[1]); ++ pgsql_free_result(&result); ++ return SLURM_SUCCESS; ++ } ++ ++ if (row && (event_time == slurm_atoul(row[2]))) { ++ /* ++ * If you are clean-restarting the controller over and over ++ * again you could get records that are duplicates in the ++ * database. If this is the case we will zero out the time_end ++ * we are just filled in. This will cause the last time to be ++ * erased from the last restart, but if you are restarting ++ * things this often the pervious one didn't mean anything ++ * anyway. This way we only get one for the last time we let it ++ * run. ++ */ ++ query = xstrdup_printf( ++ "UPDATE \"%s_%s\" SET reason='%s' WHERE " ++ "time_start=%ld AND node_name='%s';", ++ pgsql_conn->cluster_name, event_table, ++ my_reason, event_time, node_ptr->name); ++ DB_DEBUG(DB_EVENT, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ pgsql_free_result(&result); ++ return rc; ++ } ++ ++ pgsql_free_result(&result); ++ ++ DB_DEBUG(DB_EVENT, pgsql_conn->conn, ++ "inserting %s(%s) with tres of '%s'", ++ node_ptr->name, pgsql_conn->cluster_name, node_ptr->tres_str); ++ ++ query = xstrdup_printf( ++ "UPDATE \"%s_%s\" SET time_end=%ld WHERE " ++ "time_end=0 AND node_name='%s';", ++ pgsql_conn->cluster_name, event_table, ++ event_time, node_ptr->name); ++ ++ /* ++ * Reason for "on duplicate": slurmctld will send a time_start based on ++ * the state of the "node_state" state file. If the the slurmctld is ++ * "killed" before updating the state file, the slurmctld can send the ++ * same time_start for the node and cause a "Duplicate entry" error. ++ * This can particually happen when doing clean starts. ++ */ ++ xstrfmtcat(query, ++ "INSERT INTO \"%s_%s\" " ++ "(node_name, state, tres, time_start, " ++ "reason, reason_uid) " ++ "VALUES ('%s', %u, '%s', %ld, '%s', %u) " ++ "ON CONFLICT (node_name,time_start) DO UPDATE SET time_end=0;", ++ pgsql_conn->cluster_name, event_table, ++ node_ptr->name, node_ptr->node_state, ++ node_ptr->tres_str, event_time, my_reason, reason_uid); ++ DB_DEBUG(DB_EVENT, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ return rc; ++} ++ ++extern int as_pgsql_node_up(pgsql_conn_t *pgsql_conn, ++ node_record_t *node_ptr, ++ time_t event_time) ++{ ++ char* query; ++ int rc = SLURM_SUCCESS; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!pgsql_conn->cluster_name) { ++ error("%s:%d no cluster name", THIS_FILE, __LINE__); ++ return SLURM_ERROR; ++ } ++ ++ query = xstrdup_printf( ++ "UPDATE \"%s_%s\" SET time_end=%ld WHERE " ++ "time_end=0 AND node_name='%s';", ++ pgsql_conn->cluster_name, event_table, ++ event_time, node_ptr->name); ++ DB_DEBUG(DB_EVENT, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ return rc; ++} ++ ++/* This function is not used in the slurmdbd. */ ++extern int as_pgsql_register_ctld(pgsql_conn_t *pgsql_conn, ++ char *cluster, uint16_t port) ++{ ++ return SLURM_ERROR; ++} ++ ++extern int as_pgsql_fini_ctld(pgsql_conn_t *pgsql_conn, ++ slurmdb_cluster_rec_t *cluster_rec) ++{ ++ int rc = SLURM_SUCCESS; ++ time_t now = time(NULL); ++ char *query = NULL; ++ bool free_it = false; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ /* Here we need to check make sure we are updating the entry ++ correctly just in case the backup has already gained ++ control. If we check the ip and port it is a pretty safe ++ bet we have the right ctld. ++ */ ++ query = xstrdup_printf( ++ "UPDATE %s SET mod_time=%ld, control_host='', " ++ "control_port=0 WHERE name='%s' AND " ++ "control_host='%s' AND control_port=%u;", ++ cluster_table, now, cluster_rec->name, ++ cluster_rec->control_host, cluster_rec->control_port); ++ DB_DEBUG(DB_EVENT, pgsql_conn->conn, "query\n%s", query); ++ SQLHSTMT stmt_ptr = SQL_NULL_HSTMT; ++ rc = pgsql_db_query_stmt(pgsql_conn, query, &stmt_ptr); ++ xfree(query); ++ ++ if (rc != SLURM_SUCCESS) ++ { ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ return SLURM_ERROR; ++ } ++ ++ if (!last_affected_rows(stmt_ptr) || !slurmdbd_conf->track_ctld || ++ (cluster_rec->flags & CLUSTER_FLAG_EXT)) { ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ return rc; ++ } ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ /* If tres is NULL we can get the current number of tres by ++ sending NULL for the tres param in the as_pgsql_cluster_tres ++ function. ++ */ ++ if (!cluster_rec->tres_str) { ++ free_it = true; ++ as_pgsql_cluster_tres( ++ pgsql_conn, cluster_rec->control_host, ++ &cluster_rec->tres_str, now, ++ cluster_rec->rpc_version); ++ } ++ ++ /* Since as_pgsql_cluster_tres could change the ++ last_affected_rows we can't group this with the above ++ return. ++ */ ++ if (!cluster_rec->tres_str) ++ return rc; ++ ++ /* If we affected things we need to now drain the nodes in the ++ * cluster. This is to give better stats on accounting that ++ * the ctld was gone so no jobs were able to be scheduled. We ++ * drain the nodes since the rollup functionality understands ++ * how to deal with that and running jobs so we don't get bad ++ * info. ++ */ ++ query = xstrdup_printf( ++ "INSERT INTO \"%s_%s\" (tres, state, time_start, reason) " ++ "VALUES ('%s', %u, %ld, 'slurmctld disconnect');", ++ cluster_rec->name, event_table, ++ cluster_rec->tres_str, NODE_STATE_DOWN, (long)now); ++ ++ if (free_it) ++ xfree(cluster_rec->tres_str); ++ ++ DB_DEBUG(DB_EVENT, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ return rc; ++} ++ ++extern int as_pgsql_cluster_tres(pgsql_conn_t *pgsql_conn, ++ char *cluster_nodes, char **tres_str_in, ++ time_t event_time, uint16_t rpc_version) ++{ ++ char* query; ++ int rc = SLURM_SUCCESS; ++ int response = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ bool handle_disconnect = true; ++ ++ xassert(tres_str_in); ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!pgsql_conn->cluster_name) { ++ error("%s:%d no cluster name", THIS_FILE, __LINE__); ++ return SLURM_ERROR; ++ } ++ ++ /* Record the processor count */ ++ query = xstrdup_printf( ++ "SELECT tres, cluster_nodes FROM \"%s_%s\" WHERE " ++ "time_end=0 AND node_name='' AND state=0 limit 1", ++ pgsql_conn->cluster_name, event_table); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ if (pgsql_db_match_state(pgsql_errno(result), PGSQL_UNDEFINE_TABLE)) ++ rc = ESLURM_ACCESS_DENIED; ++ else ++ rc = SLURM_ERROR; ++ pgsql_free_result(&result); ++ return rc; ++ } ++ ++ /* we only are checking the first one here */ ++ if (!(row = pgsql_fetch_row(result))) { ++ debug("We don't have an entry for this machine %s " ++ "most likely a first time running.", ++ pgsql_conn->cluster_name); ++ ++ /* Get all nodes in a down state and jobs pending or running. ++ * This is for the first time a cluster registers ++ * ++ * We will return ACCOUNTING_FIRST_REG so this ++ * is taken care of since the message thread ++ * may not be up when we run this in the controller or ++ * in the slurmdbd. ++ */ ++ if (!*tres_str_in) { ++ rc = 0; ++ goto end_it; ++ } ++ ++ response = ACCOUNTING_FIRST_REG; ++ goto add_it; ++ } ++ ++ /* If tres is NULL we want to return the tres for this cluster */ ++ if (!*tres_str_in) { ++ *tres_str_in = xstrdup(row[0]); ++ goto end_it; ++ } else if (xstrcmp(*tres_str_in, row[0])) { ++ debug("%s has changed tres from %s to %s", ++ pgsql_conn->cluster_name, ++ row[0], *tres_str_in); ++ ++ /* ++ * Reset all the entries for this cluster since the tres changed ++ * some of the downed nodes may have gone away. ++ * Request them again with ACCOUNTING_NODES_CHANGE_DB ++ */ ++ ++ if (xstrcmp(cluster_nodes, row[1])) { ++ DB_DEBUG(DB_EVENT, pgsql_conn->conn, ++ "Nodes on the cluster have changed."); ++ response = ACCOUNTING_NODES_CHANGE_DB; ++ } else ++ response = ACCOUNTING_TRES_CHANGE_DB; ++ } else if (xstrcmp(cluster_nodes, row[1])) { ++ DB_DEBUG(DB_EVENT, pgsql_conn->conn, ++ "Node names on the cluster have changed."); ++ response = ACCOUNTING_NODES_CHANGE_DB; ++ } else { ++ DB_DEBUG(DB_EVENT, pgsql_conn->conn, ++ "We have the same TRES and node names as before for %s, no need to update the database.", ++ pgsql_conn->cluster_name); ++ goto remove_disconnect; ++ } ++ ++ query = xstrdup_printf( ++ "UPDATE \"%s_%s\" SET time_end=%ld WHERE time_end=0", ++ pgsql_conn->cluster_name, event_table, event_time); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ handle_disconnect = false; ++ ++ if (rc != SLURM_SUCCESS) ++ goto end_it; ++add_it: ++ query = xstrdup_printf( ++ "INSERT INTO \"%s_%s\" (cluster_nodes, tres, " ++ "time_start, reason) " ++ "VALUES ('%s', '%s', %ld, 'Cluster Registered TRES');", ++ pgsql_conn->cluster_name, event_table, ++ cluster_nodes, *tres_str_in, event_time); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ if (rc != SLURM_SUCCESS) ++ goto end_it; ++ ++remove_disconnect: ++ /* ++ * The above update clears all with time_end=0, so no ++ * need to do this again. ++ */ ++ if (handle_disconnect) { ++ query = xstrdup_printf( ++ "UPDATE \"%s_%s\" SET time_end=%ld WHERE time_end=0 AND state=%u AND node_name='';", ++ pgsql_conn->cluster_name, ++ event_table, event_time, ++ NODE_STATE_DOWN); ++ (void) pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ } ++ ++end_it: ++ pgsql_free_result(&result); ++ if (response && rc == SLURM_SUCCESS) ++ rc = response; ++ ++ return rc; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_cluster.h b/src/plugins/accounting_storage/pgsql/as_pgsql_cluster.h +new file mode 100755 +index 0000000..ff7bc88 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_cluster.h +@@ -0,0 +1,81 @@ ++/*****************************************************************************\ ++ * as_pgsql_cluster.h - functions dealing with clusters. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++#ifndef _HAVE_PGSQL_CLUSTER_H ++#define _HAVE_PGSQL_CLUSTER_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_add_clusters(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List cluster_list); ++ ++extern List as_pgsql_modify_clusters(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_cluster_cond_t *cluster_cond, ++ slurmdb_cluster_rec_t *cluster); ++ ++extern List as_pgsql_remove_clusters(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_cluster_cond_t *cluster_cond); ++ ++extern List as_pgsql_get_clusters(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_cluster_cond_t *cluster_cond); ++ ++extern List as_pgsql_get_cluster_events(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_event_cond_t *event_cond); ++ ++extern int as_pgsql_node_down(pgsql_conn_t *pgsql_conn, ++ node_record_t *node_ptr, ++ time_t event_time, char *reason, ++ uint32_t reason_uid); ++ ++extern int as_pgsql_node_up(pgsql_conn_t *pgsql_conn, node_record_t *node_ptr, ++ time_t event_time); ++ ++extern int as_pgsql_register_ctld(pgsql_conn_t *pgsql_conn, ++ char *cluster, uint16_t port); ++ ++extern int as_pgsql_fini_ctld(pgsql_conn_t *pgsql_conn, ++ slurmdb_cluster_rec_t *cluster_rec); ++ ++extern int as_pgsql_cluster_tres(pgsql_conn_t *pgsql_conn, ++ char *cluster_nodes, char **tres_str_in, ++ time_t event_time, uint16_t rpc_version); ++ ++extern int as_pgsql_get_fed_cluster_id(pgsql_conn_t *pgsql_conn, ++ const char *cluster, ++ const char *federation, ++ int last_id, int *ret_id); ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_convert.c b/src/plugins/accounting_storage/pgsql/as_pgsql_convert.c +new file mode 100755 +index 0000000..6c34e82 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_convert.c +@@ -0,0 +1,366 @@ ++/*****************************************************************************\ ++ * as_pgsql_convert.c - functions dealing with converting from tables in ++ * slurm <= 17.02. ++ ***************************************************************************** ++ * Copyright (C) 2015 SchedMD LLC. ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_convert.h" ++#include "as_pgsql_tres.h" ++#include "src/common/slurm_jobacct_gather.h" ++ ++/* ++ * Any time you have to add to an existing convert update this number. ++ * NOTE: 8 was the first version of 20.02. ++ * NOTE: 9 was the first version of 20.11. ++ * NOTE: 10 was the first version of 21.08. ++ */ ++#define CONVERT_VERSION 10 ++ ++typedef struct { ++ uint64_t count; ++ uint32_t id; ++} local_tres_t; ++ ++static uint32_t db_curr_ver = NO_VAL; ++ ++static int _convert_step_table_post( ++ pgsql_conn_t *pgsql_conn, char *cluster_name) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ ++ if (db_curr_ver < 9) { ++ /* ++ * Change the names pack_job_id and pack_job_offset to be het_* ++ */ ++ query = xstrdup_printf( ++ "update \"%s_%s\" set id_step = %d where id_step = -2;" ++ "update \"%s_%s\" set id_step = %d where id_step = -1;", ++ cluster_name, step_table, SLURM_BATCH_SCRIPT, ++ cluster_name, step_table, SLURM_EXTERN_CONT); ++ } ++ ++ if (query) { ++ DB_DEBUG(DB_QUERY, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) ++ error("%s: Can't convert %s_%s info: %m", ++ __func__, cluster_name, step_table); ++ } ++ ++ return rc; ++} ++ ++static int _rename_usage_columns(pgsql_conn_t *pgsql_conn, char *table) ++{ ++ char *query = NULL; ++ int rc = SLURM_SUCCESS; ++ ++ ++ /* ++ * Change the names pack_job_id and pack_job_offset to be het_* ++ */ ++ query = xstrdup_printf( ++ "ALTER TABLE %s RENAME COLUMN resv_secs TO plan_secs;" ++ "ALTER TABLE %s ALTER COLUMN plan_secs TYPE bigint;" ++ "ALTER TABLE %s ALTER COLUMN plan_secs SET NOT NULL;" ++ "ALTER TABLE %s ALTER COLUMN plan_secs SET DEFAULT 0;", ++ table, table, table, table); ++ ++ DB_DEBUG(DB_QUERY, pgsql_conn->conn, "query\n%s", query); ++ if ((rc = as_pgsql_convert_alter_query(pgsql_conn, query)) != ++ SLURM_SUCCESS) ++ error("Can't update %s %m", table); ++ xfree(query); ++ ++ return rc; ++} ++ ++static int _convert_usage_table_pre(pgsql_conn_t *pgsql_conn, ++ char *cluster_name) ++{ ++ int rc = SLURM_SUCCESS; ++ ++ if (db_curr_ver < 10) { ++ char table[200]; ++ ++ snprintf(table, sizeof(table), "\"%s_%s\"", ++ cluster_name, cluster_day_table); ++ if ((rc = _rename_usage_columns(pgsql_conn, table)) ++ != SLURM_SUCCESS) ++ return rc; ++ ++ snprintf(table, sizeof(table), "\"%s_%s\"", ++ cluster_name, cluster_hour_table); ++ if ((rc = _rename_usage_columns(pgsql_conn, table)) ++ != SLURM_SUCCESS) ++ return rc; ++ ++ snprintf(table, sizeof(table), "\"%s_%s\"", ++ cluster_name, cluster_month_table); ++ if ((rc = _rename_usage_columns(pgsql_conn, table)) ++ != SLURM_SUCCESS) ++ return rc; ++ } ++ ++ return rc; ++} ++ ++static int _convert_job_table_pre(pgsql_conn_t *pgsql_conn, char *cluster_name) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ ++ if (db_curr_ver < 8) { ++ /* ++ * Change the names pack_job_id and pack_job_offset to be het_* ++ */ ++ query = xstrdup_printf( ++ "ALTER TABLE \"%s_%s\" RENAME COLUMN pack_job_id TO het_job_id;" ++ "ALTER TABLE \"%s_%s\" ALTER COLUMN het_job_id TYPE integer;" ++ "ALTER TABLE \"%s_%s\" ALTER COLUMN het_job_id SET NOT NULL;" ++ "ALTER TABLE \"%s_%s\" RENAME COLUMN pack_job_offset TO het_job_offset;" ++ "ALTER TABLE \"%s_%s\" ALTER COLUMN het_job_offset TYPE integer;" ++ "ALTER TABLE \"%s_%s\" ALTER COLUMN het_job_offset SET NOT NULL;", ++ cluster_name, job_table, cluster_name, job_table, cluster_name, job_table, ++ cluster_name, job_table, cluster_name, job_table, cluster_name, job_table); ++ } ++ ++ if (query) { ++ DB_DEBUG(DB_QUERY, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = as_pgsql_convert_alter_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) ++ error("%s: Can't convert %s_%s info: %m", ++ __func__, cluster_name, job_table); ++ } ++ ++ return rc; ++} ++ ++static int _set_db_curr_ver(pgsql_conn_t *pgsql_conn) ++{ ++ char *query; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ int rc = SLURM_SUCCESS; ++ ++ if (db_curr_ver != NO_VAL) ++ return SLURM_SUCCESS; ++ ++ query = xstrdup_printf("select version from %s", convert_version_table); ++ debug4("%d(%s:%d) query\n%s", pgsql_conn->conn, ++ THIS_FILE, __LINE__, query); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ row = pgsql_fetch_row(result); ++ ++ if (row) { ++ db_curr_ver = slurm_atoul(row[0]); ++ pgsql_free_result(&result); ++ } else { ++ int tmp_ver = 0; ++ pgsql_free_result(&result); ++ ++ /* no valid clusters, just return */ ++ if (as_pgsql_total_cluster_list && ++ !list_count(as_pgsql_total_cluster_list)) ++ tmp_ver = CONVERT_VERSION; ++ ++ query = xstrdup_printf("insert into %s (version) values (%d);", ++ convert_version_table, tmp_ver); ++ debug4("(%s:%d) query\n%s", THIS_FILE, __LINE__, query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) ++ return SLURM_ERROR; ++ db_curr_ver = tmp_ver; ++ } ++ ++ return rc; ++} ++ ++extern int as_pgsql_convert_tables_pre_create(pgsql_conn_t *pgsql_conn) ++{ ++ int rc = SLURM_SUCCESS; ++ ListIterator itr; ++ char *cluster_name; ++ ++ xassert(as_pgsql_total_cluster_list); ++ ++ if ((rc = _set_db_curr_ver(pgsql_conn)) != SLURM_SUCCESS) ++ return rc; ++ ++ if (db_curr_ver == CONVERT_VERSION) { ++ debug4("%s: No conversion needed, Horray!", __func__); ++ return SLURM_SUCCESS; ++ } else if (backup_dbd) { ++ /* ++ * We do not want to create/check the database if we are the ++ * backup (see Bug 3827). This is only handled on the primary. ++ * ++ * To avoid situations where someone might upgrade the database ++ * through the backup we want to fatal so they know what ++ * happened instead of potentially starting with the older ++ * database. ++ */ ++ fatal("Backup DBD can not convert database, please start the primary DBD before starting the backup."); ++ return SLURM_ERROR; ++ } ++ ++ /* make it up to date */ ++ itr = list_iterator_create(as_pgsql_total_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ /* ++ * When calling alters on tables here please remember to use ++ * as_pgsql_convert_alter_query instead of pgsql_db_query to be ++ * able to detect a previous failed conversion. ++ */ ++ info("pre-converting usage table for %s", cluster_name); ++ if ((rc = _convert_usage_table_pre(pgsql_conn, cluster_name) ++ != SLURM_SUCCESS)) ++ break; ++ info("pre-converting job table for %s", cluster_name); ++ if ((rc = _convert_job_table_pre(pgsql_conn, cluster_name) ++ != SLURM_SUCCESS)) ++ break; ++ } ++ list_iterator_destroy(itr); ++ ++ return rc; ++} ++ ++extern int as_pgsql_convert_tables_post_create(pgsql_conn_t *pgsql_conn) ++{ ++ int rc = SLURM_SUCCESS; ++ ListIterator itr; ++ char *cluster_name; ++ ++ xassert(as_pgsql_total_cluster_list); ++ ++ if ((rc = _set_db_curr_ver(pgsql_conn)) != SLURM_SUCCESS) ++ return rc; ++ ++ if (db_curr_ver == CONVERT_VERSION) { ++ debug4("%s: No conversion needed, Horray!", __func__); ++ return SLURM_SUCCESS; ++ } else if (backup_dbd) { ++ /* ++ * We do not want to create/check the database if we are the ++ * backup (see Bug 3827). This is only handled on the primary. ++ * ++ * To avoid situations where someone might upgrade the database ++ * through the backup we want to fatal so they know what ++ * happened instead of potentially starting with the older ++ * database. ++ */ ++ fatal("Backup DBD can not convert database, please start the primary DBD before starting the backup."); ++ return SLURM_ERROR; ++ } ++ ++ /* make it up to date */ ++ itr = list_iterator_create(as_pgsql_total_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ info("post-converting step table for %s", cluster_name); ++ if ((rc = _convert_step_table_post(pgsql_conn, cluster_name) ++ != SLURM_SUCCESS)) ++ break; ++ } ++ list_iterator_destroy(itr); ++ ++ return rc; ++} ++ ++extern int as_pgsql_convert_non_cluster_tables_post_create( ++ pgsql_conn_t *pgsql_conn) ++{ ++ int rc = SLURM_SUCCESS; ++ ++ if ((rc = _set_db_curr_ver(pgsql_conn)) != SLURM_SUCCESS) ++ return rc; ++ ++ if (db_curr_ver == CONVERT_VERSION) { ++ debug4("%s: No conversion needed, Horray!", __func__); ++ return SLURM_SUCCESS; ++ } ++ ++ if (rc == SLURM_SUCCESS) { ++ char *query = xstrdup_printf( ++ "update %s set version=%d, mod_time=UNIX_TIMESTAMP()", ++ convert_version_table, CONVERT_VERSION); ++ ++ info("Conversion done: success!"); ++ ++ debug4("(%s:%d) query\n%s", THIS_FILE, __LINE__, query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ } ++ ++ return rc; ++} ++ ++/* ++ * Only use this when running "ALTER TABLE" during an upgrade. This is to get ++ * around that pgsql cannot rollback an "ALTER TABLE", but its possible that the ++ * rest of the upgrade transaction was aborted. ++ * ++ * We may not always use this function, but don't delete it just in case we ++ * need to alter tables in the future. ++ */ ++extern int as_pgsql_convert_alter_query(pgsql_conn_t *pgsql_conn, char *query) ++{ ++ int rc = SLURM_SUCCESS; ++ pgsql_res_t *result = NULL; ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ if (pgsql_db_match_state(pgsql_errno(result), PGSQL_UNDEFINE_COLUMN)) { ++ info("The database appears to have been altered by a previous upgrade attempt, continuing with upgrade."); ++ } else { ++ rc = SLURM_ERROR; ++ } ++ } ++ pgsql_free_result(&result); ++ ++ return rc; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_convert.h b/src/plugins/accounting_storage/pgsql/as_pgsql_convert.h +new file mode 100755 +index 0000000..591008f +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_convert.h +@@ -0,0 +1,66 @@ ++/*****************************************************************************\ ++ * as_pgsql_convert.h - functions dealing with converting from tables in ++ * slurm <= 17.02. ++ ***************************************************************************** ++ * Copyright (C) 2015-2017 SchedMD LLC. ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_AS_PGSQL_CONVERT_H ++#define _HAVE_AS_PGSQL_CONVERT_H ++ ++#include "accounting_storage_pgsql.h" ++ ++/* Functions for converting tables before they are created in new schema */ ++extern int as_pgsql_convert_tables_pre_create(pgsql_conn_t *pgsql_conn); ++ ++/* Functions for converting tables after they are created */ ++extern int as_pgsql_convert_tables_post_create(pgsql_conn_t *pgsql_conn); ++ ++/* ++ * Functions for converting tables that aren't cluster centric as the other ++ * functions in this deal with. ++ */ ++extern int as_pgsql_convert_non_cluster_tables_post_create( ++ pgsql_conn_t *pgsql_conn); ++ ++/* ++ * Only use this when running "ALTER TABLE" during an upgrade. This is to get ++ * around that pgsql cannot rollback an "ALTER TABLE", but its possible that the ++ * rest of the upgrade transaction was aborted. ++ * ++ * We may not always use this function, but don't delete it just in case we ++ * need to alter tables in the future. ++ */ ++extern int as_pgsql_convert_alter_query(pgsql_conn_t *pgsql_conn, char *query); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_federation.c b/src/plugins/accounting_storage/pgsql/as_pgsql_federation.c +new file mode 100755 +index 0000000..58fd122 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_federation.c +@@ -0,0 +1,738 @@ ++/*****************************************************************************\ ++ * as_pgsql_federation.c - functions dealing with federations. ++ ***************************************************************************** ++ * Copyright (C) 2016 SchedMD LLC. ++ * Written by Brian Christiansen ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_federation.h" ++#include "as_pgsql_cluster.h" ++ ++char *fed_req_inx[] = { ++ "t1.name", ++ "t1.flags", ++}; ++enum { ++ FED_REQ_NAME, ++ FED_REQ_FLAGS, ++ FED_REQ_COUNT ++}; ++ ++static int _setup_federation_cond_limits(slurmdb_federation_cond_t *fed_cond, ++ char **extra) ++{ ++ int set = 0; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ ++ if (!fed_cond) ++ return 0; ++ ++ if (fed_cond->with_deleted) ++ xstrcat(*extra, " WHERE (t1.deleted=0 OR t1.deleted=1)"); ++ else ++ xstrcat(*extra, " WHERE t1.deleted=0"); ++ ++ if (fed_cond->cluster_list ++ && list_count(fed_cond->cluster_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(fed_cond->cluster_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "t2.name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (fed_cond->federation_list ++ && list_count(fed_cond->federation_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(fed_cond->federation_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "t1.name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ return set; ++} ++ ++static int _setup_federation_rec_limits(slurmdb_federation_rec_t *fed, ++ char **cols, char **vals, char **extra) ++{ ++ if (!fed) ++ return SLURM_ERROR; ++ ++ if (!(fed->flags & FEDERATION_FLAG_NOTSET)) { ++ uint32_t flags; ++ xstrcat(*cols, ", flags"); ++ if (fed->flags & FEDERATION_FLAG_REMOVE) { ++ flags = fed->flags & ~FEDERATION_FLAG_REMOVE; ++ xstrfmtcat(*vals, ", (flags & ~%u)", flags); ++ xstrfmtcat(*extra, ", flags=(flags & ~%u)", flags); ++ } else if (fed->flags & FEDERATION_FLAG_ADD) { ++ flags = fed->flags & ~FEDERATION_FLAG_ADD; ++ xstrfmtcat(*vals, ", (flags | %u)", flags); ++ xstrfmtcat(*extra, ", flags=(flags | %u)", flags); ++ } else { ++ flags = fed->flags; ++ xstrfmtcat(*vals, ", %u", flags); ++ xstrfmtcat(*extra, ", flags=%u", flags); ++ } ++ } ++ ++ return SLURM_SUCCESS; ++} ++ ++/* ++ * Remove all clusters from federation. ++ * IN: pgsql_conn - pgsql connection ++ * IN: fed - fed to remove clusters from ++ * IN: exceptions - list of clusters to not remove. ++ */ ++static int _remove_all_clusters_from_fed(pgsql_conn_t *pgsql_conn, ++ const char *fed, List exceptions) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ char *exception_names = NULL; ++ ++ if (exceptions && list_count(exceptions)) { ++ char *tmp_name; ++ ListIterator itr; ++ ++ itr = list_iterator_create(exceptions); ++ while ((tmp_name = list_next(itr))) ++ xstrfmtcat(exception_names, "%s'%s'", ++ (exception_names) ? "," : "", ++ tmp_name); ++ list_iterator_destroy(itr); ++ } ++ ++ xstrfmtcat(query, "UPDATE %s " ++ "SET federation='', fed_id=0, fed_state=%u " ++ "WHERE federation='%s' AND deleted=0", ++ cluster_table, CLUSTER_FED_STATE_NA, fed); ++ if (exception_names) ++ xstrfmtcat(query, " AND name NOT IN (%s)", exception_names); ++ ++ DB_DEBUG(FEDR, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc) ++ error("Failed to remove all clusters from federation %s", fed); ++ ++ if (exception_names) ++ xfree(exception_names); ++ ++ return rc; ++} ++ ++static int _remove_clusters_from_fed(pgsql_conn_t *pgsql_conn, List clusters) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ char *name = NULL; ++ char *names = NULL; ++ ListIterator itr = NULL; ++ ++ xassert(clusters); ++ ++ itr = list_iterator_create(clusters); ++ while ((name = list_next(itr))) ++ xstrfmtcat(names, "%s'%s'", names ? "," : "", name ); ++ ++ xstrfmtcat(query, "UPDATE %s " ++ "SET federation='', fed_id=0, fed_state=%u " ++ "WHERE name IN (%s) AND deleted=0", ++ cluster_table, CLUSTER_FED_STATE_NA, names); ++ ++ DB_DEBUG(FEDR, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc) ++ error("Failed to remove clusters %s from federation", names); ++ xfree(names); ++ ++ return rc; ++} ++ ++static int _add_clusters_to_fed(pgsql_conn_t *pgsql_conn, List clusters, ++ const char *fed) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ char *name = NULL; ++ char *names = NULL; ++ char *indexes = NULL; ++ ListIterator itr = NULL; ++ int last_id = -1; ++ ++ xassert(fed); ++ xassert(clusters); ++ ++ itr = list_iterator_create(clusters); ++ while ((name = list_next(itr))) { ++ int id; ++ if ((rc = as_pgsql_get_fed_cluster_id(pgsql_conn, name, fed, ++ last_id, &id))) ++ goto end_it; ++ last_id = id; ++ xstrfmtcat(indexes, "WHEN name='%s' THEN %d ", name, id); ++ xstrfmtcat(names, "%s'%s'", names ? "," : "", name); ++ } ++ ++ /* Keep the same fed_state if the cluster isn't changing feds. ++ * Also note that pgsql evaluates from left to right and uses the ++ * updated column values in case statements. So the check for federation ++ * in the fed_state case statement must happen before fed_state is set ++ * or the federation will always equal the federation in the case ++ * statement. */ ++ xstrfmtcat(query, "UPDATE %s " ++ "SET " ++ "fed_state = CASE WHEN federation='%s' THEN fed_state ELSE %u END, " ++ "fed_id = CASE %s END, " ++ "federation='%s' " ++ "WHERE name IN (%s) AND deleted=0", ++ cluster_table, fed, CLUSTER_FED_STATE_ACTIVE, indexes, fed, ++ names); ++ ++ DB_DEBUG(FEDR, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ if (rc) ++ error("Failed to add clusters %s to federation %s", ++ names, fed); ++ ++end_it: ++ xfree(query); ++ xfree(names); ++ xfree(indexes); ++ list_iterator_destroy(itr); ++ ++ return rc; ++} ++ ++static int _assign_clusters_to_federation(pgsql_conn_t *pgsql_conn, ++ const char *federation, ++ List cluster_list) ++{ ++ int rc = SLURM_SUCCESS; ++ List add_list = NULL; ++ List rem_list = NULL; ++ ListIterator itr = NULL; ++ bool clear_clusters = false; ++ slurmdb_cluster_rec_t *tmp_cluster = NULL; ++ ++ xassert(federation); ++ xassert(cluster_list); ++ ++ if (!cluster_list || !federation) { ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ ++ add_list = list_create(xfree_ptr); ++ rem_list = list_create(xfree_ptr); ++ ++ itr = list_iterator_create(cluster_list); ++ while ((tmp_cluster = list_next(itr))) { ++ if (!tmp_cluster->name) ++ continue; ++ if (tmp_cluster->name[0] == '-') ++ list_append(rem_list, xstrdup(tmp_cluster->name + 1)); ++ else if (tmp_cluster->name[0] == '+') ++ list_append(add_list, xstrdup(tmp_cluster->name + 1)); ++ else { ++ list_append(add_list, xstrdup(tmp_cluster->name)); ++ clear_clusters = true; ++ } ++ } ++ list_iterator_destroy(itr); ++ ++ if (clear_clusters && ++ (rc = _remove_all_clusters_from_fed(pgsql_conn, federation, ++ add_list))) ++ goto end_it; ++ if (!clear_clusters && ++ list_count(rem_list) && ++ (rc = _remove_clusters_from_fed(pgsql_conn, rem_list))) ++ goto end_it; ++ if (list_count(add_list) && ++ (rc = _add_clusters_to_fed(pgsql_conn, add_list, federation))) ++ goto end_it; ++ ++end_it: ++ list_destroy(add_list); ++ list_destroy(rem_list); ++ ++ return rc; ++} ++ ++extern int as_pgsql_add_federations(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List federation_list) ++{ ++ ListIterator itr = NULL; ++ int rc = SLURM_SUCCESS; ++ slurmdb_federation_rec_t *object = NULL; ++ char *cols = NULL, *vals = NULL, *extra = NULL, *query = NULL, ++ *tmp_extra = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ int affect_rows = 0; ++ int added = 0; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_SUPER_USER)) ++ return ESLURM_ACCESS_DENIED; ++ ++ user_name = uid_to_string((uid_t) uid); ++ ++ itr = list_iterator_create(federation_list); ++ while ((object = list_next(itr))) { ++ if (object->cluster_list && ++ (list_count(federation_list) > 1)) { ++ xfree(user_name); ++ error("Clusters can only be assigned to one " ++ "federation"); ++ errno = ESLURM_FED_CLUSTER_MULTIPLE_ASSIGNMENT; ++ return ESLURM_FED_CLUSTER_MULTIPLE_ASSIGNMENT; ++ } ++ ++ xstrcat(cols, "creation_time, mod_time, name"); ++ xstrfmtcat(vals, "%ld, %ld, '%s'", now, now, object->name); ++ xstrfmtcat(extra, ", mod_time=%ld", now); ++ ++ _setup_federation_rec_limits(object, &cols, &vals, &extra); ++ ++ xstrfmtcat(query, ++ "INSERT INTO %s (%s) VALUES (%s) " ++ "ON CONFLICT (name) DO UPDATE SET deleted=0%s", ++ federation_table, cols, vals, extra); ++ DB_DEBUG(FEDR, pgsql_conn->conn, "query\n%s", query); ++ ++ SQLHSTMT stmt_ptr = SQL_NULL_HSTMT; ++ rc = pgsql_db_query_stmt(pgsql_conn, query, &stmt_ptr); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add federation %s", object->name); ++ xfree(cols); ++ xfree(vals); ++ xfree(extra); ++ added = 0; ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ break; ++ } ++ ++ affect_rows = last_affected_rows(stmt_ptr); ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ if (!affect_rows) { ++ debug2("nothing changed %d", affect_rows); ++ xfree(cols); ++ xfree(vals); ++ xfree(extra); ++ continue; ++ } ++ ++ if (object->cluster_list && ++ _assign_clusters_to_federation(pgsql_conn, object->name, ++ object->cluster_list)) { ++ xfree(cols); ++ xfree(vals); ++ xfree(extra); ++ xfree(user_name); ++ return SLURM_ERROR; ++ } ++ ++ /* Add Transaction */ ++ /* we always have a ', ' as the first 2 chars */ ++ tmp_extra = slurm_add_slash_to_quotes(extra+2); ++ ++ xstrfmtcat(query, ++ "INSERT INTO %s " ++ "(timestamp, action, name, actor, info) " ++ "VALUES (%ld, %u, '%s', '%s', '%s');", ++ txn_table, now, DBD_ADD_FEDERATIONS, ++ object->name, user_name, tmp_extra); ++ xfree(cols); ++ xfree(vals); ++ xfree(tmp_extra); ++ xfree(extra); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add txn"); ++ } else { ++ added++; ++ } ++ } ++ list_iterator_destroy(itr); ++ xfree(user_name); ++ ++ if (!added) ++ reset_pgsql_conn(pgsql_conn); ++ else ++ as_pgsql_add_feds_to_update_list(pgsql_conn); ++ ++ return rc; ++} ++ ++extern List as_pgsql_get_federations(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_federation_cond_t *federation_cond) ++{ ++ char *query = NULL; ++ char *extra = NULL; ++ char *tmp = NULL; ++ List federation_list = NULL; ++ int i=0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ slurmdb_federation_rec_t *fed = NULL; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!federation_cond) { ++ xstrcat(extra, " WHERE t1.deleted=0"); ++ goto empty; ++ } ++ ++ _setup_federation_cond_limits(federation_cond, &extra); ++ ++empty: ++ ++ xfree(tmp); ++ i=0; ++ xstrfmtcat(tmp, "%s", fed_req_inx[i]); ++ for(i = 1; i < FED_REQ_COUNT; i++) { ++ xstrfmtcat(tmp, ", %s", fed_req_inx[i]); ++ } ++ ++ query = xstrdup_printf( ++ "SELECT DISTINCT %s FROM %s AS t1 " ++ "LEFT JOIN %s AS t2 ON t1.name=t2.federation AND t2.deleted=0" ++ "%s ORDER BY t1.name", ++ tmp, federation_table, cluster_table, extra); ++ xfree(tmp); ++ xfree(extra); ++ ++ DB_DEBUG(FEDR, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ federation_list = list_create(slurmdb_destroy_federation_rec); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_cluster_cond_t clus_cond; ++ List tmp_list = NULL; ++ fed = xmalloc(sizeof(slurmdb_federation_rec_t)); ++ list_append(federation_list, fed); ++ ++ fed->name = xstrdup(row[FED_REQ_NAME]); ++ fed->flags = slurm_atoul(row[FED_REQ_FLAGS]); ++ ++ /* clusters in federation */ ++ slurmdb_init_cluster_cond(&clus_cond, 0); ++ clus_cond.federation_list = list_create(xfree_ptr); ++ list_append(clus_cond.federation_list, xstrdup(fed->name)); ++ ++ tmp_list = as_pgsql_get_clusters(pgsql_conn, uid, &clus_cond); ++ FREE_NULL_LIST(clus_cond.federation_list); ++ if (!tmp_list) { ++ error("Unable to get federation clusters"); ++ continue; ++ } ++ fed->cluster_list = tmp_list; ++ } ++ pgsql_free_result(&result); ++ ++ return federation_list; ++} ++ ++extern List as_pgsql_modify_federations( ++ pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_federation_cond_t *fed_cond, ++ slurmdb_federation_rec_t *fed) ++{ ++ List ret_list = NULL; ++ int rc = SLURM_SUCCESS; ++ int req_inx = 0; ++ char *object = NULL; ++ char *vals = NULL, *extra = NULL, *query = NULL, ++ *name_char = NULL, *fed_items = NULL; ++ char *tmp_char1 = NULL, *tmp_char2 = NULL; ++ time_t now = time(NULL); ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ if (!fed_cond || !fed) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, ++ SLURMDB_ADMIN_SUPER_USER)) { ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ ++ /* force to only do non-deleted federations */ ++ fed_cond->with_deleted = 0; ++ _setup_federation_cond_limits(fed_cond, &extra); ++ _setup_federation_rec_limits(fed, &tmp_char1, &tmp_char2, &vals); ++ xfree(tmp_char1); ++ xfree(tmp_char2); ++ ++ if (!extra || ++ (!vals && (!fed->cluster_list || !list_count(fed->cluster_list)))) { ++ xfree(extra); ++ xfree(vals); ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ error("Nothing to change"); ++ return NULL; ++ } ++ ++ if (fed->cluster_list && ++ fed_cond->federation_list && ++ (list_count(fed_cond->federation_list) > 1)) { ++ xfree(extra); ++ xfree(vals); ++ error("Clusters can only be assigned to one federation"); ++ errno = ESLURM_FED_CLUSTER_MULTIPLE_ASSIGNMENT; ++ return NULL; ++ } ++ ++ /* Select records that are going to get updated. ++ * 1 - to be able to report what is getting updated ++ * 2 - to create an update object to let the controller know. */ ++ xstrfmtcat(fed_items, "%s", fed_req_inx[req_inx]); ++ for(req_inx = 1; req_inx < FED_REQ_COUNT; req_inx++) { ++ xstrfmtcat(fed_items, ", %s", fed_req_inx[req_inx]); ++ } ++ ++ xstrfmtcat(query, "SELECT %s FROM %s AS t1 %s;", ++ fed_items, federation_table, extra); ++ xfree(fed_items); ++ ++ DB_DEBUG(FEDR, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ error("no result given for %s", extra); ++ xfree(query); ++ xfree(vals); ++ xfree(extra); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ xfree(extra); ++ ++ ret_list = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) { ++ object = xstrdup(row[0]); ++ ++ list_append(ret_list, object); ++ if (!name_char) { ++ xstrfmtcat(name_char, "(name='%s'", object); ++ } else { ++ xstrfmtcat(name_char, " OR name='%s'", object); ++ } ++ } ++ pgsql_free_result(&result); ++ ++ if (fed->cluster_list && ++ (_assign_clusters_to_federation(pgsql_conn, object, ++ fed->cluster_list))) { ++ xfree(vals); ++ xfree(name_char); ++ xfree(query); ++ FREE_NULL_LIST(ret_list); ++ return NULL; ++ } ++ ++ if (!list_count(ret_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(FEDR, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(vals); ++ xfree(name_char); ++ xfree(query); ++ return ret_list; ++ } ++ xfree(query); ++ xstrcat(name_char, ")"); ++ ++ if (vals) { ++ char *user_name = uid_to_string((uid_t) uid); ++ rc = modify_common(pgsql_conn, DBD_MODIFY_FEDERATIONS, now, ++ user_name, federation_table, ++ name_char, vals, NULL); ++ xfree(user_name); ++ } ++ xfree(name_char); ++ xfree(vals); ++ ++ if (rc == SLURM_ERROR) { ++ error("Couldn't modify federation"); ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ } else ++ as_pgsql_add_feds_to_update_list(pgsql_conn); ++ ++ return ret_list; ++} ++ ++extern List as_pgsql_remove_federations(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_federation_cond_t *fed_cond) ++{ ++ List ret_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *extra = NULL, *query = NULL, *name_char = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ if (!fed_cond) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_SUPER_USER)) { ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ ++ /* force to only do non-deleted federations */ ++ fed_cond->with_deleted = 0; ++ _setup_federation_cond_limits(fed_cond, &extra); ++ ++ if (!extra) { ++ error("Nothing to remove"); ++ return NULL; ++ } ++ ++ query = xstrdup_printf("SELECT name FROM %s AS t1 %s;", ++ federation_table, extra); ++ xfree(extra); ++ result = pgsql_db_query_ret( pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ rc = 0; ++ ret_list = list_create(xfree_ptr); ++ ++ if (!pgsql_num_rows(result)) { ++ pgsql_free_result(&result); ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(FEDR, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(query); ++ pgsql_free_result(&result); ++ return ret_list; ++ } ++ xfree(query); ++ ++ user_name = uid_to_string((uid_t) uid); ++ while ((row = pgsql_fetch_row(result))) { ++ char *object = xstrdup(row[0]); ++ list_append(ret_list, object); ++ ++ if ((rc = _remove_all_clusters_from_fed(pgsql_conn, object, ++ NULL))) ++ break; ++ ++ xfree(name_char); ++ xstrfmtcat(name_char, "name='%s'", object); ++ ++ if ((rc = remove_common(pgsql_conn, DBD_REMOVE_FEDERATIONS, now, ++ user_name, federation_table, name_char, ++ NULL, NULL, ret_list, NULL))) ++ break; ++ } ++ pgsql_free_result(&result); ++ xfree(user_name); ++ xfree(name_char); ++ ++ if (rc != SLURM_SUCCESS) { ++ FREE_NULL_LIST(ret_list); ++ return NULL; ++ } else ++ as_pgsql_add_feds_to_update_list(pgsql_conn); ++ ++ return ret_list; ++} ++ ++extern int as_pgsql_add_feds_to_update_list(pgsql_conn_t *pgsql_conn) ++{ ++ int rc = SLURM_ERROR; ++ List feds = as_pgsql_get_federations(pgsql_conn, 0, NULL); ++ ++ /* Even if there are no feds, need to send an empty list for the case ++ * that all feds were removed. The controller needs to know that it was ++ * removed from a federation. */ ++ if (feds && ++ ((rc = addto_update_list(pgsql_conn->update_list, ++ SLURMDB_UPDATE_FEDS, feds)) ++ != SLURM_SUCCESS)) { ++ FREE_NULL_LIST(feds); ++ } ++ return rc; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_federation.h b/src/plugins/accounting_storage/pgsql/as_pgsql_federation.h +new file mode 100755 +index 0000000..43b45b1 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_federation.h +@@ -0,0 +1,57 @@ ++/*****************************************************************************\ ++ * as_pgsql_federation.h - functions dealing with federations. ++ ***************************************************************************** ++ * Copyright (C) 2016 SchedMD LLC. ++ * Written by Brian Christiansen ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++#ifndef _HAVE_PGSQL_FEDERATION_H ++#define _HAVE_PGSQL_FEDERATION_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_add_feds_to_update_list(pgsql_conn_t *pgsql_conn); ++ ++extern int as_pgsql_add_federations(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List federation_list); ++ ++extern List as_pgsql_get_federations( ++ pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_federation_cond_t *federation_cond); ++ ++extern List as_pgsql_modify_federations( ++ pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_federation_cond_t *fed_cond, ++ slurmdb_federation_rec_t *fed); ++ ++extern List as_pgsql_remove_federations(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_federation_cond_t *fed_cond); ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_fix_runaway_jobs.c b/src/plugins/accounting_storage/pgsql/as_pgsql_fix_runaway_jobs.c +new file mode 100755 +index 0000000..9acabdf +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_fix_runaway_jobs.c +@@ -0,0 +1,200 @@ ++/*****************************************************************************\ ++ * as_pgsql_fix_runaway_jobs.c - functions dealing with runaway jobs. ++ ***************************************************************************** ++ * Copyright (C) 2016 SchedMD LLC. ++ * Written by Nathan Yee ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_fix_runaway_jobs.h" ++#include "src/common/list.h" ++#include "src/common/slurmdb_defs.h" ++ ++static int _first_job_roll_up(pgsql_conn_t *pgsql_conn, time_t first_start) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query; ++ struct tm start_tm; ++ time_t month_start; ++ ++ /* set up the month period */ ++ if (!localtime_r(&first_start, &start_tm)) { ++ error("mktime for start failed for rollup\n"); ++ return SLURM_ERROR; ++ } ++ ++ // Go to the last day of the previous month for rollup start ++ start_tm.tm_sec = 0; ++ start_tm.tm_min = 0; ++ start_tm.tm_hour = 0; ++ start_tm.tm_mday = 0; ++ month_start = slurm_mktime(&start_tm); ++ ++ debug("Need to reroll usage from %s in cluster %s because of runaway job(s)", ++ slurm_ctime2(&month_start), pgsql_conn->cluster_name); ++ ++ query = xstrdup_printf("UPDATE \"%s_%s\" SET hourly_rollup = %ld, " ++ "daily_rollup = %ld, monthly_rollup = %ld;", ++ pgsql_conn->cluster_name, last_ran_table, ++ month_start, month_start, month_start); ++ ++ /* ++ * Delete allocated time from the assoc and wckey usage tables. ++ * If the only usage during those times was runaway jobs, then rollup ++ * won't clear that usage, so we have to clear it here. Rollup will ++ * re-create the correct rows in these tables. ++ */ ++ xstrfmtcat(query, "DELETE FROM \"%s_%s\" WHERE time_start >= %ld;", ++ pgsql_conn->cluster_name, assoc_hour_table, ++ month_start); ++ xstrfmtcat(query, "DELETE FROM \"%s_%s\" WHERE time_start >= %ld;", ++ pgsql_conn->cluster_name, assoc_day_table, ++ month_start); ++ xstrfmtcat(query, "DELETE FROM \"%s_%s\" WHERE time_start >= %ld;", ++ pgsql_conn->cluster_name, assoc_month_table, ++ month_start); ++ xstrfmtcat(query, "DELETE FROM \"%s_%s\" WHERE time_start >= %ld;", ++ pgsql_conn->cluster_name, wckey_hour_table, ++ month_start); ++ xstrfmtcat(query, "DELETE FROM \"%s_%s\" WHERE time_start >= %ld;", ++ pgsql_conn->cluster_name, wckey_day_table, ++ month_start); ++ xstrfmtcat(query, "DELETE FROM \"%s_%s\" WHERE time_start >= %ld;", ++ pgsql_conn->cluster_name, wckey_month_table, ++ month_start); ++ ++ DB_DEBUG(DB_QUERY, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ if (rc != SLURM_SUCCESS) ++ error("%s Failed to rollup at the end of previous month", ++ __func__); ++ ++ xfree(query); ++ return rc; ++} ++ ++extern int as_pgsql_fix_runaway_jobs(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List runaway_jobs) ++{ ++ char *query = NULL, *job_ids = NULL; ++ slurmdb_job_rec_t *job = NULL; ++ ListIterator iter = NULL; ++ int rc = SLURM_SUCCESS; ++ slurmdb_job_rec_t *first_job; ++ char *temp_cluster_name = pgsql_conn->cluster_name; ++ ++ if (!runaway_jobs) { ++ error("%s: No List of runaway jobs to fix given.", ++ __func__); ++ rc = SLURM_ERROR; ++ goto bail; ++ } ++ ++ list_sort(runaway_jobs, slurmdb_job_sort_by_submit_time); ++ ++ if (!(first_job = list_peek(runaway_jobs))) { ++ error("%s: List of runaway jobs to fix is unexpectedly empty", ++ __func__); ++ rc = SLURM_ERROR; ++ goto bail; ++ } ++ ++ if (!first_job->submit) { ++ error("Runaway jobs all have time_submit=0, something is wrong! Aborting fix runaway jobs"); ++ rc = SLURM_ERROR; ++ goto bail; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) { ++ rc = ESLURM_DB_CONNECTION; ++ goto bail; ++ } ++ ++ /* ++ * Temporarily use pgsql_conn->cluster_name for potentially non local ++ * cluster name, change back before return ++ */ ++ pgsql_conn->cluster_name = first_job->cluster; ++ ++ /* ++ * Double check if we are at least an operator, this check should had ++ * already happened in the slurmdbd. ++ */ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { ++ rc = ESLURM_ACCESS_DENIED; ++ goto bail; ++ } ++ ++ iter = list_iterator_create(runaway_jobs); ++ while ((job = list_next(iter))) { ++ /* ++ * Currently you can only fix one cluster at a time, so we need ++ * to verify we don't have multiple cluster names. ++ */ ++ if (xstrcmp(job->cluster, first_job->cluster)) { ++ error("%s: You can only fix runaway jobs on one cluster at a time.", ++ __func__); ++ rc = SLURM_ERROR; ++ goto bail; ++ } ++ ++ xstrfmtcat(job_ids, "%s%d", ((job_ids) ? "," : ""), job->jobid); ++ } ++ list_iterator_destroy(iter); ++ ++ debug("Fixing runaway jobs: %s", job_ids); ++ ++ query = xstrdup_printf("UPDATE \"%s_%s\" SET time_end=" ++ "GREATEST(time_start, time_eligible, time_submit), " ++ "state=%d WHERE time_end=0 AND id_job IN (%s);", ++ pgsql_conn->cluster_name, job_table, ++ JOB_COMPLETE, job_ids); ++ ++ DB_DEBUG(DB_QUERY, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ if (rc) { ++ error("Failed to fix runaway jobs: update query failed"); ++ goto bail; ++ } ++ ++ /* Set rollup to the last day of the previous month of the first ++ * runaway job */ ++ rc = _first_job_roll_up(pgsql_conn, first_job->submit); ++ if (rc != SLURM_SUCCESS) ++ error("Failed to fix runaway jobs"); ++ ++bail: ++ xfree(job_ids); ++ pgsql_conn->cluster_name = temp_cluster_name; ++ return rc; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_fix_runaway_jobs.h b/src/plugins/accounting_storage/pgsql/as_pgsql_fix_runaway_jobs.h +new file mode 100755 +index 0000000..dc6eb64 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_fix_runaway_jobs.h +@@ -0,0 +1,46 @@ ++/*****************************************************************************\ ++ * as_pgsql_fix_runaway_jobs.h - functions dealing with runaway jobs. ++ ***************************************************************************** ++ * Copyright (C) 2016 SchedMD LLC. ++ * Written by Nathan Yee ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_FIX_RUNAWAY_JOBS_H ++#define _HAVE_PGSQL_FIX_RUNAWAY_JOBS_H ++ ++#include "accounting_storage_pgsql.h" ++#include "src/common/slurm_time.h" ++ ++extern int as_pgsql_fix_runaway_jobs(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List runaway_jobs); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_job.c b/src/plugins/accounting_storage/pgsql/as_pgsql_job.c +new file mode 100755 +index 0000000..db742f9 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_job.c +@@ -0,0 +1,1791 @@ ++/*****************************************************************************\ ++ * as_pgsql_job.c - functions dealing with jobs and job steps. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_job.h" ++#include "as_pgsql_jobacct_process.h" ++#include "as_pgsql_usage.h" ++#include "as_pgsql_wckey.h" ++ ++#include "src/common/assoc_mgr.h" ++#include "src/common/gres.h" ++#include "src/common/node_select.h" ++#include "src/common/parse_time.h" ++#include "src/common/slurm_jobacct_gather.h" ++#include "src/common/slurm_time.h" ++ ++#define MAX_FLUSH_JOBS 500 ++ ++typedef struct { ++ char *cluster; ++ uint32_t new; ++ uint32_t old; ++} id_switch_t; ++ ++static int _find_id_switch(void *x, void *key) ++{ ++ id_switch_t *id_switch = (id_switch_t *)x; ++ uint32_t id = *(uint32_t *)key; ++ ++ if (id_switch->old == id) ++ return 1; ++ return 0; ++} ++ ++static char *_average_tres_usage(uint32_t *tres_ids, uint64_t *tres_cnts, ++ int tres_cnt, int tasks) ++{ ++ char *ret_str = NULL; ++ int i; ++ ++ /* ++ * Don't return NULL here, we need a blank string or we will print ++ * '(null)' in the database which really isn't what we want. ++ */ ++ if (!tasks) ++ return xstrdup(""); ++ ++ for (i = 0; i < tres_cnt; i++) { ++ if (tres_cnts[i] == INFINITE64) ++ continue; ++ xstrfmtcat(ret_str, "%s%u=%"PRIu64, ++ ret_str ? "," : "", ++ tres_ids[i], tres_cnts[i] / (uint64_t)tasks); ++ } ++ ++ if (!ret_str) ++ ret_str = xstrdup(""); ++ return ret_str; ++} ++ ++/* Used in job functions for getting the database index based off the ++ * submit time and job. 0 is returned if none is found ++ */ ++static uint64_t _get_db_index(pgsql_conn_t *pgsql_conn, ++ time_t submit, uint32_t jobid) ++{ ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ uint64_t db_index = 0; ++ char *query = xstrdup_printf("select job_db_inx from \"%s_%s\" where " ++ "time_submit=%d and id_job=%u", ++ pgsql_conn->cluster_name, job_table, ++ (int)submit, jobid); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return 0; ++ } ++ xfree(query); ++ ++ row = pgsql_fetch_row(result); ++ if (!row) { ++ pgsql_free_result(&result); ++ debug4("We can't get a db_index for this combo, " ++ "time_submit=%d and id_job=%u. " ++ "We must not have heard about the start yet, " ++ "no big deal, we will get one right after this.", ++ (int)submit, jobid); ++ return 0; ++ } ++ db_index = slurm_atoull(row[0]); ++ pgsql_free_result(&result); ++ ++ return db_index; ++} ++ ++static char *_get_user_from_associd(pgsql_conn_t *pgsql_conn, ++ char *cluster, uint32_t associd) ++{ ++ char *user = NULL; ++ char *query = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ /* Just so we don't have to keep a ++ cache of the associations around we ++ will just query the db for the user ++ name of the association id. Since ++ this should sort of be a rare case ++ this isn't too bad. ++ */ ++ query = xstrdup_printf("select user from \"%s_%s\" where id_assoc=%u", ++ cluster, assoc_table, associd); ++ ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ xfree(query); ++ ++ if ((row = pgsql_fetch_row(result)) && row[0][0]) ++ user = xstrdup(row[0]); ++ ++ pgsql_free_result(&result); ++ ++ return user; ++} ++ ++static uint32_t _get_wckeyid(pgsql_conn_t *pgsql_conn, char **name, ++ uid_t uid, char *cluster, uint32_t associd) ++{ ++ uint32_t wckeyid = 0; ++ ++ if (slurm_get_track_wckey()) { ++ /* Here we are looking for the wckeyid if it doesn't ++ * exist we will create one. We don't need to check ++ * if it is good or not. Right now this is the only ++ * place things are created. We do this only on a job ++ * start, not on a job submit since we don't want to ++ * slow down getting the db_index back to the ++ * controller. ++ */ ++ slurmdb_wckey_rec_t wckey_rec; ++ char *user = NULL; ++ ++ /* since we are unable to rely on uids here (someone could ++ not have there uid in the system yet) we must ++ first get the user name from the associd */ ++ if (!(user = _get_user_from_associd( ++ pgsql_conn, cluster, associd))) { ++ error("No user for associd %u", associd); ++ goto no_wckeyid; ++ } ++ /* get the default key */ ++ if (!*name) { ++ slurmdb_user_rec_t user_rec; ++ memset(&user_rec, 0, sizeof(slurmdb_user_rec_t)); ++ user_rec.uid = NO_VAL; ++ user_rec.name = user; ++ if (assoc_mgr_fill_in_user(pgsql_conn, &user_rec, ++ 1, NULL, false) ++ != SLURM_SUCCESS) { ++ error("No user by name of %s assoc %u", ++ user, associd); ++ xfree(user); ++ goto no_wckeyid; ++ } ++ ++ if (user_rec.default_wckey) ++ *name = xstrdup_printf("*%s", ++ user_rec.default_wckey); ++ else ++ *name = xstrdup_printf("*"); ++ } ++ ++ memset(&wckey_rec, 0, sizeof(slurmdb_wckey_rec_t)); ++ wckey_rec.name = (*name); ++ wckey_rec.uid = NO_VAL; ++ wckey_rec.user = user; ++ wckey_rec.cluster = cluster; ++ if (assoc_mgr_fill_in_wckey(pgsql_conn, &wckey_rec, ++ ACCOUNTING_ENFORCE_WCKEYS, ++ NULL, false) != SLURM_SUCCESS) { ++ List wckey_list = NULL; ++ slurmdb_wckey_rec_t *wckey_ptr = NULL; ++ /* we have already checked to make ++ sure this was the slurm user before ++ calling this */ ++ ++ wckey_list = list_create(slurmdb_destroy_wckey_rec); ++ ++ wckey_ptr = xmalloc(sizeof(slurmdb_wckey_rec_t)); ++ wckey_ptr->name = xstrdup((*name)); ++ wckey_ptr->user = xstrdup(user); ++ wckey_ptr->cluster = xstrdup(cluster); ++ list_append(wckey_list, wckey_ptr); ++ /* info("adding wckey '%s' '%s' '%s'", */ ++ /* wckey_ptr->name, wckey_ptr->user, */ ++ /* wckey_ptr->cluster); */ ++ ++ if (*name[0] == '*') { ++ /* make sure the non * wckey has been added */ ++ wckey_rec.name = (*name)+1; ++ if (assoc_mgr_fill_in_wckey( ++ pgsql_conn, &wckey_rec, ++ ACCOUNTING_ENFORCE_WCKEYS, ++ NULL, false) != SLURM_SUCCESS) { ++ wckey_ptr = xmalloc( ++ sizeof(slurmdb_wckey_rec_t)); ++ wckey_ptr->name = ++ xstrdup(wckey_rec.name); ++ wckey_ptr->user = xstrdup(user); ++ wckey_ptr->cluster = xstrdup(cluster); ++ list_prepend(wckey_list, wckey_ptr); ++ /* info("adding wckey '%s' '%s' " */ ++ /* "'%s'", */ ++ /* wckey_ptr->name, */ ++ /* wckey_ptr->user, */ ++ /* wckey_ptr->cluster); */ ++ } ++ wckey_rec.name = (*name); ++ } ++ ++ if (as_pgsql_add_wckeys(pgsql_conn, ++ slurm_conf.slurm_user_id, ++ wckey_list) ++ == SLURM_SUCCESS) ++ acct_storage_p_commit(pgsql_conn, 1); ++ /* If that worked lets get it */ ++ assoc_mgr_fill_in_wckey(pgsql_conn, &wckey_rec, ++ ACCOUNTING_ENFORCE_WCKEYS, ++ NULL, false); ++ ++ FREE_NULL_LIST(wckey_list); ++ } ++ xfree(user); ++ /* info("got wckeyid of %d", wckey_rec.id); */ ++ wckeyid = wckey_rec.id; ++ } ++no_wckeyid: ++ return wckeyid; ++} ++ ++/* extern functions */ ++ ++extern int as_pgsql_job_start(pgsql_conn_t *pgsql_conn, job_record_t *job_ptr) ++{ ++ int rc = SLURM_SUCCESS; ++ char *nodes = NULL, *jname = NULL; ++ int track_steps = 0; ++ char *partition = NULL; ++ char *query = NULL; ++ int reinit = 0; ++ time_t begin_time, check_time, start_time, submit_time; ++ uint32_t wckeyid = 0; ++ uint32_t job_state; ++ uint32_t array_task_id = ++ (job_ptr->array_job_id) ? job_ptr->array_task_id : NO_VAL; ++ uint64_t job_db_inx = job_ptr->db_index; ++ job_array_struct_t *array_recs = job_ptr->array_recs; ++ char *tres_alloc_str = NULL; ++ ++ if ((!job_ptr->details || !job_ptr->details->submit_time) ++ && !job_ptr->resize_time) { ++ error("as_pgsql_job_start: " ++ "Not inputing this job, it has no submit time."); ++ return SLURM_ERROR; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ debug2("%s: called", __func__); ++ ++ job_state = job_ptr->job_state; ++ ++ if (job_ptr->resize_time) { ++ begin_time = job_ptr->resize_time; ++ submit_time = job_ptr->resize_time; ++ start_time = job_ptr->resize_time; ++ } else { ++ begin_time = job_ptr->details->begin_time; ++ submit_time = job_ptr->details->submit_time; ++ start_time = job_ptr->start_time; ++ } ++ ++ /* If the reason is WAIT_ARRAY_TASK_LIMIT we don't want to ++ * give the pending jobs an eligible time since it will add ++ * time to accounting where as these jobs aren't able to run ++ * until later so mark it as such. ++ */ ++ if (job_ptr->state_reason == WAIT_ARRAY_TASK_LIMIT) ++ begin_time = INFINITE; ++ ++ /* ++ * Strip the RESIZING flag and end the job if there was a db_index. In ++ * 21.08 we reset the db_index on the slurmctld side, previously it was ++ * done here. ++ */ ++ if (IS_JOB_RESIZING(job_ptr)) { ++ /* ++ * If we have a db_index lets end the previous record. ++ * This should only need to be around 2 versions after 21.08. ++ */ ++ if (job_ptr->db_index) { ++ as_pgsql_job_complete(pgsql_conn, job_ptr); ++ job_ptr->db_index = 0; ++ } ++ ++ job_state &= (~JOB_RESIZING); ++ } ++ ++ job_state &= JOB_STATE_BASE; ++ ++ /* See what we are hearing about here if no start time. If ++ * this job latest time is before the last roll up we will ++ * need to reset it to look at this job. */ ++ if (start_time) ++ check_time = start_time; ++ else if (begin_time) ++ check_time = begin_time; ++ else ++ check_time = submit_time; ++ ++ slurm_mutex_lock(&rollup_lock); ++ if (check_time < global_last_rollup) { ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ /* check to see if we are hearing about this time for the ++ * first time. ++ */ ++ query = xstrdup_printf("select job_db_inx " ++ "from \"%s_%s\" where id_job=%u and " ++ "time_submit=%ld and time_eligible=%ld " ++ "and time_start=%ld;", ++ pgsql_conn->cluster_name, ++ job_table, job_ptr->job_id, ++ submit_time, begin_time, start_time); ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", query); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ slurm_mutex_unlock(&rollup_lock); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ if ((row = pgsql_fetch_row(result))) { ++ pgsql_free_result(&result); ++ debug4("revieved an update for a " ++ "job (%u) already known about", ++ job_ptr->job_id); ++ slurm_mutex_unlock(&rollup_lock); ++ goto no_rollup_change; ++ } ++ pgsql_free_result(&result); ++ ++ if (job_ptr->start_time) ++ debug("Need to reroll usage from %s Job %u " ++ "from %s started then and we are just " ++ "now hearing about it.", ++ slurm_ctime2(&check_time), ++ job_ptr->job_id, pgsql_conn->cluster_name); ++ else if (begin_time) ++ debug("Need to reroll usage from %s Job %u " ++ "from %s became eligible then and we are just " ++ "now hearing about it.", ++ slurm_ctime2(&check_time), ++ job_ptr->job_id, pgsql_conn->cluster_name); ++ else ++ debug("Need to reroll usage from %s Job %u " ++ "from %s was submitted then and we are just " ++ "now hearing about it.", ++ slurm_ctime2(&check_time), ++ job_ptr->job_id, pgsql_conn->cluster_name); ++ ++ global_last_rollup = check_time; ++ slurm_mutex_unlock(&rollup_lock); ++ ++ /* If the times here are later than the daily_rollup ++ or monthly rollup it isn't a big deal since they ++ are always shrunk down to the beginning of each ++ time period. ++ */ ++ query = xstrdup_printf("update \"%s_%s\" set " ++ "hourly_rollup=%ld, " ++ "daily_rollup=%ld, monthly_rollup=%ld", ++ pgsql_conn->cluster_name, ++ last_ran_table, check_time, ++ check_time, check_time); ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ } else ++ slurm_mutex_unlock(&rollup_lock); ++ ++no_rollup_change: ++ ++ if (job_ptr->name && job_ptr->name[0]) { ++ jname = job_ptr->name; ++ if (!xstrcmp(jname, "interactive")) ++ track_steps = 1; ++ } else { ++ jname = "allocation"; ++ track_steps = 1; ++ } ++ ++ if (job_ptr->nodes && job_ptr->nodes[0]) ++ nodes = job_ptr->nodes; ++ else ++ nodes = "None assigned"; ++ ++ if (job_ptr->batch_flag) ++ track_steps = 1; ++ ++ /* Grab the wckey once to make sure it is placed. */ ++ if (job_ptr->assoc_id && (!job_ptr->db_index || job_ptr->wckey)) ++ wckeyid = _get_wckeyid(pgsql_conn, &job_ptr->wckey, ++ job_ptr->user_id, ++ pgsql_conn->cluster_name, ++ job_ptr->assoc_id); ++ ++ if (!IS_JOB_PENDING(job_ptr) && job_ptr->part_ptr) ++ partition = job_ptr->part_ptr->name; ++ else if (job_ptr->partition) ++ partition = job_ptr->partition; ++ ++ if (!job_ptr->db_index) { ++ query = xstrdup_printf( ++ "insert into \"%s_%s\" " ++ "(id_job, mod_time, id_array_job, id_array_task, " ++ "het_job_id, het_job_offset, " ++ "id_assoc, id_qos, id_user, " ++ "id_group, nodelist, id_resv, timelimit, " ++ "time_eligible, time_submit, time_start, " ++ "job_name, track_steps, state, priority, cpus_req, " ++ "nodes_alloc, mem_req, flags, state_reason_prev", ++ pgsql_conn->cluster_name, job_table); ++ ++ if (wckeyid) ++ xstrcat(query, ", id_wckey"); ++ if (job_ptr->mcs_label) ++ xstrcat(query, ", mcs_label"); ++ if (job_ptr->account) ++ xstrcat(query, ", account"); ++ if (partition) ++ xstrcat(query, ", `partition`"); ++ if (job_ptr->wckey) ++ xstrcat(query, ", wckey"); ++ if (job_ptr->network) ++ xstrcat(query, ", node_inx"); ++ if (array_recs && array_recs->task_id_str) ++ xstrcat(query, ", array_task_str, array_max_tasks, " ++ "array_task_pending"); ++ else ++ xstrcat(query, ", array_task_str, array_task_pending"); ++ ++ if (job_ptr->tres_alloc_str || tres_alloc_str) ++ xstrcat(query, ", tres_alloc"); ++ if (job_ptr->tres_req_str) ++ xstrcat(query, ", tres_req"); ++ if (job_ptr->details->work_dir) ++ xstrcat(query, ", work_dir"); ++ if (job_ptr->details->features) ++ xstrcat(query, ", constraints"); ++ if (job_ptr->details->script) ++ xstrcat(query, ", batch_script"); ++ if (job_ptr->details->env_sup) ++ xstrcat(query, ", env_vars"); ++ if (job_ptr->details->submit_line) ++ xstrcat(query, ", submit_line"); ++ if (job_ptr->container) ++ xstrcat(query, ", container"); ++ ++ xstrfmtcat(query, ++ ") values (%u, UNIX_TIMESTAMP(), " ++ "%u, %u, %u, %u, %u, %u, %u, %u, " ++ "'%s', %u, %u, %ld, %ld, %ld, " ++ "'%s', %u, %u, %u, %u, %u, %"PRIu64", %u, %u", ++ job_ptr->job_id, ++ job_ptr->array_job_id, array_task_id, ++ job_ptr->het_job_id, job_ptr->het_job_offset, ++ job_ptr->assoc_id, job_ptr->qos_id, ++ job_ptr->user_id, job_ptr->group_id, nodes, ++ job_ptr->resv_id, job_ptr->time_limit, ++ begin_time, submit_time, start_time, ++ jname, track_steps, job_state, ++ job_ptr->priority, job_ptr->details->min_cpus, ++ job_ptr->total_nodes, ++ job_ptr->details->pn_min_memory, ++ job_ptr->db_flags, ++ job_ptr->state_reason_prev_db); ++ ++ if (wckeyid) ++ xstrfmtcat(query, ", %u", wckeyid); ++ if (job_ptr->mcs_label) ++ xstrfmtcat(query, ", '%s'", job_ptr->mcs_label); ++ if (job_ptr->account) ++ xstrfmtcat(query, ", '%s'", job_ptr->account); ++ if (partition) ++ xstrfmtcat(query, ", '%s'", partition); ++ if (job_ptr->wckey) ++ xstrfmtcat(query, ", '%s'", job_ptr->wckey); ++ if (job_ptr->network) ++ xstrfmtcat(query, ", '%s'", job_ptr->network); ++ if (array_recs && array_recs->task_id_str) ++ xstrfmtcat(query, ", '%s', %u, %u", ++ array_recs->task_id_str, ++ array_recs->max_run_tasks, ++ array_recs->task_cnt); ++ else ++ xstrcat(query, ", NULL, 0"); ++ ++ if (tres_alloc_str) ++ xstrfmtcat(query, ", '%s'", tres_alloc_str); ++ else if (job_ptr->tres_alloc_str) ++ xstrfmtcat(query, ", '%s'", job_ptr->tres_alloc_str); ++ if (job_ptr->tres_req_str) ++ xstrfmtcat(query, ", '%s'", job_ptr->tres_req_str); ++ if (job_ptr->details->work_dir) ++ xstrfmtcat(query, ", '%s'", ++ job_ptr->details->work_dir); ++ if (job_ptr->details->features) ++ xstrfmtcat(query, ", '%s'", ++ job_ptr->details->features); ++ if (job_ptr->details->script) ++ xstrfmtcat(query, ", '%s'", ++ job_ptr->details->script); ++ if (job_ptr->details->env_sup) ++ xstrfmtcat(query, ", '%s'", ++ job_ptr->details->env_sup[0]); ++ if (job_ptr->details->submit_line) ++ xstrfmtcat(query, ", '%s'", ++ job_ptr->details->submit_line); ++ if (job_ptr->container) ++ xstrfmtcat(query, ", '%s'", ++ job_ptr->container); ++ ++ xstrfmtcat(query, ++ ") ON CONFLICT (id_job, time_submit) DO UPDATE SET " ++ "job_db_inx=currval(pg_get_serial_sequence('%s_%s', 'job_db_inx')), " ++ "id_assoc=%u, id_user=%u, id_group=%u, " ++ "nodelist='%s', id_resv=%u, timelimit=%u, " ++ "time_submit=%ld, time_eligible=%ld, " ++ "time_start=%ld, mod_time=UNIX_TIMESTAMP(), " ++ "job_name='%s', track_steps=%u, id_qos=%u, " ++ "state=GREATEST(state, %u), priority=%u, " ++ "cpus_req=%u, nodes_alloc=%u, " ++ "mem_req=%"PRIu64", id_array_job=%u, id_array_task=%u, " ++ "het_job_id=%u, het_job_offset=%u, flags=%u, " ++ "state_reason_prev=%u", ++ pgsql_conn->cluster_name, job_table, ++ job_ptr->assoc_id, job_ptr->user_id, ++ job_ptr->group_id, nodes, ++ job_ptr->resv_id, job_ptr->time_limit, ++ submit_time, begin_time, start_time, ++ jname, track_steps, job_ptr->qos_id, job_state, ++ job_ptr->priority, job_ptr->details->min_cpus, ++ job_ptr->total_nodes, ++ job_ptr->details->pn_min_memory, ++ job_ptr->array_job_id, array_task_id, ++ job_ptr->het_job_id, job_ptr->het_job_offset, ++ job_ptr->db_flags, ++ job_ptr->state_reason_prev_db); ++ ++ if (wckeyid) ++ xstrfmtcat(query, ", id_wckey=%u", wckeyid); ++ if (job_ptr->mcs_label) ++ xstrfmtcat(query, ", mcs_label='%s'", ++ job_ptr->mcs_label); ++ if (job_ptr->account) ++ xstrfmtcat(query, ", account='%s'", job_ptr->account); ++ if (partition) ++ xstrfmtcat(query, ", `partition`='%s'", partition); ++ if (job_ptr->wckey) ++ xstrfmtcat(query, ", wckey='%s'", job_ptr->wckey); ++ if (job_ptr->network) ++ xstrfmtcat(query, ", node_inx='%s'", job_ptr->network); ++ if (array_recs && array_recs->task_id_str) ++ xstrfmtcat(query, ", array_task_str='%s', " ++ "array_max_tasks=%u, array_task_pending=%u", ++ array_recs->task_id_str, ++ array_recs->max_run_tasks, ++ array_recs->task_cnt); ++ else ++ xstrfmtcat(query, ", array_task_str=NULL, " ++ "array_task_pending=0"); ++ ++ if (tres_alloc_str) ++ xstrfmtcat(query, ", tres_alloc='%s'", tres_alloc_str); ++ else if (job_ptr->tres_alloc_str) ++ xstrfmtcat(query, ", tres_alloc='%s'", ++ job_ptr->tres_alloc_str); ++ if (job_ptr->tres_req_str) ++ xstrfmtcat(query, ", tres_req='%s'", ++ job_ptr->tres_req_str); ++ if (job_ptr->details->work_dir) ++ xstrfmtcat(query, ", work_dir='%s'", ++ job_ptr->details->work_dir); ++ if (job_ptr->details->features) ++ xstrfmtcat(query, ", constraints='%s'", ++ job_ptr->details->features); ++ ++ if (job_ptr->details->script) ++ xstrfmtcat(query, ", batch_script='%s'", ++ job_ptr->details->script); ++ ++ if (job_ptr->details->env_sup) ++ xstrfmtcat(query, ", env_vars='%s'", ++ job_ptr->details->env_sup[0]); ++ ++ if (job_ptr->details->submit_line) ++ xstrfmtcat(query, ", submit_line='%s'", ++ job_ptr->details->submit_line); ++ if (job_ptr->container) ++ xstrfmtcat(query, ", container='%s'", ++ job_ptr->container); ++ ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", query); ++ try_again: ++ if (!(job_ptr->db_index = pgsql_db_insert_ret_id( ++ pgsql_conn, query))) { ++ if (!reinit) { ++ error("%s: It looks like the storage has gone away trying to reconnect", ++ __func__); ++ /* reconnect */ ++ check_connection(pgsql_conn); ++ reinit = 1; ++ goto try_again; ++ } else ++ rc = SLURM_ERROR; ++ } ++ } else { ++ query = xstrdup_printf("update \"%s_%s\" set nodelist='%s', ", ++ pgsql_conn->cluster_name, ++ job_table, nodes); ++ ++ if (wckeyid) ++ xstrfmtcat(query, "id_wckey=%u, ", wckeyid); ++ if (job_ptr->mcs_label) ++ xstrfmtcat(query, "mcs_label='%s', ", ++ job_ptr->mcs_label); ++ if (job_ptr->account) ++ xstrfmtcat(query, "account='%s', ", job_ptr->account); ++ if (partition) ++ xstrfmtcat(query, "`partition`='%s', ", partition); ++ if (job_ptr->wckey) ++ xstrfmtcat(query, "wckey='%s', ", job_ptr->wckey); ++ if (job_ptr->network) ++ xstrfmtcat(query, "node_inx='%s', ", job_ptr->network); ++ if (array_recs && array_recs->task_id_str) ++ xstrfmtcat(query, "array_task_str='%s', " ++ "array_max_tasks=%u, " ++ "array_task_pending=%u, ", ++ array_recs->task_id_str, ++ array_recs->max_run_tasks, ++ array_recs->task_cnt); ++ else ++ xstrfmtcat(query, "array_task_str=NULL, " ++ "array_task_pending=0, "); ++ ++ if (tres_alloc_str) ++ xstrfmtcat(query, "tres_alloc='%s', ", tres_alloc_str); ++ else if (job_ptr->tres_alloc_str) ++ xstrfmtcat(query, "tres_alloc='%s', ", ++ job_ptr->tres_alloc_str); ++ if (job_ptr->tres_req_str) ++ xstrfmtcat(query, "tres_req='%s', ", ++ job_ptr->tres_req_str); ++ if (job_ptr->details->work_dir) ++ xstrfmtcat(query, "work_dir='%s', ", ++ job_ptr->details->work_dir); ++ if (job_ptr->details->features) ++ xstrfmtcat(query, "constraints='%s', ", ++ job_ptr->details->features); ++ ++ if (job_ptr->details->script) ++ xstrfmtcat(query, "batch_script='%s', ", ++ job_ptr->details->script); ++ ++ if (job_ptr->details->env_sup) ++ xstrfmtcat(query, "env_vars='%s', ", ++ job_ptr->details->env_sup[0]); ++ ++ if (job_ptr->details->submit_line) ++ xstrfmtcat(query, "submit_line='%s', ", ++ job_ptr->details->submit_line); ++ if (job_ptr->container) ++ xstrfmtcat(query, "container='%s', ", ++ job_ptr->container); ++ ++ xstrfmtcat(query, "time_start=%ld, job_name='%s', " ++ "state=GREATEST(state, %u), " ++ "nodes_alloc=%u, id_qos=%u, " ++ "id_assoc=%u, id_resv=%u, " ++ "timelimit=%u, mem_req=%"PRIu64", " ++ "id_array_job=%u, id_array_task=%u, " ++ "het_job_id=%u, het_job_offset=%u, " ++ "flags=%u, state_reason_prev=%u, " ++ "time_eligible=%ld, mod_time=%ld " ++ "where job_db_inx=%"PRIu64, ++ start_time, jname, job_state, ++ job_ptr->total_nodes, job_ptr->qos_id, ++ job_ptr->assoc_id, ++ job_ptr->resv_id, job_ptr->time_limit, ++ job_ptr->details->pn_min_memory, ++ job_ptr->array_job_id, array_task_id, ++ job_ptr->het_job_id, job_ptr->het_job_offset, ++ job_ptr->db_flags, job_ptr->state_reason_prev_db, ++ begin_time, (long)time(NULL), job_ptr->db_index); ++ ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ } ++ ++ /* now we will reset all the steps */ ++ if (IS_JOB_RESIZING(job_ptr)) { ++ /* FIXME : Verify this is still needed */ ++ if (IS_JOB_SUSPENDED(job_ptr)) ++ as_pgsql_suspend(pgsql_conn, job_db_inx, job_ptr); ++ } ++ ++ xfree(tres_alloc_str); ++ xfree(query); ++ ++ return rc; ++} ++ ++extern List as_pgsql_modify_job(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_job_cond_t *job_cond, ++ slurmdb_job_rec_t *job) ++{ ++ List ret_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *object = NULL; ++ char *vals = NULL, *cond_char = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ List job_list = NULL; ++ slurmdb_job_rec_t *job_rec; ++ ListIterator itr; ++ List id_switch_list = NULL; ++ id_switch_t *id_switch; ++ ++ if (!job_cond || !job) { ++ error("we need something to change"); ++ return NULL; ++ } else if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (job->derived_ec != NO_VAL) ++ xstrfmtcat(vals, ", derived_ec=%u", job->derived_ec); ++ ++ if (job->derived_es) ++ xstrfmtcat(vals, ", derived_es='%s'", job->derived_es); ++ ++ if (job->system_comment) ++ xstrfmtcat(vals, ", system_comment='%s'", ++ job->system_comment); ++ ++ if (job->wckey) ++ xstrfmtcat(vals, ", wckey='%s'", job->wckey); ++ ++ if (!vals) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ error("No change specified for job modification"); ++ return NULL; ++ } ++ job_cond->flags |= JOBCOND_FLAG_NO_STEP; ++ job_cond->flags |= JOBCOND_FLAG_NO_DEFAULT_USAGE; ++ ++ job_list = as_pgsql_jobacct_process_get_jobs(pgsql_conn, uid, job_cond); ++ ++ if (!job_list || !list_count(job_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "%s: Job(s) not found\n", ++ __func__); ++ xfree(vals); ++ FREE_NULL_LIST(job_list); ++ return NULL; ++ } ++ ++ user_name = uid_to_string((uid_t) uid); ++ ++ itr = list_iterator_create(job_list); ++ while ((job_rec = list_next(itr))) { ++ char tmp_char[25]; ++ char *vals_mod = NULL; ++ ++ if ((uid != job_rec->uid) && ++ !is_user_min_admin_level(pgsql_conn, uid, ++ SLURMDB_ADMIN_OPERATOR)) { ++ errno = ESLURM_ACCESS_DENIED; ++ rc = SLURM_ERROR; ++ break; ++ } ++ ++ slurm_make_time_str(&job_rec->submit, ++ tmp_char, sizeof(tmp_char)); ++ ++ xstrfmtcat(cond_char, "job_db_inx=%"PRIu64, job_rec->db_index); ++ object = xstrdup_printf("%u submitted at %s", ++ job_rec->jobid, tmp_char); ++ ++ if (!ret_list) ++ ret_list = list_create(xfree_ptr); ++ list_append(ret_list, object); ++ ++ /* ++ * Grab the wckey id to update the job now. ++ */ ++ if (job->wckey) { ++ uint32_t wckeyid = _get_wckeyid(pgsql_conn, ++ &job->wckey, ++ job_rec->uid, ++ job_rec->cluster, ++ job_rec->associd); ++ if (!wckeyid) { ++ rc = SLURM_ERROR; ++ break; ++ } ++ vals_mod = xstrdup_printf("%s, id_wckey='%u'", ++ vals, wckeyid); ++ id_switch = NULL; ++ if (!id_switch_list) ++ id_switch_list = list_create(xfree_ptr); ++ else { ++ id_switch = list_find_first( ++ id_switch_list, ++ _find_id_switch, ++ &job_rec->wckeyid); ++ } ++ ++ if (!id_switch) { ++ id_switch = xmalloc(sizeof(id_switch_t)); ++ id_switch->cluster = job_rec->cluster; ++ id_switch->old = job_rec->wckeyid; ++ id_switch->new = wckeyid; ++ list_append(id_switch_list, id_switch); ++ } ++ } else ++ vals_mod = vals; ++ ++ rc = modify_common(pgsql_conn, DBD_MODIFY_JOB, now, user_name, ++ job_table, cond_char, vals_mod, ++ job_rec->cluster); ++ xfree(cond_char); ++ ++ if (job->wckey) ++ xfree(vals_mod); ++ ++ if (rc != SLURM_SUCCESS) ++ break; ++ } ++ list_iterator_destroy(itr); ++ ++ xfree(vals); ++ xfree(user_name); ++ ++ if (rc == SLURM_ERROR) { ++ error("Couldn't modify job(s)"); ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ } else if (id_switch_list) { ++ struct tm hour_tm; ++ time_t usage_start, usage_end; ++ char *time_str = NULL; ++ char *query = NULL; ++ ++ if (!job_cond->usage_end) ++ job_cond->usage_end = now; ++ ++ if (!localtime_r(&job_cond->usage_end, &hour_tm)) { ++ error("Couldn't get localtime from end %ld", ++ job_cond->usage_end); ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ goto endit; ++ } ++ hour_tm.tm_sec = 0; ++ hour_tm.tm_min = 0; ++ ++ usage_end = slurm_mktime(&hour_tm); ++ ++ if (!job_cond->usage_start) ++ usage_start = 0; ++ else { ++ if (!localtime_r(&job_cond->usage_start, &hour_tm)) { ++ error("Couldn't get localtime from start %ld", ++ job_cond->usage_start); ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ goto endit; ++ } ++ hour_tm.tm_sec = 0; ++ hour_tm.tm_min = 0; ++ ++ usage_start = slurm_mktime(&hour_tm); ++ } ++ ++ time_str = xstrdup_printf( ++ "(time_start < %ld && time_start >= %ld)", ++ usage_end, usage_start); ++ ++ itr = list_iterator_create(id_switch_list); ++ while ((id_switch = list_next(itr))) { ++ char *use_table = NULL; ++ ++ for (int i = 0; i < 3; i++) { ++ switch (i) { ++ case 0: ++ use_table = wckey_hour_table; ++ break; ++ case 1: ++ use_table = wckey_day_table; ++ break; ++ case 2: ++ use_table = wckey_month_table; ++ break; ++ } ++ ++ use_table = xstrdup_printf( ++ "%s_%s", ++ id_switch->cluster, use_table); ++ /* ++ * Move any of the new id lines into the old id. ++ */ ++ query = xstrdup_printf( ++ "INSERT INTO \"%s\" (creation_time, mod_time, id, id_tres, time_start, alloc_secs) " ++ "SELECT creation_time, %ld, %u, id_tres, time_start, SUM(alloc_secs) AS ASUM from \"%s\" where " ++ "(id=%u || id=%u) && %s group by id_tres, time_start ON CONFLICT (id, id_tres, time_start) DO UPDATE SET alloc_secs=ASUM;", ++ use_table, ++ now, id_switch->old, use_table, ++ id_switch->new, id_switch->old, ++ time_str); ++ ++ /* Delete all traces of the new id */ ++ xstrfmtcat(query, ++ "delete from \"%s\" where id=%u && %s;", ++ use_table, id_switch->new, time_str); ++ ++ /* Now we just need to switch the ids */ ++ xstrfmtcat(query, ++ "update \"%s\" set mod_time=%ld, id=%u where id=%u && %s;", ++ use_table, now, id_switch->new, id_switch->old, time_str); ++ ++ ++ xfree(use_table); ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", ++ query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) ++ break; ++ } ++ if (rc != SLURM_SUCCESS) { ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ break; ++ } ++ } ++ list_iterator_destroy(itr); ++ xfree(time_str); ++ } ++endit: ++ FREE_NULL_LIST(job_list); ++ FREE_NULL_LIST(id_switch_list); ++ return ret_list; ++} ++ ++extern int as_pgsql_job_complete(pgsql_conn_t *pgsql_conn, ++ job_record_t *job_ptr) ++{ ++ char *query = NULL; ++ int rc = SLURM_SUCCESS, job_state; ++ time_t submit_time, end_time; ++ uint32_t exit_code = 0; ++ char *tres_alloc_str = NULL; ++ ++ if (!job_ptr->db_index ++ && ((!job_ptr->details || !job_ptr->details->submit_time) ++ && !job_ptr->resize_time)) { ++ error("as_pgsql_job_complete: " ++ "Not inputing this job, it has no submit time."); ++ return SLURM_ERROR; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ debug2("%s() called", __func__); ++ ++ if (job_ptr->resize_time) ++ submit_time = job_ptr->resize_time; ++ else ++ submit_time = job_ptr->details->submit_time; ++ ++ if (IS_JOB_RESIZING(job_ptr)) { ++ end_time = job_ptr->resize_time; ++ job_state = JOB_RESIZING; ++ } else { ++ if (job_ptr->end_time == 0) { ++ if (job_ptr->start_time) { ++ error("%s: We are trying to end a job (%u) with no end time, setting it to the start time (%ld) of the job.", ++ __func__, ++ job_ptr->job_id, job_ptr->start_time); ++ job_ptr->end_time = job_ptr->start_time; ++ } else { ++ error("%s: job %u never started", ++ __func__, job_ptr->job_id); ++ ++ /* If we get an error with this just ++ * fall through to avoid an infinite loop */ ++ return SLURM_SUCCESS; ++ } ++ } ++ end_time = job_ptr->end_time; ++ ++ if (IS_JOB_REQUEUED(job_ptr)) ++ job_state = JOB_REQUEUE; ++ else if (IS_JOB_REVOKED(job_ptr)) ++ job_state = JOB_REVOKED; ++ else ++ job_state = job_ptr->job_state & JOB_STATE_BASE; ++ } ++ ++ slurm_mutex_lock(&rollup_lock); ++ if (end_time < global_last_rollup) { ++ debug("Need to reroll usage from %s Job %u from %s %s then and we are just now hearing about it.", ++ slurm_ctime2(&end_time), ++ job_ptr->job_id, pgsql_conn->cluster_name, ++ IS_JOB_RESIZING(job_ptr) ? "resized" : "ended"); ++ global_last_rollup = end_time; ++ slurm_mutex_unlock(&rollup_lock); ++ ++ query = xstrdup_printf("update \"%s_%s\" set " ++ "hourly_rollup=%ld, " ++ "daily_rollup=%ld, monthly_rollup=%ld", ++ pgsql_conn->cluster_name, ++ last_ran_table, end_time, ++ end_time, end_time); ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", query); ++ (void) pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ } else ++ slurm_mutex_unlock(&rollup_lock); ++ ++ if (!job_ptr->db_index) { ++ if (!(job_ptr->db_index = ++ _get_db_index(pgsql_conn, ++ submit_time, ++ job_ptr->job_id))) { ++ /* Comment is overloaded in job_start to be ++ the block_id, so we will need to store this ++ for later. ++ */ ++ char *comment = job_ptr->comment; ++ job_ptr->comment = NULL; ++ /* If we get an error with this just fall ++ * through to avoid an infinite loop ++ */ ++ if (as_pgsql_job_start( ++ pgsql_conn, job_ptr) == SLURM_ERROR) { ++ job_ptr->comment = comment; ++ error("couldn't add job %u at job completion", ++ job_ptr->job_id); ++ return SLURM_SUCCESS; ++ } ++ job_ptr->comment = comment; ++ } ++ } ++ ++ /* ++ * make sure we handle any quotes that may be in the comment ++ */ ++ ++ query = xstrdup_printf("update \"%s_%s\" set " ++ "mod_time=%ld, " ++ "time_end=%ld, state=%d", ++ pgsql_conn->cluster_name, job_table, ++ (long)time(NULL), end_time, job_state); ++ ++ if (job_ptr->derived_ec != NO_VAL) ++ xstrfmtcat(query, ", derived_ec=%u", job_ptr->derived_ec); ++ ++ if (tres_alloc_str) ++ xstrfmtcat(query, ", tres_alloc='%s'", tres_alloc_str); ++ else if (job_ptr->tres_alloc_str) ++ xstrfmtcat(query, ", tres_alloc='%s'", job_ptr->tres_alloc_str); ++ ++ if (job_ptr->comment) ++ xstrfmtcat(query, ", derived_es='%s'", job_ptr->comment); ++ ++ if (job_ptr->admin_comment) ++ xstrfmtcat(query, ", admin_comment='%s'", ++ job_ptr->admin_comment); ++ ++ if (job_ptr->system_comment) ++ xstrfmtcat(query, ", system_comment='%s'", ++ job_ptr->system_comment); ++ ++ exit_code = job_ptr->exit_code; ++ if (exit_code == 1) { ++ /* This wasn't signaled, it was set by Slurm so don't ++ * treat it like a signal. ++ */ ++ exit_code = 256; ++ } ++ ++ xstrfmtcat(query, ++ ", exit_code=%d, kill_requid=%d where job_db_inx=%"PRIu64";", ++ exit_code, job_ptr->requid, ++ job_ptr->db_index); ++ ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ xfree(tres_alloc_str); ++ return rc; ++} ++ ++extern int as_pgsql_step_start(pgsql_conn_t *pgsql_conn, ++ step_record_t *step_ptr) ++{ ++ int tasks = 0, nodes = 0, task_dist = 0; ++ int rc = SLURM_SUCCESS; ++ char temp_bit[BUF_SIZE]; ++ char *node_list = NULL; ++ char *node_inx = NULL; ++ time_t start_time, submit_time; ++ char *query = NULL; ++ ++ if (!step_ptr->job_ptr->db_index ++ && ((!step_ptr->job_ptr->details ++ || !step_ptr->job_ptr->details->submit_time) ++ && !step_ptr->job_ptr->resize_time)) { ++ error("as_pgsql_step_start: " ++ "Not inputing this job, it has no submit time."); ++ return SLURM_ERROR; ++ } ++ ++ if (step_ptr->job_ptr->resize_time) { ++ submit_time = start_time = step_ptr->job_ptr->resize_time; ++ if (step_ptr->start_time > submit_time) ++ start_time = step_ptr->start_time; ++ } else { ++ start_time = step_ptr->start_time; ++ submit_time = step_ptr->job_ptr->details->submit_time; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ if (slurmdbd_conf) { ++ if (step_ptr->job_ptr->details) ++ tasks = step_ptr->job_ptr->details->num_tasks; ++ else ++ tasks = step_ptr->cpu_count; ++ node_list = step_ptr->job_ptr->nodes; ++ nodes = step_ptr->step_layout->node_cnt; ++ task_dist = step_ptr->step_layout->task_dist; ++ node_inx = step_ptr->network; ++ } else if (step_ptr->step_id.step_id == SLURM_BATCH_SCRIPT) { ++ if (step_ptr->step_node_bitmap) { ++ node_inx = bit_fmt(temp_bit, sizeof(temp_bit), ++ step_ptr->step_node_bitmap); ++ } ++ /* ++ * We overload tres_per_node with the node name of where the ++ * script was running. ++ */ ++ node_list = step_ptr->tres_per_node; ++ nodes = tasks = 1; ++ if (!step_ptr->tres_alloc_str) ++ xstrfmtcat(step_ptr->tres_alloc_str, ++ "%s%u=%u,%u=%u", ++ step_ptr->tres_alloc_str ? "," : "", ++ TRES_CPU, 1, ++ TRES_NODE, 1); ++ } else { ++ if (step_ptr->step_node_bitmap) { ++ node_inx = bit_fmt(temp_bit, sizeof(temp_bit), ++ step_ptr->step_node_bitmap); ++ } ++ ++ if (!step_ptr->step_layout ++ || !step_ptr->step_layout->task_cnt) { ++ if (step_ptr->cpu_count) ++ tasks = step_ptr->cpu_count; ++ else { ++ if ((tasks = slurmdb_find_tres_count_in_string( ++ step_ptr->tres_alloc_str, ++ TRES_CPU)) == INFINITE64) { ++ if ((tasks = ++ slurmdb_find_tres_count_in_string( ++ step_ptr->job_ptr-> ++ tres_alloc_str, ++ TRES_CPU)) == INFINITE64) ++ tasks = step_ptr->job_ptr-> ++ total_nodes; ++ } ++ } ++ ++ nodes = step_ptr->job_ptr->total_nodes; ++ node_list = step_ptr->job_ptr->nodes; ++ } else { ++ tasks = step_ptr->step_layout->task_cnt; ++ nodes = step_ptr->step_layout->node_cnt; ++ task_dist = step_ptr->step_layout->task_dist; ++ node_list = step_ptr->step_layout->node_list; ++ } ++ } ++ ++ if (!step_ptr->job_ptr->db_index) { ++ if (!(step_ptr->job_ptr->db_index = ++ _get_db_index(pgsql_conn, ++ submit_time, ++ step_ptr->job_ptr->job_id))) { ++ /* If we get an error with this just fall ++ * through to avoid an infinite loop ++ */ ++ if (as_pgsql_job_start(pgsql_conn, step_ptr->job_ptr) ++ == SLURM_ERROR) { ++ error("couldn't add job %u at step start", ++ step_ptr->job_ptr->job_id); ++ return SLURM_SUCCESS; ++ } ++ } ++ } ++ ++ /* we want to print a -1 for the requid so leave it a ++ %d */ ++ /* The stepid could be negative so use %d not %u */ ++ query = xstrdup_printf( ++ "insert into \"%s_%s\" (job_db_inx, id_step, step_het_comp, " ++ "time_start, step_name, state, tres_alloc, " ++ "nodes_alloc, task_cnt, nodelist, node_inx, " ++ "task_dist, req_cpufreq, req_cpufreq_min, req_cpufreq_gov", ++ pgsql_conn->cluster_name, step_table); ++ ++ if (step_ptr->submit_line) ++ xstrcat(query, ", submit_line"); ++ if (step_ptr->container) ++ xstrcat(query, ", container"); ++ ++ xstrfmtcat(query, ++ ") values (%"PRIu64", %d, %u, %d, '%s', %d, '%s', %d, %d, " ++ "'%s', '%s', %d, %u, %u, %u", ++ step_ptr->job_ptr->db_index, ++ step_ptr->step_id.step_id, ++ step_ptr->step_id.step_het_comp, ++ (int)start_time, step_ptr->name, ++ JOB_RUNNING, step_ptr->tres_alloc_str, ++ nodes, tasks, node_list, node_inx, task_dist, ++ step_ptr->cpu_freq_max, step_ptr->cpu_freq_min, ++ step_ptr->cpu_freq_gov); ++ ++ if (step_ptr->submit_line) ++ xstrfmtcat(query, ", '%s'", step_ptr->submit_line); ++ if (step_ptr->container) ++ xstrfmtcat(query, ", '%s'", step_ptr->container); ++ ++ xstrfmtcat(query, ++ ") ON CONFLICT (job_db_inx, id_step, step_het_comp) DO UPDATE SET " ++ "nodes_alloc=%d, task_cnt=%d, time_end=0, state=%d, " ++ "nodelist='%s', node_inx='%s', task_dist=%d, " ++ "req_cpufreq=%u, req_cpufreq_min=%u, req_cpufreq_gov=%u," ++ "tres_alloc='%s'", ++ nodes, tasks, JOB_RUNNING, ++ node_list, node_inx, task_dist, step_ptr->cpu_freq_max, ++ step_ptr->cpu_freq_min, step_ptr->cpu_freq_gov, ++ step_ptr->tres_alloc_str); ++ ++ if (step_ptr->submit_line) ++ xstrfmtcat(query, ", submit_line='%s'", step_ptr->submit_line); ++ ++ if (step_ptr->container) ++ xstrfmtcat(query, ", container='%s'", step_ptr->container); ++ ++ DB_DEBUG(DB_STEP, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ return rc; ++} ++ ++extern int as_pgsql_step_complete(pgsql_conn_t *pgsql_conn, ++ step_record_t *step_ptr) ++{ ++ time_t now; ++ uint16_t comp_status; ++ int tasks = 0; ++ struct jobacctinfo *jobacct = (struct jobacctinfo *)step_ptr->jobacct; ++ char *query = NULL; ++ int rc = SLURM_SUCCESS; ++ uint32_t exit_code = 0; ++ time_t submit_time; ++ ++ if (!step_ptr->job_ptr->db_index ++ && ((!step_ptr->job_ptr->details ++ || !step_ptr->job_ptr->details->submit_time) ++ && !step_ptr->job_ptr->resize_time)) { ++ error("as_pgsql_step_complete: " ++ "Not inputing this job, it has no submit time."); ++ return SLURM_ERROR; ++ } ++ ++ if (step_ptr->job_ptr->resize_time) ++ submit_time = step_ptr->job_ptr->resize_time; ++ else ++ submit_time = step_ptr->job_ptr->details->submit_time; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (slurmdbd_conf) { ++ now = step_ptr->job_ptr->end_time; ++ if (step_ptr->job_ptr->details) ++ tasks = step_ptr->job_ptr->details->num_tasks; ++ else ++ tasks = step_ptr->cpu_count; ++ } else if (step_ptr->step_id.step_id == SLURM_BATCH_SCRIPT) { ++ now = time(NULL); ++ tasks = 1; ++ } else { ++ now = time(NULL); ++ if (!step_ptr->step_layout ++ || !step_ptr->step_layout->task_cnt) { ++ if (step_ptr->cpu_count) ++ tasks = step_ptr->cpu_count; ++ else { ++ if ((tasks = slurmdb_find_tres_count_in_string( ++ step_ptr->tres_alloc_str, ++ TRES_CPU)) == INFINITE64) { ++ if ((tasks = ++ slurmdb_find_tres_count_in_string( ++ step_ptr->job_ptr-> ++ tres_alloc_str, ++ TRES_CPU)) == INFINITE64) ++ tasks = step_ptr->job_ptr-> ++ total_nodes; ++ } ++ } ++ } else ++ tasks = step_ptr->step_layout->task_cnt; ++ } ++ ++ exit_code = step_ptr->exit_code; ++ comp_status = step_ptr->state & JOB_STATE_BASE; ++ if (comp_status < JOB_COMPLETE) { ++ if (exit_code == SIG_OOM) { ++ comp_status = JOB_OOM; ++ } else if (WIFSIGNALED(exit_code)) { ++ comp_status = JOB_CANCELLED; ++ } else if (exit_code) ++ comp_status = JOB_FAILED; ++ else { ++ step_ptr->requid = -1; ++ comp_status = JOB_COMPLETE; ++ } ++ } ++ ++ if (!step_ptr->job_ptr->db_index) { ++ if (!(step_ptr->job_ptr->db_index = ++ _get_db_index(pgsql_conn, ++ submit_time, ++ step_ptr->job_ptr->job_id))) { ++ /* If we get an error with this just fall ++ * through to avoid an infinite loop ++ */ ++ if (as_pgsql_job_start(pgsql_conn, step_ptr->job_ptr) ++ == SLURM_ERROR) { ++ error("couldn't add job %u " ++ "at step completion", ++ step_ptr->job_ptr->job_id); ++ return SLURM_SUCCESS; ++ } ++ } ++ } ++ ++ /* The stepid could be negative so use %d not %u */ ++ query = xstrdup_printf( ++ "update \"%s_%s\" set time_end=%d, state=%u, " ++ "kill_requid=%d, exit_code=%d", ++ pgsql_conn->cluster_name, step_table, (int)now, ++ comp_status, ++ step_ptr->requid, ++ exit_code); ++ ++ ++ if (jobacct) { ++ slurmdb_stats_t stats; ++ ++ memset(&stats, 0, sizeof(slurmdb_stats_t)); ++ ++ /* figure out the ave of the totals sent */ ++ if (tasks > 0) { ++ stats.tres_usage_in_ave = ++ _average_tres_usage(jobacct->tres_ids, ++ jobacct->tres_usage_in_tot, ++ jobacct->tres_count, ++ tasks); ++ stats.tres_usage_out_ave = ++ _average_tres_usage(jobacct->tres_ids, ++ jobacct->tres_usage_out_tot, ++ jobacct->tres_count, ++ tasks); ++ } ++ ++ /* ++ * We can't trust the assoc_mgr here as the tres may have ++ * changed, we have to go off what was sent us. We can just use ++ * the _average_tres_usage to do this by dividing by 1. ++ */ ++ stats.tres_usage_in_max = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_in_max, ++ jobacct->tres_count, 1); ++ stats.tres_usage_in_max_nodeid = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_in_max_nodeid, ++ jobacct->tres_count, 1); ++ stats.tres_usage_in_max_taskid = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_in_max_taskid, ++ jobacct->tres_count, 1); ++ stats.tres_usage_in_min = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_in_min, ++ jobacct->tres_count, 1); ++ stats.tres_usage_in_min_nodeid = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_in_min_nodeid, ++ jobacct->tres_count, 1); ++ stats.tres_usage_in_min_taskid = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_in_min_taskid, ++ jobacct->tres_count, 1); ++ stats.tres_usage_in_tot = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_in_tot, ++ jobacct->tres_count, 1); ++ stats.tres_usage_out_max = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_out_max, ++ jobacct->tres_count, 1); ++ stats.tres_usage_out_max_nodeid = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_out_max_nodeid, ++ jobacct->tres_count, 1); ++ stats.tres_usage_out_max_taskid = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_out_max_taskid, ++ jobacct->tres_count, 1); ++ stats.tres_usage_out_min = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_out_min, ++ jobacct->tres_count, 1); ++ stats.tres_usage_out_min_nodeid = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_out_min_nodeid, ++ jobacct->tres_count, 1); ++ stats.tres_usage_out_min_taskid = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_out_min_taskid, ++ jobacct->tres_count, 1); ++ stats.tres_usage_out_tot = _average_tres_usage( ++ jobacct->tres_ids, ++ jobacct->tres_usage_out_tot, ++ jobacct->tres_count, 1); ++ ++ xstrfmtcat(query, ++ ", user_sec=%"PRIu64", user_usec=%u, " ++ "sys_sec=%"PRIu64", sys_usec=%u, " ++ "act_cpufreq=%u, consumed_energy=%"PRIu64", " ++ "tres_usage_in_ave='%s', " ++ "tres_usage_out_ave='%s', " ++ "tres_usage_in_max='%s', " ++ "tres_usage_in_max_taskid='%s', " ++ "tres_usage_in_max_nodeid='%s', " ++ "tres_usage_in_min='%s', " ++ "tres_usage_in_min_taskid='%s', " ++ "tres_usage_in_min_nodeid='%s', " ++ "tres_usage_in_tot='%s', " ++ "tres_usage_out_max='%s', " ++ "tres_usage_out_max_taskid='%s', " ++ "tres_usage_out_max_nodeid='%s', " ++ "tres_usage_out_min='%s', " ++ "tres_usage_out_min_taskid='%s', " ++ "tres_usage_out_min_nodeid='%s', " ++ "tres_usage_out_tot='%s'", ++ /* user seconds */ ++ jobacct->user_cpu_sec, ++ /* user microseconds */ ++ jobacct->user_cpu_usec, ++ /* system seconds */ ++ jobacct->sys_cpu_sec, ++ /* system microsecs */ ++ jobacct->sys_cpu_usec, ++ jobacct->act_cpufreq, ++ jobacct->energy.consumed_energy, ++ stats.tres_usage_in_ave, ++ stats.tres_usage_out_ave, ++ stats.tres_usage_in_max, ++ stats.tres_usage_in_max_taskid, ++ stats.tres_usage_in_max_nodeid, ++ stats.tres_usage_in_min, ++ stats.tres_usage_in_min_taskid, ++ stats.tres_usage_in_min_nodeid, ++ stats.tres_usage_in_tot, ++ stats.tres_usage_out_max, ++ stats.tres_usage_out_max_taskid, ++ stats.tres_usage_out_max_nodeid, ++ stats.tres_usage_out_min, ++ stats.tres_usage_out_min_taskid, ++ stats.tres_usage_out_min_nodeid, ++ stats.tres_usage_out_tot); ++ ++ slurmdb_free_slurmdb_stats_members(&stats); ++ } ++ ++ /* id_step has to be %d here to handle the negative values for the batch ++ and extern steps. Don't change it to a %u. ++ */ ++ xstrfmtcat(query, ++ " where job_db_inx=%"PRIu64" and id_step=%d and step_het_comp=%u", ++ step_ptr->job_ptr->db_index, step_ptr->step_id.step_id, ++ step_ptr->step_id.step_het_comp); ++ DB_DEBUG(DB_STEP, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ /* set the energy for the entire job. */ ++ if (step_ptr->job_ptr->tres_alloc_str) { ++ query = xstrdup_printf( ++ "update \"%s_%s\" set tres_alloc='%s' where " ++ "job_db_inx=%"PRIu64, ++ pgsql_conn->cluster_name, job_table, ++ step_ptr->job_ptr->tres_alloc_str, ++ step_ptr->job_ptr->db_index); ++ DB_DEBUG(DB_STEP, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ } ++ ++ return rc; ++} ++ ++extern int as_pgsql_suspend(pgsql_conn_t *pgsql_conn, uint64_t old_db_inx, ++ job_record_t *job_ptr) ++{ ++ char *query = NULL; ++ int rc = SLURM_SUCCESS; ++ time_t submit_time; ++ uint64_t job_db_inx; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (job_ptr->resize_time) ++ submit_time = job_ptr->resize_time; ++ else ++ submit_time = job_ptr->details->submit_time; ++ ++ if (!job_ptr->db_index) { ++ if (!(job_ptr->db_index = ++ _get_db_index(pgsql_conn, ++ submit_time, ++ job_ptr->job_id))) { ++ /* If we get an error with this just fall ++ * through to avoid an infinite loop ++ */ ++ if (as_pgsql_job_start( ++ pgsql_conn, job_ptr) == SLURM_ERROR) { ++ error("couldn't suspend job %u", ++ job_ptr->job_id); ++ return SLURM_SUCCESS; ++ } ++ } ++ } ++ ++ if (IS_JOB_RESIZING(job_ptr)) { ++ if (!old_db_inx) { ++ error("No old db inx given for job %u cluster %s, " ++ "can't update suspend table.", ++ job_ptr->job_id, pgsql_conn->cluster_name); ++ return SLURM_ERROR; ++ } ++ job_db_inx = old_db_inx; ++ xstrfmtcat(query, ++ "update \"%s_%s\" set time_end=%d where " ++ "job_db_inx=%"PRIu64" && time_end=0;", ++ pgsql_conn->cluster_name, suspend_table, ++ (int)job_ptr->suspend_time, job_db_inx); ++ ++ } else ++ job_db_inx = job_ptr->db_index; ++ ++ /* use job_db_inx for this one since we want to update the ++ supend time of the job before it was resized. ++ */ ++ xstrfmtcat(query, ++ "update \"%s_%s\" set time_suspended=%d-time_suspended, " ++ "state=%d where job_db_inx=%"PRIu64";", ++ pgsql_conn->cluster_name, job_table, ++ (int)job_ptr->suspend_time, ++ job_ptr->job_state & JOB_STATE_BASE, ++ job_db_inx); ++ if (IS_JOB_SUSPENDED(job_ptr)) ++ xstrfmtcat(query, ++ "insert into \"%s_%s\" (job_db_inx, id_assoc, " ++ "time_start, time_end) " ++ "values (%"PRIu64", %u, %d, 0);", ++ pgsql_conn->cluster_name, suspend_table, ++ job_ptr->db_index, job_ptr->assoc_id, ++ (int)job_ptr->suspend_time); ++ else ++ xstrfmtcat(query, ++ "update \"%s_%s\" set time_end=%d where " ++ "job_db_inx=%"PRIu64" && time_end=0;", ++ pgsql_conn->cluster_name, suspend_table, ++ (int)job_ptr->suspend_time, job_ptr->db_index); ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ ++ xfree(query); ++ if (rc != SLURM_ERROR) { ++ xstrfmtcat(query, ++ "update \"%s_%s\" set " ++ "time_suspended=%u-time_suspended, " ++ "state=%d where job_db_inx=%"PRIu64" and time_end=0", ++ pgsql_conn->cluster_name, step_table, ++ (int)job_ptr->suspend_time, ++ job_ptr->job_state, job_ptr->db_index); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ } ++ ++ return rc; ++} ++ ++extern int as_pgsql_flush_jobs_on_cluster( ++ pgsql_conn_t *pgsql_conn, time_t event_time) ++{ ++ int rc = SLURM_SUCCESS; ++ /* put end times for a clean start */ ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *query = NULL; ++ char *id_char = NULL; ++ char *suspended_char = NULL; ++ size_t count; ++ ++again: ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ /* First we need to get the job_db_inx's and states so we can clean up ++ * the suspend table and the step table ++ */ ++ query = xstrdup_printf( ++ "select distinct t1.job_db_inx, t1.state from \"%s_%s\" " ++ "as t1 where t1.time_end=0 LIMIT %u;", ++ pgsql_conn->cluster_name, job_table, MAX_FLUSH_JOBS); ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", query); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ xfree(query); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ int state = slurm_atoul(row[1]); ++ if (state == JOB_SUSPENDED) { ++ if (suspended_char) ++ xstrfmtcat(suspended_char, ++ ", %s", row[0]); ++ else ++ xstrfmtcat(suspended_char, "job_db_inx in (%s", ++ row[0]); ++ } ++ ++ if (id_char) ++ xstrfmtcat(id_char, ", %s", row[0]); ++ else ++ xstrfmtcat(id_char, "job_db_inx in (%s", row[0]); ++ } ++ count = pgsql_num_rows(result); ++ pgsql_free_result(&result); ++ ++ if (suspended_char) { ++ xstrfmtcat(suspended_char, ")"); ++ xstrfmtcat(query, ++ "update \"%s_%s\" set " ++ "time_suspended=%ld-time_suspended " ++ "where %s;", ++ pgsql_conn->cluster_name, job_table, ++ event_time, suspended_char); ++ xstrfmtcat(query, ++ "update \"%s_%s\" set " ++ "time_suspended=%ld-time_suspended " ++ "where %s;", ++ pgsql_conn->cluster_name, step_table, ++ event_time, suspended_char); ++ xstrfmtcat(query, ++ "update \"%s_%s\" set time_end=%ld where (%s) " ++ "&& time_end=0;", ++ pgsql_conn->cluster_name, suspend_table, ++ event_time, suspended_char); ++ xfree(suspended_char); ++ } ++ if (id_char) { ++ xstrfmtcat(id_char, ")"); ++ xstrfmtcat(query, ++ "update \"%s_%s\" set state=%d, " ++ "time_end=%ld where %s;", ++ pgsql_conn->cluster_name, job_table, ++ JOB_CANCELLED, event_time, id_char); ++ xstrfmtcat(query, ++ "update \"%s_%s\" set state=%d, " ++ "time_end=%ld where %s;", ++ pgsql_conn->cluster_name, step_table, ++ JOB_CANCELLED, event_time, id_char); ++ xfree(id_char); ++ } ++ ++ if (query) { ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ } ++ ++ /* all rows were returned, there may be more to check */ ++ if (!rc && (count >= MAX_FLUSH_JOBS)) { ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, ++ "%s: possible missed jobs. Running query again.", ++ __func__); ++ goto again; ++ } ++ ++ return rc; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_job.h b/src/plugins/accounting_storage/pgsql/as_pgsql_job.h +new file mode 100755 +index 0000000..25b25b8 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_job.h +@@ -0,0 +1,64 @@ ++/*****************************************************************************\ ++ * as_pgsql_job.h - functions dealing with jobs and job steps. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_JOB_H ++#define _HAVE_PGSQL_JOB_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_job_start(pgsql_conn_t *pgsql_conn, job_record_t *job_ptr); ++ ++extern List as_pgsql_modify_job(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_job_cond_t *job_cond, ++ slurmdb_job_rec_t *job); ++ ++extern int as_pgsql_job_complete(pgsql_conn_t *pgsql_conn, ++ job_record_t *job_ptr); ++ ++extern int as_pgsql_step_start(pgsql_conn_t *pgsql_conn, ++ step_record_t *step_ptr); ++ ++extern int as_pgsql_step_complete(pgsql_conn_t *pgsql_conn, ++ step_record_t *step_ptr); ++ ++extern int as_pgsql_suspend(pgsql_conn_t *pgsql_conn, uint64_t old_db_inx, ++ job_record_t *job_ptr); ++ ++extern int as_pgsql_flush_jobs_on_cluster( ++ pgsql_conn_t *pgsql_conn, time_t event_time); ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_jobacct_process.c b/src/plugins/accounting_storage/pgsql/as_pgsql_jobacct_process.c +new file mode 100755 +index 0000000..72e9de0 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_jobacct_process.c +@@ -0,0 +1,1843 @@ ++/*****************************************************************************\ ++ * as_pgsql_jobacct_process.c - functions the processing of ++ * information from the as_pgsql jobacct ++ * storage. ++ ***************************************************************************** ++ * Copyright (C) 2008-2009 Lawrence Livermore National Security. ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++ * ++ * This file is patterned after jobcomp_linux.c, written by Morris Jette and ++ * Copyright (C) 2002 The Regents of the University of California. ++\*****************************************************************************/ ++ ++#include "as_pgsql_jobacct_process.h" ++ ++typedef struct { ++ hostlist_t hl; ++ time_t start; ++ time_t end; ++ bitstr_t *asked_bitmap; ++} local_cluster_t; ++ ++/* if this changes you will need to edit the corresponding ++ * enum below also t1 is job_table */ ++char *job_req_inx[] = { ++ "t1.account", ++ "t1.admin_comment", ++ "t1.array_max_tasks", ++ "t1.array_task_str", ++ "t1.constraints", ++ "t1.container", ++ "t1.cpus_req", ++ "t1.derived_ec", ++ "t1.derived_es", ++ "t1.exit_code", ++ "t1.flags", ++ "t1.id_array_job", ++ "t1.id_array_task", ++ "t1.id_assoc", ++ "t1.id_block", ++ "t1.id_group", ++ "t1.id_job", ++ "t1.het_job_id", ++ "t1.het_job_offset", ++ "t1.id_qos", ++ "t1.id_resv", ++ "t3.resv_name", ++ "t1.id_user", ++ "t1.id_wckey", ++ "t1.job_db_inx", ++ "t1.job_name", ++ "t1.kill_requid", ++ "t1.mem_req", ++ "t1.node_inx", ++ "t1.nodelist", ++ "t1.nodes_alloc", ++ "t1.partition", ++ "t1.priority", ++ "t1.state", ++ "t1.state_reason_prev", ++ "t1.system_comment", ++ "t1.time_eligible", ++ "t1.time_end", ++ "t1.time_start", ++ "t1.time_submit", ++ "t1.time_suspended", ++ "t1.timelimit", ++ "t1.track_steps", ++ "t1.wckey", ++ "t1.gres_used", ++ "t1.tres_alloc", ++ "t1.tres_req", ++ "t1.work_dir", ++ "t1.mcs_label", ++ "t1.batch_script", ++ "t1.submit_line", ++ "t1.env_vars", ++ "t2.acct", ++ "t2.lft", ++ "t2.user" ++}; ++ ++enum { ++ JOB_REQ_ACCOUNT1, ++ JOB_REQ_ADMIN_COMMENT, ++ JOB_REQ_ARRAY_MAX, ++ JOB_REQ_ARRAY_STR, ++ JOB_REQ_CONSTRAINTS, ++ JOB_REQ_CONTAINER, ++ JOB_REQ_REQ_CPUS, ++ JOB_REQ_DERIVED_EC, ++ JOB_REQ_DERIVED_ES, ++ JOB_REQ_EXIT_CODE, ++ JOB_REQ_FLAGS, ++ JOB_REQ_ARRAYJOBID, ++ JOB_REQ_ARRAYTASKID, ++ JOB_REQ_ASSOCID, ++ JOB_REQ_BLOCKID, ++ JOB_REQ_GID, ++ JOB_REQ_JOBID, ++ JOB_REQ_HET_JOB_ID, ++ JOB_REQ_HET_JOB_OFFSET, ++ JOB_REQ_QOS, ++ JOB_REQ_RESVID, ++ JOB_REQ_RESV_NAME, ++ JOB_REQ_UID, ++ JOB_REQ_WCKEYID, ++ JOB_REQ_DB_INX, ++ JOB_REQ_NAME, ++ JOB_REQ_KILL_REQUID, ++ JOB_REQ_REQ_MEM, ++ JOB_REQ_NODE_INX, ++ JOB_REQ_NODELIST, ++ JOB_REQ_ALLOC_NODES, ++ JOB_REQ_PARTITION, ++ JOB_REQ_PRIORITY, ++ JOB_REQ_STATE, ++ JOB_REQ_STATE_REASON, ++ JOB_REQ_SYSTEM_COMMENT, ++ JOB_REQ_ELIGIBLE, ++ JOB_REQ_END, ++ JOB_REQ_START, ++ JOB_REQ_SUBMIT, ++ JOB_REQ_SUSPENDED, ++ JOB_REQ_TIMELIMIT, ++ JOB_REQ_TRACKSTEPS, ++ JOB_REQ_WCKEY, ++ JOB_REQ_GRES_USED, ++ JOB_REQ_TRESA, ++ JOB_REQ_TRESR, ++ JOB_REQ_WORK_DIR, ++ JOB_REQ_MCS_LABEL, ++ JOB_REQ_SCRIPT, ++ JOB_REQ_SUBMIT_LINE, ++ JOB_REQ_ENV, ++ JOB_REQ_ACCOUNT, ++ JOB_REQ_LFT, ++ JOB_REQ_USER_NAME, ++ JOB_REQ_COUNT ++}; ++ ++/* if this changes you will need to edit the corresponding ++ * enum below also t1 is step_table */ ++char *step_req_inx[] = { ++ "t1.id_step", ++ "t1.step_het_comp", ++ "t1.time_start", ++ "t1.time_end", ++ "t1.time_suspended", ++ "t1.step_name", ++ "t1.nodelist", ++ "t1.node_inx", ++ "t1.state", ++ "t1.kill_requid", ++ "t1.exit_code", ++ "t1.nodes_alloc", ++ "t1.task_cnt", ++ "t1.task_dist", ++ "t1.user_sec", ++ "t1.user_usec", ++ "t1.sys_sec", ++ "t1.sys_usec", ++ "t1.act_cpufreq", ++ "t1.consumed_energy", ++ "t1.container", ++ "t1.req_cpufreq_min", ++ "t1.req_cpufreq", ++ "t1.req_cpufreq_gov", ++ "t1.submit_line", ++ "t1.tres_alloc", ++ "t1.tres_usage_in_max", ++ "t1.tres_usage_in_max_taskid", ++ "t1.tres_usage_in_max_nodeid", ++ "t1.tres_usage_in_ave", ++ "t1.tres_usage_in_min", ++ "t1.tres_usage_in_min_taskid", ++ "t1.tres_usage_in_min_nodeid", ++ "t1.tres_usage_in_tot", ++ "t1.tres_usage_out_max", ++ "t1.tres_usage_out_max_taskid", ++ "t1.tres_usage_out_max_nodeid", ++ "t1.tres_usage_out_ave", ++ "t1.tres_usage_out_min", ++ "t1.tres_usage_out_min_taskid", ++ "t1.tres_usage_out_min_nodeid", ++ "t1.tres_usage_out_tot", ++}; ++ ++enum { ++ STEP_REQ_STEPID, ++ STEP_REQ_STEP_HET_COMP, ++ STEP_REQ_START, ++ STEP_REQ_END, ++ STEP_REQ_SUSPENDED, ++ STEP_REQ_NAME, ++ STEP_REQ_NODELIST, ++ STEP_REQ_NODE_INX, ++ STEP_REQ_STATE, ++ STEP_REQ_KILL_REQUID, ++ STEP_REQ_EXIT_CODE, ++ STEP_REQ_NODES, ++ STEP_REQ_TASKS, ++ STEP_REQ_TASKDIST, ++ STEP_REQ_USER_SEC, ++ STEP_REQ_USER_USEC, ++ STEP_REQ_SYS_SEC, ++ STEP_REQ_SYS_USEC, ++ STEP_REQ_ACT_CPUFREQ, ++ STEP_REQ_CONSUMED_ENERGY, ++ STEP_REQ_CONTAINER, ++ STEP_REQ_REQ_CPUFREQ_MIN, ++ STEP_REQ_REQ_CPUFREQ_MAX, ++ STEP_REQ_REQ_CPUFREQ_GOV, ++ STEP_REQ_SUBMIT_LINE, ++ STEP_REQ_TRES, ++ STEP_REQ_TRES_USAGE_IN_MAX, ++ STEP_REQ_TRES_USAGE_IN_MAX_TASKID, ++ STEP_REQ_TRES_USAGE_IN_MAX_NODEID, ++ STEP_REQ_TRES_USAGE_IN_AVE, ++ STEP_REQ_TRES_USAGE_IN_MIN, ++ STEP_REQ_TRES_USAGE_IN_MIN_TASKID, ++ STEP_REQ_TRES_USAGE_IN_MIN_NODEID, ++ STEP_REQ_TRES_USAGE_IN_TOT, ++ STEP_REQ_TRES_USAGE_OUT_MAX, ++ STEP_REQ_TRES_USAGE_OUT_MAX_TASKID, ++ STEP_REQ_TRES_USAGE_OUT_MAX_NODEID, ++ STEP_REQ_TRES_USAGE_OUT_AVE, ++ STEP_REQ_TRES_USAGE_OUT_MIN, ++ STEP_REQ_TRES_USAGE_OUT_MIN_TASKID, ++ STEP_REQ_TRES_USAGE_OUT_MIN_NODEID, ++ STEP_REQ_TRES_USAGE_OUT_TOT, ++ STEP_REQ_COUNT ++}; ++ ++static void _setup_job_cond_selected_steps(slurmdb_job_cond_t *job_cond, ++ char *cluster_name, char **extra) ++{ ++ ListIterator itr = NULL; ++ slurm_selected_step_t *selected_step = NULL; ++ ++ if (!job_cond || (job_cond->flags & JOBCOND_FLAG_RUNAWAY)) ++ return; ++ ++ if (job_cond->step_list && list_count(job_cond->step_list)) { ++ char *job_ids = NULL, *sep = ""; ++ char *array_job_ids = NULL, *array_task_ids = NULL; ++ char *het_job_ids = NULL, *het_job_offset = NULL; ++ ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ itr = list_iterator_create(job_cond->step_list); ++ while ((selected_step = list_next(itr))) { ++ if (selected_step->array_task_id != NO_VAL) { ++ if (array_task_ids) ++ xstrcat(array_task_ids, " ,"); ++ xstrfmtcat(array_task_ids, "(%u, %u)", ++ selected_step->step_id.job_id, ++ selected_step->array_task_id); ++ } else if (selected_step->het_job_offset != NO_VAL) { ++ if (het_job_ids) ++ xstrcat(het_job_ids, " ,"); ++ if (het_job_offset) ++ xstrcat(het_job_offset, " ,"); ++ xstrfmtcat(het_job_ids, "%u", ++ selected_step->step_id.job_id); ++ xstrfmtcat(het_job_offset, "%u", ++ selected_step->het_job_offset); ++ } else { ++ if (job_ids) ++ xstrcat(job_ids, " ,"); ++ if (array_job_ids) ++ xstrcat(array_job_ids, " ,"); ++ xstrfmtcat(job_ids, "%u", ++ selected_step->step_id.job_id); ++ xstrfmtcat(array_job_ids, "%u", ++ selected_step->step_id.job_id); ++ } ++ } ++ list_iterator_destroy(itr); ++ ++ if (job_ids) { ++ if (job_cond->flags & JOBCOND_FLAG_WHOLE_HETJOB) ++ xstrfmtcat(*extra, "t1.id_job in (%s) || " ++ "(t1.het_job_offset<>%u && " ++ "t1.het_job_id in (select " ++ "t4.het_job_id from \"%s_%s\" as " ++ "t4 where t4.id_job in (%s) && " ++ "t4.het_job_id<>0))", ++ job_ids, NO_VAL, cluster_name, ++ job_table, job_ids); ++ else if (job_cond->flags & JOBCOND_FLAG_NO_WHOLE_HETJOB) ++ xstrfmtcat(*extra, "t1.id_job in (%s)", ++ job_ids); ++ else ++ xstrfmtcat(*extra, ++ "t1.id_job in (%s) || t1.het_job_id in (%s)", ++ job_ids, job_ids); ++ sep = " || "; ++ } ++ if (het_job_offset) { ++ if (job_cond->flags & JOBCOND_FLAG_WHOLE_HETJOB) ++ xstrfmtcat(*extra, "%s(t1.het_job_id in (%s))", ++ sep, het_job_ids); ++ else ++ xstrfmtcat(*extra, "%s(t1.het_job_id in (%s) " ++ "&& t1.het_job_offset in (%s))", ++ sep, het_job_ids, het_job_offset); ++ sep = " || "; ++ } ++ if (array_job_ids) { ++ xstrfmtcat(*extra, "%s(t1.id_array_job in (%s))", ++ sep, array_job_ids); ++ sep = " || "; ++ } ++ ++ if (array_task_ids) { ++ xstrfmtcat(*extra, ++ "%s((t1.id_array_job, t1.id_array_task) in (%s))", ++ sep, array_task_ids); ++ } ++ ++ xstrcat(*extra, ")"); ++ xfree(job_ids); ++ xfree(array_job_ids); ++ xfree(array_task_ids); ++ xfree(het_job_ids); ++ xfree(het_job_offset); ++ } ++} ++ ++static void _state_time_string(char **extra, char *cluster_name, uint32_t state, ++ slurmdb_job_cond_t *job_cond) ++{ ++ int base_state = state; ++ ++ if (!job_cond->usage_start && !job_cond->usage_end) { ++ xstrfmtcat(*extra, "t1.state='%u'", state); ++ return; ++ } ++ ++ switch(base_state) { ++ case JOB_PENDING: ++ /* ++ * Generic Query assuming that -S and -E are properly set in ++ * slurmdb_job_cond_def_start_end ++ * ++ * (job eligible) && ++ * (( time_start && (-S < time_start)) || ++ * (!time_start && time_end && (-S < time_end)) || -> Cancel before start ++ * (!time_start && !time_end && (state = PD) )) && -> Still PD ++ * (-E > time_eligible) ++ */ ++ xstrfmtcat(*extra, ++ "(t1.time_eligible && " ++ "(( t1.time_start && (%ld < t1.time_start)) || " ++ " (!t1.time_start && t1.time_end && (%ld < t1.time_end)) || " ++ " (!t1.time_start && !t1.time_end && (t1.state=%d))) && " ++ "(%ld > t1.time_eligible))", ++ job_cond->usage_start, ++ job_cond->usage_start, ++ base_state, ++ job_cond->usage_end); ++ break; ++ case JOB_SUSPENDED: ++ xstrfmtcat(*extra, ++ "(select count(time_start) from " ++ "\"%s_%s\" where " ++ "(time_start <= %ld && (time_end >= %ld " ++ "|| time_end = 0)) && job_db_inx=t1.job_db_inx)", ++ cluster_name, suspend_table, ++ job_cond->usage_end ? ++ job_cond->usage_end : job_cond->usage_start, ++ job_cond->usage_start); ++ break; ++ case JOB_RUNNING: ++ /* ++ * Generic Query assuming that -S and -E are properly set in ++ * slurmdb_job_cond_def_start_end ++ * ++ * (job started) && ++ * (-S < time_end || still running) && ++ * (-E > time_start) ++ */ ++ xstrfmtcat(*extra, ++ "(t1.time_start && " ++ "((%ld < t1.time_end || (!t1.time_end && t1.state=%d))) && " ++ "((%ld > t1.time_start)))", ++ job_cond->usage_start, base_state, ++ job_cond->usage_end); ++ break; ++ case JOB_COMPLETE: ++ case JOB_CANCELLED: ++ case JOB_FAILED: ++ case JOB_TIMEOUT: ++ case JOB_NODE_FAIL: ++ case JOB_PREEMPTED: ++ case JOB_BOOT_FAIL: ++ case JOB_DEADLINE: ++ case JOB_OOM: ++ case JOB_REQUEUE: ++ case JOB_RESIZING: ++ case JOB_REVOKED: ++ /* ++ * Query assuming that -S and -E are properly set in ++ * slurmdb_job_cond_def_start_end ++ * ++ * Job ending *in* the time window with the specified state. ++ */ ++ xstrfmtcat(*extra, ++ "(t1.state='%u' && (t1.time_end && " ++ "(t1.time_end between %ld and %ld)))", ++ base_state, job_cond->usage_start, ++ job_cond->usage_end); ++ break; ++ default: ++ error("Unsupported state requested: %s", ++ job_state_string(base_state)); ++ xstrfmtcat(*extra, "(t1.state='%u')", base_state); ++ } ++ ++ return; ++} ++ ++static void _destroy_local_cluster(void *object) ++{ ++ local_cluster_t *local_cluster = (local_cluster_t *)object; ++ if (local_cluster) { ++ if (local_cluster->hl) ++ hostlist_destroy(local_cluster->hl); ++ FREE_NULL_BITMAP(local_cluster->asked_bitmap); ++ xfree(local_cluster); ++ } ++} ++ ++static int _cluster_get_jobs(pgsql_conn_t *pgsql_conn, ++ slurmdb_user_rec_t *user, ++ slurmdb_job_cond_t *job_cond, ++ char *cluster_name, ++ char *job_fields, char *step_fields, ++ char *sent_extra, ++ bool is_admin, int only_pending, List sent_list) ++{ ++ char *query = NULL; ++ char *extra = xstrdup(sent_extra); ++ slurm_selected_step_t *selected_step = NULL; ++ pgsql_res_t *result = NULL, *step_result = NULL; ++ pgsql_row row, step_row; ++ slurmdb_job_rec_t *job = NULL; ++ slurmdb_step_rec_t *step = NULL; ++ time_t now = time(NULL); ++ List job_list = list_create(slurmdb_destroy_job_rec); ++ ListIterator itr = NULL, itr2 = NULL; ++ List local_cluster_list = NULL; ++ int set = 0; ++ char *prefix="t2"; ++ int rc = SLURM_SUCCESS; ++ int last_id = -1, curr_id = -1; ++ local_cluster_t *curr_cluster = NULL; ++ ++ /* This is here to make sure we are looking at only this user ++ * if this flag is set. We also include any accounts they may be ++ * coordinator of. ++ * ++ * This clause must be kept in sync with the access check in ++ * as_pgsql_jobacct_process_get_jobs(). ++ */ ++ if (!is_admin && ((slurm_conf.private_data & PRIVATE_DATA_JOBS) || ++ (job_cond->flags & JOBCOND_FLAG_SCRIPT) || ++ (job_cond->flags & JOBCOND_FLAG_ENV))) { ++ query = xstrdup_printf("select lft from \"%s_%s\" " ++ "where user='%s'", ++ cluster_name, assoc_table, user->name); ++ if (user->coord_accts) { ++ slurmdb_coord_rec_t *coord = NULL; ++ itr = list_iterator_create(user->coord_accts); ++ while ((coord = list_next(itr))) { ++ xstrfmtcat(query, " || acct='%s'", ++ coord->name); ++ } ++ list_iterator_destroy(itr); ++ } ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", query); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(extra); ++ xfree(query); ++ pgsql_free_result(&result); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ ++ xfree(query); ++ set = 0; ++ while ((row = pgsql_fetch_row(result))) { ++ if (set) { ++ xstrfmtcat(extra, ++ " || (%s between %s.lft and %s.rgt)", ++ row[0], prefix, prefix); ++ } else { ++ set = 1; ++ if (extra) ++ xstrfmtcat(extra, ++ " && ((%s between %s.lft " ++ "and %s.rgt)", ++ row[0], prefix, ++ prefix); ++ else ++ xstrfmtcat(extra, ++ " where ((%s between %s.lft " ++ "and %s.rgt)", ++ row[0], prefix, ++ prefix); ++ } ++ } ++ ++ pgsql_free_result(&result); ++ ++ if (set) ++ xstrcat(extra, ")"); ++ else { ++ xfree(extra); ++ debug("User %s has no associations, and is not admin, " ++ "so not returning any jobs.", user->name); ++ /* This user has no valid associations, so ++ * they will not have any jobs. */ ++ goto end_it; ++ } ++ } ++ ++ setup_job_cluster_cond_limits(pgsql_conn, job_cond, ++ cluster_name, &extra); ++ ++ query = xstrdup_printf("select %s from \"%s_%s\" as t1 " ++ "left join \"%s_%s\" as t2 " ++ "on t1.id_assoc=t2.id_assoc " ++ "left join \"%s_%s\" as t3 " ++ "on t1.id_resv=t3.id_resv && " ++ "((t1.time_start && " ++ "(t3.time_start < t1.time_start && " ++ "(t3.time_end >= t1.time_start || " ++ "t3.time_end = 0))) || " ++ "(t1.time_start = 0 && " ++ "((t3.time_start < t1.time_submit && " ++ "(t3.time_end >= t1.time_submit || " ++ "t3.time_end = 0)) || " ++ "(t3.time_start > t1.time_submit))))", ++ job_fields, cluster_name, job_table, ++ cluster_name, assoc_table, ++ cluster_name, resv_table); ++ ++ if (job_cond->flags & JOBCOND_FLAG_RUNAWAY) { ++ if (extra) ++ xstrcat(extra, " && (t1.time_end=0)"); ++ else ++ xstrcat(extra, " where (t1.time_end=0)"); ++ } ++ ++ if (extra) { ++ xstrcat(query, extra); ++ xfree(extra); ++ } ++ ++ /* Here we want to order them this way in such a way so it is ++ easy to look for duplicates, it is also easy to sort the ++ resized jobs. ++ */ ++ xstrcat(query, " order by id_job, time_submit desc"); ++ ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", query); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ xfree(query); ++ ++ ++ /* Here we set up environment to check used nodes of jobs. ++ Since we store the bitmap of the entire cluster we can use ++ that to set up a hostlist and set up the bitmap to make ++ things work. This should go before the setup of conds ++ since we could update the start/end time. ++ */ ++ if (job_cond && job_cond->used_nodes) { ++ local_cluster_list = setup_cluster_list_with_inx( ++ pgsql_conn, job_cond, (void **)&curr_cluster); ++ if (!local_cluster_list) { ++ pgsql_free_result(&result); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ } ++ ++ while ((row = pgsql_fetch_row(result))) { ++ char *db_inx_char = row[JOB_REQ_DB_INX]; ++ bool job_ended = 0; ++ int start = slurm_atoul(row[JOB_REQ_START]); ++ ++ curr_id = slurm_atoul(row[JOB_REQ_JOBID]); ++ ++ if (job_cond && !(job_cond->flags & JOBCOND_FLAG_DUP) ++ && (curr_id == last_id) ++ && (slurm_atoul(row[JOB_REQ_STATE]) != JOB_RESIZING)) ++ continue; ++ ++ /* check the bitmap to see if this is one of the jobs ++ we are looking for */ ++ /* Use start time instead of submit time because node ++ * indexes are determined at start time and not submit. */ ++ if (!good_nodes_from_inx(local_cluster_list, ++ (void **)&curr_cluster, ++ row[JOB_REQ_NODE_INX], start)) { ++ last_id = curr_id; ++ continue; ++ } ++ ++ job = slurmdb_create_job_rec(); ++ job->state = slurm_atoul(row[JOB_REQ_STATE]); ++ if (curr_id == last_id) ++ /* put in reverse so we order by the submit getting ++ larger which it is given to us in reverse ++ order from the database */ ++ list_prepend(job_list, job); ++ else ++ list_append(job_list, job); ++ last_id = curr_id; ++ ++ job->alloc_nodes = slurm_atoul(row[JOB_REQ_ALLOC_NODES]); ++ job->associd = slurm_atoul(row[JOB_REQ_ASSOCID]); ++ job->array_job_id = slurm_atoul(row[JOB_REQ_ARRAYJOBID]); ++ job->array_task_id = slurm_atoul(row[JOB_REQ_ARRAYTASKID]); ++ job->het_job_id = slurm_atoul(row[JOB_REQ_HET_JOB_ID]); ++ job->het_job_offset = slurm_atoul(row[JOB_REQ_HET_JOB_OFFSET]); ++ job->resvid = slurm_atoul(row[JOB_REQ_RESVID]); ++ ++ /* This shouldn't happen with new jobs, but older jobs ++ * could of been added without a start and so the ++ * array_task_id would be 0 instead of it's real value */ ++ if (!job->array_job_id && !job->array_task_id) ++ job->array_task_id = NO_VAL; ++ ++ if (row[JOB_REQ_RESV_NAME] && row[JOB_REQ_RESV_NAME][0]) ++ job->resv_name = xstrdup(row[JOB_REQ_RESV_NAME]); ++ ++ job->cluster = xstrdup(cluster_name); ++ ++ /* we want a blank wckey if the name is null */ ++ if (row[JOB_REQ_WCKEY]) ++ job->wckey = xstrdup(row[JOB_REQ_WCKEY]); ++ else ++ job->wckey = xstrdup(""); ++ job->wckeyid = slurm_atoul(row[JOB_REQ_WCKEYID]); ++ if (row[JOB_REQ_MCS_LABEL]) ++ job->mcs_label = xstrdup(row[JOB_REQ_MCS_LABEL]); ++ else ++ job->mcs_label = xstrdup(""); ++ if (row[JOB_REQ_USER_NAME]) ++ job->user = xstrdup(row[JOB_REQ_USER_NAME]); ++ ++ if (row[JOB_REQ_UID]) ++ job->uid = slurm_atoul(row[JOB_REQ_UID]); ++ ++ if (row[JOB_REQ_LFT]) ++ job->lft = slurm_atoul(row[JOB_REQ_LFT]); ++ ++ if (row[JOB_REQ_ACCOUNT] && row[JOB_REQ_ACCOUNT][0]) ++ job->account = xstrdup(row[JOB_REQ_ACCOUNT]); ++ else if (row[JOB_REQ_ACCOUNT1] && row[JOB_REQ_ACCOUNT1][0]) ++ job->account = xstrdup(row[JOB_REQ_ACCOUNT1]); ++ ++ if (row[JOB_REQ_ARRAY_STR] && row[JOB_REQ_ARRAY_STR][0]) ++ job->array_task_str = xstrdup(row[JOB_REQ_ARRAY_STR]); ++ ++ if (row[JOB_REQ_ARRAY_MAX]) ++ job->array_max_tasks = ++ slurm_atoul(row[JOB_REQ_ARRAY_MAX]); ++ ++ if (row[JOB_REQ_BLOCKID]) ++ job->blockid = xstrdup(row[JOB_REQ_BLOCKID]); ++ ++ if (row[JOB_REQ_WORK_DIR]) ++ job->work_dir = xstrdup(row[JOB_REQ_WORK_DIR]); ++ ++ job->eligible = slurm_atoul(row[JOB_REQ_ELIGIBLE]); ++ job->submit = slurm_atoul(row[JOB_REQ_SUBMIT]); ++ job->start = start; ++ job->end = slurm_atoul(row[JOB_REQ_END]); ++ job->timelimit = slurm_atoul(row[JOB_REQ_TIMELIMIT]); ++ ++ job->script = xstrdup(row[JOB_REQ_SCRIPT]); ++ ++ job->env = xstrdup(row[JOB_REQ_ENV]); ++ ++ job->submit_line = xstrdup(row[JOB_REQ_SUBMIT_LINE]); ++ ++ /* since the job->end could be set later end it here */ ++ if (job->end) { ++ job_ended = 1; ++ if (!job->start || (job->start > job->end)) ++ job->start = job->end; ++ } ++ ++ if (job_cond && !(job_cond->flags & JOBCOND_FLAG_NO_TRUNC) ){ ++ ++ if (!job_cond->usage_end || ++ (job_cond->usage_end > now)) { ++ job_cond->usage_end = now; ++ } ++ ++ if (job->start && (job->start < job_cond->usage_start)) ++ job->start = job_cond->usage_start; ++ ++ if (!job->end || job->end > job_cond->usage_end) ++ job->end = job_cond->usage_end; ++ ++ if (!job->start) ++ job->start = job->end; ++ ++ job->elapsed = job->end - job->start; ++ ++ if (row[JOB_REQ_SUSPENDED]) { ++ pgsql_res_t *result2 = NULL; ++ pgsql_row row2; ++ /* get the suspended time for this job */ ++ query = xstrdup_printf( ++ "select time_start, time_end from " ++ "\"%s_%s\" where " ++ "(time_start < %ld && (time_end >= %ld " ++ "|| time_end = 0)) && job_db_inx=%s " ++ "order by time_start", ++ cluster_name, suspend_table, ++ job_cond->usage_end, ++ job_cond->usage_start, ++ db_inx_char); ++ ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, ++ __LINE__, query); ++ result2 = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result2), SUCCESSFUL_COMPLETION)) { ++ FREE_NULL_LIST(job_list); ++ job_list = NULL; ++ xfree(query); ++ pgsql_free_result(&result2); ++ break; ++ } ++ xfree(query); ++ while ((row2 = pgsql_fetch_row(result2))) { ++ time_t local_start = ++ slurm_atoul(row2[0]); ++ time_t local_end = ++ slurm_atoul(row2[1]); ++ ++ if (!local_start) ++ continue; ++ ++ if (job->start > local_start) ++ local_start = job->start; ++ if (job->end < local_end) ++ local_end = job->end; ++ ++ if ((local_end - local_start) < 1) ++ continue; ++ ++ job->elapsed -= ++ (local_end - local_start); ++ job->suspended += ++ (local_end - local_start); ++ } ++ pgsql_free_result(&result2); ++ ++ } ++ } else { ++ job->suspended = slurm_atoul(row[JOB_REQ_SUSPENDED]); ++ ++ /* fix the suspended number to be correct */ ++ if (job->state == JOB_SUSPENDED) ++ job->suspended = now - job->suspended; ++ if (!job->start) { ++ job->elapsed = 0; ++ } else if (!job->end) { ++ job->elapsed = now - job->start; ++ } else { ++ job->elapsed = job->end - job->start; ++ } ++ ++ job->elapsed -= job->suspended; ++ } ++ ++ if ((int)job->elapsed < 0) ++ job->elapsed = 0; ++ ++ job->db_index = slurm_atoull(db_inx_char); ++ job->jobid = curr_id; ++ job->jobname = xstrdup(row[JOB_REQ_NAME]); ++ job->gid = slurm_atoul(row[JOB_REQ_GID]); ++ job->exitcode = slurm_atoul(row[JOB_REQ_EXIT_CODE]); ++ job->derived_ec = slurm_atoul(row[JOB_REQ_DERIVED_EC]); ++ job->derived_es = xstrdup(row[JOB_REQ_DERIVED_ES]); ++ job->admin_comment = xstrdup(row[JOB_REQ_ADMIN_COMMENT]); ++ job->system_comment = xstrdup(row[JOB_REQ_SYSTEM_COMMENT]); ++ job->constraints = xstrdup(row[JOB_REQ_CONSTRAINTS]); ++ job->container = xstrdup(row[JOB_REQ_CONTAINER]); ++ job->flags = slurm_atoul(row[JOB_REQ_FLAGS]); ++ job->state_reason_prev = slurm_atoul(row[JOB_REQ_STATE_REASON]); ++ ++ if (row[JOB_REQ_PARTITION]) ++ job->partition = xstrdup(row[JOB_REQ_PARTITION]); ++ ++ if (row[JOB_REQ_NODELIST]) ++ job->nodes = xstrdup(row[JOB_REQ_NODELIST]); ++ ++ if (!job->nodes || !xstrcmp(job->nodes, "(null)")) { ++ xfree(job->nodes); ++ job->nodes = xstrdup("(unknown)"); ++ } ++ ++ job->track_steps = slurm_atoul(row[JOB_REQ_TRACKSTEPS]); ++ job->priority = slurm_atoul(row[JOB_REQ_PRIORITY]); ++ job->req_cpus = slurm_atoul(row[JOB_REQ_REQ_CPUS]); ++ job->req_mem = slurm_atoull(row[JOB_REQ_REQ_MEM]); ++ job->requid = slurm_atoul(row[JOB_REQ_KILL_REQUID]); ++ job->qosid = slurm_atoul(row[JOB_REQ_QOS]); ++ job->show_full = 1; ++ ++ if (row[JOB_REQ_TRESA]) ++ job->tres_alloc_str = xstrdup(row[JOB_REQ_TRESA]); ++ if (row[JOB_REQ_TRESR]) ++ job->tres_req_str = xstrdup(row[JOB_REQ_TRESR]); ++ ++ if (only_pending || ++ (job_cond && ++ (job_cond->flags & (JOBCOND_FLAG_NO_STEP | ++ JOBCOND_FLAG_RUNAWAY)))) ++ goto skip_steps; ++ ++ if (job_cond && job_cond->step_list ++ && list_count(job_cond->step_list)) { ++ set = 0; ++ itr = list_iterator_create(job_cond->step_list); ++ while ((selected_step = list_next(itr))) { ++ if ((selected_step->step_id.job_id != ++ job->jobid) && ++ (selected_step->step_id.job_id != ++ job->het_job_id)&& ++ (selected_step->step_id.job_id != ++ job->array_job_id)) { ++ continue; ++ } else if ((selected_step->array_task_id != ++ NO_VAL) && ++ (selected_step->array_task_id != ++ job->array_task_id)) { ++ continue; ++ } else if ((selected_step->het_job_offset != ++ NO_VAL) && ++ (selected_step->het_job_offset != ++ job->het_job_offset)) { ++ continue; ++ } else if (selected_step->step_id.step_id == ++ NO_VAL) { ++ job->show_full = 1; ++ break; ++ } ++ if (set) ++ xstrcat(extra, " || "); ++ else ++ xstrcat(extra, " && ("); ++ ++ /* ++ * The stepid could be negative so use ++ * %d not %u ++ */ ++ xstrfmtcat(extra, "t1.id_step=%d", ++ selected_step->step_id.step_id); ++ ++ set = 1; ++ job->show_full = 0; ++ } ++ list_iterator_destroy(itr); ++ if (set) ++ xstrcat(extra, ")"); ++ } ++ ++ query = xstrdup_printf("select %s from \"%s_%s\" as t1 " ++ "where t1.job_db_inx=%s && " ++ "t1.time_start <= %ld && " ++ "(!t1.time_end || t1.time_end >= %ld)", ++ step_fields, cluster_name, ++ step_table, db_inx_char, ++ job_cond->usage_end, ++ job_cond->usage_start); ++ ++ if (extra) { ++ xstrcat(query, extra); ++ xfree(extra); ++ } ++ ++ DB_DEBUG(DB_STEP, pgsql_conn->conn, "query\n%s", query); ++ ++ step_result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(step_result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ rc = SLURM_ERROR; ++ pgsql_free_result(&result); ++ goto end_it; ++ } ++ xfree(query); ++ ++ /* Querying the steps in the fashion was faster than ++ doing only 1 query and then matching the steps up ++ later with the job. ++ */ ++ while ((step_row = pgsql_fetch_row(step_result))) { ++ /* check the bitmap to see if this is one of the steps ++ we are looking for */ ++ if (!good_nodes_from_inx(local_cluster_list, ++ (void **)&curr_cluster, ++ step_row[STEP_REQ_NODE_INX], ++ start)) ++ continue; ++ ++ step = slurmdb_create_step_rec(); ++ step->tot_cpu_sec = 0; ++ step->tot_cpu_usec = 0; ++ step->job_ptr = job; ++ if (!job->first_step_ptr) ++ job->first_step_ptr = step; ++ list_append(job->steps, step); ++ step->step_id.job_id = job->jobid; ++ step->step_id.step_id = slurm_atoul( ++ step_row[STEP_REQ_STEPID]); ++ step->step_id.step_het_comp = ++ slurm_atoul(step_row[STEP_REQ_STEP_HET_COMP]); ++ /* info("got %ps", &step->step_id); */ ++ step->state = slurm_atoul(step_row[STEP_REQ_STATE]); ++ step->exitcode = ++ slurm_atoul(step_row[STEP_REQ_EXIT_CODE]); ++ step->nnodes = slurm_atoul(step_row[STEP_REQ_NODES]); ++ ++ step->ntasks = slurm_atoul(step_row[STEP_REQ_TASKS]); ++ step->task_dist = ++ slurm_atoul(step_row[STEP_REQ_TASKDIST]); ++ ++ step->start = slurm_atoul(step_row[STEP_REQ_START]); ++ ++ step->end = slurm_atoul(step_row[STEP_REQ_END]); ++ /* if the job has ended end the step also */ ++ if (!step->end && job_ended) { ++ step->end = job->end; ++ step->state = job->state; ++ } ++ ++ if (job_cond && ++ !(job_cond->flags & JOBCOND_FLAG_NO_TRUNC) ++ && job_cond->usage_start) { ++ if (step->start ++ && (step->start < job_cond->usage_start)) ++ step->start = job_cond->usage_start; ++ ++ if (!step->start && step->end) ++ step->start = step->end; ++ ++ if (!step->end ++ || (step->end > job_cond->usage_end)) ++ step->end = job_cond->usage_end; ++ ++ if (step->start && step->end && ++ (step->start > step->end)) ++ step->start = step->end = 0; ++ } ++ ++ /* figure this out by start stop */ ++ step->suspended = ++ slurm_atoul(step_row[STEP_REQ_SUSPENDED]); ++ ++ /* fix the suspended number to be correct */ ++ if (step->state == JOB_SUSPENDED) ++ step->suspended = now - step->suspended; ++ if (!step->start) { ++ step->elapsed = 0; ++ } else if (!step->end) { ++ step->elapsed = now - step->start; ++ } else { ++ step->elapsed = step->end - step->start; ++ } ++ step->elapsed -= step->suspended; ++ ++ if ((int)step->elapsed < 0) ++ step->elapsed = 0; ++ ++ step->req_cpufreq_min = slurm_atoul( ++ step_row[STEP_REQ_REQ_CPUFREQ_MIN]); ++ step->req_cpufreq_max = slurm_atoul( ++ step_row[STEP_REQ_REQ_CPUFREQ_MAX]); ++ step->req_cpufreq_gov = slurm_atoul( ++ step_row[STEP_REQ_REQ_CPUFREQ_GOV]); ++ ++ step->stepname = xstrdup(step_row[STEP_REQ_NAME]); ++ step->nodes = xstrdup(step_row[STEP_REQ_NODELIST]); ++ step->requid = ++ slurm_atoul(step_row[STEP_REQ_KILL_REQUID]); ++ ++ step->submit_line = ++ xstrdup(step_row[STEP_REQ_SUBMIT_LINE]); ++ ++ step->user_cpu_sec = slurm_atoull( ++ step_row[STEP_REQ_USER_SEC]); ++ step->user_cpu_usec = slurm_atoul( ++ step_row[STEP_REQ_USER_USEC]); ++ step->sys_cpu_sec =slurm_atoull( ++ step_row[STEP_REQ_SYS_SEC]); ++ step->sys_cpu_usec = slurm_atoul( ++ step_row[STEP_REQ_SYS_USEC]); ++ step->tot_cpu_sec += ++ step->user_cpu_sec + step->sys_cpu_sec; ++ step->tot_cpu_usec += step->user_cpu_usec + ++ step->sys_cpu_usec; ++ if (step_row[STEP_REQ_TRES_USAGE_IN_MAX]) ++ step->stats.tres_usage_in_max = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_IN_MAX]); ++ if (step_row[STEP_REQ_TRES_USAGE_IN_MAX_TASKID]) ++ step->stats.tres_usage_in_max_taskid = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_IN_MAX_TASKID]); ++ if (step_row[STEP_REQ_TRES_USAGE_IN_MAX_NODEID]) ++ step->stats.tres_usage_in_max_nodeid = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_IN_MAX_NODEID]); ++ if (step_row[STEP_REQ_TRES_USAGE_IN_AVE]) ++ step->stats.tres_usage_in_ave = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_IN_AVE]); ++ if (step_row[STEP_REQ_TRES_USAGE_IN_MIN]) ++ step->stats.tres_usage_in_min = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_IN_MIN]); ++ if (step_row[STEP_REQ_TRES_USAGE_IN_MIN_TASKID]) ++ step->stats.tres_usage_in_min_taskid = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_IN_MIN_TASKID]); ++ if (step_row[STEP_REQ_TRES_USAGE_IN_MIN_NODEID]) ++ step->stats.tres_usage_in_min_nodeid = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_IN_MIN_NODEID]); ++ if (step_row[STEP_REQ_TRES_USAGE_IN_TOT]) ++ step->stats.tres_usage_in_tot = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_IN_TOT]); ++ if (step_row[STEP_REQ_TRES_USAGE_OUT_MAX]) ++ step->stats.tres_usage_out_max = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_OUT_MAX]); ++ if (step_row[STEP_REQ_TRES_USAGE_OUT_MAX_TASKID]) ++ step->stats.tres_usage_out_max_taskid = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_OUT_MAX_TASKID]); ++ if (step_row[STEP_REQ_TRES_USAGE_OUT_MAX_NODEID]) ++ step->stats.tres_usage_out_max_nodeid = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_OUT_MAX_NODEID]); ++ if (step_row[STEP_REQ_TRES_USAGE_OUT_AVE]) ++ step->stats.tres_usage_out_ave = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_OUT_AVE]); ++ if (step_row[STEP_REQ_TRES_USAGE_OUT_MIN]) ++ step->stats.tres_usage_out_min = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_OUT_MIN]); ++ if (step_row[STEP_REQ_TRES_USAGE_OUT_MIN_TASKID]) ++ step->stats.tres_usage_out_min_taskid = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_OUT_MIN_TASKID]); ++ if (step_row[STEP_REQ_TRES_USAGE_OUT_MIN_NODEID]) ++ step->stats.tres_usage_out_min_nodeid = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_OUT_MIN_NODEID]); ++ if (step_row[STEP_REQ_TRES_USAGE_OUT_TOT]) ++ step->stats.tres_usage_out_tot = ++ xstrdup(step_row[STEP_REQ_TRES_USAGE_OUT_TOT]); ++ step->stats.act_cpufreq = ++ atof(step_row[STEP_REQ_ACT_CPUFREQ]); ++ step->stats.consumed_energy = slurm_atoull( ++ step_row[STEP_REQ_CONSUMED_ENERGY]); ++ step->container = xstrdup(step_row[STEP_REQ_CONTAINER]); ++ ++ if (step_row[STEP_REQ_TRES]) ++ step->tres_alloc_str = ++ xstrdup(step_row[STEP_REQ_TRES]); ++ } ++ pgsql_free_result(&step_result); ++ ++ if (!job->track_steps) { ++ uint64_t j_cpus, s_cpus; ++ /* If we don't have track_steps we want to see ++ if we have multiple steps. If we only have ++ 1 step check the job name against the step ++ name in most all cases it will be ++ different. If it is different print out ++ the step separate. It could also be a single ++ step/allocation where the job was allocated more than ++ the step requested (eg. CR_Socket). ++ */ ++ if (list_count(job->steps) > 1) ++ job->track_steps = 1; ++ else if (step && ++ (xstrcmp(step->stepname, job->jobname) || ++ (((j_cpus = slurmdb_find_tres_count_in_string( ++ job->tres_alloc_str, TRES_CPU)) ++ != INFINITE64) && ++ ((s_cpus = slurmdb_find_tres_count_in_string( ++ step->tres_alloc_str, TRES_CPU)) ++ != INFINITE64) && ++ j_cpus != s_cpus))) ++ job->track_steps = 1; ++ } ++ skip_steps: ++ /* need to reset here to make the above test valid */ ++ step = NULL; ++ } ++ pgsql_free_result(&result); ++ ++end_it: ++ if (itr2) ++ list_iterator_destroy(itr2); ++ ++ FREE_NULL_LIST(local_cluster_list); ++ ++ if (rc == SLURM_SUCCESS) ++ list_transfer(sent_list, job_list); ++ ++ FREE_NULL_LIST(job_list); ++ return rc; ++} ++ ++extern List setup_cluster_list_with_inx(pgsql_conn_t *pgsql_conn, ++ slurmdb_job_cond_t *job_cond, ++ void **curr_cluster) ++{ ++ List local_cluster_list = NULL; ++ time_t now = time(NULL); ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ hostlist_t temp_hl = NULL; ++ hostlist_iterator_t h_itr = NULL; ++ char *query = NULL; ++ int dims = 0; ++ ++ if (!job_cond || !job_cond->used_nodes) ++ return NULL; ++ ++ if (!job_cond->cluster_list ++ || list_count(job_cond->cluster_list) != 1) { ++ error("If you are doing a query against nodes " ++ "you must only have 1 cluster " ++ "you are asking for."); ++ return NULL; ++ } ++ ++ if (get_cluster_dims(pgsql_conn, ++ (char *)list_peek(job_cond->cluster_list), ++ &dims)) ++ return NULL; ++ ++ temp_hl = hostlist_create_dims(job_cond->used_nodes, dims); ++ if (hostlist_count(temp_hl) <= 0) { ++ error("we didn't get any real hosts to look for."); ++ goto no_hosts; ++ } ++ h_itr = hostlist_iterator_create(temp_hl); ++ ++ query = xstrdup_printf("select cluster_nodes, time_start, " ++ "time_end from \"%s_%s\" where node_name='' " ++ "&& cluster_nodes !=''", ++ (char *)list_peek(job_cond->cluster_list), ++ event_table); ++ ++ if (job_cond->usage_start) { ++ if (!job_cond->usage_end) ++ job_cond->usage_end = now; ++ ++ xstrfmtcat(query, ++ " && ((time_start < %ld) " ++ "&& (time_end >= %ld || time_end = 0))", ++ job_cond->usage_end, job_cond->usage_start); ++ } ++ ++ DB_DEBUG(DB_JOB, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ goto no_hosts; ++ } ++ xfree(query); ++ ++ local_cluster_list = list_create(_destroy_local_cluster); ++ while ((row = pgsql_fetch_row(result))) { ++ char *host = NULL; ++ int loc = 0; ++ local_cluster_t *local_cluster = ++ xmalloc(sizeof(local_cluster_t)); ++ local_cluster->hl = hostlist_create_dims(row[0], dims); ++ local_cluster->start = slurm_atoul(row[1]); ++ local_cluster->end = slurm_atoul(row[2]); ++ local_cluster->asked_bitmap = ++ bit_alloc(hostlist_count(local_cluster->hl)); ++ while ((host = hostlist_next_dims(h_itr, dims))) { ++ if ((loc = hostlist_find_dims( ++ local_cluster->hl, host, dims)) != -1) ++ bit_set(local_cluster->asked_bitmap, loc); ++ free(host); ++ } ++ hostlist_iterator_reset(h_itr); ++ if (bit_ffs(local_cluster->asked_bitmap) != -1) { ++ list_append(local_cluster_list, local_cluster); ++ if (local_cluster->end == 0) { ++ local_cluster->end = now + 1; ++ (*curr_cluster) = local_cluster; ++ } else if (!(*curr_cluster) ++ || (((local_cluster_t *)(*curr_cluster))->end ++ < local_cluster->end)) { ++ (*curr_cluster) = local_cluster; ++ } ++ } else ++ _destroy_local_cluster(local_cluster); ++ } ++ pgsql_free_result(&result); ++ ++ if (!list_count(local_cluster_list)) { ++ FREE_NULL_LIST(local_cluster_list); ++ local_cluster_list = NULL; ++ goto no_hosts; ++ } ++ ++no_hosts: ++ ++ hostlist_iterator_destroy(h_itr); ++ hostlist_destroy(temp_hl); ++ ++ return local_cluster_list; ++} ++ ++extern int good_nodes_from_inx(List local_cluster_list, ++ void **object, char *node_inx, ++ int start) ++{ ++ local_cluster_t **curr_cluster = (local_cluster_t **)object; ++ ++ /* check the bitmap to see if this is one of the jobs ++ we are looking for */ ++ if (*curr_cluster) { ++ bitstr_t *job_bitmap = NULL; ++ if (!node_inx || !node_inx[0]) ++ return 0; ++ if ((start < (*curr_cluster)->start) ++ || (start >= (*curr_cluster)->end)) { ++ local_cluster_t *local_cluster = NULL; ++ ++ ListIterator itr = ++ list_iterator_create(local_cluster_list); ++ while ((local_cluster = list_next(itr))) { ++ if ((start >= local_cluster->start) ++ && (start < local_cluster->end)) { ++ *curr_cluster = local_cluster; ++ break; ++ } ++ } ++ list_iterator_destroy(itr); ++ if (!local_cluster) ++ return 0; ++ } ++ job_bitmap = bit_alloc(hostlist_count((*curr_cluster)->hl)); ++ bit_unfmt(job_bitmap, node_inx); ++ if (!bit_overlap_any((*curr_cluster)->asked_bitmap, job_bitmap)) { ++ FREE_NULL_BITMAP(job_bitmap); ++ return 0; ++ } ++ FREE_NULL_BITMAP(job_bitmap); ++ } ++ return 1; ++} ++ ++extern int setup_job_cluster_cond_limits(pgsql_conn_t *pgsql_conn, ++ slurmdb_job_cond_t *job_cond, ++ char *cluster_name, char **extra) ++{ ++ int set = 0; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ ++ if (!job_cond) ++ return SLURM_SUCCESS; ++ ++ /* this must be done before resvid_list since we set ++ resvid_list up here */ ++ if (job_cond->resv_list && list_count(job_cond->resv_list)) { ++ char *query = xstrdup_printf( ++ "select distinct job_db_inx from \"%s_%s\" where (", ++ cluster_name, job_table); ++ int my_set = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ itr = list_iterator_create(job_cond->resv_list); ++ while ((object = list_next(itr))) { ++ if (my_set) ++ xstrcat(query, " || "); ++ xstrfmtcat(query, "resv_name='%s'", object); ++ my_set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(query, ")"); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ error("couldn't query the database"); ++ goto no_resv; ++ } ++ ++ xfree(query); ++ if (!job_cond->resvid_list) ++ job_cond->resvid_list = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) { ++ list_append(job_cond->resvid_list, xstrdup(row[0])); ++ } ++ pgsql_free_result(&result); ++ } ++no_resv: ++ ++ if (job_cond->resvid_list && list_count(job_cond->resvid_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ itr = list_iterator_create(job_cond->resvid_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " || "); ++ xstrfmtcat(*extra, "t1.id_resv='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (job_cond->state_list && list_count(job_cond->state_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ itr = list_iterator_create(job_cond->state_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " || "); ++ ++ _state_time_string(extra, cluster_name, ++ (uint32_t)slurm_atoul(object), ++ job_cond); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ /* Don't show revoked sibling federated jobs w/out -D */ ++ if (!(job_cond->flags & JOBCOND_FLAG_DUP)) ++ xstrfmtcat(*extra, " %s (t1.state != %"PRIu64")", ++ *extra ? "&&" : "where", ++ JOB_REVOKED); ++ ++ return SLURM_SUCCESS; ++} ++ ++extern int setup_job_cond_limits(slurmdb_job_cond_t *job_cond, ++ char **extra) ++{ ++ int set = 0; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ ++ if (!job_cond || (job_cond->flags & JOBCOND_FLAG_RUNAWAY)) ++ return 0; ++ ++ slurmdb_job_cond_def_start_end(job_cond); ++ ++ if (job_cond->acct_list && list_count(job_cond->acct_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ itr = list_iterator_create(job_cond->acct_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " || "); ++ xstrfmtcat(*extra, "t1.account='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (job_cond->associd_list && list_count(job_cond->associd_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ itr = list_iterator_create(job_cond->associd_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " || "); ++ xstrfmtcat(*extra, "t1.id_assoc='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (job_cond->constraint_list && ++ list_count(job_cond->constraint_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ itr = list_iterator_create(job_cond->constraint_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " && "); ++ if (object[0]) ++ xstrfmtcat(*extra, ++ "t1.constraints like '%%%s%%'", ++ object); ++ else ++ xstrcat(*extra, "t1.constraints=''"); ++ ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (job_cond->db_flags != SLURMDB_JOB_FLAG_NOTSET) { ++ set = 1; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ if (job_cond->db_flags == SLURMDB_JOB_FLAG_NONE) ++ xstrfmtcat(*extra, "t1.flags = %u", job_cond->db_flags); ++ else ++ xstrfmtcat(*extra, "t1.flags & %u", job_cond->db_flags); ++ ++ xstrcat(*extra, ")"); ++ } ++ ++ if (job_cond->reason_list && list_count(job_cond->reason_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ itr = list_iterator_create(job_cond->reason_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " || "); ++ xstrfmtcat(*extra, "t1.state_reason_prev='%s'", ++ object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (job_cond->userid_list && list_count(job_cond->userid_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ itr = list_iterator_create(job_cond->userid_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " || "); ++ xstrfmtcat(*extra, "t1.id_user='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (job_cond->groupid_list && list_count(job_cond->groupid_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ itr = list_iterator_create(job_cond->groupid_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " || "); ++ xstrfmtcat(*extra, "t1.id_group='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (job_cond->jobname_list && list_count(job_cond->jobname_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ itr = list_iterator_create(job_cond->jobname_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " || "); ++ xstrfmtcat(*extra, "t1.job_name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (job_cond->partition_list && list_count(job_cond->partition_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ itr = list_iterator_create(job_cond->partition_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " || "); ++ xstrfmtcat(*extra, "t1.partition='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (job_cond->qos_list && list_count(job_cond->qos_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ itr = list_iterator_create(job_cond->qos_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " || "); ++ xstrfmtcat(*extra, "t1.id_qos='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (job_cond->cpus_min) { ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ if (job_cond->cpus_max) { ++ xstrfmtcat(*extra, "(t1.ext_1 between %u and %u))", ++ job_cond->cpus_min, job_cond->cpus_max); ++ ++ } else { ++ xstrfmtcat(*extra, "(t1.ext_1='%u'))", ++ job_cond->cpus_min); ++ ++ } ++ } ++ ++ if (job_cond->nodes_min) { ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ if (job_cond->nodes_max) { ++ xstrfmtcat(*extra, ++ "(t1.nodes_alloc between %u and %u))", ++ job_cond->nodes_min, job_cond->nodes_max); ++ ++ } else { ++ xstrfmtcat(*extra, "(t1.nodes_alloc='%u'))", ++ job_cond->nodes_min); ++ ++ } ++ } ++ ++ if (job_cond->timelimit_min) { ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ if (job_cond->timelimit_max) { ++ xstrfmtcat(*extra, "(t1.timelimit between %u and %u))", ++ job_cond->timelimit_min, ++ job_cond->timelimit_max); ++ ++ } else { ++ xstrfmtcat(*extra, "(t1.timelimit='%u'))", ++ job_cond->timelimit_min); ++ ++ } ++ } ++ ++ /* Time window should be exclusive of the end time, ie [start,end) */ ++ if (!job_cond->state_list || !list_count(job_cond->state_list)) { ++ /* ++ * There's an explicit list of jobs, so don't hide ++ * non-eligible ones. Else handle normal time query of only ++ * eligible jobs. ++ */ ++ if (job_cond->step_list && list_count(job_cond->step_list)) { ++ if (!(job_cond->flags & ++ JOBCOND_FLAG_NO_DEFAULT_USAGE)) { ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ xstrfmtcat(*extra, ++ "(t1.time_submit < %ld) && " ++ "(t1.time_end >= %ld ||" ++ " t1.time_end = 0))", ++ job_cond->usage_end, ++ job_cond->usage_start); ++ } ++ } else if (job_cond->usage_start) { ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ if (!job_cond->usage_end) ++ xstrfmtcat(*extra, ++ "(t1.time_end >= %ld " ++ "|| t1.time_end = 0))", ++ job_cond->usage_start); ++ else ++ xstrfmtcat(*extra, ++ "(t1.time_eligible " ++ "&& t1.time_eligible < %ld " ++ "&& (t1.time_end >= %ld " ++ "|| t1.time_end = 0)))", ++ job_cond->usage_end, ++ job_cond->usage_start); ++ } else if (job_cond->usage_end) { ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ xstrfmtcat(*extra, ++ "(t1.time_eligible && " ++ "t1.time_eligible < %ld))", ++ job_cond->usage_end); ++ } ++ } ++ ++ if (job_cond->wckey_list && list_count(job_cond->wckey_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " && ("); ++ else ++ xstrcat(*extra, " where ("); ++ ++ itr = list_iterator_create(job_cond->wckey_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " || "); ++ xstrfmtcat(*extra, "t1.wckey='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ return set; ++} ++ ++extern List as_pgsql_jobacct_process_get_jobs(pgsql_conn_t *pgsql_conn, ++ uid_t uid, ++ slurmdb_job_cond_t *job_cond) ++{ ++ char *extra = NULL; ++ char *tmp = NULL, *tmp2 = NULL; ++ ListIterator itr = NULL; ++ int is_admin=1; ++ int i; ++ List job_list = NULL; ++ slurmdb_user_rec_t user; ++ int only_pending = 0; ++ List use_cluster_list = NULL; ++ char *cluster_name; ++ bool locked = false; ++ assoc_mgr_lock_t locks = { NO_LOCK, NO_LOCK, NO_LOCK, NO_LOCK, ++ READ_LOCK, NO_LOCK, NO_LOCK }; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ /* ++ * This clause must be kept in sync with the access check in ++ * _cluster_get_jobs(). ++ */ ++ if ((slurm_conf.private_data & PRIVATE_DATA_JOBS) || ++ (job_cond->flags & JOBCOND_FLAG_SCRIPT) || ++ (job_cond->flags & JOBCOND_FLAG_ENV)) { ++ if (!(is_admin = is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { ++ /* ++ * Only fill in the coordinator accounts here we will ++ * check them later when we actually try to get the jobs ++ */ ++ (void) is_user_any_coord(pgsql_conn, &user); ++ } ++ if (!is_admin && !user.name) { ++ debug("User %u has no associations, and is not admin, " ++ "so not returning any jobs.", user.uid); ++ return NULL; ++ } ++ } ++ ++ if (job_cond ++ && job_cond->state_list && (list_count(job_cond->state_list) == 1) ++ && (slurm_atoul(list_peek(job_cond->state_list)) == JOB_PENDING)) ++ only_pending = 1; ++ ++ if (job_cond && ++ (!job_cond->step_list || !list_count(job_cond->step_list))) { ++ char *reason = NULL; ++ ++ if (job_cond->flags & JOBCOND_FLAG_SCRIPT) ++ reason = "job scripts"; ++ ++ if (job_cond->flags & JOBCOND_FLAG_ENV) ++ reason = "job environment"; ++ ++ if (reason) { ++ error("User %u is requesting %s, but no job requested, this is not allowed", ++ user.uid, reason); ++ return NULL; ++ } ++ } ++ ++ setup_job_cond_limits(job_cond, &extra); ++ ++ xfree(tmp); ++ xstrfmtcat(tmp, "%s", job_req_inx[0]); ++ for (i = 1; i < JOB_REQ_COUNT; i++) { ++ /* Only get the script if requesting it */ ++ if (((i == JOB_REQ_SCRIPT) && ++ (!job_cond || !(job_cond->flags & JOBCOND_FLAG_SCRIPT))) || ++ ((i == JOB_REQ_ENV) && ++ (!job_cond || !(job_cond->flags & JOBCOND_FLAG_ENV)))) ++ xstrcat(tmp, ", ''"); ++ else ++ xstrfmtcat(tmp, ", %s", job_req_inx[i]); ++ } ++ ++ xfree(tmp2); ++ xstrfmtcat(tmp2, "%s", step_req_inx[0]); ++ for (i = 1; i < STEP_REQ_COUNT; i++) { ++ xstrfmtcat(tmp2, ", %s", step_req_inx[i]); ++ } ++ ++ if (job_cond ++ && job_cond->cluster_list && list_count(job_cond->cluster_list)) ++ use_cluster_list = job_cond->cluster_list; ++ else { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = list_shallow_copy(as_pgsql_cluster_list); ++ locked = true; ++ } ++ ++ assoc_mgr_lock(&locks); ++ ++ job_list = list_create(slurmdb_destroy_job_rec); ++ itr = list_iterator_create(use_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ int rc; ++ _setup_job_cond_selected_steps(job_cond, cluster_name, &extra); ++ if ((rc = _cluster_get_jobs(pgsql_conn, &user, job_cond, ++ cluster_name, tmp, tmp2, extra, ++ is_admin, only_pending, job_list)) ++ != SLURM_SUCCESS) ++ error("Problem getting jobs for cluster %s", ++ cluster_name); ++ } ++ list_iterator_destroy(itr); ++ ++ assoc_mgr_unlock(&locks); ++ ++ if (locked) { ++ FREE_NULL_LIST(use_cluster_list); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ ++ xfree(tmp); ++ xfree(tmp2); ++ xfree(extra); ++ ++ return job_list; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_jobacct_process.h b/src/plugins/accounting_storage/pgsql/as_pgsql_jobacct_process.h +new file mode 100755 +index 0000000..032aec1 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_jobacct_process.h +@@ -0,0 +1,63 @@ ++/*****************************************************************************\ ++ * as_pgsql_jobacct_process.h - functions the processing of ++ * information from the as_pgsql jobacct ++ * storage. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++ * ++ * This file is patterned after jobcomp_linux.c, written by Morris Jette and ++ * Copyright (C) 2002 The Regents of the University of California. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_JOBSLURMDB_PROCESS_H ++#define _HAVE_PGSQL_JOBSLURMDB_PROCESS_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern List setup_cluster_list_with_inx(pgsql_conn_t *pgsql_conn, ++ slurmdb_job_cond_t *job_cond, ++ void **curr_cluster); ++extern int good_nodes_from_inx(List local_cluster_list, ++ void **object, char *node_inx, ++ int submit); ++extern int setup_job_cluster_cond_limits(pgsql_conn_t *pgsql_conn, ++ slurmdb_job_cond_t *job_cond, ++ char *cluster_name, char **extra); ++extern int setup_job_cond_limits(slurmdb_job_cond_t *job_cond, ++ char **extra); ++ ++extern List as_pgsql_jobacct_process_get_jobs(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_job_cond_t *job_cond); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_problems.c b/src/plugins/accounting_storage/pgsql/as_pgsql_problems.c +new file mode 100755 +index 0000000..3758646 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_problems.c +@@ -0,0 +1,425 @@ ++/*****************************************************************************\ ++ * as_pgsql_problems.c - functions for finding out problems in the ++ * associations and other places in the database. ++ ***************************************************************************** ++ * Copyright (C) 2009 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * CODE-OCEC-09-009. All rights reserved. ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_problems.h" ++#include "src/common/uid.h" ++ ++static int _setup_assoc_cond_limits( ++ slurmdb_assoc_cond_t *assoc_cond, ++ char **extra, bool user_query) ++{ ++ int set = 0; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ ++ xstrfmtcat(*extra, "where deleted=0"); ++ ++ if (!assoc_cond) ++ return 0; ++ ++ if (assoc_cond->acct_list && list_count(assoc_cond->acct_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(assoc_cond->acct_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "acct='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (assoc_cond->user_list && list_count(assoc_cond->user_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(assoc_cond->user_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "\"user\"='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } else if (user_query) { ++ /* we want all the users, but no non-user associations */ ++ set = 1; ++ xstrcat(*extra, " AND (\"user\"!='')"); ++ } ++ ++ if (assoc_cond->partition_list ++ && list_count(assoc_cond->partition_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(assoc_cond->partition_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "`partition`='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ return set; ++} ++ ++ ++extern int as_pgsql_acct_no_assocs(pgsql_conn_t *pgsql_conn, ++ slurmdb_assoc_cond_t *assoc_cond, ++ List ret_list) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ List use_cluster_list = NULL; ++ ListIterator itr = NULL; ++ char *cluster_name = NULL; ++ bool locked = false; ++ ++ xassert(ret_list); ++ ++ query = xstrdup_printf("select name from %s where deleted=0", ++ acct_table); ++ if (assoc_cond && ++ assoc_cond->acct_list && list_count(assoc_cond->acct_list)) { ++ int set = 0; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ xstrcat(query, " AND ("); ++ itr = list_iterator_create(assoc_cond->acct_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(query, " OR "); ++ xstrfmtcat(query, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(query, ")"); ++ } ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ if (assoc_cond && ++ assoc_cond->cluster_list && list_count(assoc_cond->cluster_list)) ++ use_cluster_list = assoc_cond->cluster_list; ++ else { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = list_shallow_copy(as_pgsql_cluster_list); ++ locked = true; ++ } ++ ++ itr = list_iterator_create(use_cluster_list); ++ while ((row = pgsql_fetch_row(result))) { ++ pgsql_res_t *result2 = NULL; ++ int cnt = 0; ++ slurmdb_assoc_rec_t *assoc = NULL; ++ ++ /* See if we have at least 1 association in the system */ ++ while ((cluster_name = list_next(itr))) { ++ if (query) ++ xstrcat(query, " union "); ++ xstrfmtcat(query, ++ "select distinct id_assoc from \"%s_%s\" " ++ "where deleted=0 AND " ++ "acct='%s'", ++ cluster_name, assoc_table, row[0]); ++ } ++ list_iterator_reset(itr); ++ if (query) ++ xstrcat(query, " limit 1"); ++ result2 = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result2), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ rc = SLURM_ERROR; ++ pgsql_free_result(&result2); ++ break; ++ } ++ xfree(query); ++ ++ cnt = pgsql_num_rows(result2); ++ pgsql_free_result(&result2); ++ ++ if (cnt) ++ continue; ++ ++ assoc = xmalloc(sizeof(slurmdb_assoc_rec_t)); ++ list_append(ret_list, assoc); ++ ++ assoc->id = SLURMDB_PROBLEM_ACCT_NO_ASSOC; ++ assoc->acct = xstrdup(row[0]); ++ } ++ pgsql_free_result(&result); ++ ++ list_iterator_destroy(itr); ++ if (locked) { ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ FREE_NULL_LIST(use_cluster_list); ++ } ++ ++ return rc; ++} ++ ++extern int as_pgsql_acct_no_users(pgsql_conn_t *pgsql_conn, ++ slurmdb_assoc_cond_t *assoc_cond, ++ List ret_list) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL, *tmp = NULL; ++ char *extra = NULL; ++ int i = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ List use_cluster_list = NULL; ++ ListIterator itr = NULL; ++ char *cluster_name; ++ bool locked = false; ++ ++ xassert(ret_list); ++ ++ _setup_assoc_cond_limits(assoc_cond, &extra, 0); ++ ++ /* if this changes you will need to edit the corresponding enum */ ++ char *assoc_req_inx[] = { ++ "id_assoc", ++ "user", ++ "acct", ++ "`partition`", ++ "parent_acct", ++ }; ++ enum { ++ ASSOC_REQ_ID, ++ ASSOC_REQ_USER, ++ ASSOC_REQ_ACCT, ++ ASSOC_REQ_PART, ++ ASSOC_REQ_PARENT, ++ ASSOC_REQ_COUNT ++ }; ++ ++ xfree(tmp); ++ xstrfmtcat(tmp, "%s", assoc_req_inx[i]); ++ for(i=1; icluster_list && list_count(assoc_cond->cluster_list)) ++ use_cluster_list = assoc_cond->cluster_list; ++ else { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = as_pgsql_cluster_list; ++ locked = true; ++ } ++ ++ itr = list_iterator_create(use_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ /* only get the account associations */ ++ if (query) ++ xstrcat(query, " union "); ++ xstrfmtcat(query, "select distinct %s, '%s' as cluster " ++ "from \"%s_%s\" %s AND \"user\"='' AND lft=(rgt-1) ", ++ tmp, cluster_name, cluster_name, ++ assoc_table, extra); ++ } ++ list_iterator_destroy(itr); ++ if (locked) ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ ++ if (query) ++ xstrcat(query, " order by cluster, acct;"); ++ ++ xfree(tmp); ++ xfree(extra); ++ DB_DEBUG(DB_QUERY, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_assoc_rec_t *assoc = ++ xmalloc(sizeof(slurmdb_assoc_rec_t)); ++ ++ list_append(ret_list, assoc); ++ ++ assoc->id = SLURMDB_PROBLEM_ACCT_NO_USERS; ++ ++ if (row[ASSOC_REQ_USER][0]) ++ assoc->user = xstrdup(row[ASSOC_REQ_USER]); ++ assoc->acct = xstrdup(row[ASSOC_REQ_ACCT]); ++ assoc->cluster = xstrdup(row[ASSOC_REQ_COUNT]); ++ ++ if (row[ASSOC_REQ_PARENT][0]) ++ assoc->parent_acct = xstrdup(row[ASSOC_REQ_PARENT]); ++ ++ if (row[ASSOC_REQ_PART][0]) ++ assoc->partition = xstrdup(row[ASSOC_REQ_PART]); ++ } ++ pgsql_free_result(&result); ++ ++ return rc; ++} ++ ++extern int as_pgsql_user_no_assocs_or_no_uid( ++ pgsql_conn_t *pgsql_conn, ++ slurmdb_assoc_cond_t *assoc_cond, ++ List ret_list) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ List use_cluster_list = NULL; ++ ListIterator itr = NULL; ++ char *cluster_name = NULL; ++ bool locked = false; ++ ++ xassert(ret_list); ++ ++ query = xstrdup_printf("select name from %s where deleted=0", ++ user_table); ++ if (assoc_cond && ++ assoc_cond->user_list && list_count(assoc_cond->user_list)) { ++ int set = 0; ++ char *object = NULL; ++ xstrcat(query, " AND ("); ++ itr = list_iterator_create(assoc_cond->user_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(query, " OR "); ++ xstrfmtcat(query, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(query, ")"); ++ } ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ if (assoc_cond && ++ assoc_cond->cluster_list && list_count(assoc_cond->cluster_list)) ++ use_cluster_list = assoc_cond->cluster_list; ++ else { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = list_shallow_copy(as_pgsql_cluster_list); ++ locked = true; ++ } ++ ++ itr = list_iterator_create(use_cluster_list); ++ while ((row = pgsql_fetch_row(result))) { ++ pgsql_res_t *result2 = NULL; ++ int cnt = 0; ++ slurmdb_assoc_rec_t *assoc = NULL; ++ uid_t pw_uid; ++ ++ if (uid_from_string (row[0], &pw_uid) < 0) { ++ assoc = xmalloc(sizeof(slurmdb_assoc_rec_t)); ++ list_append(ret_list, assoc); ++ ++ assoc->id = SLURMDB_PROBLEM_USER_NO_UID; ++ assoc->user = xstrdup(row[0]); ++ ++ continue; ++ } ++ ++ /* See if we have at least 1 association in the system */ ++ while ((cluster_name = list_next(itr))) { ++ if (query) ++ xstrcat(query, " union "); ++ xstrfmtcat(query, ++ "select distinct id_assoc from \"%s_%s\" " ++ "where deleted=0 AND " ++ "\"user\"='%s'", ++ cluster_name, assoc_table, row[0]); ++ } ++ list_iterator_reset(itr); ++ ++ if (query) ++ xstrcat(query, " limit 1"); ++ ++ result2 = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result2), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ rc = SLURM_ERROR; ++ pgsql_free_result(&result2); ++ break; ++ } ++ xfree(query); ++ ++ cnt = pgsql_num_rows(result2); ++ pgsql_free_result(&result2); ++ ++ if (cnt) ++ continue; ++ ++ assoc = xmalloc(sizeof(slurmdb_assoc_rec_t)); ++ list_append(ret_list, assoc); ++ ++ assoc->id = SLURMDB_PROBLEM_USER_NO_ASSOC; ++ assoc->user = xstrdup(row[0]); ++ } ++ pgsql_free_result(&result); ++ ++ list_iterator_destroy(itr); ++ if (locked) { ++ FREE_NULL_LIST(use_cluster_list); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ ++ return rc; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_problems.h b/src/plugins/accounting_storage/pgsql/as_pgsql_problems.h +new file mode 100755 +index 0000000..84ad9a4 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_problems.h +@@ -0,0 +1,55 @@ ++/*****************************************************************************\ ++ * as_pgsql_problems.h - functions for finding out problems in the ++ * associations and other places in the database. ++ ***************************************************************************** ++ * Copyright (C) 2009 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * CODE-OCEC-09-009. All rights reserved. ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_PROBLEMS_H ++#define _HAVE_PGSQL_PROBLEMS_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_acct_no_assocs(pgsql_conn_t *pgsql_conn, ++ slurmdb_assoc_cond_t *assoc_cond, ++ List ret_list); ++extern int as_pgsql_acct_no_users(pgsql_conn_t *pgsql_conn, ++ slurmdb_assoc_cond_t *assoc_cond, ++ List ret_list); ++extern int as_pgsql_user_no_assocs_or_no_uid( ++ pgsql_conn_t *pgsql_conn, slurmdb_assoc_cond_t *assoc_cond, ++ List ret_list); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_qos.c b/src/plugins/accounting_storage/pgsql/as_pgsql_qos.c +new file mode 100755 +index 0000000..55e4c5e +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_qos.c +@@ -0,0 +1,1549 @@ ++/*****************************************************************************\ ++ * as_pgsql_qos.c - functions dealing with qos. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_qos.h" ++ ++static char *mqos_req_inx[] = { ++ "id", ++ "name", ++ "preempt", ++ "grp_tres_mins", ++ "grp_tres_run_mins", ++ "grp_tres", ++ "max_tres_mins_pj", ++ "max_tres_run_mins_pa", ++ "max_tres_run_mins_pu", ++ "max_tres_pa", ++ "max_tres_pj", ++ "max_tres_pn", ++ "max_tres_pu", ++ "min_tres_pj", ++}; ++ ++enum { ++ MQOS_ID, ++ MQOS_NAME, ++ MQOS_PREEMPT, ++ MQOS_GTM, ++ MQOS_GTRM, ++ MQOS_GT, ++ MQOS_MTMPJ, ++ MQOS_MTRMA, ++ MQOS_MTRM, ++ MQOS_MTPA, ++ MQOS_MTPJ, ++ MQOS_MTPN, ++ MQOS_MTPU, ++ MQOS_MITPJ, ++ MQOS_COUNT ++}; ++ ++ ++static int _preemption_loop(pgsql_conn_t *pgsql_conn, int begin_qosid, ++ bitstr_t *preempt_bitstr) ++{ ++ slurmdb_qos_rec_t qos_rec; ++ int rc = 0, i = 0; ++ ++ xassert(preempt_bitstr); ++ ++ if (bit_test(preempt_bitstr, begin_qosid)) { ++ error("QOS ID %d has an internal loop", begin_qosid); ++ return 1; ++ } ++ ++ /* check in the preempt list for all qos's preempted */ ++ for (i = 0; i < bit_size(preempt_bitstr); i++) { ++ if (!bit_test(preempt_bitstr, i)) ++ continue; ++ ++ memset(&qos_rec, 0, sizeof(qos_rec)); ++ qos_rec.id = i; ++ if (assoc_mgr_fill_in_qos(pgsql_conn, &qos_rec, ++ ACCOUNTING_ENFORCE_QOS, NULL, 0) != ++ SLURM_SUCCESS) { ++ error("QOS ID %d not found", i); ++ rc = 1; ++ break; ++ } ++ /* ++ * check if the begin_qosid is preempted by this qos ++ * if so we have a loop ++ */ ++ if (qos_rec.preempt_bitstr ++ && (bit_test(qos_rec.preempt_bitstr, begin_qosid) || ++ bit_test(qos_rec.preempt_bitstr, i))) { ++ error("QOS ID %d has a loop at QOS %s", ++ begin_qosid, qos_rec.name); ++ rc = 1; ++ break; ++ } else if (qos_rec.preempt_bitstr) { ++ /* ++ * check this qos' preempt list and make sure ++ * no loops exist there either ++ */ ++ if ((rc = _preemption_loop(pgsql_conn, begin_qosid, ++ qos_rec.preempt_bitstr))) ++ break; ++ } ++ } ++ return rc; ++} ++ ++static int _setup_qos_limits(slurmdb_qos_rec_t *qos, ++ char **cols, char **vals, ++ char **extra, char **added_preempt, ++ bool for_add) ++{ ++ uint32_t tres_str_flags = TRES_STR_FLAG_REMOVE | ++ TRES_STR_FLAG_SORT_ID | TRES_STR_FLAG_SIMPLE | ++ TRES_STR_FLAG_NO_NULL; ++ ++ if (!qos) ++ return SLURM_ERROR; ++ ++ if (for_add) { ++ /* If we are adding we should make sure we don't get ++ old reside sitting around from a former life. ++ */ ++ if (!qos->description) ++ qos->description = xstrdup(""); ++ if (qos->flags & QOS_FLAG_NOTSET) ++ qos->flags = 0; ++ if (qos->grace_time == NO_VAL) ++ qos->grace_time = 0; ++ if (qos->grp_jobs == NO_VAL) ++ qos->grp_jobs = INFINITE; ++ if (qos->grp_jobs_accrue == NO_VAL) ++ qos->grp_jobs_accrue = INFINITE; ++ if (qos->grp_submit_jobs == NO_VAL) ++ qos->grp_submit_jobs = INFINITE; ++ if (qos->grp_wall == NO_VAL) ++ qos->grp_wall = INFINITE; ++ if (qos->max_jobs_pa == NO_VAL) ++ qos->max_jobs_pa = INFINITE; ++ if (qos->max_jobs_pu == NO_VAL) ++ qos->max_jobs_pu = INFINITE; ++ if (qos->max_jobs_accrue_pa == NO_VAL) ++ qos->max_jobs_accrue_pa = INFINITE; ++ if (qos->max_jobs_accrue_pu == NO_VAL) ++ qos->max_jobs_accrue_pu = INFINITE; ++ if (qos->min_prio_thresh == NO_VAL) ++ qos->min_prio_thresh = INFINITE; ++ if (qos->max_submit_jobs_pa == NO_VAL) ++ qos->max_submit_jobs_pa = INFINITE; ++ if (qos->max_submit_jobs_pu == NO_VAL) ++ qos->max_submit_jobs_pu = INFINITE; ++ if (qos->max_wall_pj == NO_VAL) ++ qos->max_wall_pj = INFINITE; ++ if (qos->preempt_exempt_time == NO_VAL) ++ qos->preempt_exempt_time = INFINITE; ++ if (qos->preempt_mode == NO_VAL16) ++ qos->preempt_mode = 0; ++ if (qos->priority == NO_VAL) ++ qos->priority = 0; ++ if (fuzzy_equal(qos->usage_factor, NO_VAL)) ++ qos->usage_factor = 1; ++ if (fuzzy_equal(qos->usage_thres, NO_VAL)) ++ qos->usage_thres = (double)INFINITE; ++ if (fuzzy_equal(qos->limit_factor, NO_VAL)) ++ qos->limit_factor = INFINITE; ++ } ++ ++ if (qos->description) { ++ xstrcat(*cols, ", description"); ++ xstrfmtcat(*vals, ", '%s'", qos->description); ++ xstrfmtcat(*extra, ", description='%s'", ++ qos->description); ++ } ++ ++ if (!(qos->flags & QOS_FLAG_NOTSET)) { ++ if (qos->flags & QOS_FLAG_REMOVE) { ++ if (qos->flags) ++ xstrfmtcat(*extra, ", flags=(flags & ~%u)", ++ qos->flags & ~QOS_FLAG_REMOVE); ++ } else { ++ /* If we are only removing there is no reason ++ to set up the cols and vals. */ ++ if (qos->flags & QOS_FLAG_ADD) { ++ if (qos->flags) { ++ xstrfmtcat(*extra, ++ ", flags=(flags | %u)", ++ qos->flags & ~QOS_FLAG_ADD); ++ } ++ } else ++ xstrfmtcat(*extra, ", flags=%u", qos->flags); ++ xstrcat(*cols, ", flags"); ++ xstrfmtcat(*vals, ", '%u'", qos->flags & ~QOS_FLAG_ADD); ++ } ++ } ++ ++ if (qos->grace_time == INFINITE) { ++ xstrcat(*cols, ", grace_time"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", grace_time=NULL"); ++ } else if ((qos->grace_time != NO_VAL) && ++ ((int32_t)qos->grace_time >= 0)) { ++ xstrcat(*cols, ", grace_time"); ++ xstrfmtcat(*vals, ", %u", qos->grace_time); ++ xstrfmtcat(*extra, ", grace_time=%u", qos->grace_time); ++ } ++ ++ if (qos->grp_jobs == INFINITE) { ++ xstrcat(*cols, ", grp_jobs"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", grp_jobs=NULL"); ++ } else if ((qos->grp_jobs != NO_VAL) ++ && ((int32_t)qos->grp_jobs >= 0)) { ++ xstrcat(*cols, ", grp_jobs"); ++ xstrfmtcat(*vals, ", %u", qos->grp_jobs); ++ xstrfmtcat(*extra, ", grp_jobs=%u", qos->grp_jobs); ++ } ++ ++ if (qos->grp_jobs_accrue == INFINITE) { ++ xstrcat(*cols, ", grp_jobs_accrue"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", grp_jobs_accrue=NULL"); ++ } else if ((qos->grp_jobs_accrue != NO_VAL) ++ && ((int32_t)qos->grp_jobs_accrue >= 0)) { ++ xstrcat(*cols, ", grp_jobs_accrue"); ++ xstrfmtcat(*vals, ", %u", qos->grp_jobs_accrue); ++ xstrfmtcat(*extra, ", grp_jobs_accrue=%u", ++ qos->grp_jobs_accrue); ++ } ++ ++ if (qos->grp_submit_jobs == INFINITE) { ++ xstrcat(*cols, ", grp_submit_jobs"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", grp_submit_jobs=NULL"); ++ } else if ((qos->grp_submit_jobs != NO_VAL) ++ && ((int32_t)qos->grp_submit_jobs >= 0)) { ++ xstrcat(*cols, ", grp_submit_jobs"); ++ xstrfmtcat(*vals, ", %u", qos->grp_submit_jobs); ++ xstrfmtcat(*extra, ", grp_submit_jobs=%u", ++ qos->grp_submit_jobs); ++ } ++ ++ if (qos->grp_wall == INFINITE) { ++ xstrcat(*cols, ", grp_wall"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", grp_wall=NULL"); ++ } else if ((qos->grp_wall != NO_VAL) ++ && ((int32_t)qos->grp_wall >= 0)) { ++ xstrcat(*cols, ", grp_wall"); ++ xstrfmtcat(*vals, ", %u", qos->grp_wall); ++ xstrfmtcat(*extra, ", grp_wall=%u", qos->grp_wall); ++ } ++ ++ if (qos->max_jobs_pa == INFINITE) { ++ xstrcat(*cols, ", max_jobs_pa"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", max_jobs_pa=NULL"); ++ } else if ((qos->max_jobs_pa != NO_VAL) ++ && ((int32_t)qos->max_jobs_pa >= 0)) { ++ xstrcat(*cols, ", max_jobs_pa"); ++ xstrfmtcat(*vals, ", %u", qos->max_jobs_pa); ++ xstrfmtcat(*extra, ", max_jobs_pa=%u", qos->max_jobs_pa); ++ } ++ ++ if (qos->max_jobs_pu == INFINITE) { ++ xstrcat(*cols, ", max_jobs_per_user"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", max_jobs_per_user=NULL"); ++ } else if ((qos->max_jobs_pu != NO_VAL) ++ && ((int32_t)qos->max_jobs_pu >= 0)) { ++ xstrcat(*cols, ", max_jobs_per_user"); ++ xstrfmtcat(*vals, ", %u", qos->max_jobs_pu); ++ xstrfmtcat(*extra, ", max_jobs_per_user=%u", qos->max_jobs_pu); ++ } ++ ++ if (qos->max_jobs_accrue_pa == INFINITE) { ++ xstrcat(*cols, ", max_jobs_accrue_pa"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", max_jobs_accrue_pa=NULL"); ++ } else if ((qos->max_jobs_accrue_pa != NO_VAL) ++ && ((int32_t)qos->max_jobs_accrue_pa >= 0)) { ++ xstrcat(*cols, ", max_jobs_accrue_pa"); ++ xstrfmtcat(*vals, ", %u", qos->max_jobs_accrue_pa); ++ xstrfmtcat(*extra, ", max_jobs_accrue_pa=%u", ++ qos->max_jobs_accrue_pa); ++ } ++ ++ if (qos->max_jobs_accrue_pu == INFINITE) { ++ xstrcat(*cols, ", max_jobs_accrue_pu"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", max_jobs_accrue_pu=NULL"); ++ } else if ((qos->max_jobs_accrue_pu != NO_VAL) ++ && ((int32_t)qos->max_jobs_accrue_pu >= 0)) { ++ xstrcat(*cols, ", max_jobs_accrue_pu"); ++ xstrfmtcat(*vals, ", %u", qos->max_jobs_accrue_pu); ++ xstrfmtcat(*extra, ", max_jobs_accrue_pu=%u", ++ qos->max_jobs_accrue_pu); ++ } ++ ++ if (qos->min_prio_thresh == INFINITE) { ++ xstrcat(*cols, ", min_prio_thresh"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", min_prio_thresh=NULL"); ++ } else if ((qos->min_prio_thresh != NO_VAL) ++ && ((int32_t)qos->min_prio_thresh >= 0)) { ++ xstrcat(*cols, ", min_prio_thresh"); ++ xstrfmtcat(*vals, ", %u", qos->min_prio_thresh); ++ xstrfmtcat(*extra, ", min_prio_thresh=%u", ++ qos->min_prio_thresh); ++ } ++ ++ if (qos->max_submit_jobs_pa == INFINITE) { ++ xstrcat(*cols, ", max_submit_jobs_pa"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", max_submit_jobs_pa=NULL"); ++ } else if ((qos->max_submit_jobs_pa != NO_VAL) ++ && ((int32_t)qos->max_submit_jobs_pa >= 0)) { ++ xstrcat(*cols, ", max_submit_jobs_pa"); ++ xstrfmtcat(*vals, ", %u", qos->max_submit_jobs_pa); ++ xstrfmtcat(*extra, ", max_submit_jobs_pa=%u", ++ qos->max_submit_jobs_pa); ++ } ++ ++ if (qos->max_submit_jobs_pu == INFINITE) { ++ xstrcat(*cols, ", max_submit_jobs_per_user"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", max_submit_jobs_per_user=NULL"); ++ } else if ((qos->max_submit_jobs_pu != NO_VAL) ++ && ((int32_t)qos->max_submit_jobs_pu >= 0)) { ++ xstrcat(*cols, ", max_submit_jobs_per_user"); ++ xstrfmtcat(*vals, ", %u", qos->max_submit_jobs_pu); ++ xstrfmtcat(*extra, ", max_submit_jobs_per_user=%u", ++ qos->max_submit_jobs_pu); ++ } ++ ++ if ((qos->max_wall_pj != NO_VAL) ++ && ((int32_t)qos->max_wall_pj >= 0)) { ++ xstrcat(*cols, ", max_wall_duration_per_job"); ++ xstrfmtcat(*vals, ", %u", qos->max_wall_pj); ++ xstrfmtcat(*extra, ", max_wall_duration_per_job=%u", ++ qos->max_wall_pj); ++ } else if (qos->max_wall_pj == INFINITE) { ++ xstrcat(*cols, ", max_wall_duration_per_job"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", max_wall_duration_per_job=NULL"); ++ } ++ ++ if (qos->preempt_list && list_count(qos->preempt_list)) { ++ char *preempt_val = NULL; ++ char *tmp_char = NULL, *last_preempt = NULL; ++ bool adding_straight = 0; ++ ListIterator preempt_itr = ++ list_iterator_create(qos->preempt_list); ++ ++ xstrcat(*cols, ", preempt"); ++ ++ while ((tmp_char = list_next(preempt_itr))) { ++ if (tmp_char[0] == '-') { ++ preempt_val = xstrdup_printf( ++ "REPLACE(%s, ',%s,', ',')", ++ last_preempt ? last_preempt : "preempt", ++ tmp_char+1); ++ xfree(last_preempt); ++ last_preempt = preempt_val; ++ preempt_val = NULL; ++ } else if (tmp_char[0] == '+') { ++ preempt_val = xstrdup_printf( ++ "REPLACE(CONCAT(REPLACE(%s, " ++ "',%s,', ''), ',%s,'), ',,', ',')", ++ last_preempt ? last_preempt : "preempt", ++ tmp_char+1, tmp_char+1); ++ if (added_preempt) ++ xstrfmtcat(*added_preempt, ",%s", ++ tmp_char+1); ++ xfree(last_preempt); ++ last_preempt = preempt_val; ++ preempt_val = NULL; ++ } else if (tmp_char[0]) { ++ xstrfmtcat(preempt_val, ",%s", tmp_char); ++ if (added_preempt) ++ xstrfmtcat(*added_preempt, ",%s", ++ tmp_char); ++ adding_straight = 1; ++ } else ++ xstrcat(preempt_val, ""); ++ } ++ list_iterator_destroy(preempt_itr); ++ if (last_preempt) { ++ preempt_val = last_preempt; ++ last_preempt = NULL; ++ } ++ if (adding_straight) { ++ xstrfmtcat(*vals, ", \'%s,\'", preempt_val); ++ xstrfmtcat(*extra, ", preempt=\'%s,\'", preempt_val); ++ } else if (preempt_val && preempt_val[0]) { ++ xstrfmtcat(*vals, ", %s", preempt_val); ++ xstrfmtcat(*extra, ", preempt=if(%s=',', '', %s)", ++ preempt_val, preempt_val); ++ } else { ++ xstrcat(*vals, ", ''"); ++ xstrcat(*extra, ", preempt=''"); ++ } ++ xfree(preempt_val); ++ } ++ ++ if (qos->preempt_exempt_time == INFINITE) { ++ xstrcat(*cols, ", preempt_exempt_time"); ++ xstrfmtcat(*vals, ", NULL"); ++ xstrfmtcat(*extra, ", preempt_exempt_time=NULL"); ++ } else if (qos->preempt_exempt_time != NO_VAL) { ++ xstrcat(*cols, ", preempt_exempt_time"); ++ xstrfmtcat(*vals, ", %u", qos->preempt_exempt_time); ++ xstrfmtcat(*extra, ", preempt_exempt_time=%u", ++ qos->preempt_exempt_time); ++ } ++ ++ if ((qos->preempt_mode != NO_VAL16) ++ && ((int16_t)qos->preempt_mode >= 0)) { ++ qos->preempt_mode &= (~PREEMPT_MODE_GANG); ++ xstrcat(*cols, ", preempt_mode"); ++ xstrfmtcat(*vals, ", %u", qos->preempt_mode); ++ xstrfmtcat(*extra, ", preempt_mode=%u", qos->preempt_mode); ++ } ++ ++ if (qos->priority == INFINITE) { ++ xstrcat(*cols, ", priority"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", priority=NULL"); ++ } else if ((qos->priority != NO_VAL) ++ && ((int32_t)qos->priority >= 0)) { ++ xstrcat(*cols, ", priority"); ++ xstrfmtcat(*vals, ", %u", qos->priority); ++ xstrfmtcat(*extra, ", priority=%u", qos->priority); ++ } ++ ++ if (fuzzy_equal(qos->usage_factor, INFINITE)) { ++ xstrcat(*cols, ", usage_factor"); ++ xstrcat(*vals, ", 1"); ++ xstrcat(*extra, ", usage_factor=1"); ++ } else if (!fuzzy_equal(qos->usage_factor, NO_VAL) ++ && (qos->usage_factor >= 0)) { ++ xstrcat(*cols, ", usage_factor"); ++ xstrfmtcat(*vals, ", %f", qos->usage_factor); ++ xstrfmtcat(*extra, ", usage_factor=%f", qos->usage_factor); ++ } ++ ++ if (fuzzy_equal(qos->usage_thres, INFINITE)) { ++ xstrcat(*cols, ", usage_thres"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", usage_thres=NULL"); ++ } else if (!fuzzy_equal(qos->usage_thres, NO_VAL) ++ && (qos->usage_thres >= 0)) { ++ xstrcat(*cols, ", usage_thres"); ++ xstrfmtcat(*vals, ", %f", qos->usage_thres); ++ xstrfmtcat(*extra, ", usage_thres=%f", qos->usage_thres); ++ } ++ ++ if (fuzzy_equal(qos->limit_factor, INFINITE)) { ++ xstrcat(*cols, ", limit_factor"); ++ xstrcat(*vals, ", NULL"); ++ xstrcat(*extra, ", limit_factor=NULL"); ++ } else if (!fuzzy_equal(qos->limit_factor, NO_VAL) ++ && (qos->limit_factor > 0)) { ++ xstrcat(*cols, ", limit_factor"); ++ xstrfmtcat(*vals, ", %f", qos->limit_factor); ++ xstrfmtcat(*extra, ", limit_factor=%f", qos->limit_factor); ++ } ++ ++ /* When modifying anything below this comment it happens in ++ * the actual function since we have to wait until we hear ++ * about the original first. ++ * What we do to make it known something needs to be changed ++ * is we cat "" onto extra which will inform the caller ++ * something needs changing. ++ */ ++ ++ if (qos->grp_tres) { ++ if (!for_add) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", grp_tres"); ++ slurmdb_combine_tres_strings( ++ &qos->grp_tres, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", qos->grp_tres); ++ xstrfmtcat(*extra, ", grp_tres='%s'", qos->grp_tres); ++ } ++ ++ if (qos->grp_tres_mins) { ++ if (!for_add) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", grp_tres_mins"); ++ slurmdb_combine_tres_strings( ++ &qos->grp_tres_mins, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", qos->grp_tres_mins); ++ xstrfmtcat(*extra, ", grp_tres_mins='%s'", ++ qos->grp_tres_mins); ++ } ++ ++ if (qos->grp_tres_run_mins) { ++ if (!for_add) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", grp_tres_run_mins"); ++ slurmdb_combine_tres_strings( ++ &qos->grp_tres_run_mins, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", qos->grp_tres_run_mins); ++ xstrfmtcat(*extra, ", grp_tres_run_mins='%s'", ++ qos->grp_tres_run_mins); ++ } ++ ++ if (qos->max_tres_pa) { ++ if (!for_add) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", max_tres_pa"); ++ slurmdb_combine_tres_strings( ++ &qos->max_tres_pa, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", qos->max_tres_pa); ++ xstrfmtcat(*extra, ", max_tres_pa='%s'", qos->max_tres_pa); ++ } ++ ++ if (qos->max_tres_pj) { ++ if (!for_add) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", max_tres_pj"); ++ slurmdb_combine_tres_strings( ++ &qos->max_tres_pj, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", qos->max_tres_pj); ++ xstrfmtcat(*extra, ", max_tres_pj='%s'", qos->max_tres_pj); ++ } ++ ++ if (qos->max_tres_pn) { ++ if (!for_add) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", max_tres_pn"); ++ slurmdb_combine_tres_strings( ++ &qos->max_tres_pn, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", qos->max_tres_pn); ++ xstrfmtcat(*extra, ", max_tres_pn='%s'", qos->max_tres_pn); ++ } ++ ++ if (qos->max_tres_pu) { ++ if (!for_add) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", max_tres_pu"); ++ slurmdb_combine_tres_strings( ++ &qos->max_tres_pu, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", qos->max_tres_pu); ++ xstrfmtcat(*extra, ", max_tres_pu='%s'", qos->max_tres_pu); ++ } ++ ++ if (qos->max_tres_mins_pj) { ++ if (!for_add) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", max_tres_mins_pj"); ++ slurmdb_combine_tres_strings( ++ &qos->max_tres_mins_pj, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", qos->max_tres_mins_pj); ++ xstrfmtcat(*extra, ", max_tres_mins_pj='%s'", ++ qos->max_tres_mins_pj); ++ } ++ ++ if (qos->max_tres_run_mins_pa) { ++ if (!for_add) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", max_tres_run_mins_pa"); ++ slurmdb_combine_tres_strings( ++ &qos->max_tres_run_mins_pa, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", qos->max_tres_run_mins_pa); ++ xstrfmtcat(*extra, ", max_tres_run_mins_pa='%s'", ++ qos->max_tres_run_mins_pa); ++ } ++ ++ if (qos->max_tres_run_mins_pu) { ++ if (!for_add) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", max_tres_run_mins_pu"); ++ slurmdb_combine_tres_strings( ++ &qos->max_tres_run_mins_pu, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", qos->max_tres_run_mins_pu); ++ xstrfmtcat(*extra, ", max_tres_run_mins_pu='%s'", ++ qos->max_tres_run_mins_pu); ++ } ++ ++ if (qos->min_tres_pj) { ++ if (!for_add) { ++ xstrcat(*extra, ""); ++ goto end_modify; ++ } ++ xstrcat(*cols, ", min_tres_pj"); ++ slurmdb_combine_tres_strings( ++ &qos->min_tres_pj, NULL, tres_str_flags); ++ xstrfmtcat(*vals, ", '%s'", qos->min_tres_pj); ++ xstrfmtcat(*extra, ", min_tres_pj='%s'", qos->min_tres_pj); ++ } ++ ++end_modify: ++ ++ return SLURM_SUCCESS; ++ ++} ++ ++extern int as_pgsql_add_qos(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List qos_list) ++{ ++ ListIterator itr = NULL; ++ int rc = SLURM_SUCCESS; ++ slurmdb_qos_rec_t *object = NULL; ++ char *cols = NULL, *extra = NULL, *vals = NULL, *query = NULL, ++ *tmp_extra = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ int affect_rows = 0; ++ int added = 0; ++ char *added_preempt = NULL; ++ uint32_t qos_cnt; ++ assoc_mgr_lock_t locks = { NO_LOCK, NO_LOCK, READ_LOCK, NO_LOCK, ++ NO_LOCK, NO_LOCK, NO_LOCK }; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_SUPER_USER)) ++ return ESLURM_ACCESS_DENIED; ++ ++ assoc_mgr_lock(&locks); ++ qos_cnt = g_qos_count; ++ assoc_mgr_unlock(&locks); ++ ++ user_name = uid_to_string((uid_t) uid); ++ itr = list_iterator_create(qos_list); ++ ++ while ((object = list_next(itr))) { ++ if (!object->name || !object->name[0]) { ++ error("We need a qos name to add."); ++ rc = SLURM_ERROR; ++ continue; ++ } ++ xstrcat(cols, "creation_time, mod_time, name"); ++ xstrfmtcat(vals, "%ld, %ld, '%s'", ++ now, now, object->name); ++ xstrfmtcat(extra, ", mod_time=%ld", now); ++ ++ _setup_qos_limits(object, &cols, &vals, ++ &extra, &added_preempt, 1); ++ if (added_preempt) { ++ object->preempt_bitstr = bit_alloc(qos_cnt); ++ bit_unfmt(object->preempt_bitstr, added_preempt+1); ++ xfree(added_preempt); ++ } ++ ++ xstrfmtcat(query, ++ "INSERT INTO %s (%s) VALUES (%s) " ++ "ON CONFLICT (id) DO UPDATE SET deleted=0, " ++ "id=CURRVAL(pg_get_serial_sequence('%s', 'id'))%s;", ++ qos_table, cols, vals, qos_table,extra); ++ ++ ++ DB_DEBUG(DB_QOS, pgsql_conn->conn, "query\n%s", query); ++ SQLHSTMT stmt_ptr = SQL_NULL_HSTMT; ++ rc = pgsql_db_query_stmt(pgsql_conn, query, &stmt_ptr); ++ xfree(query); ++ object->id = (uint32_t)pgsql_insert_id(stmt_ptr); ++ if (!object->id) { ++ error("Couldn't add qos %s", object->name); ++ added=0; ++ xfree(cols); ++ xfree(extra); ++ xfree(vals); ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ break; ++ } ++ ++ affect_rows = last_affected_rows(stmt_ptr); ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ ++ if (!affect_rows) { ++ debug2("nothing changed %d", affect_rows); ++ xfree(cols); ++ xfree(extra); ++ xfree(vals); ++ continue; ++ } ++ ++ /* we always have a ', ' as the first 2 chars */ ++ tmp_extra = slurm_add_slash_to_quotes(extra+2); ++ ++ xstrfmtcat(query, ++ "INSERT INTO %s " ++ "(timestamp, action, name, actor, info) " ++ "VALUES (%ld, %u, '%s', '%s', '%s');", ++ txn_table, ++ now, DBD_ADD_QOS, object->name, user_name, ++ tmp_extra); ++ ++ xfree(tmp_extra); ++ xfree(cols); ++ xfree(extra); ++ xfree(vals); ++ debug4("query\n%s",query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add txn"); ++ } else { ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_ADD_QOS, ++ object) == SLURM_SUCCESS) ++ list_remove(itr); ++ added++; ++ } ++ ++ } ++ list_iterator_destroy(itr); ++ xfree(user_name); ++ ++ if (!added) { ++ reset_pgsql_conn(pgsql_conn); ++ } ++ ++ return rc; ++} ++ ++extern List as_pgsql_modify_qos(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_qos_cond_t *qos_cond, ++ slurmdb_qos_rec_t *qos) ++{ ++ ListIterator itr = NULL; ++ List ret_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *object = NULL; ++ char *vals = NULL, *extra = NULL, *query = NULL, *name_char = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ int set = 0, i; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *tmp_char1=NULL, *tmp_char2=NULL; ++ bitstr_t *preempt_bitstr = NULL; ++ char *added_preempt = NULL; ++ uint32_t qos_cnt; ++ assoc_mgr_lock_t locks = { NO_LOCK, NO_LOCK, READ_LOCK, NO_LOCK, ++ NO_LOCK, NO_LOCK, NO_LOCK }; ++ ++ if (!qos_cond || !qos) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, ++ SLURMDB_ADMIN_SUPER_USER)) { ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ ++ xstrcat(extra, "WHERE deleted=0"); ++ ++ if (qos_cond->description_list ++ && list_count(qos_cond->description_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(qos_cond->description_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "description='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (qos_cond->id_list ++ && list_count(qos_cond->id_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(qos_cond->id_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "id='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (qos_cond->name_list ++ && list_count(qos_cond->name_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(qos_cond->name_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ _setup_qos_limits(qos, &tmp_char1, &tmp_char2, ++ &vals, &added_preempt, 0); ++ ++ assoc_mgr_lock(&locks); ++ qos_cnt = g_qos_count; ++ assoc_mgr_unlock(&locks); ++ ++ if (added_preempt) { ++ preempt_bitstr = bit_alloc(qos_cnt); ++ bit_unfmt(preempt_bitstr, added_preempt+1); ++ xfree(added_preempt); ++ } ++ xfree(tmp_char1); ++ xfree(tmp_char2); ++ ++ if (!extra || !vals) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ FREE_NULL_BITMAP(preempt_bitstr); ++ error("Nothing to change"); ++ return NULL; ++ } ++ ++ object = xstrdup(mqos_req_inx[0]); ++ for (i = 1; i < MQOS_COUNT; i++) ++ xstrfmtcat(object, ", %s", mqos_req_inx[i]); ++ ++ query = xstrdup_printf("SELECT %s FROM %s %s;", ++ object, qos_table, extra); ++ xfree(extra); ++ xfree(object); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ FREE_NULL_BITMAP(preempt_bitstr); ++ return NULL; ++ } ++ ++ rc = 0; ++ ret_list = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_qos_rec_t *qos_rec = NULL; ++ uint32_t id = slurm_atoul(row[MQOS_ID]); ++ if (preempt_bitstr) { ++ if (_preemption_loop(pgsql_conn, id, preempt_bitstr)) ++ break; ++ } ++ object = xstrdup(row[MQOS_NAME]); ++ list_append(ret_list, object); ++ if (!rc) { ++ xstrfmtcat(name_char, "(name='%s'", object); ++ rc = 1; ++ } else { ++ xstrfmtcat(name_char, " OR name='%s'", object); ++ } ++ ++ qos_rec = xmalloc(sizeof(slurmdb_qos_rec_t)); ++ qos_rec->name = xstrdup(object); ++ qos_rec->id = id; ++ qos_rec->flags = qos->flags; ++ ++ qos_rec->grace_time = qos->grace_time; ++ ++ mod_tres_str(&qos_rec->grp_tres, ++ qos->grp_tres, row[MQOS_GT], ++ NULL, "grp_tres", &vals, qos_rec->id, 0); ++ mod_tres_str(&qos_rec->grp_tres_mins, ++ qos->grp_tres_mins, row[MQOS_GTM], ++ NULL, "grp_tres_mins", &vals, qos_rec->id, 0); ++ mod_tres_str(&qos_rec->grp_tres_run_mins, ++ qos->grp_tres_run_mins, row[MQOS_GTRM], ++ NULL, "grp_tres_run_mins", &vals, ++ qos_rec->id, 0); ++ ++ qos_rec->grp_jobs = qos->grp_jobs; ++ qos_rec->grp_jobs_accrue = qos->grp_jobs_accrue; ++ qos_rec->grp_submit_jobs = qos->grp_submit_jobs; ++ qos_rec->grp_wall = qos->grp_wall; ++ ++ mod_tres_str(&qos_rec->max_tres_pa, ++ qos->max_tres_pa, row[MQOS_MTPA], ++ NULL, "max_tres_pa", &vals, qos_rec->id, 0); ++ mod_tres_str(&qos_rec->max_tres_pj, ++ qos->max_tres_pj, row[MQOS_MTPJ], ++ NULL, "max_tres_pj", &vals, qos_rec->id, 0); ++ mod_tres_str(&qos_rec->max_tres_pn, ++ qos->max_tres_pn, row[MQOS_MTPN], ++ NULL, "max_tres_pn", &vals, qos_rec->id, 0); ++ mod_tres_str(&qos_rec->max_tres_pu, ++ qos->max_tres_pu, row[MQOS_MTPU], ++ NULL, "max_tres_pu", &vals, qos_rec->id, 0); ++ mod_tres_str(&qos_rec->max_tres_mins_pj, ++ qos->max_tres_mins_pj, row[MQOS_MTMPJ], ++ NULL, "max_tres_mins_pj", &vals, qos_rec->id, 0); ++ mod_tres_str(&qos_rec->max_tres_run_mins_pa, ++ qos->max_tres_run_mins_pa, row[MQOS_MTRM], ++ NULL, "max_tres_run_mins_pa", &vals, ++ qos_rec->id, 0); ++ mod_tres_str(&qos_rec->max_tres_run_mins_pu, ++ qos->max_tres_run_mins_pu, row[MQOS_MTRM], ++ NULL, "max_tres_run_mins_pu", &vals, ++ qos_rec->id, 0); ++ ++ qos_rec->max_jobs_pa = qos->max_jobs_pa; ++ qos_rec->max_jobs_pu = qos->max_jobs_pu; ++ qos_rec->max_jobs_accrue_pa = qos->max_jobs_accrue_pa; ++ qos_rec->max_jobs_accrue_pu = qos->max_jobs_accrue_pu; ++ qos_rec->min_prio_thresh = qos->min_prio_thresh; ++ qos_rec->max_submit_jobs_pa = qos->max_submit_jobs_pa; ++ qos_rec->max_submit_jobs_pu = qos->max_submit_jobs_pu; ++ qos_rec->max_wall_pj = qos->max_wall_pj; ++ ++ mod_tres_str(&qos_rec->min_tres_pj, ++ qos->min_tres_pj, row[MQOS_MITPJ], ++ NULL, "min_tres_pj", &vals, qos_rec->id, 0); ++ ++ qos_rec->preempt_mode = qos->preempt_mode; ++ qos_rec->priority = qos->priority; ++ ++ if (qos->preempt_list) { ++ ListIterator new_preempt_itr = ++ list_iterator_create(qos->preempt_list); ++ char *new_preempt = NULL; ++ bool cleared = 0; ++ ++ qos_rec->preempt_bitstr = bit_alloc(qos_cnt); ++ ++ if (row[MQOS_PREEMPT] && row[MQOS_PREEMPT][0]) ++ bit_unfmt(qos_rec->preempt_bitstr, ++ row[MQOS_PREEMPT]+1); ++ ++ while ((new_preempt = list_next(new_preempt_itr))) { ++ if (new_preempt[0] == '-') { ++ bit_clear(qos_rec->preempt_bitstr, ++ atol(new_preempt+1)); ++ } else if (new_preempt[0] == '+') { ++ bit_set(qos_rec->preempt_bitstr, ++ atol(new_preempt+1)); ++ } else { ++ if (!cleared) { ++ cleared = 1; ++ bit_nclear( ++ qos_rec->preempt_bitstr, ++ 0, ++ qos_cnt-1); ++ } ++ ++ bit_set(qos_rec->preempt_bitstr, ++ atol(new_preempt)); ++ } ++ } ++ list_iterator_destroy(new_preempt_itr); ++ } ++ ++ qos_rec->preempt_exempt_time = qos->preempt_exempt_time; ++ ++ qos_rec->usage_factor = qos->usage_factor; ++ qos_rec->usage_thres = qos->usage_thres; ++ qos_rec->limit_factor = qos->limit_factor; ++ ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_MODIFY_QOS, qos_rec) ++ != SLURM_SUCCESS) ++ slurmdb_destroy_qos_rec(qos_rec); ++ } ++ pgsql_free_result(&result); ++ ++ FREE_NULL_BITMAP(preempt_bitstr); ++ ++ if (row) { ++ xfree(vals); ++ xfree(name_char); ++ xfree(query); ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ errno = ESLURM_QOS_PREEMPTION_LOOP; ++ return ret_list; ++ } ++ ++ if (!list_count(ret_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_QOS, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(vals); ++ xfree(query); ++ return ret_list; ++ } ++ xfree(query); ++ xstrcat(name_char, ")"); ++ ++ user_name = uid_to_string((uid_t) uid); ++ rc = modify_common(pgsql_conn, DBD_MODIFY_QOS, now, ++ user_name, qos_table, name_char, vals, NULL); ++ xfree(user_name); ++ xfree(name_char); ++ xfree(vals); ++ if (rc == SLURM_ERROR) { ++ error("Couldn't modify qos"); ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ } ++ ++ return ret_list; ++} ++ ++extern List as_pgsql_remove_qos(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_qos_cond_t *qos_cond) ++{ ++ ListIterator itr = NULL; ++ List ret_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *object = NULL; ++ char *extra = NULL, *query = NULL, ++ *name_char = NULL, *assoc_char = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ int set = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ List cluster_list_tmp = NULL; ++ ++ if (!qos_cond) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_SUPER_USER)) { ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ ++ xstrcat(extra, "WHERE deleted=0"); ++ if (qos_cond->description_list ++ && list_count(qos_cond->description_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(qos_cond->description_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "description='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (qos_cond->id_list ++ && list_count(qos_cond->id_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(qos_cond->id_list); ++ while ((object = list_next(itr))) { ++ if (!object[0]) ++ continue; ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "id='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (qos_cond->name_list ++ && list_count(qos_cond->name_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(qos_cond->name_list); ++ while ((object = list_next(itr))) { ++ if (!object[0]) ++ continue; ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (!extra) { ++ error("Nothing to remove"); ++ return NULL; ++ } ++ ++ query = xstrdup_printf("SELECT id, name FROM %s %s;", qos_table, extra); ++ xfree(extra); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ name_char = NULL; ++ ret_list = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_qos_rec_t *qos_rec = NULL; ++ ++ list_append(ret_list, xstrdup(row[1])); ++ if (!name_char) ++ xstrfmtcat(name_char, "id='%s'", row[0]); ++ else ++ xstrfmtcat(name_char, " OR id='%s'", row[0]); ++ if (!assoc_char) ++ xstrfmtcat(assoc_char, "id_qos='%s'", row[0]); ++ else ++ xstrfmtcat(assoc_char, " OR id_qos='%s'", row[0]); ++ xstrfmtcat(extra, ++ ", qos=REPLACE(qos, ',%s,', '')" ++ ", delta_qos=REPLACE(delta_qos, ',+%s,', '')" ++ ", delta_qos=REPLACE(delta_qos, ',-%s,', '')", ++ row[0], row[0], row[0]); ++ ++ qos_rec = xmalloc(sizeof(slurmdb_qos_rec_t)); ++ /* we only need id when removing no real need to init */ ++ qos_rec->id = slurm_atoul(row[0]); ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_REMOVE_QOS, qos_rec) ++ != SLURM_SUCCESS) ++ slurmdb_destroy_qos_rec(qos_rec); ++ } ++ pgsql_free_result(&result); ++ ++ if (!list_count(ret_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_QOS, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(query); ++ return ret_list; ++ } ++ xfree(query); ++ ++ user_name = uid_to_string((uid_t) uid); ++ ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ cluster_list_tmp = list_shallow_copy(as_pgsql_cluster_list); ++ if (list_count(cluster_list_tmp)) { ++ itr = list_iterator_create(cluster_list_tmp); ++ while ((object = list_next(itr))) { ++ /* ++ * remove this qos from all the associations ++ * that have it ++ */ ++ query = xstrdup_printf("UPDATE \"%s_%s\" SET mod_time=%ld %s WHERE deleted=0;", ++ object, assoc_table, ++ now, extra); ++ DB_DEBUG(DB_QOS, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ if (rc != SLURM_SUCCESS) { ++ reset_pgsql_conn(pgsql_conn); ++ break; ++ } ++ ++ if ((rc = remove_common(pgsql_conn, DBD_REMOVE_QOS, now, ++ user_name, qos_table, name_char, ++ assoc_char, object, NULL, NULL)) ++ != SLURM_SUCCESS) ++ break; ++ } ++ list_iterator_destroy(itr); ++ } else ++ rc = remove_common(pgsql_conn, DBD_REMOVE_QOS, now, ++ user_name, qos_table, name_char, ++ assoc_char, NULL, NULL, NULL); ++ ++ FREE_NULL_LIST(cluster_list_tmp); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ ++ xfree(extra); ++ xfree(assoc_char); ++ xfree(name_char); ++ xfree(user_name); ++ if (rc != SLURM_SUCCESS) { ++ FREE_NULL_LIST(ret_list); ++ return NULL; ++ } ++ ++ return ret_list; ++} ++ ++extern List as_pgsql_get_qos(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_qos_cond_t *qos_cond) ++{ ++ char *query = NULL; ++ char *extra = NULL; ++ char *tmp = NULL; ++ List qos_list = NULL; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ int set = 0; ++ int i=0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ uint32_t qos_cnt; ++ assoc_mgr_lock_t locks = { NO_LOCK, NO_LOCK, READ_LOCK, NO_LOCK, ++ NO_LOCK, NO_LOCK, NO_LOCK }; ++ ++ /* if this changes you will need to edit the corresponding enum */ ++ char *qos_req_inx[] = { ++ "name", ++ "description", ++ "id", ++ "flags", ++ "grace_time", ++ "grp_tres_mins", ++ "grp_tres_run_mins", ++ "grp_tres", ++ "grp_jobs", ++ "grp_jobs_accrue", ++ "grp_submit_jobs", ++ "grp_wall", ++ "max_tres_mins_pj", ++ "max_tres_run_mins_pa", ++ "max_tres_run_mins_pu", ++ "max_tres_pa", ++ "max_tres_pj", ++ "max_tres_pn", ++ "max_tres_pu", ++ "max_jobs_pa", ++ "max_jobs_per_user", ++ "max_jobs_accrue_pa", ++ "max_jobs_accrue_pu", ++ "min_prio_thresh", ++ "max_submit_jobs_pa", ++ "max_submit_jobs_per_user", ++ "max_wall_duration_per_job", ++ "CASE WHEN length(preempt) > 0 THEN substr(preempt, 1, length(preempt) - 1) ELSE '' END", ++ "preempt_mode", ++ "preempt_exempt_time", ++ "priority", ++ "usage_factor", ++ "usage_thres", ++ "min_tres_pj", ++ "limit_factor", ++ }; ++ enum { ++ QOS_REQ_NAME, ++ QOS_REQ_DESC, ++ QOS_REQ_ID, ++ QOS_REQ_FLAGS, ++ QOS_REQ_GRACE, ++ QOS_REQ_GTM, ++ QOS_REQ_GTRM, ++ QOS_REQ_GT, ++ QOS_REQ_GJ, ++ QOS_REQ_GJA, ++ QOS_REQ_GSJ, ++ QOS_REQ_GW, ++ QOS_REQ_MTMPJ, ++ QOS_REQ_MTRMA, ++ QOS_REQ_MTRM, ++ QOS_REQ_MTPA, ++ QOS_REQ_MTPJ, ++ QOS_REQ_MTPN, ++ QOS_REQ_MTPU, ++ QOS_REQ_MJPA, ++ QOS_REQ_MJPU, ++ QOS_REQ_MJAPA, ++ QOS_REQ_MJAPU, ++ QOS_REQ_MPT, ++ QOS_REQ_MSJPA, ++ QOS_REQ_MSJPU, ++ QOS_REQ_MWPJ, ++ QOS_REQ_PREE, ++ QOS_REQ_PREEM, ++ QOS_REQ_PREXMPT, ++ QOS_REQ_PRIO, ++ QOS_REQ_UF, ++ QOS_REQ_UT, ++ QOS_REQ_MITPJ, ++ QOS_REQ_LF, ++ QOS_REQ_COUNT ++ }; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!qos_cond) { ++ xstrcat(extra, "WHERE deleted=0"); ++ goto empty; ++ } ++ ++ if (qos_cond->with_deleted) ++ xstrcat(extra, "WHERE (deleted=0 OR deleted=1)"); ++ else ++ xstrcat(extra, "WHERE deleted=0"); ++ ++ ++ if (qos_cond->description_list ++ && list_count(qos_cond->description_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(qos_cond->description_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "description='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (qos_cond->id_list ++ && list_count(qos_cond->id_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(qos_cond->id_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "id='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (qos_cond->name_list ++ && list_count(qos_cond->name_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(qos_cond->name_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++empty: ++ ++ xfree(tmp); ++ xstrfmtcat(tmp, "%s", qos_req_inx[i]); ++ for(i=1; iconn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ qos_list = list_create(slurmdb_destroy_qos_rec); ++ ++ assoc_mgr_lock(&locks); ++ qos_cnt = g_qos_count; ++ assoc_mgr_unlock(&locks); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_qos_rec_t *qos = xmalloc(sizeof(slurmdb_qos_rec_t)); ++ list_append(qos_list, qos); ++ ++ if (row[QOS_REQ_DESC] && row[QOS_REQ_DESC][0]) ++ qos->description = xstrdup(row[QOS_REQ_DESC]); ++ ++ qos->id = slurm_atoul(row[QOS_REQ_ID]); ++ ++ qos->flags = slurm_atoul(row[QOS_REQ_FLAGS]); ++ ++ if (row[QOS_REQ_NAME] && row[QOS_REQ_NAME][0]) ++ qos->name = xstrdup(row[QOS_REQ_NAME]); ++ ++ if (row[QOS_REQ_GRACE]) ++ qos->grace_time = slurm_atoul(row[QOS_REQ_GRACE]); ++ ++ if (row[QOS_REQ_GT][0]) ++ qos->grp_tres = xstrdup(row[QOS_REQ_GT]); ++ if (row[QOS_REQ_GTM][0]) ++ qos->grp_tres_mins = xstrdup(row[QOS_REQ_GTM]); ++ if (row[QOS_REQ_GTRM][0]) ++ qos->grp_tres_run_mins = xstrdup(row[QOS_REQ_GTRM]); ++ ++ if (row[QOS_REQ_GJ]) ++ qos->grp_jobs = slurm_atoul(row[QOS_REQ_GJ]); ++ else ++ qos->grp_jobs = INFINITE; ++ if (row[QOS_REQ_GJA]) ++ qos->grp_jobs_accrue = slurm_atoul(row[QOS_REQ_GJA]); ++ else ++ qos->grp_jobs_accrue = INFINITE; ++ if (row[QOS_REQ_GSJ]) ++ qos->grp_submit_jobs = slurm_atoul(row[QOS_REQ_GSJ]); ++ else ++ qos->grp_submit_jobs = INFINITE; ++ if (row[QOS_REQ_GW]) ++ qos->grp_wall = slurm_atoul(row[QOS_REQ_GW]); ++ else ++ qos->grp_wall = INFINITE; ++ ++ if (row[QOS_REQ_MJPA]) ++ qos->max_jobs_pa = slurm_atoul(row[QOS_REQ_MJPA]); ++ else ++ qos->max_jobs_pa = INFINITE; ++ ++ if (row[QOS_REQ_MJPU]) ++ qos->max_jobs_pu = slurm_atoul(row[QOS_REQ_MJPU]); ++ else ++ qos->max_jobs_pu = INFINITE; ++ ++ if (row[QOS_REQ_MJAPA]) ++ qos->max_jobs_accrue_pa = ++ slurm_atoul(row[QOS_REQ_MJAPA]); ++ else ++ qos->max_jobs_accrue_pa = INFINITE; ++ ++ if (row[QOS_REQ_MJAPU]) ++ qos->max_jobs_accrue_pu = ++ slurm_atoul(row[QOS_REQ_MJAPU]); ++ else ++ qos->max_jobs_accrue_pu = INFINITE; ++ ++ if (row[QOS_REQ_MPT]) ++ qos->min_prio_thresh = slurm_atoul(row[QOS_REQ_MPT]); ++ else ++ qos->min_prio_thresh = INFINITE; ++ ++ if (row[QOS_REQ_MSJPA]) ++ qos->max_submit_jobs_pa = ++ slurm_atoul(row[QOS_REQ_MSJPA]); ++ else ++ qos->max_submit_jobs_pa = INFINITE; ++ ++ if (row[QOS_REQ_MSJPU]) ++ qos->max_submit_jobs_pu = ++ slurm_atoul(row[QOS_REQ_MSJPU]); ++ else ++ qos->max_submit_jobs_pu = INFINITE; ++ ++ if (row[QOS_REQ_MTPA][0]) ++ qos->max_tres_pa = xstrdup(row[QOS_REQ_MTPA]); ++ ++ if (row[QOS_REQ_MTPJ][0]) ++ qos->max_tres_pj = xstrdup(row[QOS_REQ_MTPJ]); ++ ++ if (row[QOS_REQ_MTPN][0]) ++ qos->max_tres_pn = xstrdup(row[QOS_REQ_MTPN]); ++ ++ if (row[QOS_REQ_MTPU][0]) ++ qos->max_tres_pu = xstrdup(row[QOS_REQ_MTPU]); ++ ++ if (row[QOS_REQ_MTMPJ][0]) ++ qos->max_tres_mins_pj = xstrdup(row[QOS_REQ_MTMPJ]); ++ ++ if (row[QOS_REQ_MTRMA][0]) ++ qos->max_tres_run_mins_pa = xstrdup(row[QOS_REQ_MTRMA]); ++ ++ if (row[QOS_REQ_MTRM][0]) ++ qos->max_tres_run_mins_pu = xstrdup(row[QOS_REQ_MTRM]); ++ ++ if (row[QOS_REQ_MWPJ]) ++ qos->max_wall_pj = slurm_atoul(row[QOS_REQ_MWPJ]); ++ else ++ qos->max_wall_pj = INFINITE; ++ ++ if (row[QOS_REQ_PREE] && row[QOS_REQ_PREE][0]) { ++ if (!qos->preempt_bitstr) ++ qos->preempt_bitstr = bit_alloc(qos_cnt); ++ bit_unfmt(qos->preempt_bitstr, row[QOS_REQ_PREE]+1); ++ } ++ if (row[QOS_REQ_PREEM]) ++ qos->preempt_mode = slurm_atoul(row[QOS_REQ_PREEM]); ++ if (row[QOS_REQ_PREXMPT]) ++ qos->preempt_exempt_time = ++ slurm_atoul(row[QOS_REQ_PREXMPT]); ++ else ++ qos->preempt_exempt_time = INFINITE; ++ if (row[QOS_REQ_PRIO]) ++ qos->priority = slurm_atoul(row[QOS_REQ_PRIO]); ++ ++ if (row[QOS_REQ_UF]) ++ qos->usage_factor = atof(row[QOS_REQ_UF]); ++ ++ if (row[QOS_REQ_UT]) ++ qos->usage_thres = atof(row[QOS_REQ_UT]); ++ else ++ qos->usage_thres = (double)INFINITE; ++ ++ if (row[QOS_REQ_MITPJ][0]) ++ qos->min_tres_pj = xstrdup(row[QOS_REQ_MITPJ]); ++ ++ if (row[QOS_REQ_LF]) ++ qos->limit_factor = atof(row[QOS_REQ_LF]); ++ else ++ qos->limit_factor = (double)INFINITE; ++ } ++ pgsql_free_result(&result); ++ ++ return qos_list; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_qos.h b/src/plugins/accounting_storage/pgsql/as_pgsql_qos.h +new file mode 100755 +index 0000000..9da6357 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_qos.h +@@ -0,0 +1,56 @@ ++/*****************************************************************************\ ++ * as_pgsql_qos.h - functions dealing with qos. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_QOS_H ++#define _HAVE_PGSQL_QOS_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_add_qos(pgsql_conn_t *pgsql_conn, uint32_t uid, List qos_list); ++ ++extern List as_pgsql_modify_qos(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_qos_cond_t *qos_cond, ++ slurmdb_qos_rec_t *qos); ++ ++extern List as_pgsql_remove_qos(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_qos_cond_t *qos_cond); ++ ++extern List as_pgsql_get_qos(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_qos_cond_t *qos_cond); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_resource.c b/src/plugins/accounting_storage/pgsql/as_pgsql_resource.c +new file mode 100755 +index 0000000..1905f92 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_resource.c +@@ -0,0 +1,1255 @@ ++/*****************************************************************************\ ++ * as_pgsql_resource.c - functions dealing with resources. ++ ***************************************************************************** ++ * Copyright (C) 2013 Bull S. A. S. ++ * Bull, Rue Jean Jaures, B.P.68, 78340, Les Clayes-sous-Bois. ++ * ++ * Written by Bill Brophy ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_assoc.h" ++#include "as_pgsql_resource.h" ++#include "as_pgsql_usage.h" ++#include "as_pgsql_wckey.h" ++#include "src/common/node_select.h" ++ ++static void _setup_res_cond(slurmdb_res_cond_t *res_cond, ++ char **extra) ++{ ++ int set = 0; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ ++ if (!res_cond) { ++ xstrcat(*extra, "WHERE t1.deleted=0"); ++ return; ++ } ++ ++ if (res_cond->with_deleted) ++ xstrcat(*extra, "WHERE (t1.deleted=0 OR t1.deleted=1)"); ++ else ++ xstrcat(*extra, "WHERE t1.deleted=0"); ++ ++ if (res_cond->description_list ++ && list_count(res_cond->description_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(res_cond->description_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "description='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (!(res_cond->flags & SLURMDB_RES_FLAG_NOTSET)) { ++ xstrfmtcat(*extra, " AND (flags & %u)", ++ res_cond->flags & SLURMDB_RES_FLAG_BASE); ++ } ++ ++ if (res_cond->id_list ++ && list_count(res_cond->id_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(res_cond->id_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "id='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (res_cond->manager_list ++ && list_count(res_cond->manager_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(res_cond->manager_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "manager='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (res_cond->name_list ++ && list_count(res_cond->name_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(res_cond->name_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (res_cond->server_list ++ && list_count(res_cond->server_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(res_cond->server_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "server='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (res_cond->type_list ++ && list_count(res_cond->type_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(res_cond->type_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "type='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++} ++ ++static int _setup_clus_res_cond(slurmdb_res_cond_t *res_cond, char **extra) ++{ ++ ListIterator itr; ++ bool set = 0; ++ char *tmp = NULL; ++ int query_clusters = 0; ++ ++ if (!res_cond) { ++ xstrfmtcat(*extra, "%st2.deleted=0", *extra ? " AND " : ""); ++ return SLURM_SUCCESS; ++ } ++ ++ if (res_cond->with_deleted) ++ xstrfmtcat(*extra, "%s(t2.deleted=0 OR t2.deleted=1)", ++ *extra ? " AND " : ""); ++ else ++ xstrfmtcat(*extra, "%st2.deleted=0", *extra ? " AND " : ""); ++ ++ if (res_cond->cluster_list && list_count(res_cond->cluster_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(res_cond->cluster_list); ++ while ((tmp = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "t2.cluster='%s'", tmp); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ query_clusters += set; ++ } ++ ++ if (res_cond->percent_list && list_count(res_cond->percent_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(res_cond->percent_list); ++ while ((tmp = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "t2.percent_allowed='%s'", tmp); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ query_clusters += set; ++ } ++ ++ return query_clusters; ++} ++ ++static int _setup_res_limits(slurmdb_res_rec_t *res, ++ char **cols, char **vals, ++ char **extra, bool for_add, bool *send_update) ++{ ++ if (!res) ++ return SLURM_ERROR; ++ ++ if (for_add) { ++ /* If we are adding we should make sure we don't get ++ old residue sitting around from a former life. ++ */ ++ if (res->count == NO_VAL) ++ res->count = 0; ++ if (res->type == SLURMDB_RESOURCE_NOTSET) ++ res->type = SLURMDB_RESOURCE_LICENSE; ++ } ++ ++ if (res->count != NO_VAL) { ++ if (cols) ++ xstrcat(*cols, ", count"); ++ xstrfmtcat(*vals, ", %u", res->count); ++ xstrfmtcat(*extra, ", count=%u", res->count); ++ if (send_update) ++ *send_update = 1; ++ } ++ ++ if (res->description) { ++ if (cols) ++ xstrcat(*cols, ", description"); ++ xstrfmtcat(*vals, ", '%s'", res->description); ++ xstrfmtcat(*extra, ", description='%s'", ++ res->description); ++ } ++ ++ if (!(res->flags & SLURMDB_RES_FLAG_NOTSET)) { ++ uint32_t base_flags = (res->flags & SLURMDB_RES_FLAG_BASE); ++ if (cols) ++ xstrcat(*cols, ", flags"); ++ if (res->flags & SLURMDB_RES_FLAG_REMOVE) { ++ xstrfmtcat(*vals, ", (VALUES(flags) & ~%u)'", ++ base_flags); ++ xstrfmtcat(*extra, ", flags=(flags & ~%u)", ++ base_flags); ++ } else if (res->flags & SLURMDB_RES_FLAG_ADD) { ++ xstrfmtcat(*vals, ", (VALUES(flags) | %u)'", ++ base_flags); ++ xstrfmtcat(*extra, ", flags=(flags | %u)", ++ base_flags); ++ } else { ++ xstrfmtcat(*vals, ", '%u'", base_flags); ++ xstrfmtcat(*extra, ", flags=%u", base_flags); ++ } ++ if (send_update) ++ *send_update = 1; ++ } ++ ++ if (res->manager) { ++ if (cols) ++ xstrcat(*cols, ", manager"); ++ xstrfmtcat(*vals, ", '%s'", res->manager); ++ xstrfmtcat(*extra, ", manager='%s'", res->manager); ++ } ++ ++ if (res->type != SLURMDB_RESOURCE_NOTSET) { ++ if (cols) ++ xstrcat(*cols, ", type"); ++ xstrfmtcat(*vals, ", %u", res->type); ++ xstrfmtcat(*extra, ", type=%u", res->type); ++ if (send_update) ++ *send_update = 1; ++ } ++ ++ return SLURM_SUCCESS; ++} ++ ++ ++static uint32_t _get_res_used(pgsql_conn_t *pgsql_conn, uint32_t res_id, ++ char *extra) ++{ ++ char *query = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ uint32_t percent_used = NO_VAL; ++ ++ xassert(res_id != NO_VAL); ++ ++ /* When extra comes in it will have deleted in there as well, ++ * it appears pgsql only uses the first one here and gives us ++ * what we want. ++ */ ++ query = xstrdup_printf("SELECT DISTINCT SUM(percent_allowed) " ++ "FROM %s AS t2 WHERE deleted=0 AND res_id=%u", ++ clus_res_table, res_id); ++ if (extra) ++ xstrfmtcat(query, " AND (%s) =0", extra); ++ ++ DB_DEBUG(DB_RES, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return percent_used; ++ } ++ ++ if (!(row = pgsql_fetch_row(result))) { ++ error("Resource id %u is not known on the system", res_id); ++ pgsql_free_result(&result); ++ return percent_used; ++ } ++ ++ /* Overwrite everything just to make sure the client side ++ didn't try anything tricky. ++ */ ++ if (row[0] && row[0][0]) ++ percent_used = slurm_atoul(row[0]); ++ ++ pgsql_free_result(&result); ++ ++ return percent_used; ++} ++ ++static int _fill_in_res_rec(pgsql_conn_t *pgsql_conn, slurmdb_res_rec_t *res) ++{ ++ int rc = SLURM_SUCCESS, i; ++ char *query = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *tmp = NULL; ++ ++ /* if this changes you will need to edit the corresponding enum */ ++ char *res_req_inx[] = { ++ "count", ++ "flags", ++ "id", ++ "name", ++ "server", ++ "type", ++ "SUM(percent_allowed)", ++ }; ++ enum { ++ RES_REQ_COUNT, ++ RES_REQ_FLAGS, ++ RES_REQ_ID, ++ RES_REQ_NAME, ++ RES_REQ_SERVER, ++ RES_REQ_TYPE, ++ RES_REQ_PU, ++ RES_REQ_NUMBER, ++ }; ++ ++ xassert(res); ++ xassert(res->id != NO_VAL); ++ ++ xfree(tmp); ++ xstrfmtcat(tmp, "%s", res_req_inx[0]); ++ for (i=1; iid); ++ ++ xfree(tmp); ++ ++ DB_DEBUG(DB_RES, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ if (!(row = pgsql_fetch_row(result))) { ++ error("Resource id %u is not known on the system", res->id); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ /* Overwrite everything just to make sure the client side ++ didn't try anything tricky. ++ */ ++ if (row[RES_REQ_COUNT] && row[RES_REQ_COUNT][0]) ++ res->count = slurm_atoul(row[RES_REQ_COUNT]); ++ if (row[RES_REQ_FLAGS] && row[RES_REQ_FLAGS][0]) ++ res->flags = slurm_atoul(row[RES_REQ_FLAGS]); ++ if (row[RES_REQ_NAME] && row[RES_REQ_NAME][0]) { ++ xfree(res->name); ++ res->name = xstrdup(row[RES_REQ_NAME]); ++ } ++ if (row[RES_REQ_SERVER] && row[RES_REQ_SERVER][0]) { ++ xfree(res->server); ++ res->server = xstrdup(row[RES_REQ_SERVER]); ++ } ++ if (row[RES_REQ_TYPE] && row[RES_REQ_TYPE][0]) ++ res->type = slurm_atoul(row[RES_REQ_TYPE]); ++ if (row[RES_REQ_PU] && row[RES_REQ_PU][0]) ++ res->percent_used = slurm_atoul(row[RES_REQ_PU]); ++ else ++ res->percent_used = 0; ++ ++ pgsql_free_result(&result); ++ ++ return rc; ++} ++ ++static int _add_res(pgsql_conn_t *pgsql_conn, slurmdb_res_rec_t *object, ++ char *user_name, int *added, ListIterator itr_in) ++{ ++ char *cols = NULL, *extra = NULL, *vals = NULL, *query = NULL, ++ *tmp_extra = NULL; ++ time_t now = time(NULL); ++ int affect_rows = 0; ++ int rc = SLURM_SUCCESS; ++ ++ if (!object->name || !object->name[0]) { ++ error("We need a resource name to add."); ++ return SLURM_ERROR; ++ } ++ if (!object->server || !object->server[0]) { ++ error("We need a resource server to add."); ++ return SLURM_ERROR; ++ } ++ ++ xstrcat(cols, "creation_time, mod_time, name, server"); ++ xstrfmtcat(vals, "%ld, %ld, '%s', '%s'", now, now, ++ object->name, object->server); ++ xstrfmtcat(extra, ", mod_time=%ld", now); ++ ++ _setup_res_limits(object, &cols, &vals, &extra, 1, NULL); ++ ++ xstrfmtcat(query, ++ "INSERT INTO %s (%s) VALUES (%s) " ++ "ON CONFLICT (id) DO UPDATE SET deleted=0, " ++ "id=CURRVAL(pg_get_serial_sequence('%s', 'id'))%s;", ++ res_table, cols, vals, res_table, extra); ++ ++ ++ DB_DEBUG(DB_RES, pgsql_conn->conn, "query\n%s", query); ++ SQLHSTMT stmt_ptr = SQL_NULL_HSTMT; ++ rc = pgsql_db_query_stmt(pgsql_conn, query, &stmt_ptr); ++ xfree(query); ++ object->id = (uint32_t)pgsql_insert_id(stmt_ptr); ++ if (!object->id) { ++ error("Couldn't add server resource %s", object->name); ++ (*added) = 0; ++ xfree(cols); ++ xfree(extra); ++ xfree(vals); ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ return SLURM_ERROR; ++ } ++ ++ affect_rows = last_affected_rows(stmt_ptr); ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ if (!affect_rows) { ++ xfree(cols); ++ xfree(extra); ++ xfree(vals); ++ return SLURM_SUCCESS; ++ } ++ ++ /* we always have a ', ' as the first 2 chars */ ++ tmp_extra = slurm_add_slash_to_quotes(extra+2); ++ ++ xstrfmtcat(query, ++ "INSERT INTO %s " ++ "(timestamp, action, name, actor, info) " ++ "VALUES (%ld, %u, '%u', '%s', '%s');", ++ txn_table, ++ now, DBD_ADD_RES, object->id, user_name, ++ tmp_extra); ++ ++ xfree(tmp_extra); ++ xfree(cols); ++ xfree(extra); ++ xfree(vals); ++ DB_DEBUG(DB_RES, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) ++ error("Couldn't add txn"); ++ else ++ (*added)++; ++ ++ return rc; ++} ++ ++static int _add_clus_res(pgsql_conn_t *pgsql_conn, slurmdb_res_rec_t *res, ++ char *user_name, int *added) ++{ ++ char *cols = NULL, *extra = NULL, *vals = NULL, *query = NULL, ++ *tmp_extra = NULL, *name = NULL; ++ time_t now = time(NULL); ++ int rc = SLURM_SUCCESS, cluster_cnt; ++ slurmdb_clus_res_rec_t *object; ++ ListIterator itr; ++ ++ if (res->id == NO_VAL) { ++ error("We need a server resource name to add to."); ++ return SLURM_ERROR; ++ } else if (!res->clus_res_list ++ || !(cluster_cnt = list_count(res->clus_res_list))) { ++ error("No clusters given to add to %s@%s", ++ res->name, res->server); ++ return SLURM_ERROR; ++ } ++ ++ xstrcat(cols, "creation_time, mod_time, " ++ "res_id, cluster, percent_allowed"); ++ xstrfmtcat(vals, "%ld, %ld, '%u'", now, now, res->id); ++ ++ itr = list_iterator_create(res->clus_res_list); ++ while ((object = list_next(itr))) { ++ res->percent_used += object->percent_allowed; ++ if (res->percent_used > 100) { ++ rc = ESLURM_OVER_ALLOCATE; ++ DB_DEBUG(DB_RES, pgsql_conn->conn, ++ "Adding a new cluster with %u%% allowed to resource %s@%s would put the usage at %u%%, (which is over 100%%). Please redo your math and resubmit.", ++ object->percent_allowed, res->name, ++ res->server, res->percent_used); ++ break; ++ } ++ xfree(extra); ++ xstrfmtcat(extra, ", mod_time=%ld, percent_allowed=%u", ++ now, object->percent_allowed); ++ xstrfmtcat(query, ++ "INSERT INTO %s (%s) VALUES (%s, '%s', %u) " ++ "ON CONFLICT (cluster,res_id) DO UPDATE SET deleted=0%s;", ++ clus_res_table, cols, vals, ++ object->cluster, object->percent_allowed, extra); ++ ++ DB_DEBUG(DB_RES, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add cluster %s to resource %s@%s", ++ object->cluster, res->name, res->server); ++ (*added) = 0; ++ xfree(extra); ++ continue; ++ } ++ ++ /* we always have a ', ' as the first 2 chars */ ++ tmp_extra = slurm_add_slash_to_quotes(extra+2); ++ name = xstrdup_printf("%u@%s", res->id, object->cluster); ++ ++ xstrfmtcat(query, ++ "INSERT INTO %s " ++ "(timestamp, action, name, actor, info) " ++ "VALUES (%ld, %u, '%s', '%s', '%s');", ++ txn_table, ++ now, DBD_ADD_RES, name, user_name, tmp_extra); ++ xfree(name); ++ xfree(tmp_extra); ++ xfree(extra); ++ DB_DEBUG(DB_RES, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) ++ error("Couldn't add txn"); ++ else { ++ slurmdb_res_rec_t *res_rec = ++ xmalloc(sizeof(slurmdb_res_rec_t)); ++ slurmdb_init_res_rec(res_rec, 0); ++ ++ res_rec->count = res->count; ++ res_rec->id = res->id; ++ res_rec->name = xstrdup(res->name); ++ res_rec->server = xstrdup(res->server); ++ res_rec->type = res->type; ++ ++ res_rec->clus_res_rec = ++ xmalloc(sizeof(slurmdb_clus_res_rec_t)); ++ res_rec->clus_res_rec->cluster = ++ xstrdup(object->cluster); ++ res_rec->clus_res_rec->percent_allowed = ++ object->percent_allowed; ++ ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_ADD_RES, ++ res_rec) != SLURM_SUCCESS) ++ slurmdb_destroy_res_rec(res_rec); ++ else ++ (*added)++; ++ } ++ } ++ xfree(cols); ++ xfree(vals); ++ ++ return rc; ++} ++ ++static List _get_clus_res(pgsql_conn_t *pgsql_conn, uint32_t res_id, ++ char *extra) ++{ ++ List ret_list; ++ char *query = NULL, *tmp = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ int i; ++ ++ /* if this changes you will need to edit the corresponding enum */ ++ char *res_req_inx[] = { ++ "cluster", ++ "percent_allowed", ++ }; ++ enum { ++ RES_REQ_CLUSTER, ++ RES_REQ_PA, ++ RES_REQ_NUMBER, ++ }; ++ ++ xfree(tmp); ++ xstrfmtcat(tmp, "%s", res_req_inx[0]); ++ for(i=1; iconn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ if (!pgsql_num_rows(result)) { ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ ret_list = list_create(slurmdb_destroy_clus_res_rec); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_clus_res_rec_t *clus_res_rec = ++ xmalloc(sizeof(slurmdb_clus_res_rec_t)); ++ ++ list_append(ret_list, clus_res_rec); ++ ++ if (row[0] && row[0][0]) ++ clus_res_rec->cluster = xstrdup(row[0]); ++ if (row[1] && row[1][0]) ++ clus_res_rec->percent_allowed = slurm_atoul(row[1]); ++ } ++ pgsql_free_result(&result); ++ ++ return ret_list; ++} ++ ++extern int as_pgsql_add_res(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List res_list) ++{ ++ ListIterator itr = NULL; ++ int rc = SLURM_SUCCESS; ++ slurmdb_res_rec_t *object = NULL; ++ char *user_name = NULL; ++ int added = 0; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_SUPER_USER)) ++ return ESLURM_ACCESS_DENIED; ++ ++ user_name = uid_to_string((uid_t) uid); ++ itr = list_iterator_create(res_list); ++ while ((object = list_next(itr))) { ++ if (object->id == NO_VAL) { ++ if (!object->name || !object->name[0]) { ++ error("We need a server resource name to add."); ++ rc = SLURM_ERROR; ++ continue; ++ } ++ if ((rc = _add_res(pgsql_conn, object, ++ user_name, &added, itr)) ++ != SLURM_SUCCESS) ++ break; ++ ++ /* Since we are adding it make sure we don't ++ * over commit it on the clusters we add. ++ */ ++ object->percent_used = 0; ++ } else { ++ if (_fill_in_res_rec(pgsql_conn, object) != ++ SLURM_SUCCESS) { ++ rc = SLURM_ERROR; ++ error("Unknown id %u", object->id); ++ continue; ++ } ++ } ++ ++ if (object->clus_res_list ++ && list_count(object->clus_res_list)) { ++ if ((rc = _add_clus_res(pgsql_conn, object, ++ user_name, &added)) ++ != SLURM_SUCCESS) ++ break; ++ } ++ } ++ list_iterator_destroy(itr); ++ xfree(user_name); ++ ++ if (!added) ++ reset_pgsql_conn(pgsql_conn); ++ ++ return rc; ++} ++ ++extern List as_pgsql_get_res(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_res_cond_t *res_cond) ++{ ++ char *query = NULL; ++ char *extra = NULL; ++ char *clus_extra = NULL; ++ char *tmp = NULL; ++ List res_list = NULL; ++ int i=0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ /* if this changes you will need to edit the corresponding enum */ ++ char *res_req_inx[] = { ++ "count", ++ "description", ++ "flags", ++ "id", ++ "manager", ++ "name", ++ "server", ++ "type", ++ "SUM(percent_allowed)", ++ }; ++ enum { ++ RES_REQ_COUNT, ++ RES_REQ_DESC, ++ RES_REQ_FLAGS, ++ RES_REQ_ID, ++ RES_REQ_MANAGER, ++ RES_REQ_NAME, ++ RES_REQ_SERVER, ++ RES_REQ_TYPE, ++ RES_REQ_PU, ++ RES_REQ_NUMBER, ++ }; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ _setup_res_cond(res_cond, &extra); ++ ++ xfree(tmp); ++ xstrfmtcat(tmp, "%s", res_req_inx[0]); ++ for(i=1; iwith_deleted) ? ++ " && t2.deleted=0" : "", ++ extra); ++ xfree(tmp); ++ xfree(extra); ++ ++ DB_DEBUG(DB_RES, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ if (res_cond && res_cond->with_clusters) ++ _setup_clus_res_cond(res_cond, &clus_extra); ++ ++ res_list = list_create(slurmdb_destroy_res_rec); ++ while ((row = pgsql_fetch_row(result))) { ++ uint32_t id = 0; ++ List clus_res_list = NULL; ++ slurmdb_res_rec_t *res; ++ ++ if (row[RES_REQ_ID] && row[RES_REQ_ID][0]) ++ id = slurm_atoul(row[RES_REQ_ID]); ++ else { ++ error("as_pgsql_get_res: no id? this " ++ "should never happen"); ++ continue; ++ } ++ ++ if (res_cond && res_cond->with_clusters) { ++ clus_res_list = _get_clus_res( ++ pgsql_conn, id, clus_extra); ++ /* This means the clusters requested don't have ++ claim to this resource, so continue. */ ++ if (!clus_res_list && (res_cond->with_clusters == 1)) ++ continue; ++ } ++ ++ res = xmalloc(sizeof(slurmdb_res_rec_t)); ++ list_append(res_list, res); ++ ++ slurmdb_init_res_rec(res, 0); ++ ++ res->id = id; ++ res->clus_res_list = clus_res_list; ++ clus_res_list = NULL; ++ ++ if (row[RES_REQ_COUNT] && row[RES_REQ_COUNT][0]) ++ res->count = slurm_atoul(row[RES_REQ_COUNT]); ++ if (row[RES_REQ_DESC] && row[RES_REQ_DESC][0]) ++ res->description = xstrdup(row[RES_REQ_DESC]); ++ if (row[RES_REQ_FLAGS] && row[RES_REQ_FLAGS][0]) ++ res->flags = slurm_atoul(row[RES_REQ_FLAGS]); ++ if (row[RES_REQ_MANAGER] && row[RES_REQ_MANAGER][0]) ++ res->manager = xstrdup(row[RES_REQ_MANAGER]); ++ if (row[RES_REQ_NAME] && row[RES_REQ_NAME][0]) ++ res->name = xstrdup(row[RES_REQ_NAME]); ++ if (row[RES_REQ_SERVER] && row[RES_REQ_SERVER][0]) ++ res->server = xstrdup(row[RES_REQ_SERVER]); ++ if (row[RES_REQ_TYPE] && row[RES_REQ_TYPE][0]) ++ res->type = slurm_atoul(row[RES_REQ_TYPE]); ++ ++ if (row[RES_REQ_PU] && row[RES_REQ_PU][0]) ++ res->percent_used = slurm_atoul(row[RES_REQ_PU]); ++ else ++ res->percent_used = 0; ++ } ++ pgsql_free_result(&result); ++ xfree(clus_extra); ++ ++ return res_list; ++} ++ ++extern List as_pgsql_remove_res(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_res_cond_t *res_cond) ++{ ++ List ret_list = NULL; ++ char *name_char = NULL, *clus_char = NULL; ++ char *user_name = NULL; ++ char *query = NULL, *extra = NULL, *clus_extra = NULL; ++ time_t now = time(NULL); ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ int query_clusters; ++ bool res_added = 0; ++ bool have_clusters = 0; ++ int last_res = -1; ++ ++ if (!res_cond) { ++ error("we need something to remove"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_SUPER_USER)) { ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ /* force to only do non-deleted server resources */ ++ res_cond->with_deleted = 0; ++ ++ _setup_res_cond(res_cond, &extra); ++ query_clusters = _setup_clus_res_cond(res_cond, &clus_extra); ++ ++ query = xstrdup_printf("SELECT id, name, server, cluster " ++ "FROM %s AS t1 left outer join " ++ "%s AS t2 ON (res_id = id%s) %s AND %s;", ++ res_table, clus_res_table, ++ (!res_cond || !res_cond->with_deleted) ? ++ " AND t2.deleted=0" : "", ++ extra, clus_extra); ++ xfree(clus_extra); ++ ++ DB_DEBUG(DB_RES, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ if (!pgsql_num_rows(result)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ query_clusters = 0; ++ query = xstrdup_printf("SELECT id, name, server " ++ "FROM %s AS t1 %s;", ++ res_table, extra); ++ DB_DEBUG(DB_RES, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ xfree(extra); ++ return NULL; ++ } ++ } else ++ have_clusters = 1; ++ ++ xfree(extra); ++ ++ name_char = NULL; ++ ret_list = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) { ++ char *name = NULL; ++ int curr_res = atoi(row[0]); ++ ++ if (last_res != curr_res) { ++ res_added = 0; ++ last_res = curr_res; ++ } ++ ++ if (query_clusters) { ++ xstrfmtcat(clus_char, ++ "%s(res_id='%s' AND cluster='%s')", ++ clus_char ? " OR " : "", row[0], row[3]); ++ } else { ++ if (!res_added) { ++ name = xstrdup_printf("%s@%s", row[1], row[2]); ++ list_append(ret_list, name); ++ res_added = 1; ++ name = NULL; ++ } ++ xstrfmtcat(name_char, "%sid='%s'", ++ name_char ? " OR " : "", row[0]); ++ xstrfmtcat(clus_char, "%sres_id='%s'", ++ clus_char ? " OR " : "", row[0]); ++ } ++ if (have_clusters && row[3] && row[3][0]) { ++ slurmdb_res_rec_t *res_rec = ++ xmalloc(sizeof(slurmdb_res_rec_t)); ++ slurmdb_init_res_rec(res_rec, 0); ++ res_rec->id = curr_res; ++ res_rec->clus_res_rec = ++ xmalloc(sizeof(slurmdb_clus_res_rec_t)); ++ res_rec->clus_res_rec->cluster = xstrdup(row[3]); ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_REMOVE_RES, res_rec) ++ != SLURM_SUCCESS) ++ slurmdb_destroy_res_rec(res_rec); ++ ++ name = xstrdup_printf("Cluster - %s\t- %s@%s", ++ row[3], row[1], row[2]); ++ } else if (!res_added) ++ name = xstrdup_printf("%s@%s", row[1], row[2]); ++ ++ if (name) ++ list_append(ret_list, name); ++ } ++ pgsql_free_result(&result); ++ ++ if (!list_count(ret_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_RES, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(query); ++ xfree(name_char); ++ xfree(clus_extra); ++ return ret_list; ++ } ++ ++ xfree(query); ++ ++ user_name = uid_to_string((uid_t) uid); ++ if (query_clusters) { ++ remove_common(pgsql_conn, DBD_REMOVE_CLUS_RES, ++ now, user_name, clus_res_table, ++ clus_char, NULL, NULL, NULL, NULL); ++ } else { ++ remove_common(pgsql_conn, DBD_REMOVE_CLUS_RES, ++ now, user_name, clus_res_table, ++ clus_char, NULL, NULL, NULL, NULL); ++ remove_common(pgsql_conn, DBD_REMOVE_RES, ++ now, user_name, res_table, ++ name_char, NULL, NULL, NULL, NULL); ++ } ++ ++ xfree(clus_char); ++ xfree(name_char); ++ xfree(user_name); ++ ++ return ret_list; ++} ++ ++extern List as_pgsql_modify_res(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_res_cond_t *res_cond, ++ slurmdb_res_rec_t *res) ++{ ++ List ret_list = NULL; ++ char *vals = NULL, *clus_vals = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL, *tmp = NULL; ++ char *name_char = NULL, *clus_char = NULL; ++ char *query = NULL; ++ char *extra = NULL; ++ char *clus_extra = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ int query_clusters = 0; ++ bool send_update = 0; ++ bool res_added = 0; ++ bool have_clusters = 0; ++ int last_res = -1; ++ uint32_t percent_used = 0; ++ ++ if (!res_cond || !res) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) { ++ return NULL; ++ } ++ ++ _setup_res_limits(res, NULL, &tmp, &vals, 0, &send_update); ++ ++ xfree(tmp); ++ ++ /* overloaded for easibility */ ++ if (res->percent_used != NO_VAL16) { ++ xstrfmtcat(clus_vals, ", percent_allowed=%u", ++ res->percent_used); ++ send_update = 1; ++ query_clusters++; ++ } ++ ++ if (!vals && !clus_vals) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ error("Nothing to change"); ++ return NULL; ++ } ++ ++ /* force to only do non-deleted resources */ ++ res_cond->with_deleted = 0; ++ _setup_res_cond(res_cond, &extra); ++ query_clusters += _setup_clus_res_cond(res_cond, &clus_extra); ++ ++ if (query_clusters || send_update) ++ query = xstrdup_printf("SELECT id, name, server, cluster " ++ "FROM %s AS t1 left outer join " ++ "%s AS t2 ON (res_id = id%s) %s AND %s;", ++ res_table, clus_res_table, ++ (!res_cond || !res_cond->with_deleted) ? ++ " AND t2.deleted=0" : "", ++ extra, clus_extra); ++ else ++ query = xstrdup_printf("SELECT id, name, server " ++ "FROM %s AS t1 %s;", ++ res_table, extra); ++ ++ DB_DEBUG(DB_RES, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(extra); ++ xfree(vals); ++ xfree(clus_extra); ++ xfree(clus_vals); ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ if (!pgsql_num_rows(result)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ result = NULL; ++ /* since no clusters are there no reason to send ++ updates */ ++ query_clusters = 0; ++ send_update = 0; ++ } else ++ have_clusters = 1; ++ ++ if (!query_clusters && !vals) { ++ xfree(clus_vals); ++ xfree(query); ++ pgsql_free_result(&result); ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ error("Nothing to change"); ++ return NULL; ++ } ++ ++ if (!result) { ++ xfree(query); ++ pgsql_free_result(&result); ++ query = xstrdup_printf("SELECT id, name, server " ++ "FROM %s AS t1 %s;", ++ res_table, extra); ++ DB_DEBUG(DB_RES, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(extra); ++ xfree(vals); ++ xfree(clus_extra); ++ xfree(clus_vals); ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ } ++ ++ xfree(extra); ++ ++ name_char = NULL; ++ ret_list = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) { ++ char *name = NULL; ++ int curr_res = atoi(row[0]); ++ ++ if (last_res != curr_res) { ++ res_added = 0; ++ last_res = curr_res; ++ ++ if (have_clusters && ++ (res->percent_used != NO_VAL16)) { ++ percent_used = _get_res_used( ++ pgsql_conn, curr_res, clus_extra); ++ ++ if (percent_used == NO_VAL) ++ percent_used = 0; ++ } ++ } ++ ++ if (query_clusters) { ++ xstrfmtcat(clus_char, ++ "%s(res_id='%s' AND cluster='%s')", ++ clus_char ? " OR " : "", row[0], row[3]); ++ } else { ++ if (!res_added) { ++ name = xstrdup_printf("%s@%s", row[1], row[2]); ++ list_append(ret_list, name); ++ res_added = 1; ++ name = NULL; ++ } ++ xstrfmtcat(name_char, "%sid='%s'", ++ name_char ? " OR " : "", row[0]); ++ xstrfmtcat(clus_char, "%sres_id='%s'", ++ clus_char ? " OR " : "", row[0]); ++ } ++ if (have_clusters && row[3] && row[3][0]) { ++ slurmdb_res_rec_t *res_rec; ++ ++ if (res->percent_used != NO_VAL16) ++ percent_used += res->percent_used; ++ if (percent_used > 100) { ++ DB_DEBUG(DB_RES, pgsql_conn->conn, ++ "Modifying resource %s@%s with %u%% allowed to each cluster would put the usage at %u%%, (which is over 100%%). Please redo your math and resubmit.", ++ row[1], row[2], res->percent_used, ++ percent_used); ++ ++ pgsql_free_result(&result); ++ xfree(clus_extra); ++ xfree(query); ++ xfree(vals); ++ xfree(name_char); ++ xfree(clus_char); ++ FREE_NULL_LIST(ret_list); ++ errno = ESLURM_OVER_ALLOCATE; ++ ++ return NULL; ++ } ++ ++ res_rec = xmalloc(sizeof(slurmdb_res_rec_t)); ++ slurmdb_init_res_rec(res_rec, 0); ++ res_rec->count = res->count; ++ res_rec->flags = res->flags; ++ res_rec->id = curr_res; ++ res_rec->type = res->type; ++ ++ res_rec->clus_res_rec = ++ xmalloc(sizeof(slurmdb_clus_res_rec_t)); ++ res_rec->clus_res_rec->cluster = xstrdup(row[3]); ++ res_rec->clus_res_rec->percent_allowed = ++ res->percent_used; ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_MODIFY_RES, res_rec) ++ != SLURM_SUCCESS) ++ slurmdb_destroy_res_rec(res_rec); ++ ++ name = xstrdup_printf("Cluster - %s\t- %s@%s", ++ row[3], row[1], row[2]); ++ } else if (!res_added) ++ name = xstrdup_printf("%s@%s", row[1], row[2]); ++ ++ if (name) ++ list_append(ret_list, name); ++ } ++ pgsql_free_result(&result); ++ ++ xfree(clus_extra); ++ ++ if (!list_count(ret_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_RES, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(query); ++ xfree(vals); ++ xfree(name_char); ++ xfree(clus_char); ++ ++ return ret_list; ++ } ++ xfree(query); ++ ++ user_name = uid_to_string((uid_t) uid); ++ if (query_clusters) { ++ modify_common(pgsql_conn, DBD_MODIFY_CLUS_RES, ++ now, user_name, clus_res_table, ++ clus_char, clus_vals, NULL); ++ } else { ++ if (clus_char && clus_vals) { ++ modify_common(pgsql_conn, DBD_MODIFY_CLUS_RES, ++ now, user_name, clus_res_table, ++ clus_char, clus_vals, NULL); ++ } ++ modify_common(pgsql_conn, DBD_MODIFY_RES, ++ now, user_name, res_table, ++ name_char, vals, NULL); ++ } ++ ++ xfree(vals); ++ xfree(clus_vals); ++ xfree(clus_char); ++ xfree(name_char); ++ xfree(user_name); ++ ++ return ret_list; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_resource.h b/src/plugins/accounting_storage/pgsql/as_pgsql_resource.h +new file mode 100755 +index 0000000..37e5c81 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_resource.h +@@ -0,0 +1,56 @@ ++/*****************************************************************************\ ++ * as_pgsql_resource.h - functions dealing with resources. ++ ***************************************************************************** ++ * Copyright (C) 2013 Bull S. A. S. ++ * Bull, Rue Jean Jaures, B.P.68, 78340, Les Clayes-sous-Bois. ++ * ++ * Written by Bill Brophy ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++#ifndef _HAVE_PGSQL_RESOURCE_H ++#define _HAVE_PGSQL_RESOURCE_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_add_res(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List res_list); ++ ++extern List as_pgsql_modify_res(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_res_cond_t *res_cond, ++ slurmdb_res_rec_t *res); ++ ++extern List as_pgsql_remove_res(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_res_cond_t *res_cond); ++ ++extern List as_pgsql_get_res(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_res_cond_t *res_cond); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_resv.c b/src/plugins/accounting_storage/pgsql/as_pgsql_resv.c +new file mode 100755 +index 0000000..e718a23 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_resv.c +@@ -0,0 +1,747 @@ ++/*****************************************************************************\ ++ * as_pgsql_resv.c - functions dealing with reservations. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_resv.h" ++#include "as_pgsql_jobacct_process.h" ++ ++static int _setup_resv_limits(slurmdb_reservation_rec_t *resv, ++ char **cols, char **vals, ++ char **extra) ++{ ++ /* strip off the action item from the flags */ ++ ++ if (resv->assocs) { ++ int start = 0; ++ int len = strlen(resv->assocs)-1; ++ ++ if (strchr(resv->assocs, '-')) { ++ int i = 0, i2 = 0; ++ char *assocs = xmalloc(len); ++ /* We will remove the negative's here. This ++ is here so if we only have negatives in the ++ reservation we don't want to keep track of ++ every other id so don't keep track of any ++ since everyone except a few can use it. ++ These id's are only used to divide up idle ++ time so it isn't that important. ++ */ ++ while (i < len) { ++ if (resv->assocs[i] == ',' && ++ resv->assocs[i+1] == '-') { ++ i+=2; ++ while (i < len) { ++ i++; ++ if (resv->assocs[i] == ',') ++ break; ++ } ++ continue; ++ } ++ assocs[i2++] = resv->assocs[i++]; ++ } ++ xfree(resv->assocs); ++ len = i2-1; ++ resv->assocs = assocs; ++ assocs = NULL; ++ } ++ ++ /* strip off extra ,'s */ ++ if (resv->assocs[0] == ',') ++ start = 1; ++ if (resv->assocs[len] == ',') ++ resv->assocs[len] = '\0'; ++ ++ xstrcat(*cols, ", assoclist"); ++ xstrfmtcat(*vals, ", '%s'", resv->assocs+start); ++ xstrfmtcat(*extra, ", assoclist='%s'", resv->assocs+start); ++ } ++ ++ if (resv->flags != NO_VAL64) { ++ xstrcat(*cols, ", flags"); ++ xstrfmtcat(*vals, ", %"PRIu64, resv->flags); ++ xstrfmtcat(*extra, ", flags=%"PRIu64, resv->flags); ++ } ++ ++ if (resv->name) { ++ xstrcat(*cols, ", resv_name"); ++ xstrfmtcat(*vals, ", '%s'", resv->name); ++ xstrfmtcat(*extra, ", resv_name='%s'", resv->name); ++ } ++ ++ if (resv->nodes) { ++ xstrcat(*cols, ", nodelist"); ++ xstrfmtcat(*vals, ", '%s'", resv->nodes); ++ xstrfmtcat(*extra, ", nodelist='%s'", resv->nodes); ++ } ++ ++ if (resv->node_inx) { ++ xstrcat(*cols, ", node_inx"); ++ xstrfmtcat(*vals, ", '%s'", resv->node_inx); ++ xstrfmtcat(*extra, ", node_inx='%s'", resv->node_inx); ++ } ++ ++ if (resv->time_end) { ++ xstrcat(*cols, ", time_end"); ++ xstrfmtcat(*vals, ", %ld", resv->time_end); ++ xstrfmtcat(*extra, ", time_end=%ld", resv->time_end); ++ } ++ ++ if (resv->time_start) { ++ xstrcat(*cols, ", time_start"); ++ xstrfmtcat(*vals, ", %ld", resv->time_start); ++ xstrfmtcat(*extra, ", time_start=%ld", resv->time_start); ++ } ++ ++ if (resv->tres_str) { ++ xstrcat(*cols, ", tres"); ++ xstrfmtcat(*vals, ", '%s'", resv->tres_str); ++ xstrfmtcat(*extra, ", tres='%s'", resv->tres_str); ++ } ++ ++ return SLURM_SUCCESS; ++} ++static int _setup_resv_cond_limits(slurmdb_reservation_cond_t *resv_cond, ++ char **extra) ++{ ++ int set = 0; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ char *prefix = "t1"; ++ time_t now = time(NULL); ++ ++ if (!resv_cond) ++ return 0; ++ ++ if (resv_cond->id_list && list_count(resv_cond->id_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " AND ("); ++ else ++ xstrcat(*extra, " WHERE ("); ++ itr = list_iterator_create(resv_cond->id_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "%s.id_resv=%s", prefix, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (resv_cond->name_list && list_count(resv_cond->name_list)) { ++ set = 0; ++ if (*extra) ++ xstrcat(*extra, " AND ("); ++ else ++ xstrcat(*extra, " WHERE ("); ++ itr = list_iterator_create(resv_cond->name_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "%s.resv_name='%s'", ++ prefix, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (resv_cond->time_start) { ++ if (!resv_cond->time_end) ++ resv_cond->time_end = now; ++ ++ if (*extra) ++ xstrcat(*extra, " AND ("); ++ else ++ xstrcat(*extra, " WHERE ("); ++ xstrfmtcat(*extra, ++ "(t1.time_start < %ld " ++ "AND (t1.time_end >= %ld OR t1.time_end = 0)))", ++ resv_cond->time_end, resv_cond->time_start); ++ } else if (resv_cond->time_end) { ++ if (*extra) ++ xstrcat(*extra, " AND ("); ++ else ++ xstrcat(*extra, " WHERE ("); ++ xstrfmtcat(*extra, ++ "(t1.time_start < %ld))", resv_cond->time_end); ++ } ++ ++ ++ return set; ++} ++ ++static int _add_usage_to_resv(void *object, void *arg) ++{ ++ slurmdb_job_rec_t *job = (slurmdb_job_rec_t *)object; ++ slurmdb_reservation_rec_t *resv = (slurmdb_reservation_rec_t *)arg; ++ int start = job->start; ++ int end = job->end; ++ int elapsed = 0; ++ ++ /* ++ * Sanity check we are dealing with the reservation we requested. ++ */ ++ if (resv->id != job->resvid) { ++ error("We got a job %u and it doesn't match the reservation we requested. We requested %d but got %d. This should never happen.", ++ job->jobid, resv->id, job->resvid); ++ return SLURM_SUCCESS; ++ } ++ ++ if (start < resv->time_start) ++ start = resv->time_start; ++ ++ if (!end || end > resv->time_end) ++ end = resv->time_end; ++ ++ if ((elapsed = (end - start)) < 1) ++ return SLURM_SUCCESS; ++ ++ slurmdb_transfer_tres_time( ++ &resv->tres_list, job->tres_alloc_str, ++ elapsed); ++ ++ return SLURM_SUCCESS; ++} ++ ++static void _get_usage_for_resv(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_reservation_rec_t *resv, ++ char *resv_id) ++{ ++ List job_list; ++ slurmdb_job_cond_t job_cond; ++ ++ memset(&job_cond, 0, sizeof(job_cond)); ++ job_cond.db_flags = SLURMDB_JOB_FLAG_NOTSET; ++ ++ job_cond.usage_start = resv->time_start; ++ job_cond.usage_end = resv->time_end; ++ ++ job_cond.cluster_list = list_create(NULL); ++ list_append(job_cond.cluster_list, resv->cluster); ++ ++ job_cond.resvid_list = list_create(NULL); ++ list_append(job_cond.resvid_list, resv_id); ++ ++ job_list = as_pgsql_jobacct_process_get_jobs( ++ pgsql_conn, uid, &job_cond); ++ ++ if (job_list && list_count(job_list)) ++ list_for_each(job_list, _add_usage_to_resv, resv); ++ ++ FREE_NULL_LIST(job_cond.cluster_list); ++ FREE_NULL_LIST(job_cond.resvid_list); ++ FREE_NULL_LIST(job_list); ++} ++ ++extern int as_pgsql_add_resv(pgsql_conn_t *pgsql_conn, ++ slurmdb_reservation_rec_t *resv) ++{ ++ int rc = SLURM_SUCCESS; ++ char *cols = NULL, *vals = NULL, *extra = NULL, ++ *query = NULL; ++ ++ if (!resv) { ++ error("No reservation was given to add."); ++ return SLURM_ERROR; ++ } ++ ++ if (!resv->id) { ++ error("We need an id to add a reservation."); ++ return SLURM_ERROR; ++ } ++ if (!resv->time_start) { ++ error("We need a start time to add a reservation."); ++ return SLURM_ERROR; ++ } ++ if (!resv->cluster || !resv->cluster[0]) { ++ error("We need a cluster name to add a reservation."); ++ return SLURM_ERROR; ++ } ++ ++ _setup_resv_limits(resv, &cols, &vals, &extra); ++ ++ xstrfmtcat(query, ++ "INSERT INTO \"%s_%s\" (id_resv%s) VALUES (%u%s) " ++ "ON CONFLICT (id_resv,time_start) DO UPDATE SET deleted=0%s;", ++ resv->cluster, resv_table, cols, resv->id, vals, extra); ++ ++ DB_DEBUG(DB_RESV, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ ++ xfree(query); ++ xfree(cols); ++ xfree(vals); ++ xfree(extra); ++ ++ return rc; ++} ++ ++extern int as_pgsql_modify_resv(pgsql_conn_t *pgsql_conn, ++ slurmdb_reservation_rec_t *resv) ++{ ++ pgsql_res_t *result = NULL; ++ pgsql_row row, row2; ++ int rc = SLURM_SUCCESS; ++ char *cols = NULL, *vals = NULL, *extra = NULL, ++ *query = NULL; ++ time_t start = 0, now = time(NULL); ++ int i; ++ int set = 0; ++ ++ char *resv_req_inx[] = { ++ "assoclist", ++ "deleted", ++ "time_start", ++ "time_end", ++ "resv_name", ++ "nodelist", ++ "node_inx", ++ "flags", ++ "tres" ++ }; ++ enum { ++ RESV_ASSOCS, ++ RESV_DELETED, ++ RESV_START, ++ RESV_END, ++ RESV_NAME, ++ RESV_NODES, ++ RESV_NODE_INX, ++ RESV_FLAGS, ++ RESV_TRES, ++ RESV_COUNT ++ }; ++ ++ if (!resv) { ++ error("No reservation was given to edit"); ++ return SLURM_ERROR; ++ } ++ ++ if (!resv->id) { ++ error("We need an id to edit a reservation."); ++ return SLURM_ERROR; ++ } ++ if (!resv->time_start) { ++ error("We need a start time to edit a reservation."); ++ return SLURM_ERROR; ++ } ++ if (!resv->cluster || !resv->cluster[0]) { ++ error("We need a cluster name to edit a reservation."); ++ return SLURM_ERROR; ++ } ++ ++ if (!resv->time_start_prev) { ++ error("We need a time to check for last " ++ "start of reservation."); ++ return SLURM_ERROR; ++ } ++ ++ xstrfmtcat(cols, "%s", resv_req_inx[0]); ++ for (i=1; i= %ld " ++ "ORDER BY time_start desc " ++ "FOR UPDATE;", ++ cols, resv->cluster, resv_table, resv->id, ++ MIN(resv->time_start, resv->time_start_prev)); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ ++ /* Get the first one that isn't deleted */ ++ do { ++ if (!(row = pgsql_fetch_row(result))) { ++ pgsql_free_result(&result); ++ error("%s: There is no reservation by id %u, time_start %ld, and cluster '%s', creating it", ++ __func__, resv->id, resv->time_start_prev, ++ resv->cluster); ++ /* ++ * Don't set the time_start to time_start_prev as we ++ * have no idea what the reservation looked like at that ++ * time. Doing so will also mess up future updates. ++ */ ++ /* resv->time_start = resv->time_start_prev; */ ++ rc = as_pgsql_add_resv(pgsql_conn, resv); ++ goto end_it; ++ } ++ } while (slurm_atoul(row[RESV_DELETED])); ++ ++ start = slurm_atoul(row[RESV_START]); ++ ++ xfree(query); ++ xfree(cols); ++ ++ /* ++ * Check to see if the start is after the time we are looking for and ++ * before now to make sure we are the latest update. If we aren't throw ++ * this one away. This should rarely if ever happen. ++ */ ++ if ((start > resv->time_start) && (start <= now)) { ++ error("There is newer record for reservation with id %u, drop modification request:", ++ resv->id); ++ error("assocs:'%s', cluster:'%s', flags:%"PRIu64", id:%u, name:'%s', nodes:'%s', nodes_inx:'%s', time_end:%ld, time_start:%ld, time_start_prev:%ld, tres_str:'%s', unused_wall:%f", ++ resv->assocs, resv->cluster, resv->flags, resv->id, ++ resv->name, resv->nodes, resv->node_inx, resv->time_end, ++ resv->time_start, resv->time_start_prev, resv->tres_str, ++ resv->unused_wall); ++ rc = SLURM_SUCCESS; ++ goto end_it; ++ } ++ ++ /* ++ * Here we are making sure we don't get a potential duplicate entry in ++ * the database. If we find one then we will delete it. This should ++ * never happen in practice but is more a sanity check. ++ */ ++ while ((row2 = pgsql_fetch_row(result))) { ++ if (resv->time_start != slurm_atoul(row2[RESV_START])) ++ continue; ++ ++ query = xstrdup_printf("DELETE FROM \"%s_%s\" WHERE " ++ "id_resv=%u AND time_start=%ld;", ++ resv->cluster, resv_table, ++ resv->id, resv->time_start); ++ info("When trying to update a reservation an already existing row that would create a duplicate entry was found. Replacing this old row with the current request. This should rarely if ever happen."); ++ rc = pgsql_db_query(pgsql_conn, query); ++ if (rc != SLURM_SUCCESS) { ++ error("problem with update query"); ++ goto end_it; ++ } ++ xfree(query); ++ } ++ ++ /* check differences here */ ++ ++ if (!resv->name ++ && row[RESV_NAME] && row[RESV_NAME][0]) ++ // if this changes we just update the ++ // record, no need to create a new one since ++ // this doesn't really effect the ++ // reservation accounting wise ++ resv->name = xstrdup(row[RESV_NAME]); ++ ++ if (xstrcmp(resv->assocs, row[RESV_ASSOCS]) || ++ (resv->flags != slurm_atoul(row[RESV_FLAGS])) || ++ xstrcmp(resv->nodes, row[RESV_NODE_INX]) || ++ xstrcmp(resv->tres_str, row[RESV_TRES])) ++ set = 1; ++ ++ if (!resv->time_end) ++ resv->time_end = slurm_atoul(row[RESV_END]); ++ ++ _setup_resv_limits(resv, &cols, &vals, &extra); ++ /* use start below instead of resv->time_start_prev ++ * just in case we have a different one from being out ++ * of sync ++ */ ++ if ((start > now) || !set) { ++ /* we haven't started the reservation yet, or ++ we are changing the associations or end ++ time which we can just update it */ ++ query = xstrdup_printf("UPDATE \"%s_%s\" SET deleted=0%s " ++ "WHERE deleted=0 AND id_resv=%u " ++ "AND time_start=%ld;", ++ resv->cluster, resv_table, ++ extra, resv->id, start); ++ } else { ++ if (start != resv->time_start) ++ /* time_start is already done above and we ++ * changed something that is in need on a new ++ * entry. */ ++ query = xstrdup_printf( ++ "UPDATE \"%s_%s\" SET time_end=%ld " ++ "WHERE deleted=0 AND id_resv=%u " ++ "AND time_start=%ld;", ++ resv->cluster, resv_table, ++ resv->time_start, ++ resv->id, start); ++ xstrfmtcat(query, ++ "INSERT INTO \"%s_%s\" (id_resv%s) " ++ "VALUES (%u%s) " ++ "ON CONFLICT (id_resv,time_start) DO UPDATE SET deleted=0%s;", ++ resv->cluster, resv_table, cols, resv->id, ++ vals, extra); ++ } ++ ++ DB_DEBUG(DB_RESV, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ ++end_it: ++ ++ xfree(query); ++ xfree(cols); ++ xfree(vals); ++ xfree(extra); ++ pgsql_free_result(&result); ++ ++ return rc; ++} ++ ++extern int as_pgsql_remove_resv(pgsql_conn_t *pgsql_conn, ++ slurmdb_reservation_rec_t *resv) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ ++ if (!resv) { ++ error("No reservation was given to remove"); ++ return SLURM_ERROR; ++ } ++ ++ if (!resv->id) { ++ error("An id is needed to remove a reservation."); ++ return SLURM_ERROR; ++ } ++ ++ if (!resv->time_start) { ++ error("A start time is needed to remove a reservation."); ++ return SLURM_ERROR; ++ } ++ ++ if (!resv->cluster || !resv->cluster[0]) { ++ error("A cluster name is needed to remove a reservation."); ++ return SLURM_ERROR; ++ } ++ ++ ++ /* first delete the resv that hasn't happened yet. */ ++ query = xstrdup_printf("DELETE FROM \"%s_%s\" WHERE time_start > %ld " ++ "AND id_resv=%u AND time_start=%ld;", ++ resv->cluster, resv_table, resv->time_start_prev, ++ resv->id, ++ resv->time_start); ++ /* then update the remaining ones with a deleted flag and end ++ * time of the time_start_prev which is set to when the ++ * command was issued */ ++ xstrfmtcat(query, ++ "UPDATE \"%s_%s\" SET time_end=%ld, " ++ "deleted=1 WHERE deleted=0 AND " ++ "id_resv=%u AND time_start=%ld;", ++ resv->cluster, resv_table, resv->time_start_prev, ++ resv->id, resv->time_start); ++ ++ DB_DEBUG(DB_RESV, pgsql_conn->conn, "query\n%s", query); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ ++ xfree(query); ++ ++ return rc; ++} ++ ++extern List as_pgsql_get_resvs(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_reservation_cond_t *resv_cond) ++{ ++ //DEF_TIMERS; ++ char *query = NULL; ++ char *extra = NULL; ++ char *tmp = NULL; ++ List resv_list = NULL; ++ int i=0, is_admin=1; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ void *curr_cluster = NULL; ++ List local_cluster_list = NULL; ++ List use_cluster_list = NULL; ++ ListIterator itr = NULL; ++ char *cluster_name = NULL; ++ /* needed if we don't have an resv_cond */ ++ uint16_t with_usage = 0; ++ bool locked = false; ++ ++ /* if this changes you will need to edit the corresponding enum */ ++ char *resv_req_inx[] = { ++ "id_resv", ++ "assoclist", ++ "flags", ++ "nodelist", ++ "node_inx", ++ "resv_name", ++ "time_start", ++ "time_end", ++ "tres", ++ "unused_wall" ++ }; ++ ++ enum { ++ RESV_REQ_ID, ++ RESV_REQ_ASSOCS, ++ RESV_REQ_FLAGS, ++ RESV_REQ_NODES, ++ RESV_REQ_NODE_INX, ++ RESV_REQ_NAME, ++ RESV_REQ_START, ++ RESV_REQ_END, ++ RESV_REQ_TRES, ++ RESV_REQ_UNUSED, ++ RESV_REQ_COUNT ++ }; ++ ++ if (!resv_cond) { ++ xstrcat(extra, " where deleted=0"); ++ goto empty; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (slurm_conf.private_data & PRIVATE_DATA_RESERVATIONS) { ++ if (!(is_admin = is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { ++ error("Only admins can look at reservations"); ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ } ++ ++ with_usage = resv_cond->with_usage; ++ ++ if (resv_cond->nodes) { ++ slurmdb_job_cond_t job_cond; ++ memset(&job_cond, 0, sizeof(slurmdb_job_cond_t)); ++ job_cond.db_flags = SLURMDB_JOB_FLAG_NOTSET; ++ job_cond.usage_start = resv_cond->time_start; ++ job_cond.usage_end = resv_cond->time_end; ++ job_cond.used_nodes = resv_cond->nodes; ++ if (!resv_cond->cluster_list) ++ resv_cond->cluster_list = list_create(xfree_ptr); ++ /* ++ * If they didn't specify a cluster, give them the one they are ++ * calling from. ++ */ ++ if (!list_count(resv_cond->cluster_list)) ++ list_append(resv_cond->cluster_list, ++ xstrdup(pgsql_conn->cluster_name)); ++ job_cond.cluster_list = resv_cond->cluster_list; ++ local_cluster_list = setup_cluster_list_with_inx( ++ pgsql_conn, &job_cond, (void **)&curr_cluster); ++ } ++ ++ (void) _setup_resv_cond_limits(resv_cond, &extra); ++ ++empty: ++ xfree(tmp); ++ xstrfmtcat(tmp, "t1.%s", resv_req_inx[i]); ++ for(i=1; icluster_list && ++ list_count(resv_cond->cluster_list)) { ++ use_cluster_list = resv_cond->cluster_list; ++ } else { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = as_pgsql_cluster_list; ++ locked = true; ++ } ++ ++ itr = list_iterator_create(use_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ if (query) ++ xstrcat(query, " union "); ++ //START_TIMER; ++ xstrfmtcat(query, "SELECT DISTINCT %s,'%s' AS cluster " ++ "FROM \"%s_%s\" AS t1%s", ++ tmp, cluster_name, cluster_name, resv_table, ++ extra ? extra : ""); ++ } ++ list_iterator_destroy(itr); ++ if (locked) ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ ++ if (query) ++ xstrcat(query, " order by cluster, time_start, resv_name;"); ++ ++ xfree(tmp); ++ xfree(extra); ++ DB_DEBUG(DB_RESV, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ FREE_NULL_LIST(local_cluster_list); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ resv_list = list_create(slurmdb_destroy_reservation_rec); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_reservation_rec_t *resv; ++ int start = slurm_atoul(row[RESV_REQ_START]); ++ ++ if (!good_nodes_from_inx(local_cluster_list, &curr_cluster, ++ row[RESV_REQ_NODE_INX], start)) ++ continue; ++ ++ resv = xmalloc(sizeof(slurmdb_reservation_rec_t)); ++ list_append(resv_list, resv); ++ resv->id = slurm_atoul(row[RESV_REQ_ID]); ++ resv->name = xstrdup(row[RESV_REQ_NAME]); ++ resv->node_inx = xstrdup(row[RESV_REQ_NODE_INX]); ++ resv->cluster = xstrdup(row[RESV_REQ_COUNT]); ++ resv->assocs = xstrdup(row[RESV_REQ_ASSOCS]); ++ resv->nodes = xstrdup(row[RESV_REQ_NODES]); ++ resv->time_start = start; ++ resv->time_end = slurm_atoul(row[RESV_REQ_END]); ++ resv->flags = slurm_atoull(row[RESV_REQ_FLAGS]); ++ resv->tres_str = xstrdup(row[RESV_REQ_TRES]); ++ resv->unused_wall = atof(row[RESV_REQ_UNUSED]); ++ if (with_usage) ++ _get_usage_for_resv( ++ pgsql_conn, uid, resv, row[RESV_REQ_ID]); ++ } ++ ++ FREE_NULL_LIST(local_cluster_list); ++ ++ /* free result after we use the list with resv id's in it. */ ++ pgsql_free_result(&result); ++ ++ //END_TIMER2("get_resvs"); ++ return resv_list; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_resv.h b/src/plugins/accounting_storage/pgsql/as_pgsql_resv.h +new file mode 100755 +index 0000000..cb153d4 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_resv.h +@@ -0,0 +1,54 @@ ++/*****************************************************************************\ ++ * as_pgsql_resv.h - functions dealing with reservations. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++#ifndef _HAVE_PGSQL_RESV_H ++#define _HAVE_PGSQL_RESV_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_add_resv(pgsql_conn_t *pgsql_conn, ++ slurmdb_reservation_rec_t *resv); ++ ++extern int as_pgsql_modify_resv(pgsql_conn_t *pgsql_conn, ++ slurmdb_reservation_rec_t *resv); ++ ++extern int as_pgsql_remove_resv(pgsql_conn_t *pgsql_conn, ++ slurmdb_reservation_rec_t *resv); ++ ++extern List as_pgsql_get_resvs(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_reservation_cond_t *resv_cond); ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_rollup.c b/src/plugins/accounting_storage/pgsql/as_pgsql_rollup.c +new file mode 100755 +index 0000000..4275bf9 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_rollup.c +@@ -0,0 +1,2081 @@ ++/*****************************************************************************\ ++ * as_pgsql_rollup.c - functions for rolling up data for associations ++ * and machines from the as_pgsql 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 ++ * CODE-OCEC-09-009. All rights reserved. ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_rollup.h" ++#include "as_pgsql_archive.h" ++#include "src/common/parse_time.h" ++#include "src/common/slurm_time.h" ++ ++enum { ++ TIME_ALLOC, ++ TIME_DOWN, ++ TIME_PDOWN, ++ TIME_RESV ++}; ++ ++enum { ++ ASSOC_TABLES, ++ WCKEY_TABLES ++}; ++ ++typedef struct { ++ uint64_t count; ++ uint32_t id; ++ uint64_t time_alloc; ++ uint64_t time_down; ++ uint64_t time_idle; ++ uint64_t time_over; ++ uint64_t time_pd; ++ uint64_t time_resv; ++ uint64_t total_time; ++} local_tres_usage_t; ++ ++typedef struct { ++ int id; ++ List loc_tres; ++} local_id_usage_t; ++ ++typedef struct { ++ time_t end; ++ int id; /*only needed for reservations */ ++ List loc_tres; ++ time_t start; ++} local_cluster_usage_t; ++ ++typedef struct { ++ time_t end; ++ uint32_t flags; ++ int id; ++ hostlist_t hl; ++ List local_assocs; /* list of assocs to spread unused time ++ over of type local_id_usage_t */ ++ List loc_tres; ++ time_t orig_start; ++ time_t start; ++ double unused_wall; ++} local_resv_usage_t; ++ ++static void _destroy_local_tres_usage(void *object) ++{ ++ local_tres_usage_t *a_usage = (local_tres_usage_t *)object; ++ if (a_usage) { ++ xfree(a_usage); ++ } ++} ++ ++static void _destroy_local_id_usage(void *object) ++{ ++ local_id_usage_t *a_usage = (local_id_usage_t *)object; ++ if (a_usage) { ++ FREE_NULL_LIST(a_usage->loc_tres); ++ xfree(a_usage); ++ } ++} ++ ++static void _destroy_local_cluster_usage(void *object) ++{ ++ local_cluster_usage_t *c_usage = (local_cluster_usage_t *)object; ++ if (c_usage) { ++ FREE_NULL_LIST(c_usage->loc_tres); ++ xfree(c_usage); ++ } ++} ++ ++static void _destroy_local_resv_usage(void *object) ++{ ++ local_resv_usage_t *r_usage = (local_resv_usage_t *)object; ++ if (r_usage) { ++ FREE_NULL_HOSTLIST(r_usage->hl); ++ FREE_NULL_LIST(r_usage->local_assocs); ++ FREE_NULL_LIST(r_usage->loc_tres); ++ xfree(r_usage); ++ } ++} ++ ++static int _find_loc_tres(void *x, void *key) ++{ ++ local_tres_usage_t *loc_tres = (local_tres_usage_t *)x; ++ uint32_t tres_id = *(uint32_t *)key; ++ ++ if (loc_tres->id == tres_id) ++ return 1; ++ return 0; ++} ++ ++static int _find_id_usage(void *x, void *key) ++{ ++ local_id_usage_t *loc = (local_id_usage_t *)x; ++ uint32_t id = *(uint32_t *)key; ++ ++ if (loc->id == id) ++ return 1; ++ return 0; ++} ++ ++static void _remove_job_tres_time_from_cluster(List c_tres, List j_tres, ++ int seconds) ++{ ++ ListIterator c_itr; ++ local_tres_usage_t *loc_c_tres, *loc_j_tres; ++ uint64_t time; ++ ++ if ((seconds <= 0) || !c_tres || !j_tres || ++ !list_count(c_tres) || !list_count(j_tres)) ++ return; ++ ++ c_itr = list_iterator_create(c_tres); ++ while ((loc_c_tres = list_next(c_itr))) { ++ if (!(loc_j_tres = list_find_first( ++ j_tres, _find_loc_tres, &loc_c_tres->id))) ++ continue; ++ time = seconds * loc_j_tres->count; ++ ++ if (time >= loc_c_tres->total_time) ++ loc_c_tres->total_time = 0; ++ else ++ loc_c_tres->total_time -= time; ++ } ++ list_iterator_destroy(c_itr); ++} ++ ++ ++static local_tres_usage_t *_add_time_tres(List tres_list, int type, uint32_t id, ++ uint64_t time, bool times_count) ++{ ++ local_tres_usage_t *loc_tres; ++ ++ /* Energy TRES could have a NO_VAL64, we want to skip those as it is the ++ * same as a 0 since nothing was gathered. ++ */ ++ if (!time || (time == NO_VAL64)) ++ return NULL; ++ ++ loc_tres = list_find_first(tres_list, _find_loc_tres, &id); ++ ++ if (!loc_tres) { ++ if (times_count) ++ return NULL; ++ loc_tres = xmalloc(sizeof(local_tres_usage_t)); ++ loc_tres->id = id; ++ list_append(tres_list, loc_tres); ++ } ++ ++ if (times_count) { ++ if (!loc_tres->count) ++ return NULL; ++ time *= loc_tres->count; ++ } ++ ++ switch (type) { ++ case TIME_ALLOC: ++ loc_tres->time_alloc += time; ++ break; ++ case TIME_DOWN: ++ loc_tres->time_down += time; ++ break; ++ case TIME_PDOWN: ++ loc_tres->time_pd += time; ++ break; ++ case TIME_RESV: ++ loc_tres->time_resv += time; ++ break; ++ default: ++ error("_add_time_tres: unknown type %d given", type); ++ xassert(0); ++ break; ++ } ++ ++ return loc_tres; ++} ++ ++static void _add_time_tres_list(List tres_list_out, List tres_list_in, int type, ++ uint64_t time_in, bool times_count) ++{ ++ ListIterator itr; ++ local_tres_usage_t *loc_tres; ++ ++ xassert(tres_list_in); ++ xassert(tres_list_out); ++ ++ itr = list_iterator_create(tres_list_in); ++ while ((loc_tres = list_next(itr))) ++ _add_time_tres(tres_list_out, type, ++ loc_tres->id, ++ time_in ? time_in : loc_tres->total_time, ++ times_count); ++ list_iterator_destroy(itr); ++} ++ ++/* ++ * Job usage is a ratio of its tres to the reservation's tres: ++ * Unused wall = unused wall - job_seconds * job_tres / resv_tres ++ */ ++static int _update_unused_wall(local_resv_usage_t *r_usage, List job_tres, ++ int job_seconds) ++{ ++ ListIterator resv_itr; ++ local_tres_usage_t *loc_tres; ++ uint32_t resv_tres_id; ++ uint64_t resv_tres_count; ++ double tres_ratio = 0.0; ++ ++ /* Get TRES counts. Make sure the TRES types match. */ ++ resv_itr = list_iterator_create(r_usage->loc_tres); ++ while ((loc_tres = list_next(resv_itr))) { ++ /* Avoid dividing by zero. */ ++ if (!loc_tres->count) ++ continue; ++ resv_tres_id = loc_tres->id; ++ resv_tres_count = loc_tres->count; ++ if ((loc_tres = list_find_first(job_tres, ++ _find_loc_tres, ++ &resv_tres_id))) { ++ tres_ratio = (double)loc_tres->count / ++ (double)resv_tres_count; ++ break; ++ } ++ } ++ list_iterator_destroy(resv_itr); ++ ++ /* ++ * Here we are converting TRES seconds to wall seconds. This is needed ++ * to determine how much time is actually idle in the reservation. ++ */ ++ r_usage->unused_wall -= (double)job_seconds * tres_ratio; ++ ++ if (r_usage->unused_wall < 0) { ++ /* ++ * With a Flex reservation you can easily have more time than is ++ * possible. Just print this debug3 warning if it happens. ++ */ ++ debug3("WARNING: Unused wall is less than zero; this should never happen outside a Flex reservation. Setting it to zero for resv id = %d, start = %ld.", ++ r_usage->id, r_usage->orig_start); ++ r_usage->unused_wall = 0; ++ } ++ return SLURM_SUCCESS; ++} ++ ++static void _add_job_alloc_time_to_cluster(List c_tres_list, List j_tres) ++{ ++ ListIterator c_itr = list_iterator_create(c_tres_list); ++ local_tres_usage_t *loc_c_tres, *loc_j_tres; ++ ++ while ((loc_c_tres = list_next(c_itr))) { ++ if (!(loc_j_tres = list_find_first( ++ j_tres, _find_loc_tres, &loc_c_tres->id))) ++ continue; ++ loc_c_tres->time_alloc += loc_j_tres->time_alloc; ++ } ++ list_iterator_destroy(c_itr); ++} ++ ++static void _setup_cluster_tres(List tres_list, uint32_t id, ++ uint64_t count, int seconds) ++{ ++ local_tres_usage_t *loc_tres = ++ list_find_first(tres_list, _find_loc_tres, &id); ++ ++ if (!loc_tres) { ++ loc_tres = xmalloc(sizeof(local_tres_usage_t)); ++ loc_tres->id = id; ++ list_append(tres_list, loc_tres); ++ } ++ ++ loc_tres->count = count; ++ loc_tres->total_time += seconds * loc_tres->count; ++} ++ ++static void _add_tres_2_list(List tres_list, char *tres_str, int seconds) ++{ ++ char *tmp_str = tres_str; ++ int id; ++ uint64_t count; ++ ++ xassert(tres_list); ++ ++ if (!tres_str || !tres_str[0]) ++ return; ++ ++ while (tmp_str) { ++ id = atoi(tmp_str); ++ if (id < 1) { ++ error("_add_tres_2_list: no id " ++ "found at %s instead", tmp_str); ++ break; ++ } ++ ++ /* We don't run rollup on a node basis ++ * because they are shared resources on ++ * many systems so it will almost always ++ * have over committed resources. ++ */ ++ if (id != TRES_NODE) { ++ if (!(tmp_str = strchr(tmp_str, '='))) { ++ error("_add_tres_2_list: no value found"); ++ xassert(0); ++ break; ++ } ++ count = slurm_atoull(++tmp_str); ++ _setup_cluster_tres(tres_list, id, count, seconds); ++ } ++ ++ if (!(tmp_str = strchr(tmp_str, ','))) ++ break; ++ tmp_str++; ++ } ++ ++ return; ++} ++ ++static void _add_job_alloc_time_to_assoc(List a_tres_list, List j_tres_list) ++{ ++ local_tres_usage_t *loc_a_tres, *loc_j_tres; ++ ++ /* ++ * NOTE: You have to use slurm_list_pop here, since ++ * mysql is exporting something of the same type as a ++ * macro, which messes everything up ++ * (my_list.h is the bad boy). ++ */ ++ while ((loc_j_tres = slurm_list_pop(j_tres_list))) { ++ if (!(loc_a_tres = list_find_first( ++ a_tres_list, _find_loc_tres, &loc_j_tres->id))) { ++ /* ++ * New TRES we haven't seen before in this association ++ * just transfer it over. ++ */ ++ list_append(a_tres_list, loc_j_tres); ++ continue; ++ } ++ loc_a_tres->time_alloc += loc_j_tres->time_alloc; ++ _destroy_local_tres_usage(loc_j_tres); ++ } ++} ++ ++/* This will destroy the *loc_tres given after it is transfered */ ++static void _transfer_loc_tres(List *loc_tres, local_id_usage_t *usage) ++{ ++ if (!usage || !*loc_tres) { ++ FREE_NULL_LIST(*loc_tres); ++ return; ++ } ++ ++ if (!usage->loc_tres) { ++ usage->loc_tres = *loc_tres; ++ *loc_tres = NULL; ++ } else { ++ _add_job_alloc_time_to_assoc(usage->loc_tres, *loc_tres); ++ FREE_NULL_LIST(*loc_tres); ++ } ++} ++ ++static void _add_tres_time_2_list(List tres_list, char *tres_str, ++ int type, int seconds, int suspend_seconds, ++ bool times_count) ++{ ++ char *tmp_str = tres_str; ++ int id; ++ uint64_t time, count; ++ local_tres_usage_t *loc_tres; ++ ++ xassert(tres_list); ++ ++ if (!tres_str || !tres_str[0]) ++ return; ++ ++ while (tmp_str) { ++ int loc_seconds = seconds; ++ ++ id = atoi(tmp_str); ++ if (id < 1) { ++ error("_add_tres_time_2_list: no id " ++ "found at %s", tmp_str); ++ break; ++ } ++ if (!(tmp_str = strchr(tmp_str, '='))) { ++ error("_add_tres_time_2_list: no value found for " ++ "id %d '%s'", id, tres_str); ++ xassert(0); ++ break; ++ } ++ ++ /* Take away suspended time from TRES that are idle when the ++ * job was suspended, currently only CPU's fill that bill. ++ */ ++ if (suspend_seconds && (id == TRES_CPU)) { ++ loc_seconds -= suspend_seconds; ++ if (loc_seconds < 1) ++ loc_seconds = 0; ++ } ++ ++ time = count = slurm_atoull(++tmp_str); ++ /* ENERGY is already totalled for the entire job so don't ++ * multiple with time. ++ */ ++ if (id != TRES_ENERGY) ++ time *= loc_seconds; ++ ++ loc_tres = _add_time_tres(tres_list, type, id, ++ time, times_count); ++ ++ if (loc_tres && !loc_tres->count) ++ loc_tres->count = count; ++ ++ if (!(tmp_str = strchr(tmp_str, ','))) ++ break; ++ tmp_str++; ++ } ++ ++ return; ++} ++ ++static int _process_purge(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, ++ uint16_t archive_data, ++ uint32_t purge_period) ++{ ++ int rc = SLURM_SUCCESS; ++ slurmdb_archive_cond_t arch_cond; ++ slurmdb_job_cond_t job_cond; ++ ++ /* if we didn't ask for archive data return here and don't do ++ anything extra just rollup */ ++ ++ if (!archive_data) ++ return SLURM_SUCCESS; ++ ++ if (!slurmdbd_conf) ++ return SLURM_SUCCESS; ++ ++ memset(&job_cond, 0, sizeof(job_cond)); ++ memset(&arch_cond, 0, sizeof(arch_cond)); ++ arch_cond.archive_dir = slurmdbd_conf->archive_dir; ++ arch_cond.archive_script = slurmdbd_conf->archive_script; ++ ++ if (purge_period & slurmdbd_conf->purge_event) ++ arch_cond.purge_event = slurmdbd_conf->purge_event; ++ else ++ arch_cond.purge_event = NO_VAL; ++ if (purge_period & slurmdbd_conf->purge_job) ++ arch_cond.purge_job = slurmdbd_conf->purge_job; ++ else ++ arch_cond.purge_job = NO_VAL; ++ ++ if (purge_period & slurmdbd_conf->purge_resv) ++ arch_cond.purge_resv = slurmdbd_conf->purge_resv; ++ else ++ arch_cond.purge_resv = NO_VAL; ++ ++ if (purge_period & slurmdbd_conf->purge_step) ++ arch_cond.purge_step = slurmdbd_conf->purge_step; ++ else ++ arch_cond.purge_step = NO_VAL; ++ if (purge_period & slurmdbd_conf->purge_suspend) ++ arch_cond.purge_suspend = slurmdbd_conf->purge_suspend; ++ else ++ arch_cond.purge_suspend = NO_VAL; ++ if (purge_period & slurmdbd_conf->purge_txn) ++ arch_cond.purge_txn = slurmdbd_conf->purge_txn; ++ else ++ arch_cond.purge_txn = NO_VAL; ++ if (purge_period & slurmdbd_conf->purge_usage) ++ arch_cond.purge_usage = slurmdbd_conf->purge_usage; ++ else ++ arch_cond.purge_usage = NO_VAL; ++ ++ job_cond.cluster_list = list_create(NULL); ++ list_append(job_cond.cluster_list, cluster_name); ++ ++ arch_cond.job_cond = &job_cond; ++ rc = as_pgsql_jobacct_process_archive(pgsql_conn, &arch_cond); ++ FREE_NULL_LIST(job_cond.cluster_list); ++ ++ return rc; ++} ++ ++static void _setup_cluster_tres_usage(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, ++ time_t curr_start, time_t curr_end, ++ time_t now, time_t use_start, ++ local_tres_usage_t *loc_tres, ++ char **query) ++{ ++ char start_char[20], end_char[20]; ++ uint64_t total_used; ++ ++ if (!loc_tres) ++ return; ++ ++ /* Now put the lists into the usage tables */ ++ ++ /* sanity check to make sure we don't have more ++ allocated cpus than possible. */ ++ if (loc_tres->total_time ++ && (loc_tres->total_time < loc_tres->time_alloc)) { ++ slurm_make_time_str(&curr_start, start_char, ++ sizeof(start_char)); ++ slurm_make_time_str(&curr_end, end_char, ++ sizeof(end_char)); ++ error("We have more allocated time than is possible (%"PRIu64" > %"PRIu64") for cluster %s(%"PRIu64") from %s - %s tres %u (this may happen if oversubscription of resources is allowed without Gang)", ++ loc_tres->time_alloc, loc_tres->total_time, ++ cluster_name, loc_tres->count, ++ start_char, end_char, loc_tres->id); ++ loc_tres->time_alloc = loc_tres->total_time; ++ } ++ ++ total_used = loc_tres->time_alloc + ++ loc_tres->time_down + loc_tres->time_pd; ++ ++ /* Make sure the total time we care about ++ doesn't go over the limit */ ++ if (loc_tres->total_time && (loc_tres->total_time < total_used)) { ++ int64_t overtime; ++ ++ slurm_make_time_str(&curr_start, start_char, ++ sizeof(start_char)); ++ slurm_make_time_str(&curr_end, end_char, ++ sizeof(end_char)); ++ error("We have more time than is possible (%"PRIu64"+%"PRIu64"+%"PRIu64")(%"PRIu64") > %"PRIu64" for cluster %s(%"PRIu64") from %s - %s tres %u (this may happen if oversubscription of resources is allowed without Gang)", ++ loc_tres->time_alloc, loc_tres->time_down, ++ loc_tres->time_pd, total_used, ++ loc_tres->total_time, ++ cluster_name, loc_tres->count, ++ start_char, end_char, loc_tres->id); ++ ++ /* First figure out how much actual down time ++ we have and then how much ++ planned down time we have. */ ++ overtime = (int64_t)(loc_tres->total_time - ++ (loc_tres->time_alloc + ++ loc_tres->time_down)); ++ if (overtime < 0) { ++ loc_tres->time_down += overtime; ++ if ((int64_t)loc_tres->time_down < 0) ++ loc_tres->time_down = 0; ++ } ++ ++ overtime = (int64_t)(loc_tres->total_time - ++ (loc_tres->time_alloc + ++ loc_tres->time_down + ++ loc_tres->time_pd)); ++ if (overtime < 0) { ++ loc_tres->time_pd += overtime; ++ if ((int64_t)loc_tres->time_pd < 0) ++ loc_tres->time_pd = 0; ++ } ++ ++ total_used = loc_tres->time_alloc + ++ loc_tres->time_down + loc_tres->time_pd; ++ /* info("We now have (%"PRIu64"+%"PRIu64"+" */ ++ /* "%"PRIu64")(%"PRIu64") " */ ++ /* "?= %"PRIu64"", */ ++ /* loc_tres->time_alloc, loc_tres->time_down, */ ++ /* loc_tres->time_pd, total_used, */ ++ /* loc_tres->total_time); */ ++ } ++ /* info("Cluster %s now has (%"PRIu64"+%"PRIu64"+" */ ++ /* "%"PRIu64")(%"PRIu64") ?= %"PRIu64"", */ ++ /* cluster_name, */ ++ /* c_usage->a_cpu, c_usage->d_cpu, */ ++ /* c_usage->pd_cpu, total_used, */ ++ /* c_usage->total_time); */ ++ ++ loc_tres->time_idle = loc_tres->total_time - ++ total_used - loc_tres->time_resv; ++ /* sanity check just to make sure we have a ++ * legitimate time after we calulated ++ * idle/reserved time put extra in the over ++ * commit field ++ */ ++ /* info("%s got idle of %lld", loc_tres->name, */ ++ /* (int64_t)loc_tres->time_idle); */ ++ if ((int64_t)loc_tres->time_idle < 0) { ++ /* info("got %d %d %d", loc_tres->time_resv, */ ++ /* loc_tres->time_idle, loc_tres->time_over); */ ++ loc_tres->time_resv += (int64_t)loc_tres->time_idle; ++ loc_tres->time_over -= (int64_t)loc_tres->time_idle; ++ loc_tres->time_idle = 0; ++ if ((int64_t)loc_tres->time_resv < 0) ++ loc_tres->time_resv = 0; ++ } ++ ++ /* info("cluster %s(%u) down %"PRIu64" alloc %"PRIu64" " */ ++ /* "resv %"PRIu64" idle %"PRIu64" over %"PRIu64" " */ ++ /* "total= %"PRIu64" ?= %"PRIu64" from %s", */ ++ /* cluster_name, */ ++ /* loc_tres->count, loc_tres->time_down, */ ++ /* loc_tres->time_alloc, */ ++ /* loc_tres->time_resv, loc_tres->time_idle, */ ++ /* loc_tres->time_over, */ ++ /* loc_tres->time_down + loc_tres->time_alloc + */ ++ /* loc_tres->time_resv + loc_tres->time_idle, */ ++ /* loc_tres->total_time, */ ++ /* slurm_ctime2(&loc_tres->start)); */ ++ /* info("to %s", slurm_ctime2(&loc_tres->end)); */ ++ if (*query) ++ xstrfmtcat(*query, ", (%ld, %ld, %ld, %u, %"PRIu64", " ++ "%"PRIu64", %"PRIu64", %"PRIu64", " ++ "%"PRIu64", %"PRIu64", %"PRIu64")", ++ now, now, use_start, loc_tres->id, ++ loc_tres->count, ++ loc_tres->time_alloc, ++ loc_tres->time_down, ++ loc_tres->time_pd, ++ loc_tres->time_idle, ++ loc_tres->time_over, ++ loc_tres->time_resv); ++ else ++ xstrfmtcat(*query, "insert into \"%s_%s\" " ++ "(creation_time, mod_time, " ++ "time_start, id_tres, count, " ++ "alloc_secs, down_secs, pdown_secs, " ++ "idle_secs, over_secs, plan_secs) " ++ "values (%ld, %ld, %ld, %u, %"PRIu64", " ++ "%"PRIu64", %"PRIu64", %"PRIu64", " ++ "%"PRIu64", %"PRIu64", %"PRIu64")", ++ cluster_name, cluster_hour_table, ++ now, now, ++ use_start, loc_tres->id, ++ loc_tres->count, ++ loc_tres->time_alloc, ++ loc_tres->time_down, ++ loc_tres->time_pd, ++ loc_tres->time_idle, ++ loc_tres->time_over, ++ loc_tres->time_resv); ++ ++ return; ++} ++ ++static int _process_cluster_usage(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, ++ time_t curr_start, time_t curr_end, ++ time_t now, local_cluster_usage_t *c_usage) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ ListIterator itr; ++ local_tres_usage_t *loc_tres; ++ ++ if (!c_usage) ++ return rc; ++ /* Now put the lists into the usage tables */ ++ ++ xassert(c_usage->loc_tres); ++ itr = list_iterator_create(c_usage->loc_tres); ++ while ((loc_tres = list_next(itr))) { ++ _setup_cluster_tres_usage(pgsql_conn, cluster_name, ++ curr_start, curr_end, now, ++ c_usage->start, loc_tres, &query); ++ } ++ list_iterator_destroy(itr); ++ ++ if (!query) ++ return rc; ++ ++// xstrfmtcat(query, ++// " on duplicate key update " ++// "mod_time=%ld, count=VALUES(count), " ++// "alloc_secs=VALUES(alloc_secs), " ++// "down_secs=VALUES(down_secs), " ++// "pdown_secs=VALUES(pdown_secs), " ++// "idle_secs=VALUES(idle_secs), " ++// "over_secs=VALUES(over_secs), " ++// "plan_secs=VALUES(plan_secs)", ++// now); ++ ++ xstrfmtcat(query, ++ " on conflict (id_tres, time_start) do update set " ++ "mod_time=%ld, count=EXCLUDED.count, " ++ "alloc_secs=EXCLUDED.alloc_secs, " ++ "down_secs=EXCLUDED.down_secs, " ++ "pdown_secs=EXCLUDED.pdown_secs, " ++ "idle_secs=EXCLUDED.idle_secs, " ++ "over_secs=EXCLUDED.over_secs, " ++ "plan_secs=EXCLUDED.plan_secs", ++ now); ++ ++ ++ /* Spacing out the inserts here instead of doing them ++ all at once in the end proves to be faster. Just FYI ++ so we don't go testing again and again. ++ */ ++ DB_DEBUG(DB_USAGE, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) ++ error("Couldn't add cluster hour rollup"); ++ ++ return rc; ++} ++ ++static void _create_id_usage_insert(char *cluster_name, int type, ++ time_t curr_start, time_t now, ++ local_id_usage_t *id_usage, ++ char **query) ++{ ++ local_tres_usage_t *loc_tres; ++ ListIterator itr; ++ bool first; ++ char *table = NULL, *id_name = NULL; ++ ++ xassert(query); ++ ++ switch (type) { ++ case ASSOC_TABLES: ++ id_name = "id_assoc"; ++ table = assoc_hour_table; ++ break; ++ case WCKEY_TABLES: ++ id_name = "id_wckey"; ++ table = wckey_hour_table; ++ break; ++ default: ++ error("_create_id_usage_insert: unknown type %d", type); ++ return; ++ break; ++ } ++ ++ if (!id_usage->loc_tres || !list_count(id_usage->loc_tres)) { ++ error("%s %d doesn't have any tres", id_name, id_usage->id); ++ return; ++ } ++ ++ first = 1; ++ itr = list_iterator_create(id_usage->loc_tres); ++ while ((loc_tres = list_next(itr))) { ++ if (!first) { ++ xstrfmtcat(*query, ++ ", (%ld, %ld, %u, %ld, %u, %"PRIu64")", ++ now, now, ++ id_usage->id, curr_start, loc_tres->id, ++ loc_tres->time_alloc); ++ } else { ++ xstrfmtcat(*query, ++ "insert into \"%s_%s\" " ++ "(creation_time, mod_time, id, " ++ "time_start, id_tres, alloc_secs) " ++ "values (%ld, %ld, %u, %ld, %u, %"PRIu64")", ++ cluster_name, table, now, now, ++ id_usage->id, curr_start, loc_tres->id, ++ loc_tres->time_alloc); ++ first = 0; ++ } ++ } ++ list_iterator_destroy(itr); ++ xstrfmtcat(*query, ++ " on conflict (id, id_tres, time_start) do update set mod_time=%ld, " ++ "alloc_secs=EXCLUDED.alloc_secs;", now); ++} ++ ++static int _add_resv_usage_to_cluster(void *object, void *arg) ++{ ++ local_resv_usage_t *r_usage = (local_resv_usage_t *)object; ++ local_cluster_usage_t *c_usage = (local_cluster_usage_t *)arg; ++ ++ xassert(c_usage); ++ ++ /* ++ * Only record time for the clusters that have ++ * registered, or if a reservation has the IGNORE_JOBS ++ * flag we don't have an easy way to distinguish the ++ * cpus a job not running in the reservation, but on ++ * it's cpus. ++ * We still need them for figuring out unused wall time, ++ * but for cluster utilization we will just ignore them. ++ */ ++ if (r_usage->flags & RESERVE_FLAG_IGN_JOBS) ++ return SLURM_SUCCESS; ++ ++ /* ++ * Since this reservation was added to the ++ * cluster and only certain people could run ++ * there we will use this as allocated time on ++ * the system. If the reservation was a ++ * maintenance then we add the time to planned ++ * down time. ++ */ ++ ++ _add_time_tres_list(c_usage->loc_tres, ++ r_usage->loc_tres, ++ (r_usage->flags & RESERVE_FLAG_MAINT) ? ++ TIME_PDOWN : TIME_ALLOC, 0, 0); ++ ++ /* slurm_make_time_str(&r_usage->start, start_char, */ ++ /* sizeof(start_char)); */ ++ /* slurm_make_time_str(&r_usage->end, end_char, */ ++ /* sizeof(end_char)); */ ++ /* info("adding this much %lld to cluster %s " */ ++ /* "%d %d %s - %s", */ ++ /* r_usage->total_time, c_usage->name, */ ++ /* (row_flags & RESERVE_FLAG_MAINT), */ ++ /* r_usage->id, start_char, end_char); */ ++ ++ return SLURM_SUCCESS; ++} ++ ++static local_cluster_usage_t *_setup_cluster_usage(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, ++ time_t curr_start, ++ time_t curr_end, ++ List resv_usage_list, ++ List cluster_down_list, ++ int dims) ++{ ++ local_cluster_usage_t *c_usage = NULL; ++ char *query = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ int i = 0; ++ ListIterator d_itr = NULL; ++ ListIterator r_itr = NULL; ++ local_cluster_usage_t *loc_c_usage; ++ local_resv_usage_t *loc_r_usage; ++ ++ char *event_req_inx[] = { ++ "node_name", ++ "time_start", ++ "time_end", ++ "state", ++ "tres", ++ }; ++ char *event_str = NULL; ++ enum { ++ EVENT_REQ_NAME, ++ EVENT_REQ_START, ++ EVENT_REQ_END, ++ EVENT_REQ_STATE, ++ EVENT_REQ_TRES, ++ EVENT_REQ_COUNT ++ }; ++ ++ xstrfmtcat(event_str, "%s", event_req_inx[i]); ++ for(i=1; i= %ld " ++ "OR time_end = 0)) " ++ "order by node_name, time_start", ++ event_str, cluster_name, event_table, ++ NODE_STATE_MAINT, ++ curr_end, curr_start); ++ xfree(event_str); ++ ++ DB_DEBUG(DB_USAGE, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ xfree(query); ++ ++ d_itr = list_iterator_create(cluster_down_list); ++ r_itr = list_iterator_create(resv_usage_list); ++ while ((row = pgsql_fetch_row(result))) { ++ time_t row_start = slurm_atoul(row[EVENT_REQ_START]); ++ time_t row_end = slurm_atoul(row[EVENT_REQ_END]); ++ uint16_t state = slurm_atoul(row[EVENT_REQ_STATE]); ++ time_t local_start, local_end; ++ int seconds, resv_seconds; ++ ++ if (row_start < curr_start) ++ row_start = curr_start; ++ ++ if (!row_end || row_end > curr_end) ++ row_end = curr_end; ++ ++ /* Don't worry about it if the time is less ++ * than 1 second. ++ */ ++ if ((seconds = (row_end - row_start)) < 1) ++ continue; ++ ++ /* this means we are a cluster registration ++ entry */ ++ if (!row[EVENT_REQ_NAME][0]) { ++ local_cluster_usage_t *loc_c_usage; ++ ++ /* if the cpu count changes we will ++ * only care about the last cpu count but ++ * we will keep a total of the time for ++ * all cpus to get the correct cpu time ++ * for the entire period. ++ */ ++ ++ if (state || !c_usage) { ++ loc_c_usage = xmalloc( ++ sizeof(local_cluster_usage_t)); ++ loc_c_usage->start = row_start; ++ loc_c_usage->loc_tres = ++ list_create(_destroy_local_tres_usage); ++ /* If this has a state it ++ means the slurmctld went ++ down and we should put this ++ on the list and remove any ++ jobs from this time that ++ were running later. ++ */ ++ if (state) ++ list_append(cluster_down_list, ++ loc_c_usage); ++ else ++ c_usage = loc_c_usage; ++ } else ++ loc_c_usage = c_usage; ++ ++ loc_c_usage->end = row_end; ++ ++ _add_tres_2_list(loc_c_usage->loc_tres, ++ row[EVENT_REQ_TRES], seconds); ++ ++ continue; ++ } ++ ++ /* ++ * Only record down time for the cluster we ++ * are looking for. If it was during this ++ * time period we would already have it. ++ */ ++ if (!c_usage) ++ continue; ++ ++ resv_seconds = 0; ++ /* ++ * Now switch this time from any non-maint ++ * reservations that may have had the node ++ * allocated during this time. ++ */ ++ list_iterator_reset(r_itr); ++ while ((loc_r_usage = list_next(r_itr))) { ++ time_t temp_end = row_end; ++ time_t temp_start = row_start; ++ List loc_tres = NULL; ++ ++ if (hostlist_find_dims(loc_r_usage->hl, ++ row[EVENT_REQ_NAME], dims) ++ < 0) ++ continue; ++ ++ if (loc_r_usage->start > temp_start) ++ temp_start = loc_r_usage->start; ++ if (loc_r_usage->end < temp_end) ++ temp_end = loc_r_usage->end; ++ if ((resv_seconds = (temp_end - temp_start)) < 1) ++ continue; ++ ++ loc_tres = list_create(_destroy_local_tres_usage); ++ ++ _add_tres_time_2_list(loc_tres, ++ row[EVENT_REQ_TRES], ++ loc_r_usage->flags & ++ RESERVE_FLAG_MAINT ? ++ TIME_PDOWN : TIME_DOWN, ++ resv_seconds, ++ 0, 0); ++ _add_tres_time_2_list(c_usage->loc_tres, ++ row[EVENT_REQ_TRES], ++ loc_r_usage->flags & ++ RESERVE_FLAG_MAINT ? ++ TIME_PDOWN : TIME_DOWN, ++ resv_seconds, ++ 0, 0); ++ ++ _remove_job_tres_time_from_cluster( ++ loc_r_usage->loc_tres, ++ loc_tres, resv_seconds); ++ ++ FREE_NULL_LIST(loc_tres); ++ } ++ ++ local_start = row_start; ++ local_end = row_end; ++ ++ if (local_start < c_usage->start) ++ local_start = c_usage->start; ++ if (local_end > c_usage->end) ++ local_end = c_usage->end; ++ ++ /* Don't worry about it if the time is less than 1 second. */ ++ if ((seconds = (local_end - local_start)) < 1) ++ continue; ++ ++ seconds -= resv_seconds; ++ if (seconds > 0) ++ _add_tres_time_2_list(c_usage->loc_tres, ++ row[EVENT_REQ_TRES], ++ TIME_DOWN, ++ seconds, 0, 0); ++ ++ /* ++ * Now remove this time if there was a ++ * disconnected slurmctld during the down time. ++ */ ++ list_iterator_reset(d_itr); ++ while ((loc_c_usage = list_next(d_itr))) { ++ time_t temp_end = row_end; ++ time_t temp_start = row_start; ++ if (loc_c_usage->start > temp_start) ++ temp_start = loc_c_usage->start; ++ if (loc_c_usage->end < temp_end) ++ temp_end = loc_c_usage->end; ++ seconds = (temp_end - temp_start); ++ if (seconds < 1) ++ continue; ++ ++ _remove_job_tres_time_from_cluster( ++ loc_c_usage->loc_tres, ++ c_usage->loc_tres, seconds); ++ /* info("Node %s was down for " */ ++ /* "%d seconds while " */ ++ /* "cluster %s's slurmctld " */ ++ /* "wasn't responding", */ ++ /* row[EVENT_REQ_NAME], */ ++ /* seconds, cluster_name); */ ++ } ++ } ++ pgsql_free_result(&result); ++ ++ list_iterator_destroy(d_itr); ++ ++ if (c_usage) ++ (void)list_for_each(resv_usage_list, ++ _add_resv_usage_to_cluster, ++ c_usage); ++ return c_usage; ++} ++ ++extern int _setup_resv_usage(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, ++ time_t curr_start, ++ time_t curr_end, ++ List resv_usage_list, ++ int dims) ++{ ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ int i; ++ char *query; ++ char *resv_str = NULL; ++ local_resv_usage_t *r_usage = NULL; ++ char *resv_req_inx[] = { ++ "id_resv", ++ "assoclist", ++ "flags", ++ "nodelist", ++ "tres", ++ "time_start", ++ "time_end", ++ "unused_wall" ++ }; ++ enum { ++ RESV_REQ_ID, ++ RESV_REQ_ASSOCS, ++ RESV_REQ_FLAGS, ++ RESV_REQ_NODES, ++ RESV_REQ_TRES, ++ RESV_REQ_START, ++ RESV_REQ_END, ++ RESV_REQ_UNUSED, ++ RESV_REQ_COUNT ++ }; ++ ++ /* now get the reservations during this time */ ++ ++ i=0; ++ xstrfmtcat(resv_str, "%s", resv_req_inx[i]); ++ for(i=1; i= %ld) " ++ "order by time_start", ++ resv_str, cluster_name, resv_table, ++ curr_end, curr_start); ++ xfree(resv_str); ++ DB_DEBUG(DB_USAGE, pgsql_conn->conn, "query\n%s", query); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ /* ++ * If a reservation overlaps another reservation we ++ * total up everything here as if they didn't but when ++ * calculating the total time for a cluster we will ++ * remove the extra time received. This may result in ++ * unexpected results with association based reports ++ * since the association is given the total amount of ++ * time of each reservation, thus equaling more time ++ * than is available. Job/Cluster/Reservation reports ++ * should be fine though since we really don't over ++ * allocate resources. The issue with us not being ++ * able to handle overlapping reservations here is ++ * unless the reservation completely overlaps the ++ * other reservation we have no idea how many cpus ++ * should be removed since this could be a ++ * heterogeneous system. This same problem exists ++ * when a reservation is created with the ignore_jobs ++ * option which will allow jobs to continue to run in the ++ * reservation that aren't suppose to. ++ */ ++ while ((row = pgsql_fetch_row(result))) { ++ time_t row_start = slurm_atoul(row[RESV_REQ_START]); ++ time_t row_end = slurm_atoul(row[RESV_REQ_END]); ++ int unused; ++ int resv_seconds; ++ time_t orig_start = row_start; ++ ++ if (row_start >= curr_start) { ++ /* ++ * This is the first time we are seeing this ++ * reservation, so set our unused to be 0. ++ * This is mostly helpful when ++ * rerolling set it back to 0. ++ */ ++ unused = 0; ++ } else ++ unused = slurm_atoul(row[RESV_REQ_UNUSED]); ++ ++ if (row_start <= curr_start) ++ row_start = curr_start; ++ ++ if (!row_end || row_end > curr_end) ++ row_end = curr_end; ++ ++ /* Don't worry about it if the time is less ++ * than 1 second. ++ */ ++ if ((resv_seconds = (row_end - row_start)) < 1) ++ continue; ++ ++ r_usage = xmalloc(sizeof(local_resv_usage_t)); ++ r_usage->flags = slurm_atoul(row[RESV_REQ_FLAGS]); ++ r_usage->id = slurm_atoul(row[RESV_REQ_ID]); ++ ++ r_usage->local_assocs = list_create(xfree_ptr); ++ slurm_addto_char_list(r_usage->local_assocs, ++ row[RESV_REQ_ASSOCS]); ++ r_usage->loc_tres = ++ list_create(_destroy_local_tres_usage); ++ ++ _add_tres_2_list(r_usage->loc_tres, ++ row[RESV_REQ_TRES], resv_seconds); ++ ++ /* ++ * Original start is needed when updating the ++ * reservation's unused_wall later on. ++ */ ++ r_usage->orig_start = orig_start; ++ r_usage->start = row_start; ++ r_usage->end = row_end; ++ r_usage->unused_wall = unused + resv_seconds; ++ r_usage->hl = hostlist_create_dims(row[RESV_REQ_NODES], dims); ++ list_append(resv_usage_list, r_usage); ++ } ++ pgsql_free_result(&result); ++ ++ return SLURM_SUCCESS; ++} ++ ++extern int as_pgsql_hourly_rollup(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, ++ time_t start, time_t end, ++ uint16_t archive_data) ++{ ++ int rc = SLURM_SUCCESS; ++ int add_sec = 3600; ++ int i=0, dims; ++ time_t now = time(NULL); ++ time_t curr_start = start; ++ time_t curr_end = curr_start + add_sec; ++ char *query = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ListIterator a_itr = NULL; ++ ListIterator c_itr = NULL; ++ ListIterator w_itr = NULL; ++ ListIterator r_itr = NULL; ++ List assoc_usage_list = list_create(_destroy_local_id_usage); ++ List cluster_down_list = list_create(_destroy_local_cluster_usage); ++ List wckey_usage_list = list_create(_destroy_local_id_usage); ++ List resv_usage_list = list_create(_destroy_local_resv_usage); ++ uint16_t track_wckey = slurm_get_track_wckey(); ++ local_cluster_usage_t *loc_c_usage = NULL; ++ local_cluster_usage_t *c_usage = NULL; ++ local_resv_usage_t *r_usage = NULL; ++ local_id_usage_t *a_usage = NULL; ++ local_id_usage_t *w_usage = NULL; ++ /* char start_char[20], end_char[20]; */ ++ ++ char *job_req_inx[] = { ++ "job.job_db_inx", ++// "job.id_job", ++ "job.id_assoc", ++ "job.id_wckey", ++ "job.array_task_pending", ++ "job.time_eligible", ++ "job.time_start", ++ "job.time_end", ++ "job.time_suspended", ++ "job.cpus_req", ++ "job.id_resv", ++ "job.tres_alloc" ++ }; ++ char *job_str = NULL; ++ enum { ++ JOB_REQ_DB_INX, ++// JOB_REQ_JOBID, ++ JOB_REQ_ASSOCID, ++ JOB_REQ_WCKEYID, ++ JOB_REQ_ARRAY_PENDING, ++ JOB_REQ_ELG, ++ JOB_REQ_START, ++ JOB_REQ_END, ++ JOB_REQ_SUSPENDED, ++ JOB_REQ_RCPU, ++ JOB_REQ_RESVID, ++ JOB_REQ_TRES, ++ JOB_REQ_COUNT ++ }; ++ ++ char *suspend_req_inx[] = { ++ "time_start", ++ "time_end" ++ }; ++ char *suspend_str = NULL; ++ enum { ++ SUSPEND_REQ_START, ++ SUSPEND_REQ_END, ++ SUSPEND_REQ_COUNT ++ }; ++ ++ i=0; ++ xstrfmtcat(job_str, "%s", job_req_inx[i]); ++ for(i=1; iconn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ error("%s: error querying cluster_table", __func__); ++ rc = SLURM_ERROR; ++ pgsql_free_result(&result); ++ goto end_it; ++ } ++ row = pgsql_fetch_row(result); ++ ++ if (!row) { ++ error("%s: no cluster by name %s known", ++ __func__, cluster_name); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ ++ dims = atoi(row[0]); ++ pgsql_free_result(&result); ++ ++/* info("begin start %s", slurm_ctime2(&curr_start)); */ ++/* info("begin end %s", slurm_ctime2(&curr_end)); */ ++ a_itr = list_iterator_create(assoc_usage_list); ++ c_itr = list_iterator_create(cluster_down_list); ++ w_itr = list_iterator_create(wckey_usage_list); ++ r_itr = list_iterator_create(resv_usage_list); ++ while (curr_start < end) { ++ int last_id = -1; ++ int last_wckeyid = -1; ++ ++ DB_DEBUG(DB_USAGE, pgsql_conn->conn, ++ "%s curr hour is now %ld-%ld", ++ cluster_name, curr_start, curr_end); ++/* info("start %s", slurm_ctime2(&curr_start)); */ ++/* info("end %s", slurm_ctime2(&curr_end)); */ ++ ++ if ((rc = _setup_resv_usage(pgsql_conn, cluster_name, ++ curr_start, curr_end, ++ resv_usage_list, dims)) ++ != SLURM_SUCCESS) ++ goto end_it; ++ ++ c_usage = _setup_cluster_usage(pgsql_conn, cluster_name, ++ curr_start, curr_end, ++ resv_usage_list, ++ cluster_down_list, ++ dims); ++ ++ if (c_usage) ++ xassert(c_usage->loc_tres); ++ ++ /* now get the jobs during this time only */ ++ query = xstrdup_printf("select %s from \"%s_%s\" as job " ++ "where (job.time_eligible AND " ++ "job.time_eligible < %ld AND " ++ "(job.time_end >= %ld OR " ++ "job.time_end = 0)) " ++ "group by job.job_db_inx " ++ "order by job.id_assoc, " ++ "job.time_eligible", ++ job_str, cluster_name, job_table, ++ curr_end, curr_start); ++ ++ DB_DEBUG(DB_USAGE, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ rc = SLURM_ERROR; ++ pgsql_free_result(&result); ++ goto end_it; ++ } ++ ++ xfree(query); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ //uint32_t job_id = slurm_atoul(row[JOB_REQ_JOBID]); ++ uint32_t assoc_id = slurm_atoul(row[JOB_REQ_ASSOCID]); ++ uint32_t wckey_id = slurm_atoul(row[JOB_REQ_WCKEYID]); ++ uint32_t array_pending = ++ slurm_atoul(row[JOB_REQ_ARRAY_PENDING]); ++ uint32_t resv_id = slurm_atoul(row[JOB_REQ_RESVID]); ++ time_t row_eligible = slurm_atoul(row[JOB_REQ_ELG]); ++ time_t row_start = slurm_atoul(row[JOB_REQ_START]); ++ time_t row_end = slurm_atoul(row[JOB_REQ_END]); ++ uint32_t row_rcpu = slurm_atoul(row[JOB_REQ_RCPU]); ++ List loc_tres = NULL; ++ int loc_seconds = 0; ++ int seconds = 0, suspend_seconds = 0; ++ ++ if (row_start && (row_start < curr_start)) ++ row_start = curr_start; ++ ++ if (!row_start && row_end) ++ row_start = row_end; ++ ++ if (!row_end || row_end > curr_end) ++ row_end = curr_end; ++ ++ if (!row_start || ((row_end - row_start) < 1)) ++ goto calc_cluster; ++ ++ seconds = (row_end - row_start); ++ ++ if (slurm_atoul(row[JOB_REQ_SUSPENDED])) { ++ pgsql_res_t *result2 = NULL; ++ pgsql_row row2; ++ /* get the suspended time for this job */ ++ query = xstrdup_printf( ++ "select %s from \"%s_%s\" where " ++ "(time_start < %ld AND (time_end >= %ld " ++ "OR time_end = 0)) AND job_db_inx=%s " ++ "order by time_start", ++ suspend_str, cluster_name, ++ suspend_table, ++ curr_end, curr_start, ++ row[JOB_REQ_DB_INX]); ++ ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, ++ __LINE__, query); ++ result2 = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result2), SUCCESSFUL_COMPLETION)){ ++ rc = SLURM_ERROR; ++ pgsql_free_result(&result2); ++ goto end_it; ++ } ++ xfree(query); ++ while ((row2 = pgsql_fetch_row(result2))) { ++ int tot_time = 0; ++ time_t local_start = slurm_atoul( ++ row2[SUSPEND_REQ_START]); ++ time_t local_end = slurm_atoul( ++ row2[SUSPEND_REQ_END]); ++ ++ if (!local_start) ++ continue; ++ ++ if (row_start > local_start) ++ local_start = row_start; ++ if (!local_end || row_end < local_end) ++ local_end = row_end; ++ tot_time = (local_end - local_start); ++ ++ if (tot_time > 0) ++ suspend_seconds += tot_time; ++ } ++ pgsql_free_result(&result2); ++ } ++ ++ if (last_id != assoc_id) { ++ a_usage = xmalloc(sizeof(local_id_usage_t)); ++ a_usage->id = assoc_id; ++ list_append(assoc_usage_list, a_usage); ++ last_id = assoc_id; ++ /* a_usage->loc_tres is made later, ++ don't do it here. ++ */ ++ } ++ ++ /* Short circuit this so so we don't get a pointer. */ ++ if (!track_wckey) ++ last_wckeyid = wckey_id; ++ ++ /* do the wckey calculation */ ++ if (last_wckeyid != wckey_id) { ++ list_iterator_reset(w_itr); ++ while ((w_usage = list_next(w_itr))) ++ if (w_usage->id == wckey_id) ++ break; ++ ++ if (!w_usage) { ++ w_usage = xmalloc( ++ sizeof(local_id_usage_t)); ++ w_usage->id = wckey_id; ++ list_append(wckey_usage_list, ++ w_usage); ++ w_usage->loc_tres = list_create( ++ _destroy_local_tres_usage); ++ } ++ last_wckeyid = wckey_id; ++ } ++ ++ /* do the cluster allocated calculation */ ++ calc_cluster: ++ ++ /* ++ * We need to have this clean for each job ++ * since we add the time to the cluster individually. ++ */ ++ loc_tres = list_create(_destroy_local_tres_usage); ++ ++ _add_tres_time_2_list(loc_tres, row[JOB_REQ_TRES], ++ TIME_ALLOC, seconds, ++ suspend_seconds, 0); ++ if (w_usage) ++ _add_tres_time_2_list(w_usage->loc_tres, ++ row[JOB_REQ_TRES], ++ TIME_ALLOC, seconds, ++ suspend_seconds, 0); ++ ++ /* ++ * Now figure out there was a disconnected ++ * slurmctld during this job. ++ */ ++ list_iterator_reset(c_itr); ++ while ((loc_c_usage = list_next(c_itr))) { ++ int temp_end = row_end; ++ int temp_start = row_start; ++ if (loc_c_usage->start > temp_start) ++ temp_start = loc_c_usage->start; ++ if (loc_c_usage->end < temp_end) ++ temp_end = loc_c_usage->end; ++ loc_seconds = (temp_end - temp_start); ++ if (loc_seconds < 1) ++ continue; ++ ++ _remove_job_tres_time_from_cluster( ++ loc_c_usage->loc_tres, ++ loc_tres, ++ loc_seconds); ++ /* info("Job %u was running for " */ ++ /* "%d seconds while " */ ++ /* "cluster %s's slurmctld " */ ++ /* "wasn't responding", */ ++ /* job_id, loc_seconds, cluster_name); */ ++ } ++ ++ /* first figure out the reservation */ ++ if (resv_id) { ++ if (seconds <= 0) { ++ _transfer_loc_tres(&loc_tres, a_usage); ++ continue; ++ } ++ /* ++ * Since we have already added the entire ++ * reservation as used time on the cluster we ++ * only need to calculate the used time for the ++ * reservation and then divy up the unused time ++ * over the associations able to run in the ++ * reservation. Since the job was to run, or ran ++ * a reservation we don't care about eligible ++ * time since that could totally skew the ++ * clusters reserved time since the job may be ++ * able to run outside of the reservation. ++ */ ++ list_iterator_reset(r_itr); ++ while ((r_usage = list_next(r_itr))) { ++ int temp_end, temp_start; ++ /* ++ * since the reservation could have ++ * changed in some way, thus making a ++ * new reservation record in the ++ * database, we have to make sure all ++ * of the reservations are checked to ++ * see if such a thing has happened ++ */ ++ if (r_usage->id != resv_id) ++ continue; ++ temp_end = row_end; ++ temp_start = row_start; ++ if (r_usage->start > temp_start) ++ temp_start = ++ r_usage->start; ++ if (r_usage->end < temp_end) ++ temp_end = r_usage->end; ++ ++ loc_seconds = (temp_end - temp_start); ++ ++ if (loc_seconds <= 0) ++ continue; ++ ++ if (c_usage && ++ (r_usage->flags & ++ RESERVE_FLAG_IGN_JOBS)) ++ /* ++ * job usage was not ++ * bundled with resv ++ * usage so need to ++ * account for it ++ * individually here ++ */ ++ _add_tres_time_2_list( ++ c_usage->loc_tres, ++ row[JOB_REQ_TRES], ++ TIME_ALLOC, ++ loc_seconds, ++ 0, 0); ++ ++ _add_time_tres_list( ++ r_usage->loc_tres, ++ loc_tres, TIME_ALLOC, ++ loc_seconds, 1); ++ if ((rc = _update_unused_wall( ++ r_usage, ++ loc_tres, ++ loc_seconds)) ++ != SLURM_SUCCESS) ++ goto end_it; ++ } ++ ++ _transfer_loc_tres(&loc_tres, a_usage); ++ continue; ++ } ++ ++ /* ++ * only record time for the clusters that have ++ * registered. This continue should rarely if ++ * ever happen. ++ */ ++ if (!c_usage) { ++ _transfer_loc_tres(&loc_tres, a_usage); ++ continue; ++ } ++ ++ if (row_start && (seconds > 0)) { ++ /* info("%d assoc %d adds " */ ++ /* "(%d)(%d-%d) * %d = %d " */ ++ /* "to %d", */ ++ /* job_id, */ ++ /* a_usage->id, */ ++ /* seconds, */ ++ /* row_end, row_start, */ ++ /* row_acpu, */ ++ /* seconds * row_acpu, */ ++ /* row_acpu); */ ++ ++ _add_job_alloc_time_to_cluster( ++ c_usage->loc_tres, ++ loc_tres); ++ } ++ ++ /* ++ * The loc_tres isn't needed after this so transfer to ++ * the association and go on our merry way. ++ */ ++ _transfer_loc_tres(&loc_tres, a_usage); ++ ++ /* now reserved time */ ++ if (!row_start || (row_start >= c_usage->start)) { ++ int temp_end = row_start; ++ int temp_start = row_eligible; ++ if (c_usage->start > temp_start) ++ temp_start = c_usage->start; ++ if (!temp_end || (c_usage->end < temp_end)) ++ temp_end = c_usage->end; ++ loc_seconds = (temp_end - temp_start); ++ if (loc_seconds > 0) { ++ /* ++ * If we have pending jobs in an array ++ * they haven't been inserted into the ++ * database yet as proper job records, ++ * so handle them here. ++ */ ++ if (array_pending) ++ loc_seconds *= array_pending; ++ ++ /* info("%d assoc %d reserved " */ ++ /* "(%d)(%d-%d) * %d * %d = %d " */ ++ /* "to %d", */ ++ /* job_id, */ ++ /* assoc_id, */ ++ /* temp_end - temp_start, */ ++ /* temp_end, temp_start, */ ++ /* row_rcpu, */ ++ /* array_pending, */ ++ /* loc_seconds, */ ++ /* row_rcpu); */ ++ ++ _add_time_tres(c_usage->loc_tres, ++ TIME_RESV, TRES_CPU, ++ loc_seconds * ++ (uint64_t) row_rcpu, ++ 0); ++ } ++ } ++ } ++ pgsql_free_result(&result); ++ ++ /* now figure out how much more to add to the ++ associations that could had run in the reservation ++ */ ++ query = NULL; ++ list_iterator_reset(r_itr); ++ while ((r_usage = list_next(r_itr))) { ++ ListIterator t_itr; ++ local_tres_usage_t *loc_tres; ++ ++ xstrfmtcat(query, "update \"%s_%s\" set unused_wall=%f where id_resv=%u and time_start=%ld;", ++ cluster_name, resv_table, ++ r_usage->unused_wall, r_usage->id, ++ r_usage->orig_start); ++ ++ if (!r_usage->loc_tres || ++ !list_count(r_usage->loc_tres)) ++ continue; ++ ++ t_itr = list_iterator_create(r_usage->loc_tres); ++ while ((loc_tres = list_next(t_itr))) { ++ int64_t idle = loc_tres->total_time - ++ loc_tres->time_alloc; ++ char *assoc = NULL; ++ ListIterator tmp_itr = NULL; ++ int assoc_cnt, resv_unused_secs; ++ ++ if (idle <= 0) ++ break; /* since this will be ++ * the same for all TRES */ ++ ++ /* now divide that time by the number of ++ associations in the reservation and add ++ them to each association */ ++ resv_unused_secs = idle; ++ assoc_cnt = list_count(r_usage->local_assocs); ++ if (assoc_cnt) ++ resv_unused_secs /= assoc_cnt; ++ /* info("resv %d got %d seconds for TRES %u " */ ++ /* "for %d assocs", */ ++ /* r_usage->id, resv_unused_secs, */ ++ /* loc_tres->id, */ ++ /* list_count(r_usage->local_assocs)); */ ++ tmp_itr = list_iterator_create( ++ r_usage->local_assocs); ++ while ((assoc = list_next(tmp_itr))) { ++ uint32_t associd = slurm_atoul(assoc); ++ if ((last_id != associd) && ++ !(a_usage = list_find_first( ++ assoc_usage_list, ++ _find_id_usage, ++ &associd))) { ++ a_usage = xmalloc( ++ sizeof(local_id_usage_t)); ++ a_usage->id = associd; ++ list_append(assoc_usage_list, ++ a_usage); ++ last_id = associd; ++ a_usage->loc_tres = list_create( ++ _destroy_local_tres_usage); ++ } ++ ++ _add_time_tres(a_usage->loc_tres, ++ TIME_ALLOC, loc_tres->id, ++ resv_unused_secs, 0); ++ } ++ list_iterator_destroy(tmp_itr); ++ } ++ list_iterator_destroy(t_itr); ++ } ++ ++ if (query) { ++ DB_DEBUG(DB_USAGE, pgsql_conn->conn, "query\n%s", ++ query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("couldn't update reservations with unused time"); ++ goto end_it; ++ } ++ } ++ ++ /* now apply the down time from the slurmctld disconnects */ ++ if (c_usage) { ++ list_iterator_reset(c_itr); ++ while ((loc_c_usage = list_next(c_itr))) { ++ local_tres_usage_t *loc_tres; ++ ListIterator tmp_itr = list_iterator_create( ++ loc_c_usage->loc_tres); ++ while ((loc_tres = list_next(tmp_itr))) ++ _add_time_tres(c_usage->loc_tres, ++ TIME_DOWN, ++ loc_tres->id, ++ loc_tres->total_time, ++ 0); ++ list_iterator_destroy(tmp_itr); ++ } ++ ++ if ((rc = _process_cluster_usage( ++ pgsql_conn, cluster_name, curr_start, ++ curr_end, now, c_usage)) ++ != SLURM_SUCCESS) { ++ goto end_it; ++ } ++ } ++ ++ list_iterator_reset(a_itr); ++ while ((a_usage = list_next(a_itr))) ++ _create_id_usage_insert(cluster_name, ASSOC_TABLES, ++ curr_start, now, ++ a_usage, &query); ++ if (query) { ++ DB_DEBUG(DB_USAGE, pgsql_conn->conn, "query\n%s", ++ query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add assoc hour rollup"); ++ goto end_it; ++ } ++ } ++ ++ if (!track_wckey) ++ goto end_loop; ++ ++ list_iterator_reset(w_itr); ++ while ((w_usage = list_next(w_itr))) ++ _create_id_usage_insert(cluster_name, WCKEY_TABLES, ++ curr_start, now, ++ w_usage, &query); ++ if (query) { ++ DB_DEBUG(DB_USAGE, pgsql_conn->conn, "query\n%s", ++ query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add wckey hour rollup"); ++ goto end_it; ++ } ++ } ++ ++ end_loop: ++ _destroy_local_cluster_usage(c_usage); ++ ++ c_usage = NULL; ++ r_usage = NULL; ++ a_usage = NULL; ++ w_usage = NULL; ++ ++ list_flush(assoc_usage_list); ++ list_flush(cluster_down_list); ++ list_flush(wckey_usage_list); ++ list_flush(resv_usage_list); ++ curr_start = curr_end; ++ curr_end = curr_start + add_sec; ++ } ++end_it: ++ xfree(query); ++ xfree(suspend_str); ++ xfree(job_str); ++ _destroy_local_cluster_usage(c_usage); ++ ++ if (a_itr) ++ list_iterator_destroy(a_itr); ++ if (c_itr) ++ list_iterator_destroy(c_itr); ++ if (w_itr) ++ list_iterator_destroy(w_itr); ++ if (r_itr) ++ list_iterator_destroy(r_itr); ++ ++ FREE_NULL_LIST(assoc_usage_list); ++ FREE_NULL_LIST(cluster_down_list); ++ FREE_NULL_LIST(wckey_usage_list); ++ FREE_NULL_LIST(resv_usage_list); ++ ++/* info("stop start %s", slurm_ctime2(&curr_start)); */ ++/* info("stop end %s", slurm_ctime2(&curr_end)); */ ++ ++ /* go check to see if we archive and purge */ ++ ++ if (rc == SLURM_SUCCESS) { ++ if (pgsql_db_commit(pgsql_conn)) { ++ char start[25], end[25]; ++ error("Couldn't commit cluster (%s) " ++ "hour rollup for %s - %s", ++ cluster_name, slurm_ctime2_r(&curr_start, start), ++ slurm_ctime2_r(&curr_end, end)); ++ rc = SLURM_ERROR; ++ } else ++ rc = _process_purge(pgsql_conn, cluster_name, ++ archive_data, SLURMDB_PURGE_HOURS); ++ } ++ ++ return rc; ++} ++extern int as_pgsql_nonhour_rollup(pgsql_conn_t *pgsql_conn, ++ bool run_month, ++ char *cluster_name, ++ time_t start, time_t end, ++ uint16_t archive_data) ++{ ++ /* can't just add 86400 since daylight savings starts and ends every ++ * once in a while ++ */ ++ int rc = SLURM_SUCCESS; ++ struct tm start_tm; ++ time_t curr_start = start; ++ time_t curr_end; ++ time_t now = time(NULL); ++ char *query = NULL; ++ uint16_t track_wckey = slurm_get_track_wckey(); ++ char *unit_name; ++ ++ while (curr_start < end) { ++ if (!localtime_r(&curr_start, &start_tm)) { ++ error("Couldn't get localtime from start %ld", ++ curr_start); ++ return SLURM_ERROR; ++ } ++ start_tm.tm_sec = 0; ++ start_tm.tm_min = 0; ++ start_tm.tm_hour = 0; ++ ++ if (run_month) { ++ unit_name = "month"; ++ start_tm.tm_mday = 1; ++ start_tm.tm_mon++; ++ } else { ++ unit_name = "day"; ++ start_tm.tm_mday++; ++ } ++ ++ curr_end = slurm_mktime(&start_tm); ++ ++ DB_DEBUG(DB_USAGE, pgsql_conn->conn, ++ "curr %s is now %ld-%ld", ++ unit_name, curr_start, curr_end); ++/* info("start %s", slurm_ctime2(&curr_start)); */ ++/* info("end %s", slurm_ctime2(&curr_end)); */ ++// query = xstrdup_printf( ++// "insert into \"%s_%s\" (creation_time, mod_time, id, " ++// "id_tres, time_start, alloc_secs) " ++// "select %ld, %ld, id, id_tres, " ++// "%ld, @ASUM:=SUM(alloc_secs) from \"%s_%s\" where " ++// "(time_start < %ld && time_start >= %ld) " ++// "group by id, id_tres on duplicate key update " ++// "mod_time=%ld, alloc_secs=@ASUM;", ++// cluster_name, ++// run_month ? assoc_month_table : assoc_day_table, ++// now, now, curr_start, ++// cluster_name, ++// run_month ? assoc_day_table : assoc_hour_table, ++// curr_end, curr_start, now); ++ ++ query = xstrdup_printf( ++ "insert into \"%s_%s\" (creation_time, mod_time, id, " ++ "id_tres, time_start, alloc_secs) " ++ "select %ld, %ld, id, id_tres, " ++ "%ld, sum(alloc_secs) AS ASUM from \"%s_%s\" where " ++ "(time_start < %ld AND time_start >= %ld) " ++ "group by id, id_tres on CONFLICT (id,id_tres,time_start) do update set " ++ "mod_time=%ld, alloc_secs=EXCLUDED.ASUM;", ++ cluster_name, ++ run_month ? assoc_month_table : assoc_day_table, ++ now, now, curr_start, ++ cluster_name, ++ run_month ? assoc_day_table : assoc_hour_table, ++ curr_end, curr_start, now); ++ ++ /* We group on deleted here so if there are no entries ++ we don't get an error, just nothing is returned. ++ Else we get a bunch of NULL's ++ */ ++// xstrfmtcat(query, ++// "insert into \"%s_%s\" (creation_time, " ++// "mod_time, time_start, id_tres, count, " ++// "alloc_secs, down_secs, pdown_secs, " ++// "idle_secs, over_secs, plan_secs) " ++// "select %ld, %ld, " ++// "%ld, id_tres, @CPU:=MAX(count), " ++// "@ASUM:=SUM(alloc_secs), " ++// "@DSUM:=SUM(down_secs), " ++// "@PDSUM:=SUM(pdown_secs), " ++// "@ISUM:=SUM(idle_secs), " ++// "@OSUM:=SUM(over_secs), " ++// "@PSUM:=SUM(plan_secs) from \"%s_%s\" where " ++// "(time_start < %ld && time_start >= %ld) " ++// "group by deleted, id_tres " ++// "on duplicate key update " ++// "mod_time=%ld, count=@CPU, " ++// "alloc_secs=@ASUM, down_secs=@DSUM, " ++// "pdown_secs=@PDSUM, idle_secs=@ISUM, " ++// "over_secs=@OSUM, plan_secs=@PSUM;", ++// cluster_name, ++// run_month ? cluster_month_table : cluster_day_table, ++// now, now, curr_start, ++// cluster_name, ++// run_month ? cluster_day_table : cluster_hour_table, ++// curr_end, curr_start, now); ++ ++ xstrfmtcat(query, ++ "insert into \"%s_%s\" (creation_time, " ++ "mod_time, time_start, id_tres, count, " ++ "alloc_secs, down_secs, pdown_secs, " ++ "idle_secs, over_secs, plan_secs) " ++ "select %ld, %ld, " ++ "%ld, id_tres, MAX(count) AS CPU, " ++ "SUM(alloc_secs) AS ASUM, " ++ "SUM(down_secs) AS DSUM, " ++ "SUM(pdown_secs) AS PDSUM, " ++ "SUM(idle_secs) AS ISUM, " ++ "SUM(over_secs) AS OSUM, " ++ "SUM(plan_secs) AS PSUM from \"%s_%s\" where " ++ "(time_start < %ld AND time_start >= %ld) " ++ "group by deleted, id_tres " ++ "on conflict (id_tres,time_start) do update set " ++ "mod_time=%ld, count=exclude.CPU, " ++ "alloc_secs=exclude.ASUM, down_secs=exclude.DSUM, " ++ "pdown_secs=exclude.PDSUM, idle_secs=exclude.ISUM, " ++ "over_secs=exclude.OSUM, plan_secs=exclude.PSUM;", ++ cluster_name, ++ run_month ? cluster_month_table : cluster_day_table, ++ now, now, curr_start, ++ cluster_name, ++ run_month ? cluster_day_table : cluster_hour_table, ++ curr_end, curr_start, now); ++ if (track_wckey) { ++// xstrfmtcat(query, ++// "insert into \"%s_%s\" (creation_time, " ++// "mod_time, id, id_tres, time_start, " ++// "alloc_secs) " ++// "select %ld, %ld, " ++// "id, id_tres, %ld, @ASUM:=SUM(alloc_secs) " ++// "from \"%s_%s\" where (time_start < %ld && " ++// "time_start >= %ld) group by id, id_tres " ++// "on duplicate key update " ++// "mod_time=%ld, alloc_secs=@ASUM;", ++// cluster_name, ++// run_month ? wckey_month_table : ++// wckey_day_table, ++// now, now, curr_start, ++// cluster_name, ++// run_month ? wckey_day_table : ++// wckey_hour_table, ++// curr_end, curr_start, now); ++ ++ xstrfmtcat(query, ++ "insert into \"%s_%s\" (creation_time, " ++ "mod_time, id, id_tres, time_start, " ++ "alloc_secs) " ++ "select %ld, %ld, " ++ "id, id_tres, %ld, SUM(alloc_secs) AS ASUM " ++ "from \"%s_%s\" where (time_start < %ld AND " ++ "time_start >= %ld) group by id, id_tres " ++ "on conflict (id,id_tres,time_start) do update set " ++ "mod_time=%ld, alloc_secs=exclude.ASUM;", ++ cluster_name, ++ run_month ? wckey_month_table : ++ wckey_day_table, ++ now, now, curr_start, ++ cluster_name, ++ run_month ? wckey_day_table : ++ wckey_hour_table, ++ curr_end, curr_start, now); ++ } ++ DB_DEBUG(DB_USAGE, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add %s rollup", unit_name); ++ return SLURM_ERROR; ++ } ++ ++ curr_start = curr_end; ++ } ++ ++/* info("stop start %s", slurm_ctime2(&curr_start)); */ ++/* info("stop end %s", slurm_ctime2(&curr_end)); */ ++ ++ /* go check to see if we archive and purge */ ++ rc = _process_purge(pgsql_conn, cluster_name, archive_data, ++ run_month ? SLURMDB_PURGE_MONTHS : ++ SLURMDB_PURGE_DAYS); ++ return rc; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_rollup.h b/src/plugins/accounting_storage/pgsql/as_pgsql_rollup.h +new file mode 100755 +index 0000000..47bbd8d +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_rollup.h +@@ -0,0 +1,57 @@ ++/*****************************************************************************\ ++ * as_pgsql_rollup.h - functions for rolling up data for associations ++ * and machines from the as_pgsql storage. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * CODE-OCEC-09-009. All rights reserved. ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_ROLLUP_H ++#define _HAVE_PGSQL_ROLLUP_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_hourly_rollup(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, ++ time_t start, ++ time_t end, ++ uint16_t archive_data); ++extern int as_pgsql_nonhour_rollup(pgsql_conn_t *pgsql_conn, ++ bool run_month, ++ char *cluster_name, ++ time_t start, ++ time_t end, ++ uint16_t archive_data); ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_tres.c b/src/plugins/accounting_storage/pgsql/as_pgsql_tres.c +new file mode 100755 +index 0000000..72b13e4 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_tres.c +@@ -0,0 +1,316 @@ ++/*****************************************************************************\ ++ * as_pgsql_tres.c - functions dealing with accounts. ++ ***************************************************************************** ++ * Copyright (C) 2015 SchedMD LLC. ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_tres.h" ++#include "as_pgsql_usage.h" ++#include "src/common/xstring.h" ++ ++extern int as_pgsql_add_tres(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, List tres_list_in) ++{ ++ ListIterator itr = NULL; ++ int rc = SLURM_SUCCESS; ++ slurmdb_tres_rec_t *object = NULL; ++ char *cols = NULL, *extra = NULL, *vals = NULL, *query = NULL, ++ *tmp_extra = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ int affect_rows = 0; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) ++ return ESLURM_ACCESS_DENIED; ++ ++ if (!tres_list_in) { ++ error("as_pgsql_add_tres: Trying to add a blank list"); ++ return SLURM_ERROR; ++ } ++ ++ user_name = uid_to_string((uid_t) uid); ++ itr = list_iterator_create(tres_list_in); ++ while ((object = list_next(itr))) { ++ if (!object->type || !object->type[0]) { ++ error("We need a tres type."); ++ rc = SLURM_ERROR; ++ continue; ++ } else if ((!xstrcasecmp(object->type, "gres") || ++ !xstrcasecmp(object->type, "bb") || ++ !xstrcasecmp(object->type, "license") || ++ !xstrcasecmp(object->type, "fs") || ++ !xstrcasecmp(object->type, "ic"))) { ++ if (!object->name) { ++ error("%s type tres " ++ "need to have a name, " ++ "(i.e. Gres/GPU). You gave none", ++ object->type); ++ rc = SLURM_ERROR; ++ continue; ++ } ++ } else /* only the above have a name */ ++ xfree(object->name); ++ ++ xstrcat(cols, "creation_time, type"); ++ xstrfmtcat(vals, "%ld, '%s'", now, object->type); ++ xstrfmtcat(extra, "type='%s'", object->type); ++ if (object->name) { ++ xstrcat(cols, ", name"); ++ xstrfmtcat(vals, ", '%s'", object->name); ++ xstrfmtcat(extra, ", name='%s'", object->name); ++ } ++ ++ xstrfmtcat(query, ++ "insert into %s (%s) values (%s) " ++ "on conflict (id) do update set deleted=0," ++ "id=LASTVAL();", ++ tres_table, cols, vals); ++ ++ DB_DEBUG(DB_TRES, pgsql_conn->conn, "query\n%s", query); ++// object->id = (uint32_t)pgsql_db_insert_ret_id( ++// pgsql_conn, query); ++// xfree(query); ++// if (!object->id) { ++// error("Couldn't add tres %s%s%s", object->type, ++// object->name ? "/" : "", ++// object->name ? object->name : ""); ++// xfree(cols); ++// xfree(extra); ++// xfree(vals); ++// break; ++// } ++ SQLHSTMT stmt = NULL; ++ int rc = pgsql_db_query_stmt(pgsql_conn, query, &stmt); ++ xfree(query); ++ if(rc != SLURM_SUCCESS || !(object->id = pgsql_insert_id(stmt))){ ++ error("Couldn't add tres %s%s%s", object->type, ++ object->name ? "/" : "", ++ object->name ? object->name : ""); ++ xfree(cols); ++ xfree(extra); ++ xfree(vals); ++ pgsql_db_free_statement(&stmt); ++ break; ++ } ++ ++// affect_rows = last_affected_rows(pgsql_conn); ++ affect_rows = last_affected_rows(stmt); ++ pgsql_db_free_statement(&stmt); ++ ++ if (!affect_rows) { ++ debug2("nothing changed %d", affect_rows); ++ xfree(cols); ++ xfree(extra); ++ xfree(vals); ++ continue; ++ } ++ ++ tmp_extra = slurm_add_slash_to_quotes(extra); ++ ++ xstrfmtcat(query, ++ "insert into %s " ++ "(timestamp, action, name, actor, info, cluster) " ++ "values (%ld, %u, 'id=%d', '%s', '%s', '%s');", ++ txn_table, ++ now, DBD_ADD_TRES, object->id, user_name, ++ tmp_extra, pgsql_conn->cluster_name); ++ ++ xfree(tmp_extra); ++ xfree(cols); ++ xfree(extra); ++ xfree(vals); ++ debug4("query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add txn"); ++ } else { ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_ADD_TRES, ++ object) == SLURM_SUCCESS) ++ list_remove(itr); ++ } ++ ++ } ++ list_iterator_destroy(itr); ++ xfree(user_name); ++ ++ if (list_count(pgsql_conn->update_list)) { ++ /* We only want to update the local cache DBD or ctld */ ++ assoc_mgr_update(pgsql_conn->update_list, 0); ++ list_flush(pgsql_conn->update_list); ++ } ++ ++ return rc; ++} ++ ++extern List as_pgsql_get_tres(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_tres_cond_t *tres_cond) ++{ ++ char *query = NULL; ++ char *extra = NULL; ++ char *tmp = NULL; ++ List my_tres_list = NULL; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ int set = 0; ++ int i=0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ /* if this changes you will need to edit the corresponding enum */ ++ char *tres_req_inx[] = { ++ "id", ++ "type", ++ "name" ++ }; ++ enum { ++ SLURMDB_REQ_ID, ++ SLURMDB_REQ_TYPE, ++ SLURMDB_REQ_NAME, ++ SLURMDB_REQ_COUNT ++ }; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!tres_cond) { ++ xstrcat(extra, "where deleted=0"); ++ goto empty; ++ } ++ ++ if (tres_cond->with_deleted) ++ xstrcat(extra, "where (deleted=0 OR deleted=1)"); ++ else ++ xstrcat(extra, "where deleted=0"); ++ ++ if (tres_cond->id_list ++ && list_count(tres_cond->id_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(tres_cond->id_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "id='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (tres_cond->type_list ++ && list_count(tres_cond->type_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(tres_cond->type_list); ++ while ((object = list_next(itr))) { ++ char *slash; ++ if (set) ++ xstrcat(extra, " OR "); ++ if (!(slash = strchr(object, '/'))) ++ xstrfmtcat(extra, "type='%s'", object); ++ else { ++ /* This means we have the name ++ * attached, so split the string and ++ * handle it this way, only on this type. ++ */ ++ char *name = slash; ++ *slash = '\0'; ++ name++; ++ xstrfmtcat(extra, "(type='%s' AND name='%s')", ++ object, name); ++ } ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (tres_cond->name_list ++ && list_count(tres_cond->name_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(tres_cond->name_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++empty: ++ ++ xfree(tmp); ++ xstrfmtcat(tmp, "%s", tres_req_inx[i]); ++ for(i=1; iconn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ xfree(query); ++ ++ my_tres_list = list_create(slurmdb_destroy_tres_rec); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_tres_rec_t *tres = ++ xmalloc(sizeof(slurmdb_tres_rec_t)); ++ list_append(my_tres_list, tres); ++ ++ tres->id = slurm_atoul(row[SLURMDB_REQ_ID]); ++ if (row[SLURMDB_REQ_TYPE] && row[SLURMDB_REQ_TYPE][0]) ++ tres->type = xstrdup(row[SLURMDB_REQ_TYPE]); ++ if (row[SLURMDB_REQ_NAME] && row[SLURMDB_REQ_NAME][0]) ++ tres->name = xstrdup(row[SLURMDB_REQ_NAME]); ++ } ++ pgsql_free_result(&result); ++ ++ return my_tres_list; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_tres.h b/src/plugins/accounting_storage/pgsql/as_pgsql_tres.h +new file mode 100755 +index 0000000..00e389a +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_tres.h +@@ -0,0 +1,48 @@ ++/*****************************************************************************\ ++ * as_pgsql_tres.c - functions dealing with tres. ++ ***************************************************************************** ++ * Copyright (C) 2015 SchedMD LLC. ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_AS_PGSQL_TRES_H ++#define _HAVE_AS_PGSQL_TRES_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_add_tres(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, List tres_list_in); ++ ++extern List as_pgsql_get_tres(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_tres_cond_t *tres_cond); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_txn.c b/src/plugins/accounting_storage/pgsql/as_pgsql_txn.c +new file mode 100755 +index 0000000..f247ba8 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_txn.c +@@ -0,0 +1,459 @@ ++/*****************************************************************************\ ++ * as_pgsql_txn.c - functions dealing with transactions. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_txn.h" ++ ++extern List as_pgsql_get_txn(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_txn_cond_t *txn_cond) ++{ ++ char *query = NULL; ++ char *assoc_extra = NULL; ++ char *name_extra = NULL; ++ char *extra = NULL; ++ char *tmp = NULL; ++ List txn_list = NULL; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ int set = 0; ++ int i=0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ List use_cluster_list = NULL; ++ bool locked = 0; ++ ++ /* if this changes you will need to edit the corresponding enum */ ++ char *txn_req_inx[] = { ++ "id", ++ "timestamp", ++ "action", ++ "name", ++ "actor", ++ "info", ++ "cluster" ++ }; ++ enum { ++ TXN_REQ_ID, ++ TXN_REQ_TS, ++ TXN_REQ_ACTION, ++ TXN_REQ_NAME, ++ TXN_REQ_ACTOR, ++ TXN_REQ_INFO, ++ TXN_REQ_CLUSTER, ++ TXN_REQ_COUNT ++ }; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!txn_cond) ++ goto empty; ++ ++ /* handle query for associations first */ ++ if (txn_cond->acct_list && list_count(txn_cond->acct_list)) { ++ set = 0; ++ if (assoc_extra) ++ xstrcat(assoc_extra, " AND ("); ++ else ++ xstrcat(assoc_extra, " where ("); ++ ++ if (name_extra) ++ xstrcat(name_extra, " AND ("); ++ else ++ xstrcat(name_extra, " ("); ++ itr = list_iterator_create(txn_cond->acct_list); ++ while ((object = list_next(itr))) { ++ if (set) { ++ xstrcat(assoc_extra, " OR "); ++ xstrcat(name_extra, " OR "); ++ } ++ ++ xstrfmtcat(assoc_extra, "acct='%s'", object); ++ ++ xstrfmtcat(name_extra, "(name like '%%\\'%s\\'%%'" ++ " OR name='%s')" ++ " OR (info like '%%acct=\\'%s\\'%%')", ++ object, object, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(assoc_extra, ")"); ++ xstrcat(name_extra, ")"); ++ } ++ ++ if (txn_cond->cluster_list && list_count(txn_cond->cluster_list)) { ++ set = 0; ++ if (name_extra) ++ xstrcat(name_extra, " AND ("); ++ else ++ xstrcat(name_extra, "("); ++ ++ itr = list_iterator_create(txn_cond->cluster_list); ++ while ((object = list_next(itr))) { ++ if (set) { ++ xstrcat(name_extra, " OR "); ++ } ++ xstrfmtcat(name_extra, "(cluster='%s' OR " ++ "name like '%%\\'%s\\'%%' OR name='%s')" ++ " OR (info like '%%cluster=\\'%s\\'%%')", ++ object, object, object, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(name_extra, ")"); ++ use_cluster_list = txn_cond->cluster_list; ++ } ++ ++ if (txn_cond->user_list && list_count(txn_cond->user_list)) { ++ set = 0; ++ if (assoc_extra) ++ xstrcat(assoc_extra, " AND ("); ++ else ++ xstrcat(assoc_extra, " where ("); ++ ++ if (name_extra) ++ xstrcat(name_extra, " AND ("); ++ else ++ xstrcat(name_extra, "("); ++ ++ itr = list_iterator_create(txn_cond->user_list); ++ while ((object = list_next(itr))) { ++ if (set) { ++ xstrcat(assoc_extra, " OR "); ++ xstrcat(name_extra, " OR "); ++ } ++ xstrfmtcat(assoc_extra, "\"user\"='%s'", object); ++ ++ xstrfmtcat(name_extra, "(name like '%%\\'%s\\'%%'" ++ " OR name='%s')" ++ " OR (info like '%%\"user\"=\\'%s\\'%%')", ++ object, object, object); ++ ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(assoc_extra, ")"); ++ xstrcat(name_extra, ")"); ++ } ++ ++ if (assoc_extra) { ++ if (!locked && !use_cluster_list) { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = list_shallow_copy( ++ as_pgsql_cluster_list); ++ locked = 1; ++ } ++ ++ itr = list_iterator_create(use_cluster_list); ++ while ((object = list_next(itr))) { ++ xstrfmtcat(query, "select id_assoc from \"%s_%s\"%s", ++ object, assoc_table, assoc_extra); ++ DB_DEBUG(DB_QUERY, pgsql_conn->conn, "query\n%s", ++ query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ break; ++ } ++ xfree(query); ++ ++ if (pgsql_num_rows(result)) { ++ if (extra) ++ xstrfmtcat(extra, ++ " OR (cluster='%s' AND (", ++ object); ++ else ++ xstrfmtcat(extra, ++ " where (cluster='%s' AND (", ++ object); ++ ++ set = 0; ++ ++ while ((row = pgsql_fetch_row(result))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ ++ xstrfmtcat(extra, ++ "(name like " ++ "'%%id_assoc=%s %%' " ++ "OR name like " ++ "'%%id_assoc=%s)')", ++ row[0], row[0]); ++ set = 1; ++ } ++ xstrcat(extra, "))"); ++ } ++ pgsql_free_result(&result); ++ } ++ list_iterator_destroy(itr); ++ ++ xfree(assoc_extra); ++ } ++ ++ if (name_extra) { ++ if (extra) ++ xstrfmtcat(extra, " OR (%s)", name_extra); ++ else ++ xstrfmtcat(extra, " where (%s)", name_extra); ++ xfree(name_extra); ++ } ++ /*******************************************/ ++ ++ if (txn_cond->action_list && list_count(txn_cond->action_list)) { ++ set = 0; ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " where ("); ++ itr = list_iterator_create(txn_cond->action_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "action='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (txn_cond->actor_list && list_count(txn_cond->actor_list)) { ++ set = 0; ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " where ("); ++ itr = list_iterator_create(txn_cond->actor_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "actor='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (txn_cond->id_list && list_count(txn_cond->id_list)) { ++ set = 0; ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " where ("); ++ itr = list_iterator_create(txn_cond->id_list); ++ while ((object = list_next(itr))) { ++ char *ptr = NULL; ++ long num = strtol(object, &ptr, 10); ++ if ((num == 0) && ptr && ptr[0]) { ++ error("Invalid value for txn id (%s)", ++ object); ++ xfree(extra); ++ list_iterator_destroy(itr); ++ goto end_it; ++ } ++ ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "id=%s", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (txn_cond->info_list && list_count(txn_cond->info_list)) { ++ set = 0; ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " where ("); ++ itr = list_iterator_create(txn_cond->info_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "info like '%%%s%%'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (txn_cond->name_list && list_count(txn_cond->name_list)) { ++ set = 0; ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " where ("); ++ itr = list_iterator_create(txn_cond->name_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "name like '%%%s%%'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (txn_cond->time_start && txn_cond->time_end) { ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " where ("); ++ xstrfmtcat(extra, "timestamp < %ld AND timestamp >= %ld)", ++ txn_cond->time_end, txn_cond->time_start); ++ } else if (txn_cond->time_start) { ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " where ("); ++ xstrfmtcat(extra, "timestamp >= %ld)", txn_cond->time_start); ++ ++ } else if (txn_cond->time_end) { ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, " where ("); ++ xstrfmtcat(extra, "timestamp < %ld)", txn_cond->time_end); ++ } ++ ++ /* make sure we can get the max length out of the database ++ * when grouping the names ++ */ ++ if (txn_cond->with_assoc_info) ++ pgsql_db_query(pgsql_conn, ++// "set session group_concat_max_len=65536;"); ++ "set local array_agg_max_len = 65536;"); ++ ++empty: ++ if (!locked && !use_cluster_list) { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = list_shallow_copy(as_pgsql_cluster_list); ++ locked = 1; ++ } ++ ++ xfree(tmp); ++ xstrfmtcat(tmp, "%s", txn_req_inx[i]); ++ for(i=1; iconn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ goto end_it; ++ } ++ ++ xfree(query); ++ ++ txn_list = list_create(slurmdb_destroy_txn_rec); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_txn_rec_t *txn = xmalloc(sizeof(slurmdb_txn_rec_t)); ++ ++ list_append(txn_list, txn); ++ ++ txn->action = slurm_atoul(row[TXN_REQ_ACTION]); ++ txn->actor_name = xstrdup(row[TXN_REQ_ACTOR]); ++ txn->id = slurm_atoul(row[TXN_REQ_ID]); ++ txn->set_info = xstrdup(row[TXN_REQ_INFO]); ++ txn->timestamp = slurm_atoul(row[TXN_REQ_TS]); ++ txn->where_query = xstrdup(row[TXN_REQ_NAME]); ++ txn->clusters = xstrdup(row[TXN_REQ_CLUSTER]); ++ ++ if (txn_cond && txn_cond->with_assoc_info ++ && (txn->action == DBD_ADD_ASSOCS ++ || txn->action == DBD_MODIFY_ASSOCS ++ || txn->action == DBD_REMOVE_ASSOCS)) { ++ pgsql_res_t *result2 = NULL; ++ pgsql_row row2; ++ if (txn->clusters) { ++ query = xstrdup_printf( ++ "select " ++ "string_agg(distinct \"user\" " ++ "order by \"user\"), " ++ "string_agg(distinct acct " ++ "order by acct) " ++ "from \"%s_%s\" where %s", ++ txn->clusters, assoc_table, ++ row[TXN_REQ_NAME]); ++ debug4("%d(%s:%d) query\n%s", pgsql_conn->conn, ++ THIS_FILE, __LINE__, query); ++ result2 = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result2), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result2); ++ continue; ++ } ++ xfree(query); ++ ++ if ((row2 = pgsql_fetch_row(result2))) { ++ if (row2[0] && row2[0][0]) ++ txn->users = xstrdup(row2[0]); ++ if (row2[1] && row2[1][0]) ++ txn->accts = xstrdup(row2[1]); ++ } ++ pgsql_free_result(&result2); ++ } else { ++ error("We can't handle associations " ++ "from action %s yet.", ++ slurmdbd_msg_type_2_str(txn->action, 1)); ++ } ++ } ++ } ++ pgsql_free_result(&result); ++ ++end_it: ++ if (locked) { ++ FREE_NULL_LIST(use_cluster_list); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ ++ return txn_list; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_txn.h b/src/plugins/accounting_storage/pgsql/as_pgsql_txn.h +new file mode 100755 +index 0000000..620053c +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_txn.h +@@ -0,0 +1,47 @@ ++/*****************************************************************************\ ++ * as_pgsql_txn.h - functions dealing with transactions. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_TXN_H ++#define _HAVE_PGSQL_TXN_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern List as_pgsql_get_txn(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_txn_cond_t *txn_cond); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_usage.c b/src/plugins/accounting_storage/pgsql/as_pgsql_usage.c +new file mode 100755 +index 0000000..7f4d472 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_usage.c +@@ -0,0 +1,976 @@ ++/*****************************************************************************\ ++ * as_pgsql_usage.c - functions dealing with usage. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_cluster.h" ++#include "as_pgsql_usage.h" ++#include "as_pgsql_rollup.h" ++#include "src/common/macros.h" ++#include "src/common/slurm_time.h" ++ ++time_t global_last_rollup = 0; ++pthread_mutex_t rollup_lock = PTHREAD_MUTEX_INITIALIZER; ++pthread_mutex_t usage_rollup_lock = PTHREAD_MUTEX_INITIALIZER; ++ ++typedef struct { ++ uint16_t archive_data; ++ char *cluster_name; ++ pgsql_conn_t *pgsql_conn; ++ int *rc; ++ int *rolledup; ++ pthread_mutex_t *rolledup_lock; ++ pthread_cond_t *rolledup_cond; ++ slurmdb_rollup_stats_t *rollup_stats; ++ time_t sent_end; ++ time_t sent_start; ++} local_rollup_t; ++ ++static void *_cluster_rollup_usage(void *arg) ++{ ++ local_rollup_t *local_rollup = (local_rollup_t *)arg; ++ int i, rc = SLURM_SUCCESS; ++ char timer_str[128]; ++ pgsql_conn_t pgsql_conn; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *query = NULL; ++ struct tm start_tm; ++ struct tm end_tm; ++ time_t my_time = local_rollup->sent_end; ++ time_t last_hour = local_rollup->sent_start; ++ time_t last_day = local_rollup->sent_start; ++ time_t last_month = local_rollup->sent_start; ++ slurmdb_rollup_stats_t *rollup_stats = local_rollup->rollup_stats; ++ time_t hour_start; ++ time_t hour_end; ++ time_t day_start; ++ time_t day_end; ++ time_t month_start; ++ time_t month_end; ++ DEF_TIMERS; ++ ++ char *update_req_inx[] = { ++ "hourly_rollup", ++ "daily_rollup", ++ "monthly_rollup" ++ }; ++ ++ /* ++ * rollup_stats is unique per thread so should be ok to alter ++ * unprotected. ++ */ ++ xassert(rollup_stats); ++ ++ memset(&pgsql_conn, 0, sizeof(pgsql_conn_t)); ++ pgsql_conn.rollback = 1; ++ pgsql_conn.conn = local_rollup->pgsql_conn->conn; ++ slurm_mutex_init(&pgsql_conn.lock); ++ ++ /* Each thread needs it's own connection we can't use the one ++ * sent from the parent thread. */ ++ rc = check_connection(&pgsql_conn); ++ ++ if (rc != SLURM_SUCCESS) ++ goto end_it; ++ ++ if (!local_rollup->sent_start) { ++ char *tmp = NULL, *sep = ""; ++ for (i = 0; i < DBD_ROLLUP_COUNT; i++) { ++ xstrfmtcat(tmp, "%s%s", sep, update_req_inx[i]); ++ sep = ", "; ++ } ++ query = xstrdup_printf("select %s from \"%s_%s\"", ++ tmp, local_rollup->cluster_name, ++ last_ran_table); ++ xfree(tmp); ++ ++ debug4("%d(%s:%d) query\n%s", pgsql_conn.conn, ++ THIS_FILE, __LINE__, query); ++ result = pgsql_db_query_ret(&pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ rc = SLURM_ERROR; ++ pgsql_free_result(&result); ++ goto end_it; ++ } ++ ++ xfree(query); ++ row = pgsql_fetch_row(result); ++ if (row) { ++ last_hour = slurm_atoul(row[DBD_ROLLUP_HOUR]); ++ last_day = slurm_atoul(row[DBD_ROLLUP_DAY]); ++ last_month = slurm_atoul(row[DBD_ROLLUP_MONTH]); ++ ++ /* only record timestamps if db provided */ ++ rollup_stats->timestamp[DBD_ROLLUP_HOUR] = last_hour; ++ rollup_stats->timestamp[DBD_ROLLUP_DAY] = last_day; ++ rollup_stats->timestamp[DBD_ROLLUP_MONTH] = last_month; ++ pgsql_free_result(&result); ++ } else { ++ time_t now = time(NULL); ++ time_t lowest = now; ++ ++ pgsql_free_result(&result); ++ ++ query = xstrdup_printf( ++ "select time_start from \"%s_%s\" " ++ "where node_name='' order by " ++ "time_start asc limit 1;", ++ local_rollup->cluster_name, event_table); ++ DB_DEBUG(DB_USAGE, pgsql_conn.conn, "query\n%s", query); ++ result = pgsql_db_query_ret(&pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ rc = SLURM_ERROR; ++ pgsql_free_result(&result); ++ goto end_it; ++ } ++ xfree(query); ++ if ((row = pgsql_fetch_row(result))) { ++ time_t check = slurm_atoul(row[0]); ++ if (check < lowest) ++ lowest = check; ++ } ++ pgsql_free_result(&result); ++ ++ /* If we don't have any events like adding a ++ * cluster this will not work correctly, so we ++ * will insert now as a starting point. ++ */ ++ ++ query = xstrdup_printf( ++ "insert into \"%s_%s\" " ++ "(hourly_rollup, daily_rollup, monthly_rollup) " ++ "values (%ld, %ld, %ld);", ++ local_rollup->cluster_name, last_ran_table, ++ lowest, lowest, lowest); ++ ++ DB_DEBUG(DB_USAGE, pgsql_conn.conn, "query\n%s", query); ++ rc = pgsql_db_query(&pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ ++ if (lowest == now) { ++ debug("Cluster %s not registered, " ++ "not doing rollup", ++ local_rollup->cluster_name); ++ rc = SLURM_SUCCESS; ++ goto end_it; ++ } ++ ++ last_hour = last_day = last_month = lowest; ++ } ++ } ++ ++ if (!my_time) ++ my_time = time(NULL); ++ ++ /* test month gap */ ++/* last_hour = 1212299999; */ ++/* last_day = 1212217200; */ ++/* last_month = 1212217200; */ ++/* my_time = 1212307200; */ ++ ++/* last_hour = 1211475599; */ ++/* last_day = 1211475599; */ ++/* last_month = 1211475599; */ ++ ++// last_hour = 1211403599; ++ // last_hour = 1206946800; ++// last_day = 1207033199; ++// last_day = 1197033199; ++// last_month = 1204358399; ++ ++ if (!localtime_r(&last_hour, &start_tm)) { ++ error("Couldn't get localtime from hour start %ld", last_hour); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ ++ if (!localtime_r(&my_time, &end_tm)) { ++ error("Couldn't get localtime from hour end %ld", my_time); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ ++ /* Below and anywhere in a rollup plugin when dealing with ++ * epoch times we need to set the tm_isdst = -1 so we don't ++ * have to worry about the time changes. Not setting it to -1 ++ * will cause problems in the day and month with the date change. ++ * ++ * NOTE: slurm_mktime() implementation already sets it to -1 so ++ * there's no need to manually set it beforehand. ++ */ ++ ++ start_tm.tm_sec = 0; ++ start_tm.tm_min = 0; ++ hour_start = slurm_mktime(&start_tm); ++ ++ end_tm.tm_sec = 0; ++ end_tm.tm_min = 0; ++ hour_end = slurm_mktime(&end_tm); ++ ++/* info("hour start %s", slurm_ctime2(&hour_start)); */ ++/* info("hour end %s", slurm_ctime2(&hour_end)); */ ++/* info("diff is %d", hour_end-hour_start); */ ++ ++ slurm_mutex_lock(&rollup_lock); ++ global_last_rollup = hour_end; ++ slurm_mutex_unlock(&rollup_lock); ++ ++ /* set up the day period */ ++ if (!localtime_r(&last_day, &start_tm)) { ++ error("Couldn't get localtime from day %ld", last_day); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ ++ start_tm.tm_sec = 0; ++ start_tm.tm_min = 0; ++ start_tm.tm_hour = 0; ++ day_start = slurm_mktime(&start_tm); ++ ++ end_tm.tm_hour = 0; ++ day_end = slurm_mktime(&end_tm); ++ ++/* info("day start %s", slurm_ctime2(&day_start)); */ ++/* info("day end %s", slurm_ctime2(&day_end)); */ ++/* info("diff is %d", day_end-day_start); */ ++ ++ /* set up the month period */ ++ if (!localtime_r(&last_month, &start_tm)) { ++ error("Couldn't get localtime from month %ld", last_month); ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ ++ start_tm.tm_sec = 0; ++ start_tm.tm_min = 0; ++ start_tm.tm_hour = 0; ++ start_tm.tm_mday = 1; ++ month_start = slurm_mktime(&start_tm); ++ ++ end_tm.tm_sec = 0; ++ end_tm.tm_min = 0; ++ end_tm.tm_hour = 0; ++ end_tm.tm_mday = 1; ++ month_end = slurm_mktime(&end_tm); ++ ++/* info("month start %s", slurm_ctime2(&month_start)); */ ++/* info("month end %s", slurm_ctime2(&month_end)); */ ++/* info("diff is %d", month_end-month_start); */ ++ ++ if ((hour_end - hour_start) > 0) { ++ START_TIMER; ++ rc = as_pgsql_hourly_rollup(&pgsql_conn, ++ local_rollup->cluster_name, ++ hour_start, ++ hour_end, ++ local_rollup->archive_data); ++ snprintf(timer_str, sizeof(timer_str), ++ "hourly_rollup for %s", local_rollup->cluster_name); ++ END_TIMER3(timer_str, 5000000); ++ rollup_stats->time_total[DBD_ROLLUP_HOUR] += DELTA_TIMER; ++ rollup_stats->timestamp[DBD_ROLLUP_HOUR] = hour_end; ++ if (rc != SLURM_SUCCESS) ++ goto end_it; ++ } ++ ++ if ((day_end - day_start) > 0) { ++ START_TIMER; ++ rc = as_pgsql_nonhour_rollup(&pgsql_conn, 0, ++ local_rollup->cluster_name, ++ day_start, ++ day_end, ++ local_rollup->archive_data); ++ snprintf(timer_str, sizeof(timer_str), ++ "daily_rollup for %s", local_rollup->cluster_name); ++ END_TIMER3(timer_str, 5000000); ++ rollup_stats->time_total[DBD_ROLLUP_DAY] += DELTA_TIMER; ++ rollup_stats->timestamp[DBD_ROLLUP_DAY] = day_end; ++ if (rc != SLURM_SUCCESS) ++ goto end_it; ++ } ++ ++ if ((month_end - month_start) > 0) { ++ START_TIMER; ++ rc = as_pgsql_nonhour_rollup(&pgsql_conn, 1, ++ local_rollup->cluster_name, ++ month_start, ++ month_end, ++ local_rollup->archive_data); ++ snprintf(timer_str, sizeof(timer_str), ++ "monthly_rollup for %s", local_rollup->cluster_name); ++ END_TIMER3(timer_str, 5000000); ++ rollup_stats->time_total[DBD_ROLLUP_MONTH] += DELTA_TIMER; ++ rollup_stats->timestamp[DBD_ROLLUP_MONTH] = month_end; ++ if (rc != SLURM_SUCCESS) ++ goto end_it; ++ } ++ ++ if ((hour_end - hour_start) > 0) { ++ /* If we have a sent_end do not update the last_run_table */ ++ if (!local_rollup->sent_end) ++ query = xstrdup_printf( ++ "update \"%s_%s\" set hourly_rollup=%ld", ++ local_rollup->cluster_name, ++ last_ran_table, hour_end); ++ } else ++ debug2("No need to roll cluster %s this hour %ld <= %ld", ++ local_rollup->cluster_name, hour_end, hour_start); ++ ++ if ((day_end - day_start) > 0) { ++ if (query && !local_rollup->sent_end) ++ xstrfmtcat(query, ", daily_rollup=%ld", day_end); ++ else if (!local_rollup->sent_end) ++ query = xstrdup_printf( ++ "update \"%s_%s\" set daily_rollup=%ld", ++ local_rollup->cluster_name, ++ last_ran_table, day_end); ++ } else ++ debug2("No need to roll cluster %s this day %ld <= %ld", ++ local_rollup->cluster_name, day_end, day_start); ++ ++ if ((month_end - month_start) > 0) { ++ if (query && !local_rollup->sent_end) ++ xstrfmtcat(query, ", monthly_rollup=%ld", month_end); ++ else if (!local_rollup->sent_end) ++ query = xstrdup_printf( ++ "update \"%s_%s\" set monthly_rollup=%ld", ++ local_rollup->cluster_name, ++ last_ran_table, month_end); ++ } else ++ debug2("No need to roll cluster %s this month %ld <= %ld", ++ local_rollup->cluster_name, month_end, month_start); ++ ++ if (query) { ++ DB_DEBUG(DB_USAGE, pgsql_conn.conn, "query\n%s", query); ++ rc = pgsql_db_query(&pgsql_conn, query); ++ xfree(query); ++ } ++end_it: ++ if (rc == SLURM_SUCCESS) { ++ if (pgsql_db_commit(&pgsql_conn)) { ++ error("Couldn't commit rollup of cluster %s", ++ local_rollup->cluster_name); ++ rc = SLURM_ERROR; ++ } ++ } else { ++ error("Cluster %s rollup failed", local_rollup->cluster_name); ++ if (pgsql_db_rollback(&pgsql_conn)) ++ error("rollback failed"); ++ } ++ ++ pgsql_db_close_db_connection(&pgsql_conn); ++ slurm_mutex_destroy(&pgsql_conn.lock); ++ ++ slurm_mutex_lock(local_rollup->rolledup_lock); ++ (*local_rollup->rolledup)++; ++ ++ if ((rc != SLURM_SUCCESS) && ((*local_rollup->rc) == SLURM_SUCCESS)) ++ (*local_rollup->rc) = rc; ++ slurm_cond_signal(local_rollup->rolledup_cond); ++ slurm_mutex_unlock(local_rollup->rolledup_lock); ++ xfree(local_rollup); ++ ++ return NULL; ++} ++ ++/* assoc_mgr locks need to be unlocked before coming here */ ++static int _get_object_usage(pgsql_conn_t *pgsql_conn, ++ slurmdbd_msg_type_t type, char *my_usage_table, ++ char *cluster_name, char *id_str, ++ time_t start, time_t end, List *usage_list) ++{ ++ char *tmp = NULL; ++ int i = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *query = NULL; ++ assoc_mgr_lock_t locks = { NO_LOCK, NO_LOCK, NO_LOCK, NO_LOCK, ++ READ_LOCK, NO_LOCK, NO_LOCK }; ++ ++ char *usage_req_inx[] = { ++ "t3.id_assoc", ++ "t1.id_tres", ++ "t1.time_start", ++ "t1.alloc_secs", ++ }; ++ enum { ++ USAGE_ID, ++ USAGE_TRES, ++ USAGE_START, ++ USAGE_ALLOC, ++ USAGE_COUNT ++ }; ++ ++ if (type == DBD_GET_WCKEY_USAGE) ++ usage_req_inx[0] = "t1.id"; ++ ++ xstrfmtcat(tmp, "%s", usage_req_inx[i]); ++ for (i=1; i= %ld) " ++ "AND t1.id=t2.id_assoc AND (%s) AND " ++ "t2.lft between t3.lft and t3.rgt " ++ "order by t3.id_assoc, time_start;", ++ tmp, cluster_name, my_usage_table, ++ cluster_name, assoc_table, cluster_name, assoc_table, ++ end, start, id_str); ++ break; ++ case DBD_GET_WCKEY_USAGE: ++ query = xstrdup_printf( ++ "select %s from \"%s_%s\" as t1 " ++ "where (time_start < %ld AND time_start >= %ld) " ++ "AND (%s) order by id, time_start;", ++ tmp, cluster_name, my_usage_table, end, start, id_str); ++ break; ++ default: ++ error("Unknown usage type %d", type); ++ xfree(tmp); ++ return SLURM_ERROR; ++ break; ++ } ++ xfree(tmp); ++ ++ DB_DEBUG(DB_USAGE, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ if (!(*usage_list)) ++ (*usage_list) = list_create(slurmdb_destroy_accounting_rec); ++ ++ assoc_mgr_lock(&locks); ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_tres_rec_t *tres_rec; ++ slurmdb_accounting_rec_t *accounting_rec = ++ xmalloc(sizeof(slurmdb_accounting_rec_t)); ++ ++ accounting_rec->tres_rec.id = slurm_atoul(row[USAGE_TRES]); ++ if ((tres_rec = list_find_first( ++ assoc_mgr_tres_list, slurmdb_find_tres_in_list, ++ &accounting_rec->tres_rec.id))) { ++ accounting_rec->tres_rec.name = ++ xstrdup(tres_rec->name); ++ accounting_rec->tres_rec.type = ++ xstrdup(tres_rec->type); ++ } ++ ++ accounting_rec->id = slurm_atoul(row[USAGE_ID]); ++ accounting_rec->period_start = slurm_atoul(row[USAGE_START]); ++ accounting_rec->alloc_secs = slurm_atoull(row[USAGE_ALLOC]); ++ ++ list_append(*usage_list, accounting_rec); ++ } ++ assoc_mgr_unlock(&locks); ++ ++ pgsql_free_result(&result); ++ ++ return SLURM_SUCCESS; ++} ++ ++/* assoc_mgr locks need to unlocked before you get here */ ++static int _get_cluster_usage(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_cluster_rec_t *cluster_rec, ++ slurmdbd_msg_type_t type, ++ time_t start, time_t end) ++{ ++ int rc = SLURM_SUCCESS; ++ int i=0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *tmp = NULL; ++ char *my_usage_table = cluster_day_table; ++ char *query = NULL; ++ assoc_mgr_lock_t locks = { NO_LOCK, NO_LOCK, NO_LOCK, NO_LOCK, ++ READ_LOCK, NO_LOCK, NO_LOCK }; ++ char *cluster_req_inx[] = { ++ "id_tres", ++ "alloc_secs", ++ "down_secs", ++ "pdown_secs", ++ "idle_secs", ++ "plan_secs", ++ "over_secs", ++ "count", ++ "time_start", ++ }; ++ ++ enum { ++ CLUSTER_TRES, ++ CLUSTER_ACPU, ++ CLUSTER_DCPU, ++ CLUSTER_PDCPU, ++ CLUSTER_ICPU, ++ CLUSTER_PCPU, ++ CLUSTER_OCPU, ++ CLUSTER_CNT, ++ CLUSTER_START, ++ CLUSTER_COUNT ++ }; ++ ++ if (!cluster_rec->name || !cluster_rec->name[0]) { ++ error("We need a cluster name to set data for"); ++ return SLURM_ERROR; ++ } ++ ++ if (set_usage_information(&my_usage_table, type, &start, &end) ++ != SLURM_SUCCESS) { ++ return SLURM_ERROR; ++ } ++ ++ xfree(tmp); ++ i=0; ++ xstrfmtcat(tmp, "%s", cluster_req_inx[i]); ++ for(i=1; i= %ld)", ++ tmp, cluster_rec->name, my_usage_table, end, start); ++ ++ xfree(tmp); ++ DB_DEBUG(DB_USAGE, pgsql_conn->conn, "query\n%s", query); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ if (!cluster_rec->accounting_list) ++ cluster_rec->accounting_list = ++ list_create(slurmdb_destroy_cluster_accounting_rec); ++ ++ assoc_mgr_lock(&locks); ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_tres_rec_t *tres_rec; ++ slurmdb_cluster_accounting_rec_t *accounting_rec = ++ xmalloc(sizeof(slurmdb_cluster_accounting_rec_t)); ++ ++ accounting_rec->tres_rec.id = slurm_atoul(row[CLUSTER_TRES]); ++ accounting_rec->tres_rec.count = slurm_atoul(row[CLUSTER_CNT]); ++ if ((tres_rec = list_find_first( ++ assoc_mgr_tres_list, slurmdb_find_tres_in_list, ++ &accounting_rec->tres_rec.id))) { ++ accounting_rec->tres_rec.name = ++ xstrdup(tres_rec->name); ++ accounting_rec->tres_rec.type = ++ xstrdup(tres_rec->type); ++ } ++ ++ accounting_rec->alloc_secs = slurm_atoull(row[CLUSTER_ACPU]); ++ accounting_rec->down_secs = slurm_atoull(row[CLUSTER_DCPU]); ++ accounting_rec->pdown_secs = slurm_atoull(row[CLUSTER_PDCPU]); ++ accounting_rec->idle_secs = slurm_atoull(row[CLUSTER_ICPU]); ++ accounting_rec->over_secs = slurm_atoull(row[CLUSTER_OCPU]); ++ accounting_rec->plan_secs = slurm_atoull(row[CLUSTER_PCPU]); ++ accounting_rec->period_start = slurm_atoul(row[CLUSTER_START]); ++ list_append(cluster_rec->accounting_list, accounting_rec); ++ } ++ assoc_mgr_unlock(&locks); ++ ++ pgsql_free_result(&result); ++ return rc; ++} ++ ++/* checks should already be done before this to see if this is a valid ++ user or not. The assoc_mgr locks should be unlocked before coming here. ++*/ ++extern int get_usage_for_list(pgsql_conn_t *pgsql_conn, ++ slurmdbd_msg_type_t type, List object_list, ++ char *cluster_name, time_t start, time_t end) ++{ ++ int rc = SLURM_SUCCESS; ++ char *my_usage_table = NULL; ++ List usage_list = NULL; ++ char *id_str = NULL, *name_char = NULL; ++ ListIterator itr = NULL, u_itr = NULL; ++ void *object = NULL; ++ slurmdb_assoc_rec_t *assoc = NULL; ++ slurmdb_wckey_rec_t *wckey = NULL; ++ slurmdb_accounting_rec_t *accounting_rec = NULL; ++ ++ if (!object_list) { ++ error("We need an object to set data for getting usage"); ++ return SLURM_ERROR; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ switch (type) { ++ case DBD_GET_ASSOC_USAGE: ++ name_char = "t3.id_assoc"; ++ itr = list_iterator_create(object_list); ++ while ((assoc = list_next(itr))) { ++ if (id_str) ++ xstrfmtcat(id_str, ",%u", assoc->id); ++ else ++ xstrfmtcat(id_str, "%s in (%u", ++ name_char, assoc->id); ++ } ++ list_iterator_destroy(itr); ++ my_usage_table = assoc_day_table; ++ break; ++ case DBD_GET_WCKEY_USAGE: ++ name_char = "id"; ++ itr = list_iterator_create(object_list); ++ while ((wckey = list_next(itr))) { ++ if (id_str) ++ xstrfmtcat(id_str, ",%u", wckey->id); ++ else ++ xstrfmtcat(id_str, "%s in (%u", ++ name_char, wckey->id); ++ } ++ list_iterator_destroy(itr); ++ my_usage_table = wckey_day_table; ++ break; ++ default: ++ error("Unknown usage type %d", type); ++ return SLURM_ERROR; ++ break; ++ } ++ ++ if (id_str) ++ xstrcat(id_str, ")"); ++ ++ if (set_usage_information(&my_usage_table, type, &start, &end) ++ != SLURM_SUCCESS) { ++ xfree(id_str); ++ return SLURM_ERROR; ++ } ++ ++ if (_get_object_usage(pgsql_conn, type, my_usage_table, cluster_name, ++ id_str, start, end, &usage_list) ++ != SLURM_SUCCESS) { ++ xfree(id_str); ++ return SLURM_ERROR; ++ } ++ ++ xfree(id_str); ++ ++ if (!usage_list) { ++ error("No usage given back? This should never happen"); ++ return SLURM_ERROR; ++ } ++ ++ u_itr = list_iterator_create(usage_list); ++ itr = list_iterator_create(object_list); ++ while ((object = list_next(itr))) { ++ int found = 0; ++ int id = 0; ++ List acct_list = NULL; ++ ++ switch (type) { ++ case DBD_GET_ASSOC_USAGE: ++ assoc = (slurmdb_assoc_rec_t *)object; ++ if (!assoc->accounting_list) ++ assoc->accounting_list = list_create( ++ slurmdb_destroy_accounting_rec); ++ acct_list = assoc->accounting_list; ++ id = assoc->id; ++ break; ++ case DBD_GET_WCKEY_USAGE: ++ wckey = (slurmdb_wckey_rec_t *)object; ++ if (!wckey->accounting_list) ++ wckey->accounting_list = list_create( ++ slurmdb_destroy_accounting_rec); ++ acct_list = wckey->accounting_list; ++ id = wckey->id; ++ break; ++ default: ++ continue; ++ break; ++ } ++ ++ while ((accounting_rec = list_next(u_itr))) { ++ if (id == accounting_rec->id) { ++ list_append(acct_list, accounting_rec); ++ list_remove(u_itr); ++ found = 1; ++ } else if (found) { ++ /* here we know the ++ list is in id order so ++ if the next record ++ isn't the correct id ++ just continue since ++ there is no reason to ++ go through the rest of ++ the list when we know ++ it isn't going to be ++ the correct id */ ++ break; ++ } ++ } ++ list_iterator_reset(u_itr); ++ } ++ list_iterator_destroy(itr); ++ list_iterator_destroy(u_itr); ++ ++ if (list_count(usage_list)) ++ error("we have %d records not added " ++ "to the association list", ++ list_count(usage_list)); ++ FREE_NULL_LIST(usage_list); ++ ++ return rc; ++} ++ ++/* The assoc_mgr locks should be unlocked before coming here. */ ++extern int as_pgsql_get_usage(pgsql_conn_t *pgsql_conn, uid_t uid, ++ void *in, slurmdbd_msg_type_t type, ++ time_t start, time_t end) ++{ ++ int rc = SLURM_SUCCESS; ++ int is_admin=1; ++ char *my_usage_table = NULL; ++ slurmdb_assoc_rec_t *slurmdb_assoc = in; ++ slurmdb_wckey_rec_t *slurmdb_wckey = in; ++ char *username = NULL; ++ List *my_list = NULL; ++ char *cluster_name = NULL; ++ char *id_str = NULL; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ switch (type) { ++ case DBD_GET_ASSOC_USAGE: ++ if (!slurmdb_assoc->id) { ++ error("We need an id to set data for getting usage"); ++ return SLURM_ERROR; ++ } ++ id_str = xstrdup_printf("t3.id_assoc=%u", slurmdb_assoc->id); ++ cluster_name = slurmdb_assoc->cluster; ++ username = slurmdb_assoc->user; ++ my_list = &slurmdb_assoc->accounting_list; ++ my_usage_table = assoc_day_table; ++ break; ++ case DBD_GET_WCKEY_USAGE: ++ if (!slurmdb_wckey->id) { ++ error("We need an id to set data for getting usage"); ++ return SLURM_ERROR; ++ } ++ id_str = xstrdup_printf("id=%d", slurmdb_wckey->id); ++ cluster_name = slurmdb_wckey->cluster; ++ username = slurmdb_wckey->user; ++ my_list = &slurmdb_wckey->accounting_list; ++ my_usage_table = wckey_day_table; ++ break; ++ case DBD_GET_CLUSTER_USAGE: ++ rc = _get_cluster_usage(pgsql_conn, uid, in, ++ type, start, end); ++ return rc; ++ break; ++ default: ++ error("Unknown usage type %d", type); ++ return SLURM_ERROR; ++ break; ++ } ++ ++ if (!cluster_name) { ++ error("We need a cluster_name to set data for getting usage"); ++ xfree(id_str); ++ return SLURM_ERROR; ++ } ++ ++ if (slurm_conf.private_data & PRIVATE_DATA_USAGE) { ++ if (!(is_admin = is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { ++ ListIterator itr = NULL; ++ slurmdb_coord_rec_t *coord = NULL; ++ slurmdb_user_rec_t user; ++ bool is_coord; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ is_coord = is_user_any_coord(pgsql_conn, &user); ++ ++ if (username && !xstrcmp(username, user.name)) ++ goto is_user; ++ ++ if (type != DBD_GET_ASSOC_USAGE) ++ goto bad_user; ++ ++ if (!slurmdb_assoc->acct) { ++ debug("No account name given " ++ "in association."); ++ goto bad_user; ++ } ++ ++ if (!is_coord) { ++ debug4("This user is not a coordinator."); ++ goto bad_user; ++ } ++ ++ /* Existance of user.coord_accts is checked in ++ is_user_any_coord. ++ */ ++ itr = list_iterator_create(user.coord_accts); ++ while ((coord = list_next(itr))) ++ if (!xstrcasecmp(coord->name, ++ slurmdb_assoc->acct)) ++ break; ++ list_iterator_destroy(itr); ++ ++ if (coord) ++ goto is_user; ++ ++ bad_user: ++ errno = ESLURM_ACCESS_DENIED; ++ xfree(id_str); ++ return SLURM_ERROR; ++ } ++ } ++is_user: ++ ++ if (set_usage_information(&my_usage_table, type, &start, &end) ++ != SLURM_SUCCESS) { ++ xfree(id_str); ++ return SLURM_ERROR; ++ } ++ ++ _get_object_usage(pgsql_conn, type, my_usage_table, cluster_name, ++ id_str, start, end, my_list); ++ xfree(id_str); ++ ++ return rc; ++} ++ ++extern int as_pgsql_roll_usage(pgsql_conn_t *pgsql_conn, time_t sent_start, ++ time_t sent_end, uint16_t archive_data, ++ List *rollup_stats_list_in) ++{ ++ int rc = SLURM_SUCCESS; ++ int rolledup = 0; ++ int roll_started = 0; ++ char *cluster_name = NULL; ++ ListIterator itr; ++ pthread_mutex_t rolledup_lock = PTHREAD_MUTEX_INITIALIZER; ++ pthread_cond_t rolledup_cond; ++ //DEF_TIMERS; ++ ++ xassert(rollup_stats_list_in); ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ slurm_mutex_lock(&usage_rollup_lock); ++ ++ slurm_mutex_init(&rolledup_lock); ++ slurm_cond_init(&rolledup_cond, NULL); ++ ++ //START_TIMER; ++ xassert(!*rollup_stats_list_in); ++ *rollup_stats_list_in = list_create(slurmdb_destroy_rollup_stats); ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ itr = list_iterator_create(as_pgsql_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ local_rollup_t *local_rollup = xmalloc(sizeof(local_rollup_t)); ++ local_rollup->archive_data = archive_data; ++ local_rollup->cluster_name = cluster_name; ++ ++ local_rollup->pgsql_conn = pgsql_conn; ++ local_rollup->rc = &rc; ++ local_rollup->rolledup = &rolledup; ++ local_rollup->rolledup_lock = &rolledup_lock; ++ local_rollup->rolledup_cond = &rolledup_cond; ++ ++ local_rollup->sent_end = sent_end; ++ local_rollup->sent_start = sent_start; ++ ++ local_rollup->rollup_stats = ++ xmalloc(sizeof(slurmdb_rollup_stats_t)); ++ local_rollup->rollup_stats->cluster_name = ++ xstrdup(cluster_name); ++ list_append(*rollup_stats_list_in, local_rollup->rollup_stats); ++ /* _cluster_rollup_usage is responsible for freeing ++ this local_rollup */ ++ /* If you have many jobs in your system the ++ * _cluster_rollup_usage call takes up a bunch of time ++ * and all the while the as_pgsql_cluster_list_lock is ++ * locked. If a slurmctld is starting up while this ++ * is locked it will hang waiting to get information ++ * from the DBD. So threading this makes a lot of ++ * sense. While it only buys a very small victory in ++ * terms of speed, having the ++ * as_pgsql_cluster_list_lock lock unlock in a timely ++ * fashion buys a bunch on systems with lots ++ * (millions) of jobs. ++ */ ++ slurm_thread_create_detached(NULL, _cluster_rollup_usage, ++ local_rollup); ++ roll_started++; ++ } ++ slurm_mutex_lock(&rolledup_lock); ++ list_iterator_destroy(itr); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ ++ while (rolledup < roll_started) { ++ slurm_cond_wait(&rolledup_cond, &rolledup_lock); ++ debug2("Got %d of %d rolled up", rolledup, roll_started); ++ } ++ slurm_mutex_unlock(&rolledup_lock); ++ debug2("Everything rolled up"); ++ slurm_mutex_destroy(&rolledup_lock); ++ slurm_cond_destroy(&rolledup_cond); ++ /* END_TIMER; */ ++ /* info("total time was %s", TIME_STR); */ ++ ++ slurm_mutex_unlock(&usage_rollup_lock); ++ ++ return rc; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_usage.h b/src/plugins/accounting_storage/pgsql/as_pgsql_usage.h +new file mode 100755 +index 0000000..4f12d2f +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_usage.h +@@ -0,0 +1,60 @@ ++/*****************************************************************************\ ++ * as_pgsql_usage.h - functions dealing with usage. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Copyright (C) 2010-2016 SchedMD LLC. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_USAGE_H ++#define _HAVE_PGSQL_USAGE_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern time_t global_last_rollup; ++extern pthread_mutex_t rollup_lock; ++extern pthread_mutex_t usage_rollup_lock; ++ ++extern int get_usage_for_list(pgsql_conn_t *pgsql_conn, ++ slurmdbd_msg_type_t type, List object_list, ++ char *cluster_name, time_t start, time_t end); ++extern int as_pgsql_get_usage(pgsql_conn_t *pgsql_conn, uid_t uid, ++ void *in, slurmdbd_msg_type_t type, ++ time_t start, time_t end); ++extern int as_pgsql_roll_usage(pgsql_conn_t *pgsql_conn, ++ time_t sent_start, time_t sent_end, ++ uint16_t archive_data, ++ List *rollup_stats_list_in); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_user.c b/src/plugins/accounting_storage/pgsql/as_pgsql_user.c +new file mode 100755 +index 0000000..f1d41b0 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_user.c +@@ -0,0 +1,1530 @@ ++/*****************************************************************************\ ++ * as_pgsql_user.c - functions dealing with users and coordinators. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_assoc.h" ++#include "as_pgsql_user.h" ++#include "as_pgsql_wckey.h" ++ ++static int _change_user_name(pgsql_conn_t *pgsql_conn, slurmdb_user_rec_t *user) ++{ ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ ListIterator itr = NULL; ++ char *cluster_name = NULL; ++ ++ xassert(user->old_name); ++ xassert(user->name); ++ ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ itr = list_iterator_create(as_pgsql_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ // Change assoc_tables ++ xstrfmtcat(query, "update \"%s_%s\" set \"user\"='%s' " ++ "where \"user\"='%s';", cluster_name, assoc_table, ++ user->name, user->old_name); ++ // Change wckey_tables ++ xstrfmtcat(query, "update \"%s_%s\" set \"user\"='%s' " ++ "where \"user\"='%s';", cluster_name, wckey_table, ++ user->name, user->old_name); ++ } ++ list_iterator_destroy(itr); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ // Change coord_tables ++ xstrfmtcat(query, "update %s set \"user\"='%s' where \"user\"='%s';", ++ acct_coord_table, user->name, user->old_name); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ ++ if (rc != SLURM_SUCCESS) ++ reset_pgsql_conn(pgsql_conn); ++ ++ return rc; ++} ++ ++static List _get_other_user_names_to_mod(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_user_cond_t *user_cond) ++{ ++ List tmp_list = NULL; ++ List ret_list = NULL; ++ ListIterator itr = NULL; ++ ++ slurmdb_assoc_cond_t assoc_cond; ++ slurmdb_wckey_cond_t wckey_cond; ++ ++ if (!user_cond->def_acct_list || !list_count(user_cond->def_acct_list)) ++ goto no_assocs; ++ ++ /* We have to use a different association_cond here because ++ other things could be set here we don't care about in the ++ user's. (So to be safe just move over the info we care about) */ ++ memset(&assoc_cond, 0, sizeof(slurmdb_assoc_cond_t)); ++ assoc_cond.acct_list = user_cond->def_acct_list; ++ if (user_cond->assoc_cond) { ++ if (user_cond->assoc_cond->cluster_list) ++ assoc_cond.cluster_list = ++ user_cond->assoc_cond->cluster_list; ++ if (user_cond->assoc_cond->user_list) ++ assoc_cond.user_list = user_cond->assoc_cond->user_list; ++ } ++ assoc_cond.only_defs = 1; ++ tmp_list = as_pgsql_get_assocs(pgsql_conn, uid, &assoc_cond); ++ if (tmp_list) { ++ slurmdb_assoc_rec_t *object = NULL; ++ itr = list_iterator_create(tmp_list); ++ while ((object = list_next(itr))) { ++ if (!ret_list) ++ ret_list = list_create(xfree_ptr); ++ slurm_addto_char_list(ret_list, object->user); ++ } ++ list_iterator_destroy(itr); ++ FREE_NULL_LIST(tmp_list); ++ } ++ ++no_assocs: ++ if (!user_cond->def_wckey_list ++ || !list_count(user_cond->def_wckey_list)) ++ goto no_wckeys; ++ ++ memset(&wckey_cond, 0, sizeof(slurmdb_wckey_cond_t)); ++ if (user_cond->assoc_cond) { ++ if (user_cond->assoc_cond->cluster_list) ++ wckey_cond.cluster_list = ++ user_cond->assoc_cond->cluster_list; ++ if (user_cond->assoc_cond->user_list) ++ wckey_cond.user_list = user_cond->assoc_cond->user_list; ++ } ++ wckey_cond.name_list = user_cond->def_wckey_list; ++ wckey_cond.only_defs = 1; ++ ++ tmp_list = as_pgsql_get_wckeys(pgsql_conn, uid, &wckey_cond); ++ if (tmp_list) { ++ slurmdb_wckey_rec_t *object = NULL; ++ itr = list_iterator_create(tmp_list); ++ while ((object = list_next(itr))) { ++ if (!ret_list) ++ ret_list = list_create(xfree_ptr); ++ slurm_addto_char_list(ret_list, object->user); ++ } ++ list_iterator_destroy(itr); ++ FREE_NULL_LIST(tmp_list); ++ } ++ ++no_wckeys: ++ ++ return ret_list; ++} ++ ++/* Fill in all the accounts this user is coordinator over. This ++ * will fill in all the sub accounts they are coordinator over also. ++ */ ++static int _get_user_coords(pgsql_conn_t *pgsql_conn, slurmdb_user_rec_t *user) ++{ ++ char *query = NULL; ++ slurmdb_coord_rec_t *coord = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ListIterator itr = NULL, itr2 = NULL; ++ char *cluster_name = NULL; ++ ++ if (!user) { ++ error("We need a user to fill in."); ++ return SLURM_ERROR; ++ } ++ ++ if (!user->coord_accts) ++ user->coord_accts = list_create(slurmdb_destroy_coord_rec); ++ ++ query = xstrdup_printf( ++ "select acct from %s where \"user\"='%s' AND deleted=0", ++ acct_coord_table, user->name); ++ ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ coord = xmalloc(sizeof(slurmdb_coord_rec_t)); ++ list_append(user->coord_accts, coord); ++ coord->name = xstrdup(row[0]); ++ coord->direct = 1; ++ } ++ pgsql_free_result(&result); ++ ++ if (!list_count(user->coord_accts)) ++ return SLURM_SUCCESS; ++ ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ itr2 = list_iterator_create(as_pgsql_cluster_list); ++ itr = list_iterator_create(user->coord_accts); ++ while ((cluster_name = list_next(itr2))) { ++ int set = 0; ++ if (query) ++ xstrcat(query, " union "); ++ ++ while ((coord = list_next(itr))) { ++ if (set) ++ xstrcat(query, " OR "); ++ else ++ xstrfmtcat(query, ++ "select distinct t1.acct from " ++ "\"%s_%s\" as t1, \"%s_%s\" " ++ "as t2 where t1.deleted=0 AND (", ++ cluster_name, assoc_table, ++ cluster_name, assoc_table); ++ /* Make sure we don't get the same ++ * account back since we want to keep ++ * track of the sub-accounts. ++ */ ++ xstrfmtcat(query, "(t2.acct='%s' " ++ "AND t1.lft between t2.lft " ++ "and t2.rgt AND t1.\"user\"='' " ++ "AND t1.acct!='%s')", ++ coord->name, coord->name); ++ set = 1; ++ } ++ list_iterator_reset(itr); ++ if (set) ++ xstrcat(query, ")"); ++ ++ } ++ list_iterator_destroy(itr2); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ ++ if (query) { ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ xfree(query); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ list_iterator_reset(itr); ++ while ((coord = list_next(itr))) { ++ if (!xstrcmp(coord->name, row[0])) ++ break; ++ } ++ ++ if (coord) ++ continue; ++ ++ coord = xmalloc(sizeof(slurmdb_coord_rec_t)); ++ list_append(user->coord_accts, coord); ++ coord->name = xstrdup(row[0]); ++ coord->direct = 0; ++ } ++ pgsql_free_result(&result); ++ } ++ list_iterator_destroy(itr); ++ ++ return SLURM_SUCCESS; ++} ++ ++extern int as_pgsql_add_users(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List user_list) ++{ ++ ListIterator itr = NULL; ++ int rc = SLURM_SUCCESS; ++ slurmdb_user_rec_t *object = NULL; ++ char *cols = NULL, *vals = NULL, *query = NULL, *txn_query = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ char *extra = NULL, *tmp_extra = NULL; ++ int affect_rows = 0; ++ List assoc_list; ++ List wckey_list; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { ++ slurmdb_user_rec_t user; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (!is_user_any_coord(pgsql_conn, &user)) { ++ error("Only admins/operators/coordinators " ++ "can add accounts"); ++ return ESLURM_ACCESS_DENIED; ++ } ++ /* If the user is a coord of any acct they can add ++ * accounts they are only able to make associations to ++ * these accounts if they are coordinators of the ++ * parent they are trying to add to ++ */ ++ } ++ ++ assoc_list = list_create(slurmdb_destroy_assoc_rec); ++ wckey_list = list_create(slurmdb_destroy_wckey_rec); ++ ++ user_name = uid_to_string((uid_t) uid); ++ itr = list_iterator_create(user_list); ++ while ((object = list_next(itr))) { ++ if (!object->name || !object->name[0]) { ++ error("We need a user name and " ++ "default acct to add."); ++ rc = SLURM_ERROR; ++ continue; ++ } ++ ++ if (object->coord_accts) ++ debug("Adding coordinators with users is not supported, ignored, use as_pgsql_add_coord() separately instead."); ++ ++ xstrcat(cols, "creation_time, mod_time, name"); ++ xstrfmtcat(vals, "%ld, %ld, '%s'", ++ (long)now, (long)now, object->name); ++ ++ if (object->admin_level != SLURMDB_ADMIN_NOTSET) { ++ xstrcat(cols, ", admin_level"); ++ xstrfmtcat(vals, ", %u", object->admin_level); ++ xstrfmtcat(extra, ", admin_level=%u", ++ object->admin_level); ++ } else ++ xstrfmtcat(extra, ", admin_level=%u", ++ SLURMDB_ADMIN_NONE); ++ ++ query = xstrdup_printf( ++ "insert into %s (%s) values (%s) " ++// "on duplicate key update name=VALUES(name), deleted=0, mod_time=%ld %s;", ++ "on conflict(name) do update set name=EXCLUDED.name, deleted=0, mod_time=%ld %s;", ++ user_table, cols, vals, ++ (long)now, extra); ++ xfree(cols); ++ xfree(vals); ++ ++ SQLHSTMT stmt = NULL; ++ rc = pgsql_db_query_stmt(pgsql_conn, query, &stmt); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add user %s", object->name); ++ xfree(extra); ++ pgsql_db_free_statement(&stmt); ++ continue; ++ } ++ ++ affect_rows = last_affected_rows(stmt); ++ pgsql_db_free_statement(&stmt); ++ if (!affect_rows) { ++ debug("nothing changed"); ++ xfree(extra); ++ continue; ++ } ++ ++ if (addto_update_list(pgsql_conn->update_list, SLURMDB_ADD_USER, ++ object) == SLURM_SUCCESS) ++ list_remove(itr); ++ ++ /* we always have a ', ' as the first 2 chars */ ++ tmp_extra = slurm_add_slash_to_quotes(extra+2); ++ ++ if (txn_query) ++ xstrfmtcat(txn_query, ++ ", (%ld, %u, '%s', '%s', '%s')", ++ (long)now, DBD_ADD_USERS, object->name, ++ user_name, tmp_extra); ++ else ++ xstrfmtcat(txn_query, ++ "insert into %s " ++ "(timestamp, action, name, actor, info) " ++ "values (%ld, %u, '%s', '%s', '%s')", ++ txn_table, ++ (long)now, DBD_ADD_USERS, object->name, ++ user_name, tmp_extra); ++ xfree(tmp_extra); ++ xfree(extra); ++ ++ /* For < 2.2 systems we need to set the is_def flag in ++ the default association/wckey so as to make sure we get ++ it set correctly. ++ */ ++ if (object->assoc_list) { ++ slurmdb_assoc_rec_t *assoc = NULL; ++ ListIterator assoc_itr = ++ list_iterator_create(object->assoc_list); ++ while ((assoc = list_next(assoc_itr))) { ++ /* We need to mark all of the ++ associations with this account ++ since there could be multiple ++ clusters here. ++ */ ++ if (!xstrcmp(assoc->acct, object->default_acct)) ++ assoc->is_def = 1; ++ } ++ list_iterator_destroy(assoc_itr); ++ list_transfer(assoc_list, object->assoc_list); ++ } ++ ++ if (object->wckey_list) { ++ if (object->default_wckey) { ++ slurmdb_wckey_rec_t *wckey = NULL; ++ ListIterator wckey_itr = list_iterator_create( ++ object->wckey_list); ++ while ((wckey = list_next(wckey_itr))) { ++ /* We need to mark all of the ++ wckeys with this account ++ since there could be multiple ++ clusters here. ++ */ ++ if (!xstrcmp(wckey->name, ++ object->default_wckey)) ++ wckey->is_def = 1; ++ } ++ list_iterator_destroy(wckey_itr); ++ } ++ list_transfer(wckey_list, object->wckey_list); ++ } ++ } ++ list_iterator_destroy(itr); ++ xfree(user_name); ++ ++ if (rc != SLURM_ERROR) { ++ if (txn_query) { ++ xstrcat(txn_query, ";"); ++ rc = pgsql_db_query(pgsql_conn, ++ txn_query); ++ xfree(txn_query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add txn"); ++ rc = SLURM_SUCCESS; ++ } ++ } ++ } else ++ xfree(txn_query); ++ ++ if (list_count(assoc_list)) { ++ if ((rc = as_pgsql_add_assocs(pgsql_conn, uid, assoc_list)) ++ != SLURM_SUCCESS) ++ error("Problem adding user associations"); ++ } ++ FREE_NULL_LIST(assoc_list); ++ ++ if (rc == SLURM_SUCCESS && list_count(wckey_list)) { ++ if ((rc = as_pgsql_add_wckeys(pgsql_conn, uid, wckey_list)) ++ != SLURM_SUCCESS) ++ error("Problem adding user wckeys"); ++ } ++ FREE_NULL_LIST(wckey_list); ++ return rc; ++} ++ ++extern int as_pgsql_add_coord(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List acct_list, slurmdb_user_cond_t *user_cond) ++{ ++ char *query = NULL, *user = NULL, *acct = NULL; ++ char *user_name = NULL, *txn_query = NULL; ++ ListIterator itr, itr2; ++ time_t now = time(NULL); ++ int rc = SLURM_SUCCESS; ++ slurmdb_user_rec_t *user_rec = NULL; ++ ++ if (!user_cond || !user_cond->assoc_cond ++ || !user_cond->assoc_cond->user_list ++ || !list_count(user_cond->assoc_cond->user_list) ++ || !acct_list || !list_count(acct_list)) { ++ error("we need something to add"); ++ return SLURM_ERROR; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { ++ slurmdb_user_rec_t user; ++ slurmdb_coord_rec_t *coord = NULL; ++ char *acct = NULL; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (!is_user_any_coord(pgsql_conn, &user)) { ++ error("Only admins/operators/coordinators " ++ "can add account coordinators"); ++ return ESLURM_ACCESS_DENIED; ++ } ++ ++ itr = list_iterator_create(acct_list); ++ itr2 = list_iterator_create(user.coord_accts); ++ while ((acct = list_next(itr))) { ++ while ((coord = list_next(itr2))) { ++ if (!xstrcasecmp(coord->name, acct)) ++ break; ++ } ++ if (!coord) ++ break; ++ list_iterator_reset(itr2); ++ } ++ list_iterator_destroy(itr2); ++ list_iterator_destroy(itr); ++ ++ if (!coord) { ++ error("Coordinator %s(%d) tried to add another " ++ "coordinator to an account they aren't " ++ "coordinator over.", ++ user.name, user.uid); ++ return ESLURM_ACCESS_DENIED; ++ } ++ } ++ ++ user_name = uid_to_string((uid_t) uid); ++ itr = list_iterator_create(user_cond->assoc_cond->user_list); ++ itr2 = list_iterator_create(acct_list); ++ while ((user = list_next(itr))) { ++ if (!user[0]) ++ continue; ++ while ((acct = list_next(itr2))) { ++ if (!acct[0]) ++ continue; ++ if (query) ++ xstrfmtcat(query, ", (%ld, %ld, '%s', '%s')", ++ (long)now, (long)now, acct, user); ++ else ++ query = xstrdup_printf( ++ "insert into %s (creation_time, " ++ "mod_time, acct, \"user\") values " ++ "(%ld, %ld, '%s', '%s')", ++ acct_coord_table, ++ (long)now, (long)now, acct, user); ++ ++ if (txn_query) ++ xstrfmtcat(txn_query, ++ ", (%ld, %u, '%s', '%s', '%s')", ++ (long)now, DBD_ADD_ACCOUNT_COORDS, ++ user, user_name, acct); ++ else ++ xstrfmtcat(txn_query, ++ "insert into %s " ++ "(timestamp, action, name, " ++ "actor, info) " ++ "values (%ld, %u, '%s', " ++ "'%s', '%s')", ++ txn_table, ++ (long)now, DBD_ADD_ACCOUNT_COORDS, ++ user, ++ user_name, acct); ++ } ++ list_iterator_reset(itr2); ++ } ++ xfree(user_name); ++ list_iterator_destroy(itr); ++ list_iterator_destroy(itr2); ++ ++ if (query) { ++ xstrfmtcat(query, ++// " on duplicate key update mod_time=%ld, " ++// "deleted=0, user=VALUES(user);%s", ++ " on conflict(acct, \"user\") do update set mod_time=%ld, " ++ "deleted=0, \"user\"=EXCLUDED.\"user\";%s", ++ (long)now, txn_query); ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ xfree(txn_query); ++ ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add cluster hour rollup"); ++ return rc; ++ } ++ /* get the update list set */ ++ itr = list_iterator_create(user_cond->assoc_cond->user_list); ++ while ((user = list_next(itr))) { ++ user_rec = xmalloc(sizeof(slurmdb_user_rec_t)); ++ user_rec->name = xstrdup(user); ++ _get_user_coords(pgsql_conn, user_rec); ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_ADD_COORD, user_rec) ++ != SLURM_SUCCESS) ++ slurmdb_destroy_user_rec(user_rec); ++ } ++ list_iterator_destroy(itr); ++ } ++ ++ return SLURM_SUCCESS; ++} ++ ++extern List as_pgsql_modify_users(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_user_cond_t *user_cond, ++ slurmdb_user_rec_t *user) ++{ ++ ListIterator itr = NULL; ++ List ret_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *object = NULL; ++ char *vals = NULL, *extra = NULL, *query = NULL, *name_char = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ int set = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ if (!user_cond || !user) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (user_cond->assoc_cond && user_cond->assoc_cond->user_list ++ && list_count(user_cond->assoc_cond->user_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(user_cond->assoc_cond->user_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (user_cond->admin_level != SLURMDB_ADMIN_NOTSET) ++ xstrfmtcat(extra, " AND admin_level=%u", ++ user_cond->admin_level); ++ ++ ret_list = _get_other_user_names_to_mod(pgsql_conn, uid, user_cond); ++ ++ if (user->name) ++ xstrfmtcat(vals, ", name='%s'", user->name); ++ ++ if (user->admin_level != SLURMDB_ADMIN_NOTSET) ++ xstrfmtcat(vals, ", admin_level=%u", user->admin_level); ++ ++ if ((!extra && !ret_list) ++ || (!vals && !user->default_acct && !user->default_wckey)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ error("Nothing to change"); ++ return NULL; ++ } ++ ++ if (!extra) { ++ /* means we got a ret_list and don't need to look at ++ the user_table. */ ++ goto no_user_table; ++ } ++ ++ query = xstrdup_printf( ++ "select distinct name from %s where deleted=0 %s;", ++ user_table, extra); ++ xfree(extra); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ FREE_NULL_LIST(ret_list); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ if (!ret_list) ++ ret_list = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_user_rec_t *user_rec = NULL; ++ ++ object = row[0]; ++ slurm_addto_char_list(ret_list, object); ++ if (!name_char) ++ xstrfmtcat(name_char, "(name='%s'", object); ++ else ++ xstrfmtcat(name_char, " OR name='%s'", object); ++ ++ user_rec = xmalloc(sizeof(slurmdb_user_rec_t)); ++ ++ if (!user->name) ++ user_rec->name = xstrdup(object); ++ else { ++ user_rec->name = xstrdup(user->name); ++ user_rec->old_name = xstrdup(object); ++ if (_change_user_name(pgsql_conn, user_rec) ++ != SLURM_SUCCESS) ++ break; ++ } ++ ++ user_rec->admin_level = user->admin_level; ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_MODIFY_USER, user_rec) ++ != SLURM_SUCCESS) ++ slurmdb_destroy_user_rec(user_rec); ++ } ++ pgsql_free_result(&result); ++ ++no_user_table: ++ if (!list_count(ret_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(vals); ++ xfree(query); ++ return ret_list; ++ } else if (user->name && (list_count(ret_list) != 1)) { ++ errno = ESLURM_ONE_CHANGE; ++ xfree(vals); ++ xfree(query); ++ FREE_NULL_LIST(ret_list); ++ return NULL; ++ } ++ ++ xfree(query); ++ ++ if (name_char && vals) { ++ xstrcat(name_char, ")"); ++ user_name = uid_to_string((uid_t) uid); ++ rc = modify_common(pgsql_conn, DBD_MODIFY_USERS, now, ++ user_name, user_table, name_char, ++ vals, NULL); ++ xfree(user_name); ++ } ++ ++ xfree(name_char); ++ xfree(vals); ++ if (rc == SLURM_ERROR) { ++ error("Couldn't modify users"); ++ FREE_NULL_LIST(ret_list); ++ } ++ ++ if (user->default_acct) { ++ slurmdb_assoc_cond_t assoc_cond; ++ slurmdb_assoc_rec_t assoc; ++ List tmp_list = NULL; ++ ++ memset(&assoc_cond, 0, sizeof(slurmdb_assoc_cond_t)); ++ slurmdb_init_assoc_rec(&assoc, 0); ++ assoc.is_def = 1; ++ assoc_cond.acct_list = list_create(NULL); ++ list_append(assoc_cond.acct_list, user->default_acct); ++ assoc_cond.user_list = ret_list; ++ if (user_cond->assoc_cond ++ && user_cond->assoc_cond->cluster_list) ++ assoc_cond.cluster_list = ++ user_cond->assoc_cond->cluster_list; ++ tmp_list = as_pgsql_modify_assocs(pgsql_conn, uid, ++ &assoc_cond, &assoc); ++ FREE_NULL_LIST(assoc_cond.acct_list); ++ ++ if (!tmp_list) { ++ FREE_NULL_LIST(ret_list); ++ goto end_it; ++ } ++ /* char *names = NULL; */ ++ /* ListIterator itr = list_iterator_create(tmp_list); */ ++ /* while ((names = list_next(itr))) { */ ++ /* info("%s", names); */ ++ /* } */ ++ /* list_iterator_destroy(itr); */ ++ FREE_NULL_LIST(tmp_list); ++ } ++ ++ if (user->default_wckey) { ++ slurmdb_wckey_cond_t wckey_cond; ++ slurmdb_wckey_rec_t wckey; ++ List tmp_list = NULL; ++ ++ memset(&wckey_cond, 0, sizeof(slurmdb_wckey_cond_t)); ++ slurmdb_init_wckey_rec(&wckey, 0); ++ wckey.is_def = 1; ++ wckey_cond.name_list = list_create(NULL); ++ list_append(wckey_cond.name_list, user->default_wckey); ++ wckey_cond.user_list = ret_list; ++ if (user_cond->assoc_cond ++ && user_cond->assoc_cond->cluster_list) ++ wckey_cond.cluster_list = ++ user_cond->assoc_cond->cluster_list; ++ tmp_list = as_pgsql_modify_wckeys(pgsql_conn, uid, ++ &wckey_cond, &wckey); ++ FREE_NULL_LIST(wckey_cond.name_list); ++ ++ if (!tmp_list) { ++ FREE_NULL_LIST(ret_list); ++ goto end_it; ++ } ++ /* char *names = NULL; */ ++ /* ListIterator itr = list_iterator_create(tmp_list); */ ++ /* while ((names = list_next(itr))) { */ ++ /* info("%s", names); */ ++ /* } */ ++ /* list_iterator_destroy(itr); */ ++ FREE_NULL_LIST(tmp_list); ++ } ++end_it: ++ return ret_list; ++} ++ ++/* ++ * If the coordinator has permissions to modify every account ++ * belonging to each user, return true. Otherwise return false. ++ */ ++static bool _is_coord_over_all_accts(pgsql_conn_t *pgsql_conn, ++ char *cluster_name, char *user_char, ++ slurmdb_user_rec_t *coord) ++{ ++ bool has_access; ++ char *query = NULL, *sep_str = ""; ++ pgsql_res_t *result; ++ ListIterator itr; ++ slurmdb_coord_rec_t *coord_acct; ++ ++ if (!coord->coord_accts || !list_count(coord->coord_accts)) { ++ /* This should never happen */ ++ error("%s: We are here with no coord accts", __func__); ++ return false; ++ } ++ ++ query = xstrdup_printf("select distinct acct from \"%s_%s\" where deleted=0 AND (%s) AND (", ++ cluster_name, assoc_table, user_char); ++ ++ /* ++ * Add the accounts we are coordinator of. If anything is returned ++ * outside of this list we will know there are accounts in the request ++ * that we are not coordinator over. ++ */ ++ itr = list_iterator_create(coord->coord_accts); ++ while ((coord_acct = (list_next(itr)))) { ++ xstrfmtcat(query, "%sacct != '%s'", sep_str, coord_acct->name); ++ sep_str = " AND "; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(query, ");"); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return false; ++ } ++ xfree(query); ++ ++ /* ++ * If nothing was returned we are coordinator over all these accounts ++ * and users. ++ */ ++ has_access = !pgsql_num_rows(result); ++ ++ pgsql_free_result(&result); ++ return has_access; ++} ++ ++extern List as_pgsql_remove_users(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_user_cond_t *user_cond) ++{ ++ ListIterator itr = NULL; ++ List ret_list = NULL; ++ List coord_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *object = NULL; ++ char *extra = NULL, *query = NULL, ++ *name_char = NULL, *assoc_char = NULL, *user_char = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ int set = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ slurmdb_user_cond_t user_coord_cond; ++ slurmdb_user_rec_t user; ++ slurmdb_assoc_cond_t assoc_cond; ++ slurmdb_wckey_cond_t wckey_cond; ++ bool jobs_running = 0; ++ bool is_coord = false; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (!user_cond) { ++ error("we need something to remove"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { ++ /* ++ * Allow coordinators to delete users from accounts that ++ * they coordinate. After we have gotten every association that ++ * the users belong to, check that the coordinator has access ++ * to modify every affected account. ++ */ ++ is_coord = is_user_any_coord(pgsql_conn, &user); ++ if (!is_coord) { ++ error("Only admins/coordinators can remove users"); ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ } ++ ++ if (user_cond->assoc_cond && user_cond->assoc_cond->user_list ++ && list_count(user_cond->assoc_cond->user_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(user_cond->assoc_cond->user_list); ++ while ((object = list_next(itr))) { ++ if (!object[0]) ++ continue; ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ ret_list = _get_other_user_names_to_mod(pgsql_conn, uid, user_cond); ++ ++ if (user_cond->admin_level != SLURMDB_ADMIN_NOTSET) { ++ xstrfmtcat(extra, " AND admin_level=%u", user_cond->admin_level); ++ } ++ ++ if (!extra && !ret_list) { ++ error("Nothing to remove"); ++ return NULL; ++ } else if (!extra) { ++ /* means we got a ret_list and don't need to look at ++ the user_table. */ ++ goto no_user_table; ++ } ++ ++ /* Only handle this if we need to actually query the ++ user_table. If a request comes in stating they want to ++ remove all users with default account of whatever then that ++ doesn't deal with the user_table. ++ */ ++ query = xstrdup_printf("select name from %s where deleted=0 %s;", ++ user_table, extra); ++ xfree(extra); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ ++ if (!ret_list) ++ ret_list = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) ++ slurm_addto_char_list(ret_list, row[0]); ++ pgsql_free_result(&result); ++ ++no_user_table: ++ ++ if (!list_count(ret_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(query); ++ return ret_list; ++ } ++ xfree(query); ++ ++ memset(&user_coord_cond, 0, sizeof(slurmdb_user_cond_t)); ++ memset(&assoc_cond, 0, sizeof(slurmdb_assoc_cond_t)); ++ /* we do not need to free the objects we put in here since ++ they are also placed in a list that will be freed ++ */ ++ assoc_cond.user_list = list_create(NULL); ++ user_coord_cond.assoc_cond = &assoc_cond; ++ ++ itr = list_iterator_create(ret_list); ++ while ((object = list_next(itr))) { ++ slurmdb_user_rec_t *user_rec; ++ ++ /* ++ * Skip empty names or else will select account associations ++ * and remove all associations. ++ */ ++ if (!object[0]) { ++ list_delete_item(itr); ++ continue; ++ } ++ ++ user_rec = xmalloc(sizeof(slurmdb_user_rec_t)); ++ list_append(assoc_cond.user_list, object); ++ ++ if (name_char) { ++ xstrfmtcat(name_char, " OR name='%s'", object); ++ xstrfmtcat(user_char, " OR \"user\"='%s'", object); ++ xstrfmtcat(assoc_char, " OR t2.\"user\"='%s'", object); ++ } else { ++ xstrfmtcat(name_char, "name='%s'", object); ++ xstrfmtcat(user_char, "\"user\"='%s'", object); ++ xstrfmtcat(assoc_char, "t2.\"user\"='%s'", object); ++ } ++ ++ user_rec->name = xstrdup(object); ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_REMOVE_USER, user_rec) ++ != SLURM_SUCCESS) ++ slurmdb_destroy_user_rec(user_rec); ++ } ++ list_iterator_destroy(itr); ++ ++ /* We need to remove these accounts from the coord's that have it */ ++ coord_list = as_pgsql_remove_coord( ++ pgsql_conn, uid, NULL, &user_coord_cond); ++ FREE_NULL_LIST(coord_list); ++ ++ /* We need to remove these users from the wckey table */ ++ memset(&wckey_cond, 0, sizeof(slurmdb_wckey_cond_t)); ++ wckey_cond.user_list = assoc_cond.user_list; ++ coord_list = as_pgsql_remove_wckeys(pgsql_conn, uid, &wckey_cond); ++ FREE_NULL_LIST(coord_list); ++ ++ FREE_NULL_LIST(assoc_cond.user_list); ++ ++ user_name = uid_to_string((uid_t) uid); ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ itr = list_iterator_create(as_pgsql_cluster_list); ++ while ((object = list_next(itr))) { ++ ++ if (is_coord) { ++ if (!_is_coord_over_all_accts(pgsql_conn, object, ++ user_char, &user)) { ++ errno = ESLURM_ACCESS_DENIED; ++ rc = SLURM_ERROR; ++ break; ++ } ++ } ++ ++ if ((rc = remove_common(pgsql_conn, DBD_REMOVE_USERS, now, ++ user_name, user_table, name_char, ++ assoc_char, object, ret_list, ++ &jobs_running)) ++ != SLURM_SUCCESS) ++ break; ++ } ++ list_iterator_destroy(itr); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ ++ xfree(user_name); ++ xfree(name_char); ++ xfree(user_char); ++ if (rc == SLURM_ERROR) { ++ FREE_NULL_LIST(ret_list); ++ xfree(assoc_char); ++ return NULL; ++ } ++ ++ query = xstrdup_printf( ++ "update %s as t2 set deleted=1, mod_time=%ld where %s", ++ acct_coord_table, (long)now, assoc_char); ++ xfree(assoc_char); ++ ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't remove user coordinators"); ++ FREE_NULL_LIST(ret_list); ++ return NULL; ++ } ++ ++ if (jobs_running) ++ errno = ESLURM_JOBS_RUNNING_ON_ASSOC; ++ else ++ errno = SLURM_SUCCESS; ++ return ret_list; ++} ++ ++extern List as_pgsql_remove_coord(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List acct_list, ++ slurmdb_user_cond_t *user_cond) ++{ ++ char *query = NULL, *object = NULL, *extra = NULL, *last_user = NULL; ++ char *user_name = NULL; ++ time_t now = time(NULL); ++ int set = 0, is_admin=0, rc = SLURM_SUCCESS; ++ ListIterator itr = NULL; ++ slurmdb_user_rec_t *user_rec = NULL; ++ List ret_list = NULL; ++ List user_list = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ slurmdb_user_rec_t user; ++ ++ if (!user_cond && !acct_list) { ++ error("we need something to remove"); ++ return NULL; ++ } else if (user_cond && user_cond->assoc_cond) ++ user_list = user_cond->assoc_cond->user_list; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (!(is_admin = is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { ++ if (!is_user_any_coord(pgsql_conn, &user)) { ++ error("Only admins/coordinators can " ++ "remove coordinators"); ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ } ++ ++ /* Leave it this way since we are using extra below */ ++ ++ if (user_list && list_count(user_list)) { ++ set = 0; ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, "("); ++ ++ itr = list_iterator_create(user_list); ++ while ((object = list_next(itr))) { ++ if (!object[0]) ++ continue; ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "\"user\"='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (acct_list && list_count(acct_list)) { ++ set = 0; ++ if (extra) ++ xstrcat(extra, " AND ("); ++ else ++ xstrcat(extra, "("); ++ ++ itr = list_iterator_create(acct_list); ++ while ((object = list_next(itr))) { ++ if (!object[0]) ++ continue; ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "acct='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (!extra) { ++ errno = SLURM_ERROR; ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "No conditions given"); ++ return NULL; ++ } ++ ++ query = xstrdup_printf( ++ "select \"user\", acct from %s where deleted=0 AND %s order by \"user\"", ++ acct_coord_table, extra); ++ ++ DB_DEBUG(DB_ASSOC, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ xfree(extra); ++ errno = SLURM_ERROR; ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ xfree(query); ++ ret_list = list_create(xfree_ptr); ++ user_list = list_create(xfree_ptr); ++ while ((row = pgsql_fetch_row(result))) { ++ if (!is_admin) { ++ slurmdb_coord_rec_t *coord = NULL; ++ if (!user.coord_accts) { // This should never ++ // happen ++ error("We are here with no coord accts"); ++ errno = ESLURM_ACCESS_DENIED; ++ FREE_NULL_LIST(ret_list); ++ FREE_NULL_LIST(user_list); ++ xfree(extra); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ itr = list_iterator_create(user.coord_accts); ++ while ((coord = list_next(itr))) { ++ if (!xstrcasecmp(coord->name, row[1])) ++ break; ++ } ++ list_iterator_destroy(itr); ++ ++ if (!coord) { ++ error("User %s(%d) does not have the " ++ "ability to change this account (%s)", ++ user.name, user.uid, row[1]); ++ errno = ESLURM_ACCESS_DENIED; ++ FREE_NULL_LIST(ret_list); ++ FREE_NULL_LIST(user_list); ++ xfree(extra); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ } ++ if (!last_user || xstrcasecmp(last_user, row[0])) { ++ list_append(user_list, xstrdup(row[0])); ++ last_user = row[0]; ++ } ++ list_append(ret_list, xstrdup_printf("U = %-9s A = %-10s", ++ row[0], row[1])); ++ } ++ pgsql_free_result(&result); ++ ++ user_name = uid_to_string((uid_t) uid); ++ rc = remove_common(pgsql_conn, DBD_REMOVE_ACCOUNT_COORDS, ++ now, user_name, acct_coord_table, ++ extra, NULL, NULL, NULL, NULL); ++ xfree(user_name); ++ xfree(extra); ++ if (rc == SLURM_ERROR) { ++ FREE_NULL_LIST(ret_list); ++ FREE_NULL_LIST(user_list); ++ errno = SLURM_ERROR; ++ return NULL; ++ } ++ ++ /* get the update list set */ ++ itr = list_iterator_create(user_list); ++ while ((last_user = list_next(itr))) { ++ user_rec = xmalloc(sizeof(slurmdb_user_rec_t)); ++ user_rec->name = xstrdup(last_user); ++ _get_user_coords(pgsql_conn, user_rec); ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_REMOVE_COORD, user_rec) ++ != SLURM_SUCCESS) ++ slurmdb_destroy_user_rec(user_rec); ++ } ++ list_iterator_destroy(itr); ++ FREE_NULL_LIST(user_list); ++ ++ return ret_list; ++} ++ ++extern List as_pgsql_get_users(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_user_cond_t *user_cond) ++{ ++ char *query = NULL; ++ char *extra = NULL; ++ char *tmp = NULL; ++ List user_list = NULL; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ int set = 0; ++ int i=0, is_admin=1; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ slurmdb_user_rec_t user; ++ ++ /* if this changes you will need to edit the corresponding enum */ ++ char *user_req_inx[] = { ++ "name", ++ "admin_level", ++ "deleted", ++ }; ++ enum { ++ USER_REQ_NAME, ++ USER_REQ_AL, ++ USER_REQ_DELETED, ++ USER_REQ_COUNT ++ }; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (slurm_conf.private_data & PRIVATE_DATA_USERS) { ++ if (!(is_admin = is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { ++ assoc_mgr_fill_in_user( ++ pgsql_conn, &user, 1, NULL, false); ++ } ++ if (!is_admin && !user.name) { ++ debug("User %u has no associations, and is not admin, " ++ "so not returning any users.", user.uid); ++ return NULL; ++ } ++ } ++ ++ if (!user_cond) { ++ xstrcat(extra, "where deleted=0"); ++ goto empty; ++ } ++ ++ if (user_cond->with_deleted) ++ xstrcat(extra, "where (deleted=0 OR deleted=1)"); ++ else ++ xstrcat(extra, "where deleted=0"); ++ ++ ++ user_list = _get_other_user_names_to_mod(pgsql_conn, uid, user_cond); ++ if (user_list) { ++ if (!user_cond->assoc_cond) ++ user_cond->assoc_cond = ++ xmalloc(sizeof(slurmdb_assoc_rec_t)); ++ ++ if (!user_cond->assoc_cond->user_list) ++ user_cond->assoc_cond->user_list = user_list; ++ else { ++ list_transfer(user_cond->assoc_cond->user_list, ++ user_list); ++ FREE_NULL_LIST(user_list); ++ } ++ user_list = NULL; ++ } else if ((user_cond->def_acct_list ++ && list_count(user_cond->def_acct_list)) ++ || (user_cond->def_wckey_list ++ && list_count(user_cond->def_wckey_list))) ++ return NULL; ++ ++ if (user_cond->assoc_cond && ++ user_cond->assoc_cond->user_list ++ && list_count(user_cond->assoc_cond->user_list)) { ++ set = 0; ++ xstrcat(extra, " AND ("); ++ itr = list_iterator_create(user_cond->assoc_cond->user_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " OR "); ++ xstrfmtcat(extra, "name='%s'", object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (user_cond->admin_level != SLURMDB_ADMIN_NOTSET) { ++ xstrfmtcat(extra, " AND admin_level=%u", ++ user_cond->admin_level); ++ } ++ ++empty: ++ /* This is here to make sure we are looking at only this user ++ * if this flag is set. ++ */ ++ if (!is_admin && (slurm_conf.private_data & PRIVATE_DATA_USERS)) { ++ xstrfmtcat(extra, " AND name='%s'", user.name); ++ } ++ ++ xfree(tmp); ++ xstrfmtcat(tmp, "%s", user_req_inx[0]); ++ for(i=1; iconn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if(!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)){ ++ xfree(query); ++ pgsql_free_result(&result); ++ return NULL; ++ } ++ xfree(query); ++ ++ user_list = list_create(slurmdb_destroy_user_rec); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_user_rec_t *user = xmalloc(sizeof(slurmdb_user_rec_t)); ++ ++ list_append(user_list, user); ++ ++ user->name = xstrdup(row[USER_REQ_NAME]); ++ user->admin_level = slurm_atoul(row[USER_REQ_AL]); ++ ++ if (slurm_atoul(row[USER_REQ_DELETED])) ++ user->flags |= SLURMDB_USER_FLAG_DELETED; ++ ++ if (user_cond && user_cond->with_coords) ++ _get_user_coords(pgsql_conn, user); ++ } ++ pgsql_free_result(&result); ++ ++ if (user_cond && (user_cond->with_assocs ++ || (user_cond->assoc_cond ++ && user_cond->assoc_cond->only_defs))) { ++ ListIterator assoc_itr = NULL; ++ slurmdb_user_rec_t *user = NULL; ++ slurmdb_assoc_rec_t *assoc = NULL; ++ List assoc_list = NULL; ++ ++ /* Make sure we don't get any non-user associations ++ * this is done by at least having a user_list ++ * defined */ ++ if (!user_cond->assoc_cond) ++ user_cond->assoc_cond = ++ xmalloc(sizeof(slurmdb_assoc_cond_t)); ++ ++ if (!user_cond->assoc_cond->user_list) ++ user_cond->assoc_cond->user_list = list_create(NULL); ++ ++ user_cond->assoc_cond->with_deleted = user_cond->with_deleted; ++ ++ assoc_list = as_pgsql_get_assocs( ++ pgsql_conn, uid, user_cond->assoc_cond); ++ ++ if (!assoc_list) { ++ error("no associations"); ++ goto get_wckeys; ++ } ++ ++ itr = list_iterator_create(user_list); ++ assoc_itr = list_iterator_create(assoc_list); ++ while ((user = list_next(itr))) { ++ while ((assoc = list_next(assoc_itr))) { ++ if (xstrcmp(assoc->user, user->name)) ++ continue; ++ /* Set up the default. This is needed ++ * for older versions primarily that ++ * don't have the notion of default ++ * account per cluster. */ ++ if (!user->default_acct && (assoc->is_def == 1)) ++ user->default_acct = ++ xstrdup(assoc->acct); ++ ++ if (!user_cond->with_assocs) { ++ /* We just got the default so no ++ reason to hang around if we aren't ++ getting the associations. ++ */ ++ if (user->default_acct) ++ break; ++ else ++ continue; ++ } ++ ++ if (!user->assoc_list) ++ user->assoc_list = list_create( ++ slurmdb_destroy_assoc_rec); ++ list_append(user->assoc_list, assoc); ++ list_remove(assoc_itr); ++ } ++ list_iterator_reset(assoc_itr); ++ } ++ list_iterator_destroy(itr); ++ list_iterator_destroy(assoc_itr); ++ FREE_NULL_LIST(assoc_list); ++ } ++ ++get_wckeys: ++ if (user_cond && (user_cond->with_wckeys ++ || (user_cond->assoc_cond ++ && user_cond->assoc_cond->only_defs))) { ++ ListIterator wckey_itr = NULL; ++ slurmdb_user_rec_t *user = NULL; ++ slurmdb_wckey_rec_t *wckey = NULL; ++ List wckey_list = NULL; ++ slurmdb_wckey_cond_t wckey_cond; ++ ++ memset(&wckey_cond, 0, sizeof(slurmdb_wckey_cond_t)); ++ if (user_cond->assoc_cond) { ++ wckey_cond.user_list = ++ user_cond->assoc_cond->user_list; ++ wckey_cond.cluster_list = ++ user_cond->assoc_cond->cluster_list; ++ wckey_cond.only_defs = ++ user_cond->assoc_cond->only_defs; ++ } ++ wckey_list = as_pgsql_get_wckeys(pgsql_conn, uid, &wckey_cond); ++ ++ if (!wckey_list) ++ return user_list; ++ ++ itr = list_iterator_create(user_list); ++ wckey_itr = list_iterator_create(wckey_list); ++ while ((user = list_next(itr))) { ++ while ((wckey = list_next(wckey_itr))) { ++ if (xstrcmp(wckey->user, user->name)) ++ continue; ++ ++ /* Set up the default. This is needed ++ * for older versions primarily that ++ * don't have the notion of default ++ * wckey per cluster. */ ++ if (!user->default_wckey ++ && (wckey->is_def == 1)) ++ user->default_wckey = ++ xstrdup(wckey->name); ++ ++ /* We just got the default so no ++ reason to hang around if we aren't ++ getting the wckeys. ++ */ ++ if (!user_cond->with_wckeys) { ++ /* We just got the default so no ++ reason to hang around if we aren't ++ getting the wckeys. ++ */ ++ if (user->default_wckey) ++ break; ++ else ++ continue; ++ } ++ ++ if (!user->wckey_list) ++ user->wckey_list = list_create( ++ slurmdb_destroy_wckey_rec); ++ list_append(user->wckey_list, wckey); ++ list_remove(wckey_itr); ++ } ++ list_iterator_reset(wckey_itr); ++ /* If a user doesn't have a default wckey (they ++ might not of had track_wckeys on), set it now. ++ */ ++ if (!user->default_wckey) ++ user->default_wckey = xstrdup(""); ++ } ++ list_iterator_destroy(itr); ++ list_iterator_destroy(wckey_itr); ++ ++ FREE_NULL_LIST(wckey_list); ++ } ++ ++ return user_list; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_user.h b/src/plugins/accounting_storage/pgsql/as_pgsql_user.h +new file mode 100755 +index 0000000..c3d9af4 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_user.h +@@ -0,0 +1,63 @@ ++/*****************************************************************************\ ++ * as_pgsql_user.h - functions dealing with users and coordinators. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_USER_H ++#define _HAVE_PGSQL_USER_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_add_users(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List user_list); ++ ++extern int as_pgsql_add_coord(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List acct_list, slurmdb_user_cond_t *user_cond); ++ ++extern List as_pgsql_modify_users(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_user_cond_t *user_cond, ++ slurmdb_user_rec_t *user); ++ ++extern List as_pgsql_remove_users(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ slurmdb_user_cond_t *user_cond); ++ ++extern List as_pgsql_remove_coord(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List acct_list, slurmdb_user_cond_t *user_cond); ++ ++extern List as_pgsql_get_users(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_user_cond_t *user_cond); ++ ++#endif +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_wckey.c b/src/plugins/accounting_storage/pgsql/as_pgsql_wckey.c +new file mode 100755 +index 0000000..f0a9d39 +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_wckey.c +@@ -0,0 +1,910 @@ ++/*****************************************************************************\ ++ * as_pgsql_wckey.c - functions dealing with the wckey. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "as_pgsql_wckey.h" ++#include "as_pgsql_usage.h" ++ ++/* if this changes you will need to edit the corresponding enum */ ++char *wckey_req_inx[] = { ++ "id_wckey", ++ "is_def", ++ "wckey_name", ++ "\"user\"", ++ "deleted", ++}; ++ ++enum { ++ WCKEY_REQ_ID, ++ WCKEY_REQ_DEFAULT, ++ WCKEY_REQ_NAME, ++ WCKEY_REQ_USER, ++ WCKEY_REQ_DELETED, ++ WCKEY_REQ_COUNT ++}; ++ ++static int _reset_default_wckey(pgsql_conn_t *pgsql_conn, ++ slurmdb_wckey_rec_t *wckey) ++{ ++ time_t now = time(NULL); ++ int rc = SLURM_SUCCESS; ++ char *query = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ if ((wckey->is_def != 1) ++ || !wckey->cluster || !wckey->user || !wckey->name) ++ return SLURM_ERROR; ++ ++ xstrfmtcat(query, "UPDATE \"%s_%s\" SET is_def=0, mod_time=%ld " ++ "WHERE (\"user\"='%s' AND wckey_name!='%s' AND is_def=1);" ++ "SELECT id_wckey FROM \"%s_%s\" " ++ "WHERE (\"user\"='%s' AND wckey_name!='%s' AND is_def=1);", ++ wckey->cluster, wckey_table, (long)now, ++ wckey->user, wckey->name, ++ wckey->cluster, wckey_table, ++ wckey->user, wckey->name); ++ DB_DEBUG(DB_WCKEY, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 1); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ rc = SLURM_ERROR; ++ goto end_it; ++ } ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_wckey_rec_t *mod_wckey = ++ xmalloc(sizeof(slurmdb_wckey_rec_t)); ++ slurmdb_init_wckey_rec(mod_wckey, 0); ++ ++ mod_wckey->id = slurm_atoul(row[0]); ++ mod_wckey->is_def = 0; ++ ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_MODIFY_WCKEY, ++ mod_wckey) ++ != SLURM_SUCCESS) { ++ slurmdb_destroy_wckey_rec(mod_wckey); ++ error("couldn't add to the update list"); ++ rc = SLURM_ERROR; ++ break; ++ } ++ } ++ ++end_it: ++ xfree(query); ++ pgsql_free_result(&result); ++ return rc; ++} ++ ++/* This needs to happen to make since 2.1 code doesn't have enough ++ * smarts to figure out it isn't adding a default wckey if just ++ * adding a new wckey for a user that has never been on the cluster before. ++ */ ++static int _make_sure_users_have_default( ++ pgsql_conn_t *pgsql_conn, List user_list, List cluster_list) ++{ ++ char *query = NULL, *cluster = NULL, *user = NULL; ++ ListIterator itr = NULL, clus_itr = NULL; ++ int rc = SLURM_SUCCESS; ++ ++ if (!user_list) ++ return SLURM_SUCCESS; ++ ++ clus_itr = list_iterator_create(cluster_list); ++ itr = list_iterator_create(user_list); ++ ++ while ((user = list_next(itr))) { ++ while ((cluster = list_next(clus_itr))) { ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ ++ /* only look at non * and non deleted ones */ ++ query = xstrdup_printf( ++ "SELECT DISTINCT is_def, wckey_name FROM " ++ "\"%s_%s\" WHERE \"user\"='%s' AND wckey_name " ++ "not like '*%%' AND deleted=0 ORDER BY " ++ "is_def desc, creation_time desc LIMIT 1;", ++ cluster, wckey_table, user); ++ debug4("%d(%s:%d) query\n%s", ++ pgsql_conn->conn, THIS_FILE, __LINE__, query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ error("couldn't query the database"); ++ rc = SLURM_ERROR; ++ break; ++ } ++ /* Check to see if the user is even added to ++ the cluster. ++ */ ++ if (!pgsql_num_rows(result)) { ++ pgsql_free_result(&result); ++ continue; ++ } ++ ++ /* check if row is default */ ++ row = pgsql_fetch_row(result); ++ if (row[0][0] == '1') { ++ /* default found, continue */ ++ pgsql_free_result(&result); ++ continue; ++ } ++ ++ /* if we made it here, there is no default */ ++ query = xstrdup_printf( ++ "UPDATE \"%s_%s\" SET is_def=1 WHERE " ++ "\"user\"='%s' AND wckey_name='%s';", ++ cluster, wckey_table, user, row[1]); ++ pgsql_free_result(&result); ++ ++ DB_DEBUG(DB_WCKEY, pgsql_conn->conn, "query\n%s", ++ query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("problem with update query"); ++ rc = SLURM_ERROR; ++ break; ++ } ++ } ++ if (rc != SLURM_SUCCESS) ++ break; ++ list_iterator_reset(clus_itr); ++ } ++ list_iterator_destroy(itr); ++ list_iterator_destroy(clus_itr); ++ ++ return rc; ++} ++ ++/* when doing a select on this all the select should have a prefix of ++ * t1. */ ++static int _setup_wckey_cond_limits(slurmdb_wckey_cond_t *wckey_cond, ++ char **extra) ++{ ++ int set = 0; ++ ListIterator itr = NULL; ++ char *object = NULL; ++ char *prefix = "t1"; ++ if (!wckey_cond) ++ return 0; ++ ++ if (wckey_cond->with_deleted) ++ xstrfmtcat(*extra, " WHERE (%s.deleted=0 OR %s.deleted=1)", ++ prefix, prefix); ++ else ++ xstrfmtcat(*extra, " WHERE %s.deleted=0", prefix); ++ ++ if (wckey_cond->only_defs) { ++ set = 1; ++ xstrfmtcat(*extra, " AND (%s.is_def=1)", prefix); ++ } ++ ++ if (wckey_cond->name_list && list_count(wckey_cond->name_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(wckey_cond->name_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "%s.wckey_name='%s'", ++ prefix, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (wckey_cond->id_list && list_count(wckey_cond->id_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(wckey_cond->id_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "%s.id_wckey=%s", prefix, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ if (wckey_cond->user_list && list_count(wckey_cond->user_list)) { ++ set = 0; ++ xstrcat(*extra, " AND ("); ++ itr = list_iterator_create(wckey_cond->user_list); ++ while ((object = list_next(itr))) { ++ if (set) ++ xstrcat(*extra, " OR "); ++ xstrfmtcat(*extra, "%s.user='%s'", prefix, object); ++ set = 1; ++ } ++ list_iterator_destroy(itr); ++ xstrcat(*extra, ")"); ++ } ++ ++ return set; ++} ++ ++static int _cluster_remove_wckeys(pgsql_conn_t *pgsql_conn, ++ char *extra, ++ char *cluster_name, ++ char *user_name, ++ List ret_list) ++{ ++ int rc = SLURM_SUCCESS; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *assoc_char = NULL; ++ time_t now = time(NULL); ++ char *query = xstrdup_printf("SELECT t1.id_wckey, t1.wckey_name, " ++ "t1.user FROM \"%s_%s\" AS t1%s;", ++ cluster_name, wckey_table, extra); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ if (!pgsql_num_rows(result)) { ++ pgsql_free_result(&result); ++ xfree(query); ++ return SLURM_SUCCESS; ++ } ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_wckey_rec_t *wckey_rec = NULL; ++ char *object = xstrdup_printf("C = %-10s W = %-20s U = %-9s", ++ cluster_name, row[1], row[2]); ++ list_append(ret_list, object); ++ ++ if (!assoc_char) ++ xstrfmtcat(assoc_char, "id_wckey='%s'", row[0]); ++ else ++ xstrfmtcat(assoc_char, " OR id_wckey='%s'", row[0]); ++ ++ wckey_rec = xmalloc(sizeof(slurmdb_wckey_rec_t)); ++ /* we only need id and cluster when removing ++ no real need to init */ ++ wckey_rec->id = slurm_atoul(row[0]); ++ wckey_rec->cluster = xstrdup(cluster_name); ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_REMOVE_WCKEY, wckey_rec) ++ != SLURM_SUCCESS) ++ slurmdb_destroy_wckey_rec(wckey_rec); ++ } ++ pgsql_free_result(&result); ++ ++ if (!list_count(ret_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_WCKEY, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(query); ++ xfree(assoc_char); ++ return SLURM_SUCCESS; ++ } ++ ++ xfree(query); ++ rc = remove_common(pgsql_conn, DBD_REMOVE_WCKEYS, now, ++ user_name, wckey_table, assoc_char, assoc_char, ++ cluster_name, NULL, NULL); ++ xfree(assoc_char); ++ ++ if (rc == SLURM_ERROR) { ++ FREE_NULL_LIST(ret_list); ++ return SLURM_ERROR; ++ } ++ ++ return SLURM_SUCCESS; ++} ++ ++static int _cluster_modify_wckeys(pgsql_conn_t *pgsql_conn, ++ slurmdb_wckey_rec_t *wckey, ++ char *cluster_name, char *extra, ++ char *vals, char *user_name, ++ List ret_list) ++{ ++ int rc = SLURM_SUCCESS; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *wckey_char = NULL; ++ time_t now = time(NULL); ++ char *query = NULL; ++ ++ query = xstrdup_printf("SELECT t1.id_wckey, t1.wckey_name, t1.user " ++ "FROM \"%s_%s\" AS t1%s;", ++ cluster_name, wckey_table, extra); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ ++ /* This key doesn't exist on this cluster, that is ok. */ ++ if (!pgsql_num_rows(result)) ++ xfree(query); ++ pgsql_free_result(&result); ++ return SLURM_SUCCESS; ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_wckey_rec_t *wckey_rec = NULL; ++ char *object = xstrdup_printf( ++ "C = %-10s W = %-20s U = %-9s", ++ cluster_name, row[1], row[2]); ++ list_append(ret_list, object); ++ if (!wckey_char) ++ xstrfmtcat(wckey_char, "id_wckey='%s'", row[0]); ++ else ++ xstrfmtcat(wckey_char, " OR id_wckey='%s'", row[0]); ++ ++ wckey_rec = xmalloc(sizeof(slurmdb_wckey_rec_t)); ++ /* we only need id and cluster when removing ++ no real need to init */ ++ wckey_rec->id = slurm_atoul(row[0]); ++ wckey_rec->cluster = xstrdup(cluster_name); ++ wckey_rec->is_def = wckey->is_def; ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_MODIFY_WCKEY, wckey_rec) ++ != SLURM_SUCCESS) ++ slurmdb_destroy_wckey_rec(wckey_rec); ++ ++ if (wckey->is_def == 1) { ++ /* Use fresh one here so we don't have to ++ worry about dealing with bad values. ++ */ ++ slurmdb_wckey_rec_t tmp_wckey; ++ slurmdb_init_wckey_rec(&tmp_wckey, 0); ++ tmp_wckey.is_def = 1; ++ tmp_wckey.cluster = cluster_name; ++ tmp_wckey.name = row[1]; ++ tmp_wckey.user = row[2]; ++ if ((rc = _reset_default_wckey(pgsql_conn, &tmp_wckey)) ++ != SLURM_SUCCESS) ++ break; ++ } ++ } ++ pgsql_free_result(&result); ++ ++ if (!list_count(ret_list)) { ++ errno = SLURM_NO_CHANGE_IN_DATA; ++ DB_DEBUG(DB_WCKEY, pgsql_conn->conn, ++ "didn't affect anything\n%s", query); ++ xfree(query); ++ xfree(wckey_char); ++ return SLURM_SUCCESS; ++ } ++ ++ xfree(query); ++ rc = modify_common(pgsql_conn, DBD_MODIFY_WCKEYS, now, ++ user_name, wckey_table, wckey_char, ++ vals, cluster_name); ++ xfree(wckey_char); ++ ++ return rc; ++} ++ ++static int _cluster_get_wckeys(pgsql_conn_t *pgsql_conn, ++ slurmdb_wckey_cond_t *wckey_cond, ++ char *fields, ++ char *extra, ++ char *cluster_name, ++ List sent_list) ++{ ++ List wckey_list = NULL; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ char *query = NULL; ++ bool with_usage = 0; ++ ++ if (wckey_cond) ++ with_usage = wckey_cond->with_usage; ++ ++ xstrfmtcat(query, "SELECT DISTINCT %s FROM \"%s_%s\" AS t1%s " ++ "order by wckey_name, \"user\";", ++ fields, cluster_name, wckey_table, extra); ++ ++ DB_DEBUG(DB_WCKEY, pgsql_conn->conn, "query\n%s", query); ++ result = pgsql_db_query_ret(pgsql_conn, query, 0); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ xfree(query); ++ if (pgsql_db_match_state(pgsql_errno(result), PGSQL_UNDEFINE_TABLE)){ ++ pgsql_free_result(&result); ++ return SLURM_SUCCESS; ++ } ++ else { ++ pgsql_free_result(&result); ++ return SLURM_ERROR; ++ } ++ } ++ xfree(query); ++ ++ if (!pgsql_num_rows(result)) { ++ pgsql_free_result(&result); ++ return SLURM_SUCCESS; ++ } ++ ++ wckey_list = list_create(slurmdb_destroy_wckey_rec); ++ ++ while ((row = pgsql_fetch_row(result))) { ++ slurmdb_wckey_rec_t *wckey = ++ xmalloc(sizeof(slurmdb_wckey_rec_t)); ++ list_append(wckey_list, wckey); ++ ++ wckey->id = slurm_atoul(row[WCKEY_REQ_ID]); ++ wckey->is_def = slurm_atoul(row[WCKEY_REQ_DEFAULT]); ++ wckey->user = xstrdup(row[WCKEY_REQ_USER]); ++ ++ if (slurm_atoul(row[WCKEY_REQ_DELETED])) ++ wckey->flags |= SLURMDB_WCKEY_FLAG_DELETED; ++ ++ /* we want a blank wckey if the name is null */ ++ if (row[WCKEY_REQ_NAME]) ++ wckey->name = xstrdup(row[WCKEY_REQ_NAME]); ++ else ++ wckey->name = xstrdup(""); ++ ++ wckey->cluster = xstrdup(cluster_name); ++ } ++ pgsql_free_result(&result); ++ ++ if (with_usage && wckey_list && list_count(wckey_list)) ++ get_usage_for_list(pgsql_conn, DBD_GET_WCKEY_USAGE, ++ wckey_list, cluster_name, ++ wckey_cond->usage_start, ++ wckey_cond->usage_end); ++ list_transfer(sent_list, wckey_list); ++ FREE_NULL_LIST(wckey_list); ++ return SLURM_SUCCESS; ++} ++ ++/* extern functions */ ++ ++extern int as_pgsql_add_wckeys(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List wckey_list) ++{ ++ ListIterator itr = NULL; ++ int rc = SLURM_SUCCESS; ++ slurmdb_wckey_rec_t *object = NULL; ++ char *cols = NULL, *extra = NULL, *vals = NULL, *query = NULL, ++ *tmp_extra = NULL; ++ time_t now = time(NULL); ++ char *user_name = NULL; ++ int affect_rows = 0; ++ int added = 0; ++ List local_cluster_list = NULL; ++ List added_user_list = NULL; ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return ESLURM_DB_CONNECTION; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) ++ return ESLURM_ACCESS_DENIED; ++ ++ local_cluster_list = list_create(NULL); ++ ++ user_name = uid_to_string((uid_t) uid); ++ itr = list_iterator_create(wckey_list); ++ while ((object = list_next(itr))) { ++ if (!object->cluster || !object->cluster[0] ++ || !object->user || !object->user[0] ++ || !object->name) { ++ error("We need a wckey name (%s), cluster (%s), " ++ "and user (%s) to add.", ++ object->name, object->cluster, object->user); ++ rc = SLURM_ERROR; ++ continue; ++ } ++ ++ if (!added_user_list) ++ added_user_list = list_create(NULL); ++ if (!list_find_first(added_user_list, ++ slurm_find_char_in_list, ++ object->user)) ++ list_append(added_user_list, object->user); ++ xstrcat(cols, "creation_time, mod_time, \"user\""); ++ xstrfmtcat(vals, "%ld, %ld, '%s'", ++ now, now, object->user); ++ xstrfmtcat(extra, ", mod_time=%ld, \"user\"='%s'", ++ now, object->user); ++ ++ if (object->name) { ++ xstrcat(cols, ", wckey_name"); ++ xstrfmtcat(vals, ", '%s'", object->name); ++ xstrfmtcat(extra, ", wckey_name='%s'", object->name); ++ } ++ ++ /* When adding, if this isn't a default might as well ++ force it to be 0 to avoid confusion since ++ uninitialized it is NO_VAL. ++ */ ++ if (object->is_def == 1) { ++ xstrcat(cols, ", is_def"); ++ xstrcat(vals, ", 1"); ++ xstrcat(extra, ", is_def=1"); ++ } else { ++ object->is_def = 0; ++ xstrcat(cols, ", is_def"); ++ xstrcat(vals, ", 0"); ++ xstrcat(extra, ", is_def=0"); ++ } ++ ++ xstrfmtcat(query, ++ "INSERT INTO \"%s_%s\" (%s) VALUES (%s) " ++ "ON CONFLICT (id_wckey) DO UPDATE SET deleted=0, " ++ "id_wckey=CURRVAL(pg_get_serial_sequence('%s_%s', 'id_wckey'))%s;", ++ object->cluster, wckey_table, cols, vals, object->cluster, wckey_table, extra); ++ ++ DB_DEBUG(DB_WCKEY, pgsql_conn->conn, "query\n%s", query); ++ SQLHSTMT stmt_ptr = SQL_NULL_HSTMT; ++ rc = pgsql_db_query_stmt(pgsql_conn, query, &stmt_ptr); ++ xfree(query); ++ ++ object->id = (uint32_t)pgsql_insert_id(stmt_ptr); ++ if (!object->id) { ++ error("Couldn't add wckey %s", object->name); ++ added=0; ++ xfree(cols); ++ xfree(extra); ++ xfree(vals); ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ break; ++ } ++ ++ affect_rows = last_affected_rows(stmt_ptr); ++ (void)pgsql_db_free_statement(&stmt_ptr); ++ ++ if (!affect_rows) { ++ debug2("nothing changed %d", affect_rows); ++ xfree(cols); ++ xfree(extra); ++ xfree(vals); ++ continue; ++ } ++ ++ if (!list_find_first(local_cluster_list, ++ slurm_find_char_in_list, ++ object->cluster)) ++ list_append(local_cluster_list, object->cluster); ++ ++ /* we always have a ', ' as the first 2 chars */ ++ tmp_extra = slurm_add_slash_to_quotes(extra+2); ++ ++ xstrfmtcat(query, ++ "INSERT INTO %s " ++ "(timestamp, action, name, actor, info, cluster) " ++ "VALUES (%ld, %u, 'id_wckey=%d', '%s', '%s', '%s');", ++ txn_table, ++ now, DBD_ADD_WCKEYS, object->id, user_name, ++ tmp_extra, object->cluster); ++ ++ xfree(tmp_extra); ++ xfree(cols); ++ xfree(extra); ++ xfree(vals); ++ debug4("query\n%s",query); ++ rc = pgsql_db_query(pgsql_conn, query); ++ xfree(query); ++ if (rc != SLURM_SUCCESS) { ++ error("Couldn't add txn"); ++ } else { ++ if (addto_update_list(pgsql_conn->update_list, ++ SLURMDB_ADD_WCKEY, ++ object) == SLURM_SUCCESS) ++ list_remove(itr); ++ added++; ++ } ++ ++ } ++ list_iterator_destroy(itr); ++ xfree(user_name); ++ ++ if (!added) { ++ reset_pgsql_conn(pgsql_conn); ++ goto end_it; ++ } ++ ++ /* now reset all the other defaults accordingly. (if needed) */ ++ itr = list_iterator_create(wckey_list); ++ while ((object = list_next(itr))) { ++ if ((object->is_def != 1) || !object->cluster ++ || !object->user || !object->name) ++ continue; ++ if ((rc = _reset_default_wckey(pgsql_conn, object) ++ != SLURM_SUCCESS)) ++ break; ++ } ++ list_iterator_destroy(itr); ++end_it: ++ if (rc == SLURM_SUCCESS) ++ _make_sure_users_have_default(pgsql_conn, added_user_list, ++ local_cluster_list); ++ FREE_NULL_LIST(added_user_list); ++ FREE_NULL_LIST(local_cluster_list); ++ ++ return rc; ++} ++ ++extern List as_pgsql_modify_wckeys(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ slurmdb_wckey_cond_t *wckey_cond, ++ slurmdb_wckey_rec_t *wckey) ++{ ++ List ret_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *extra = NULL, *object = NULL, *vals = NULL; ++ char *user_name = NULL; ++ List use_cluster_list = NULL; ++ ListIterator itr; ++ bool locked = false; ++ ++ if (!wckey_cond || !wckey) { ++ error("we need something to change"); ++ return NULL; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { ++ if (wckey_cond->user_list ++ && (list_count(wckey_cond->user_list) == 1)) { ++ uid_t pw_uid; ++ char *name; ++ name = list_peek(wckey_cond->user_list); ++ if ((uid_from_string (name, &pw_uid) >= 0) ++ && (pw_uid == uid)) { ++ /* Make sure they aren't trying to ++ change something else and then set ++ this association as a default. ++ */ ++ slurmdb_init_wckey_rec(wckey, 1); ++ wckey->is_def = 1; ++ goto is_same_user; ++ } ++ } ++ ++ error("Only admins can modify wckeys"); ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++is_same_user: ++ ++ (void) _setup_wckey_cond_limits(wckey_cond, &extra); ++ ++ if (wckey->is_def == 1) ++ xstrcat(vals, ", is_def=1"); ++ ++ if (!extra || !vals) { ++ error("Nothing to modify '%s' '%s'", extra, vals); ++ return NULL; ++ } ++ ++ user_name = uid_to_string((uid_t) uid); ++ ++ if (wckey_cond->cluster_list && list_count(wckey_cond->cluster_list)) ++ use_cluster_list = wckey_cond->cluster_list; ++ else { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = list_shallow_copy(as_pgsql_cluster_list); ++ locked = true; ++ } ++ ++ ret_list = list_create(xfree_ptr); ++ itr = list_iterator_create(use_cluster_list); ++ while ((object = list_next(itr))) { ++ if ((rc = _cluster_modify_wckeys( ++ pgsql_conn, wckey, object, ++ extra, vals, user_name, ret_list)) ++ != SLURM_SUCCESS) ++ break; ++ } ++ list_iterator_destroy(itr); ++ xfree(extra); ++ xfree(user_name); ++ ++ if (locked) { ++ FREE_NULL_LIST(use_cluster_list); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ ++ if (rc == SLURM_ERROR) { ++ FREE_NULL_LIST(ret_list); ++ ret_list = NULL; ++ } ++ ++ return ret_list; ++} ++ ++extern List as_pgsql_remove_wckeys(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ slurmdb_wckey_cond_t *wckey_cond) ++{ ++ List ret_list = NULL; ++ int rc = SLURM_SUCCESS; ++ char *extra = NULL, *object = NULL; ++ char *user_name = NULL; ++ List use_cluster_list = NULL; ++ ListIterator itr; ++ bool locked = false; ++ ++ if (!wckey_cond) { ++ xstrcat(extra, " where deleted=0"); ++ goto empty; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ if (!is_user_min_admin_level(pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR)) { ++ errno = ESLURM_ACCESS_DENIED; ++ return NULL; ++ } ++ ++ (void) _setup_wckey_cond_limits(wckey_cond, &extra); ++ ++empty: ++ if (!extra) { ++ error("Nothing to remove"); ++ return NULL; ++ } ++ ++ user_name = uid_to_string((uid_t) uid); ++ ++ if (wckey_cond && wckey_cond->cluster_list && ++ list_count(wckey_cond->cluster_list)) { ++ use_cluster_list = wckey_cond->cluster_list; ++ } else { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = list_shallow_copy(as_pgsql_cluster_list); ++ locked = true; ++ } ++ ret_list = list_create(xfree_ptr); ++ itr = list_iterator_create(use_cluster_list); ++ while ((object = list_next(itr))) { ++ if ((rc = _cluster_remove_wckeys( ++ pgsql_conn, extra, object, user_name, ret_list)) ++ != SLURM_SUCCESS) ++ break; ++ } ++ list_iterator_destroy(itr); ++ xfree(extra); ++ xfree(user_name); ++ ++ if (locked) { ++ FREE_NULL_LIST(use_cluster_list); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ ++ if (rc == SLURM_ERROR) { ++ FREE_NULL_LIST(ret_list); ++ return NULL; ++ } ++ ++ return ret_list; ++} ++ ++extern List as_pgsql_get_wckeys(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_wckey_cond_t *wckey_cond) ++{ ++ //DEF_TIMERS; ++ char *extra = NULL; ++ char *tmp = NULL; ++ char *cluster_name = NULL; ++ List wckey_list = NULL; ++ int i=0, is_admin=1; ++ slurmdb_user_rec_t user; ++ List use_cluster_list = NULL; ++ ListIterator itr; ++ bool locked = false; ++ ++ if (!wckey_cond) { ++ xstrcat(extra, " where deleted=0"); ++ goto empty; ++ } ++ ++ if (check_connection(pgsql_conn) != SLURM_SUCCESS) ++ return NULL; ++ ++ memset(&user, 0, sizeof(slurmdb_user_rec_t)); ++ user.uid = uid; ++ ++ if (slurm_conf.private_data & PRIVATE_DATA_USERS) { ++ if (!(is_admin = is_user_min_admin_level( ++ pgsql_conn, uid, SLURMDB_ADMIN_OPERATOR))) { ++ assoc_mgr_fill_in_user( ++ pgsql_conn, &user, 1, NULL, false); ++ } ++ if (!is_admin && !user.name) { ++ debug("User %u has no associations, and is not admin, " ++ "so not returning any wckeys.", user.uid); ++ return NULL; ++ } ++ } ++ ++ (void) _setup_wckey_cond_limits(wckey_cond, &extra); ++ ++empty: ++ xfree(tmp); ++ xstrfmtcat(tmp, "t1.%s", wckey_req_inx[i]); ++ for(i=1; icluster_list && ++ list_count(wckey_cond->cluster_list)) { ++ use_cluster_list = wckey_cond->cluster_list; ++ } else { ++ slurm_rwlock_rdlock(&as_pgsql_cluster_list_lock); ++ use_cluster_list = list_shallow_copy(as_pgsql_cluster_list); ++ locked = true; ++ } ++ //START_TIMER; ++ itr = list_iterator_create(use_cluster_list); ++ while ((cluster_name = list_next(itr))) { ++ if (_cluster_get_wckeys(pgsql_conn, wckey_cond, tmp, extra, ++ cluster_name, wckey_list) ++ != SLURM_SUCCESS) { ++ FREE_NULL_LIST(wckey_list); ++ wckey_list = NULL; ++ break; ++ } ++ } ++ list_iterator_destroy(itr); ++ ++ if (locked) { ++ FREE_NULL_LIST(use_cluster_list); ++ slurm_rwlock_unlock(&as_pgsql_cluster_list_lock); ++ } ++ ++ xfree(tmp); ++ xfree(extra); ++ ++ //END_TIMER2("get_wckeys"); ++ return wckey_list; ++} +diff --git a/src/plugins/accounting_storage/pgsql/as_pgsql_wckey.h b/src/plugins/accounting_storage/pgsql/as_pgsql_wckey.h +new file mode 100755 +index 0000000..670a75d +--- /dev/null ++++ b/src/plugins/accounting_storage/pgsql/as_pgsql_wckey.h +@@ -0,0 +1,55 @@ ++/*****************************************************************************\ ++ * as_pgsql_wckey.h - functions dealing with the wckey. ++ ***************************************************************************** ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Copyright (C) 2008-2010 Lawrence Livermore National Security. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_WCKEY_H ++#define _HAVE_PGSQL_WCKEY_H ++ ++#include "accounting_storage_pgsql.h" ++ ++extern int as_pgsql_add_wckeys(pgsql_conn_t *pgsql_conn, uint32_t uid, ++ List wckey_list); ++extern List as_pgsql_modify_wckeys(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ slurmdb_wckey_cond_t *wckey_cond, ++ slurmdb_wckey_rec_t *wckey); ++extern List as_pgsql_remove_wckeys(pgsql_conn_t *pgsql_conn, ++ uint32_t uid, ++ slurmdb_wckey_cond_t *wckey_cond); ++extern List as_pgsql_get_wckeys(pgsql_conn_t *pgsql_conn, uid_t uid, ++ slurmdb_wckey_cond_t *wckey_cond); ++#endif +diff --git a/src/plugins/jobcomp/Makefile.am b/src/plugins/jobcomp/Makefile.am +index 10d254f..3f6a699 100644 +--- a/src/plugins/jobcomp/Makefile.am ++++ b/src/plugins/jobcomp/Makefile.am +@@ -1,3 +1,3 @@ + # Makefile for jobcomp plugins + +-SUBDIRS = elasticsearch filetxt lua none script mysql ++SUBDIRS = elasticsearch filetxt lua none script mysql pgsql +diff --git a/src/plugins/jobcomp/Makefile.in b/src/plugins/jobcomp/Makefile.in +index 69d86b3..0643803 100644 +--- a/src/plugins/jobcomp/Makefile.in ++++ b/src/plugins/jobcomp/Makefile.in +@@ -258,7 +258,7 @@ GTK_CFLAGS = @GTK_CFLAGS@ + GTK_LIBS = @GTK_LIBS@ + H5CC = @H5CC@ + H5FC = @H5FC@ +-HAVEMYSQLCONFIG = @HAVEMYSQLCONFIG@ ++HAVEODBCCONFIG = @HAVEODBCCONFIG@ + HAVE_MAN2HTML = @HAVE_MAN2HTML@ + HDF5_CC = @HDF5_CC@ + HDF5_CFLAGS = @HDF5_CFLAGS@ +@@ -308,8 +308,8 @@ MUNGE_CPPFLAGS = @MUNGE_CPPFLAGS@ + MUNGE_DIR = @MUNGE_DIR@ + MUNGE_LDFLAGS = @MUNGE_LDFLAGS@ + MUNGE_LIBS = @MUNGE_LIBS@ +-MYSQL_CFLAGS = @MYSQL_CFLAGS@ +-MYSQL_LIBS = @MYSQL_LIBS@ ++PGSQL_CFLAGS = @PGSQL_CFLAGS@ ++PGSQL_LIBS = @PGSQL_LIBS@ + NETLOC_CPPFLAGS = @NETLOC_CPPFLAGS@ + NETLOC_LDFLAGS = @NETLOC_LDFLAGS@ + NETLOC_LIBS = @NETLOC_LIBS@ +@@ -456,7 +456,7 @@ target_vendor = @target_vendor@ + top_build_prefix = @top_build_prefix@ + top_builddir = @top_builddir@ + top_srcdir = @top_srcdir@ +-SUBDIRS = elasticsearch filetxt lua none script mysql ++SUBDIRS = elasticsearch filetxt lua none script pgsql + all: all-recursive + + .SUFFIXES: +diff --git a/src/plugins/jobcomp/pgsql/Makefile.am b/src/plugins/jobcomp/pgsql/Makefile.am +new file mode 100755 +index 0000000..7132dd5 +--- /dev/null ++++ b/src/plugins/jobcomp/pgsql/Makefile.am +@@ -0,0 +1,26 @@ ++# Makefile for jobcomp/pgsql plugin ++ ++AUTOMAKE_OPTIONS = foreign ++ ++PLUGIN_FLAGS = -module -avoid-version --export-dynamic ++ ++AM_CPPFLAGS = -DSLURM_PLUGIN_DEBUG -I$(top_srcdir) -I$(top_srcdir)/src/common ++ ++if WITH_ODBC ++pkglib_LTLIBRARIES = jobcomp_pgsql.la ++ ++# pgsql storage plugin. ++jobcomp_pgsql_la_SOURCES = jobcomp_pgsql.c \ ++ pgsql_jobcomp_process.c pgsql_jobcomp_process.h ++jobcomp_pgsql_la_LDFLAGS = $(PLUGIN_FLAGS) ++jobcomp_pgsql_la_CFLAGS = $(ODBC_CFLAGS) ++jobcomp_pgsql_la_LIBADD = $(top_builddir)/src/database/libslurm_pgsql.la \ ++ $(ODBC_LIBS) ++ ++force: ++$(jobcomp_pgsql_la_LIBADD) : force ++ @cd `dirname $@` && $(MAKE) `basename $@` ++else ++EXTRA_jobcomp_pgsql_la_SOURCES = jobcomp_pgsql.c \ ++ pgsql_jobcomp_process.c pgsql_jobcomp_process.h ++endif +diff --git a/src/plugins/jobcomp/pgsql/Makefile.in b/src/plugins/jobcomp/pgsql/Makefile.in +new file mode 100755 +index 0000000..3c1ab33 +--- /dev/null ++++ b/src/plugins/jobcomp/pgsql/Makefile.in +@@ -0,0 +1,846 @@ ++# Makefile.in generated by automake 1.16.4 from Makefile.am. ++# @configure_input@ ++ ++# Copyright (C) 1994-2021 Free Software Foundation, Inc. ++ ++# This Makefile.in is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY, to the extent permitted by law; without ++# even the implied warranty of MERCHANTABILITY or FITNESS FOR A ++# PARTICULAR PURPOSE. ++ ++@SET_MAKE@ ++ ++# Makefile for jobcomp/pgsql plugin ++ ++VPATH = @srcdir@ ++am__is_gnu_make = { \ ++ if test -z '$(MAKELEVEL)'; then \ ++ false; \ ++ elif test -n '$(MAKE_HOST)'; then \ ++ true; \ ++ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ ++ true; \ ++ else \ ++ false; \ ++ fi; \ ++} ++am__make_running_with_option = \ ++ case $${target_option-} in \ ++ ?) ;; \ ++ *) echo "am__make_running_with_option: internal error: invalid" \ ++ "target option '$${target_option-}' specified" >&2; \ ++ exit 1;; \ ++ esac; \ ++ has_opt=no; \ ++ sane_makeflags=$$MAKEFLAGS; \ ++ if $(am__is_gnu_make); then \ ++ sane_makeflags=$$MFLAGS; \ ++ else \ ++ case $$MAKEFLAGS in \ ++ *\\[\ \ ]*) \ ++ bs=\\; \ ++ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ ++ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ ++ esac; \ ++ fi; \ ++ skip_next=no; \ ++ strip_trailopt () \ ++ { \ ++ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ ++ }; \ ++ for flg in $$sane_makeflags; do \ ++ test $$skip_next = yes && { skip_next=no; continue; }; \ ++ case $$flg in \ ++ *=*|--*) continue;; \ ++ -*I) strip_trailopt 'I'; skip_next=yes;; \ ++ -*I?*) strip_trailopt 'I';; \ ++ -*O) strip_trailopt 'O'; skip_next=yes;; \ ++ -*O?*) strip_trailopt 'O';; \ ++ -*l) strip_trailopt 'l'; skip_next=yes;; \ ++ -*l?*) strip_trailopt 'l';; \ ++ -[dEDm]) skip_next=yes;; \ ++ -[JT]) skip_next=yes;; \ ++ esac; \ ++ case $$flg in \ ++ *$$target_option*) has_opt=yes; break;; \ ++ esac; \ ++ done; \ ++ test $$has_opt = yes ++am__make_dryrun = (target_option=n; $(am__make_running_with_option)) ++am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) ++pkgdatadir = $(datadir)/@PACKAGE@ ++pkgincludedir = $(includedir)/@PACKAGE@ ++pkglibdir = $(libdir)/@PACKAGE@ ++pkglibexecdir = $(libexecdir)/@PACKAGE@ ++am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd ++install_sh_DATA = $(install_sh) -c -m 644 ++install_sh_PROGRAM = $(install_sh) -c ++install_sh_SCRIPT = $(install_sh) -c ++INSTALL_HEADER = $(INSTALL_DATA) ++transform = $(program_transform_name) ++NORMAL_INSTALL = : ++PRE_INSTALL = : ++POST_INSTALL = : ++NORMAL_UNINSTALL = : ++PRE_UNINSTALL = : ++POST_UNINSTALL = : ++build_triplet = @build@ ++host_triplet = @host@ ++target_triplet = @target@ ++subdir = src/plugins/jobcomp/pgsql ++ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 ++am__aclocal_m4_deps = $(top_srcdir)/auxdir/ax_check_compile_flag.m4 \ ++ $(top_srcdir)/auxdir/ax_gcc_builtin.m4 \ ++ $(top_srcdir)/auxdir/ax_lib_hdf5.m4 \ ++ $(top_srcdir)/auxdir/ax_pthread.m4 \ ++ $(top_srcdir)/auxdir/libtool.m4 \ ++ $(top_srcdir)/auxdir/ltoptions.m4 \ ++ $(top_srcdir)/auxdir/ltsugar.m4 \ ++ $(top_srcdir)/auxdir/ltversion.m4 \ ++ $(top_srcdir)/auxdir/lt~obsolete.m4 \ ++ $(top_srcdir)/auxdir/slurm.m4 \ ++ $(top_srcdir)/auxdir/slurmrestd.m4 \ ++ $(top_srcdir)/auxdir/x_ac_affinity.m4 \ ++ $(top_srcdir)/auxdir/x_ac_c99.m4 \ ++ $(top_srcdir)/auxdir/x_ac_cgroup.m4 \ ++ $(top_srcdir)/auxdir/x_ac_cray.m4 \ ++ $(top_srcdir)/auxdir/x_ac_curl.m4 \ ++ $(top_srcdir)/auxdir/x_ac_databases.m4 \ ++ $(top_srcdir)/auxdir/x_ac_debug.m4 \ ++ $(top_srcdir)/auxdir/x_ac_deprecated.m4 \ ++ $(top_srcdir)/auxdir/x_ac_dlfcn.m4 \ ++ $(top_srcdir)/auxdir/x_ac_env.m4 \ ++ $(top_srcdir)/auxdir/x_ac_freeipmi.m4 \ ++ $(top_srcdir)/auxdir/x_ac_http_parser.m4 \ ++ $(top_srcdir)/auxdir/x_ac_hwloc.m4 \ ++ $(top_srcdir)/auxdir/x_ac_json.m4 \ ++ $(top_srcdir)/auxdir/x_ac_jwt.m4 \ ++ $(top_srcdir)/auxdir/x_ac_lua.m4 \ ++ $(top_srcdir)/auxdir/x_ac_lz4.m4 \ ++ $(top_srcdir)/auxdir/x_ac_man2html.m4 \ ++ $(top_srcdir)/auxdir/x_ac_munge.m4 \ ++ $(top_srcdir)/auxdir/x_ac_netloc.m4 \ ++ $(top_srcdir)/auxdir/x_ac_nvml.m4 \ ++ $(top_srcdir)/auxdir/x_ac_ofed.m4 \ ++ $(top_srcdir)/auxdir/x_ac_pam.m4 \ ++ $(top_srcdir)/auxdir/x_ac_pmix.m4 \ ++ $(top_srcdir)/auxdir/x_ac_printf_null.m4 \ ++ $(top_srcdir)/auxdir/x_ac_ptrace.m4 \ ++ $(top_srcdir)/auxdir/x_ac_readline.m4 \ ++ $(top_srcdir)/auxdir/x_ac_rrdtool.m4 \ ++ $(top_srcdir)/auxdir/x_ac_rsmi.m4 \ ++ $(top_srcdir)/auxdir/x_ac_selinux.m4 \ ++ $(top_srcdir)/auxdir/x_ac_setproctitle.m4 \ ++ $(top_srcdir)/auxdir/x_ac_systemd.m4 \ ++ $(top_srcdir)/auxdir/x_ac_ucx.m4 \ ++ $(top_srcdir)/auxdir/x_ac_uid_gid_size.m4 \ ++ $(top_srcdir)/auxdir/x_ac_x11.m4 \ ++ $(top_srcdir)/auxdir/x_ac_yaml.m4 $(top_srcdir)/configure.ac ++am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ ++ $(ACLOCAL_M4) ++DIST_COMMON = $(srcdir)/Makefile.am ++mkinstalldirs = $(install_sh) -d ++CONFIG_HEADER = $(top_builddir)/config.h \ ++ $(top_builddir)/slurm/slurm_version.h ++CONFIG_CLEAN_FILES = ++CONFIG_CLEAN_VPATH_FILES = ++am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; ++am__vpath_adj = case $$p in \ ++ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ ++ *) f=$$p;; \ ++ esac; ++am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; ++am__install_max = 40 ++am__nobase_strip_setup = \ ++ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` ++am__nobase_strip = \ ++ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" ++am__nobase_list = $(am__nobase_strip_setup); \ ++ for p in $$list; do echo "$$p $$p"; done | \ ++ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ ++ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ ++ if (++n[$$2] == $(am__install_max)) \ ++ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ ++ END { for (dir in files) print dir, files[dir] }' ++am__base_list = \ ++ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ ++ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' ++am__uninstall_files_from_dir = { \ ++ test -z "$$files" \ ++ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ ++ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ ++ $(am__cd) "$$dir" && rm -f $$files; }; \ ++ } ++am__installdirs = "$(DESTDIR)$(pkglibdir)" ++LTLIBRARIES = $(pkglib_LTLIBRARIES) ++am__DEPENDENCIES_1 = ++@WITH_ODBC_TRUE@jobcomp_pgsql_la_DEPENDENCIES = $(top_builddir)/src/database/libslurm_pgsql.la \ ++@WITH_ODBC_TRUE@ $(am__DEPENDENCIES_1) ++@WITH_ODBC_TRUE@am_jobcomp_pgsql_la_OBJECTS = \ ++@WITH_ODBC_TRUE@ jobcomp_pgsql_la-jobcomp_pgsql.lo \ ++@WITH_ODBC_TRUE@ jobcomp_pgsql_la-pgsql_jobcomp_process.lo ++jobcomp_pgsql_la_OBJECTS = $(am_jobcomp_pgsql_la_OBJECTS) ++AM_V_lt = $(am__v_lt_@AM_V@) ++am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) ++am__v_lt_0 = --silent ++am__v_lt_1 = ++jobcomp_pgsql_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ ++ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ ++ $(jobcomp_pgsql_la_CFLAGS) $(CFLAGS) \ ++ $(jobcomp_pgsql_la_LDFLAGS) $(LDFLAGS) -o $@ ++@WITH_ODBC_TRUE@am_jobcomp_pgsql_la_rpath = -rpath $(pkglibdir) ++AM_V_P = $(am__v_P_@AM_V@) ++am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) ++am__v_P_0 = false ++am__v_P_1 = : ++AM_V_GEN = $(am__v_GEN_@AM_V@) ++am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) ++am__v_GEN_0 = @echo " GEN " $@; ++am__v_GEN_1 = ++AM_V_at = $(am__v_at_@AM_V@) ++am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) ++am__v_at_0 = @ ++am__v_at_1 = ++DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -I$(top_builddir)/slurm ++depcomp = $(SHELL) $(top_srcdir)/auxdir/depcomp ++am__maybe_remake_depfiles = depfiles ++am__depfiles_remade = ./$(DEPDIR)/jobcomp_pgsql_la-jobcomp_pgsql.Plo \ ++ ./$(DEPDIR)/jobcomp_pgsql_la-pgsql_jobcomp_process.Plo ++am__mv = mv -f ++COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ ++ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) ++LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ ++ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ ++ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ ++ $(AM_CFLAGS) $(CFLAGS) ++AM_V_CC = $(am__v_CC_@AM_V@) ++am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) ++am__v_CC_0 = @echo " CC " $@; ++am__v_CC_1 = ++CCLD = $(CC) ++LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ ++ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ ++ $(AM_LDFLAGS) $(LDFLAGS) -o $@ ++AM_V_CCLD = $(am__v_CCLD_@AM_V@) ++am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) ++am__v_CCLD_0 = @echo " CCLD " $@; ++am__v_CCLD_1 = ++SOURCES = $(jobcomp_pgsql_la_SOURCES) \ ++ $(EXTRA_jobcomp_pgsql_la_SOURCES) ++am__can_run_installinfo = \ ++ case $$AM_UPDATE_INFO_DIR in \ ++ n|no|NO) false;; \ ++ *) (install-info --version) >/dev/null 2>&1;; \ ++ esac ++am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) ++# Read a list of newline-separated strings from the standard input, ++# and print each of them once, without duplicates. Input order is ++# *not* preserved. ++am__uniquify_input = $(AWK) '\ ++ BEGIN { nonempty = 0; } \ ++ { items[$$0] = 1; nonempty = 1; } \ ++ END { if (nonempty) { for (i in items) print i; }; } \ ++' ++# Make sure the list of sources is unique. This is necessary because, ++# e.g., the same source file might be shared among _SOURCES variables ++# for different programs/libraries. ++am__define_uniq_tagged_files = \ ++ list='$(am__tagged_files)'; \ ++ unique=`for i in $$list; do \ ++ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ ++ done | $(am__uniquify_input)` ++ACLOCAL = @ACLOCAL@ ++AMTAR = @AMTAR@ ++AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ ++AR = @AR@ ++AR_FLAGS = @AR_FLAGS@ ++AUTOCONF = @AUTOCONF@ ++AUTOHEADER = @AUTOHEADER@ ++AUTOMAKE = @AUTOMAKE@ ++AWK = @AWK@ ++CC = @CC@ ++CCDEPMODE = @CCDEPMODE@ ++CFLAGS = @CFLAGS@ ++CHECK_CFLAGS = @CHECK_CFLAGS@ ++CHECK_LIBS = @CHECK_LIBS@ ++CPP = @CPP@ ++CPPFLAGS = @CPPFLAGS@ ++CRAY_JOB_CPPFLAGS = @CRAY_JOB_CPPFLAGS@ ++CRAY_JOB_LDFLAGS = @CRAY_JOB_LDFLAGS@ ++CRAY_SELECT_CPPFLAGS = @CRAY_SELECT_CPPFLAGS@ ++CRAY_SELECT_LDFLAGS = @CRAY_SELECT_LDFLAGS@ ++CRAY_SWITCH_CPPFLAGS = @CRAY_SWITCH_CPPFLAGS@ ++CRAY_SWITCH_LDFLAGS = @CRAY_SWITCH_LDFLAGS@ ++CRAY_TASK_CPPFLAGS = @CRAY_TASK_CPPFLAGS@ ++CRAY_TASK_LDFLAGS = @CRAY_TASK_LDFLAGS@ ++CSCOPE = @CSCOPE@ ++CTAGS = @CTAGS@ ++CXX = @CXX@ ++CXXCPP = @CXXCPP@ ++CXXDEPMODE = @CXXDEPMODE@ ++CXXFLAGS = @CXXFLAGS@ ++CYGPATH_W = @CYGPATH_W@ ++DATAWARP_CPPFLAGS = @DATAWARP_CPPFLAGS@ ++DATAWARP_LDFLAGS = @DATAWARP_LDFLAGS@ ++DEFS = @DEFS@ ++DEPDIR = @DEPDIR@ ++DLLTOOL = @DLLTOOL@ ++DL_LIBS = @DL_LIBS@ ++DSYMUTIL = @DSYMUTIL@ ++DUMPBIN = @DUMPBIN@ ++ECHO_C = @ECHO_C@ ++ECHO_N = @ECHO_N@ ++ECHO_T = @ECHO_T@ ++EGREP = @EGREP@ ++ETAGS = @ETAGS@ ++EXEEXT = @EXEEXT@ ++FGREP = @FGREP@ ++FREEIPMI_CPPFLAGS = @FREEIPMI_CPPFLAGS@ ++FREEIPMI_LDFLAGS = @FREEIPMI_LDFLAGS@ ++FREEIPMI_LIBS = @FREEIPMI_LIBS@ ++GLIB_CFLAGS = @GLIB_CFLAGS@ ++GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@ ++GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ ++GLIB_LIBS = @GLIB_LIBS@ ++GLIB_MKENUMS = @GLIB_MKENUMS@ ++GOBJECT_QUERY = @GOBJECT_QUERY@ ++GREP = @GREP@ ++GTK_CFLAGS = @GTK_CFLAGS@ ++GTK_LIBS = @GTK_LIBS@ ++H5CC = @H5CC@ ++H5FC = @H5FC@ ++HAVEODBCCONFIG = @HAVEODBCCONFIG@ ++HAVE_MAN2HTML = @HAVE_MAN2HTML@ ++HDF5_CC = @HDF5_CC@ ++HDF5_CFLAGS = @HDF5_CFLAGS@ ++HDF5_CPPFLAGS = @HDF5_CPPFLAGS@ ++HDF5_FC = @HDF5_FC@ ++HDF5_FFLAGS = @HDF5_FFLAGS@ ++HDF5_FLIBS = @HDF5_FLIBS@ ++HDF5_LDFLAGS = @HDF5_LDFLAGS@ ++HDF5_LIBS = @HDF5_LIBS@ ++HDF5_TYPE = @HDF5_TYPE@ ++HDF5_VERSION = @HDF5_VERSION@ ++HTTP_PARSER_CPPFLAGS = @HTTP_PARSER_CPPFLAGS@ ++HTTP_PARSER_LDFLAGS = @HTTP_PARSER_LDFLAGS@ ++HWLOC_CPPFLAGS = @HWLOC_CPPFLAGS@ ++HWLOC_LDFLAGS = @HWLOC_LDFLAGS@ ++HWLOC_LIBS = @HWLOC_LIBS@ ++INSTALL = @INSTALL@ ++INSTALL_DATA = @INSTALL_DATA@ ++INSTALL_PROGRAM = @INSTALL_PROGRAM@ ++INSTALL_SCRIPT = @INSTALL_SCRIPT@ ++INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ ++JSON_CPPFLAGS = @JSON_CPPFLAGS@ ++JSON_LDFLAGS = @JSON_LDFLAGS@ ++JWT_CPPFLAGS = @JWT_CPPFLAGS@ ++JWT_LDFLAGS = @JWT_LDFLAGS@ ++LD = @LD@ ++LDFLAGS = @LDFLAGS@ ++LIBCURL = @LIBCURL@ ++LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@ ++LIBOBJS = @LIBOBJS@ ++LIBS = @LIBS@ ++LIBTOOL = @LIBTOOL@ ++LIB_SLURM = @LIB_SLURM@ ++LIB_SLURM_BUILD = @LIB_SLURM_BUILD@ ++LIPO = @LIPO@ ++LN_S = @LN_S@ ++LTLIBOBJS = @LTLIBOBJS@ ++LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ ++LZ4_CPPFLAGS = @LZ4_CPPFLAGS@ ++LZ4_LDFLAGS = @LZ4_LDFLAGS@ ++LZ4_LIBS = @LZ4_LIBS@ ++MAINT = @MAINT@ ++MAKEINFO = @MAKEINFO@ ++MANIFEST_TOOL = @MANIFEST_TOOL@ ++MKDIR_P = @MKDIR_P@ ++MUNGE_CPPFLAGS = @MUNGE_CPPFLAGS@ ++MUNGE_DIR = @MUNGE_DIR@ ++MUNGE_LDFLAGS = @MUNGE_LDFLAGS@ ++MUNGE_LIBS = @MUNGE_LIBS@ ++PGSQL_CFLAGS = @PGSQL_CFLAGS@ ++PGSQL_LIBS = @PGSQL_LIBS@ ++NETLOC_CPPFLAGS = @NETLOC_CPPFLAGS@ ++NETLOC_LDFLAGS = @NETLOC_LDFLAGS@ ++NETLOC_LIBS = @NETLOC_LIBS@ ++NM = @NM@ ++NMEDIT = @NMEDIT@ ++NUMA_LIBS = @NUMA_LIBS@ ++NVML_CPPFLAGS = @NVML_CPPFLAGS@ ++NVML_LIBS = @NVML_LIBS@ ++OBJCOPY = @OBJCOPY@ ++OBJDUMP = @OBJDUMP@ ++OBJEXT = @OBJEXT@ ++OFED_CPPFLAGS = @OFED_CPPFLAGS@ ++OFED_LDFLAGS = @OFED_LDFLAGS@ ++OFED_LIBS = @OFED_LIBS@ ++OTOOL = @OTOOL@ ++OTOOL64 = @OTOOL64@ ++PACKAGE = @PACKAGE@ ++PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ ++PACKAGE_NAME = @PACKAGE_NAME@ ++PACKAGE_STRING = @PACKAGE_STRING@ ++PACKAGE_TARNAME = @PACKAGE_TARNAME@ ++PACKAGE_URL = @PACKAGE_URL@ ++PACKAGE_VERSION = @PACKAGE_VERSION@ ++PAM_DIR = @PAM_DIR@ ++PAM_LIBS = @PAM_LIBS@ ++PATH_SEPARATOR = @PATH_SEPARATOR@ ++PKG_CONFIG = @PKG_CONFIG@ ++PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ ++PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ ++PMIX_V1_CPPFLAGS = @PMIX_V1_CPPFLAGS@ ++PMIX_V1_LDFLAGS = @PMIX_V1_LDFLAGS@ ++PMIX_V2_CPPFLAGS = @PMIX_V2_CPPFLAGS@ ++PMIX_V2_LDFLAGS = @PMIX_V2_LDFLAGS@ ++PMIX_V3_CPPFLAGS = @PMIX_V3_CPPFLAGS@ ++PMIX_V3_LDFLAGS = @PMIX_V3_LDFLAGS@ ++PMIX_V4_CPPFLAGS = @PMIX_V4_CPPFLAGS@ ++PMIX_V4_LDFLAGS = @PMIX_V4_LDFLAGS@ ++PROJECT = @PROJECT@ ++PTHREAD_CC = @PTHREAD_CC@ ++PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ ++PTHREAD_LIBS = @PTHREAD_LIBS@ ++RANLIB = @RANLIB@ ++READLINE_LIBS = @READLINE_LIBS@ ++RELEASE = @RELEASE@ ++RRDTOOL_CPPFLAGS = @RRDTOOL_CPPFLAGS@ ++RRDTOOL_LDFLAGS = @RRDTOOL_LDFLAGS@ ++RRDTOOL_LIBS = @RRDTOOL_LIBS@ ++RSMI_CPPFLAGS = @RSMI_CPPFLAGS@ ++RSMI_LDFLAGS = @RSMI_LDFLAGS@ ++RSMI_LIBS = @RSMI_LIBS@ ++SED = @SED@ ++SET_MAKE = @SET_MAKE@ ++SHELL = @SHELL@ ++SLEEP_CMD = @SLEEP_CMD@ ++SLURMCTLD_PORT = @SLURMCTLD_PORT@ ++SLURMCTLD_PORT_COUNT = @SLURMCTLD_PORT_COUNT@ ++SLURMDBD_PORT = @SLURMDBD_PORT@ ++SLURMD_PORT = @SLURMD_PORT@ ++SLURMRESTD_PORT = @SLURMRESTD_PORT@ ++SLURM_API_AGE = @SLURM_API_AGE@ ++SLURM_API_CURRENT = @SLURM_API_CURRENT@ ++SLURM_API_MAJOR = @SLURM_API_MAJOR@ ++SLURM_API_REVISION = @SLURM_API_REVISION@ ++SLURM_API_VERSION = @SLURM_API_VERSION@ ++SLURM_MAJOR = @SLURM_MAJOR@ ++SLURM_MICRO = @SLURM_MICRO@ ++SLURM_MINOR = @SLURM_MINOR@ ++SLURM_PREFIX = @SLURM_PREFIX@ ++SLURM_VERSION_NUMBER = @SLURM_VERSION_NUMBER@ ++SLURM_VERSION_STRING = @SLURM_VERSION_STRING@ ++STRIP = @STRIP@ ++SUCMD = @SUCMD@ ++SYSTEMD_TASKSMAX_OPTION = @SYSTEMD_TASKSMAX_OPTION@ ++UCX_CPPFLAGS = @UCX_CPPFLAGS@ ++UCX_LDFLAGS = @UCX_LDFLAGS@ ++UCX_LIBS = @UCX_LIBS@ ++UTIL_LIBS = @UTIL_LIBS@ ++VERSION = @VERSION@ ++YAML_CPPFLAGS = @YAML_CPPFLAGS@ ++YAML_LDFLAGS = @YAML_LDFLAGS@ ++_libcurl_config = @_libcurl_config@ ++abs_builddir = @abs_builddir@ ++abs_srcdir = @abs_srcdir@ ++abs_top_builddir = @abs_top_builddir@ ++abs_top_srcdir = @abs_top_srcdir@ ++ac_ct_AR = @ac_ct_AR@ ++ac_ct_CC = @ac_ct_CC@ ++ac_ct_CXX = @ac_ct_CXX@ ++ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ ++ac_have_man2html = @ac_have_man2html@ ++am__include = @am__include@ ++am__leading_dot = @am__leading_dot@ ++am__quote = @am__quote@ ++am__tar = @am__tar@ ++am__untar = @am__untar@ ++ax_pthread_config = @ax_pthread_config@ ++bindir = @bindir@ ++build = @build@ ++build_alias = @build_alias@ ++build_cpu = @build_cpu@ ++build_os = @build_os@ ++build_vendor = @build_vendor@ ++builddir = @builddir@ ++datadir = @datadir@ ++datarootdir = @datarootdir@ ++docdir = @docdir@ ++dvidir = @dvidir@ ++exec_prefix = @exec_prefix@ ++host = @host@ ++host_alias = @host_alias@ ++host_cpu = @host_cpu@ ++host_os = @host_os@ ++host_vendor = @host_vendor@ ++htmldir = @htmldir@ ++includedir = @includedir@ ++infodir = @infodir@ ++install_sh = @install_sh@ ++libdir = @libdir@ ++libexecdir = @libexecdir@ ++libselinux_CFLAGS = @libselinux_CFLAGS@ ++libselinux_LIBS = @libselinux_LIBS@ ++localedir = @localedir@ ++localstatedir = @localstatedir@ ++lua_CFLAGS = @lua_CFLAGS@ ++lua_LIBS = @lua_LIBS@ ++mandir = @mandir@ ++mkdir_p = @mkdir_p@ ++oldincludedir = @oldincludedir@ ++pdfdir = @pdfdir@ ++prefix = @prefix@ ++program_transform_name = @program_transform_name@ ++psdir = @psdir@ ++runstatedir = @runstatedir@ ++sbindir = @sbindir@ ++sharedstatedir = @sharedstatedir@ ++srcdir = @srcdir@ ++sysconfdir = @sysconfdir@ ++systemdsystemunitdir = @systemdsystemunitdir@ ++target = @target@ ++target_alias = @target_alias@ ++target_cpu = @target_cpu@ ++target_os = @target_os@ ++target_vendor = @target_vendor@ ++top_build_prefix = @top_build_prefix@ ++top_builddir = @top_builddir@ ++top_srcdir = @top_srcdir@ ++AUTOMAKE_OPTIONS = foreign ++PLUGIN_FLAGS = -module -avoid-version --export-dynamic ++AM_CPPFLAGS = -DSLURM_PLUGIN_DEBUG -I$(top_srcdir) -I$(top_srcdir)/src/common ++@WITH_ODBC_TRUE@pkglib_LTLIBRARIES = jobcomp_pgsql.la ++ ++# Mysql storage plugin. ++@WITH_ODBC_TRUE@jobcomp_pgsql_la_SOURCES = jobcomp_pgsql.c \ ++@WITH_ODBC_TRUE@ pgsql_jobcomp_process.c pgsql_jobcomp_process.h ++ ++@WITH_ODBC_TRUE@jobcomp_pgsql_la_LDFLAGS = $(PLUGIN_FLAGS) ++@WITH_ODBC_TRUE@jobcomp_pgsql_la_CFLAGS = $(PGSQL_CFLAGS) ++@WITH_ODBC_TRUE@jobcomp_pgsql_la_LIBADD = $(top_builddir)/src/database/libslurm_pgsql.la \ ++@WITH_ODBC_TRUE@ $(PGSQL_LIBS) ++ ++@WITH_ODBC_FALSE@EXTRA_jobcomp_pgsql_la_SOURCES = jobcomp_pgsql.c \ ++@WITH_ODBC_FALSE@ pgsql_jobcomp_process.c pgsql_jobcomp_process.h ++ ++all: all-am ++ ++.SUFFIXES: ++.SUFFIXES: .c .lo .o .obj ++$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) ++ @for dep in $?; do \ ++ case '$(am__configure_deps)' in \ ++ *$$dep*) \ ++ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ ++ && { if test -f $@; then exit 0; else break; fi; }; \ ++ exit 1;; \ ++ esac; \ ++ done; \ ++ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/plugins/jobcomp/pgsql/Makefile'; \ ++ $(am__cd) $(top_srcdir) && \ ++ $(AUTOMAKE) --foreign src/plugins/jobcomp/pgsql/Makefile ++Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status ++ @case '$?' in \ ++ *config.status*) \ ++ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ ++ *) \ ++ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ ++ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ ++ esac; ++ ++$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) ++ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ++ ++$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) ++ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ++$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) ++ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ++$(am__aclocal_m4_deps): ++ ++install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) ++ @$(NORMAL_INSTALL) ++ @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ ++ list2=; for p in $$list; do \ ++ if test -f $$p; then \ ++ list2="$$list2 $$p"; \ ++ else :; fi; \ ++ done; \ ++ test -z "$$list2" || { \ ++ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ ++ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ ++ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ ++ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ ++ } ++ ++uninstall-pkglibLTLIBRARIES: ++ @$(NORMAL_UNINSTALL) ++ @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ ++ for p in $$list; do \ ++ $(am__strip_dir) \ ++ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ ++ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ ++ done ++ ++clean-pkglibLTLIBRARIES: ++ -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) ++ @list='$(pkglib_LTLIBRARIES)'; \ ++ locs=`for p in $$list; do echo $$p; done | \ ++ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ ++ sort -u`; \ ++ test -z "$$locs" || { \ ++ echo rm -f $${locs}; \ ++ rm -f $${locs}; \ ++ } ++ ++jobcomp_pgsql.la: $(jobcomp_pgsql_la_OBJECTS) $(jobcomp_pgsql_la_DEPENDENCIES) $(EXTRA_jobcomp_pgsql_la_DEPENDENCIES) ++ $(AM_V_CCLD)$(jobcomp_pgsql_la_LINK) $(am_jobcomp_pgsql_la_rpath) $(jobcomp_pgsql_la_OBJECTS) $(jobcomp_pgsql_la_LIBADD) $(LIBS) ++ ++mostlyclean-compile: ++ -rm -f *.$(OBJEXT) ++ ++distclean-compile: ++ -rm -f *.tab.c ++ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jobcomp_pgsql_la-jobcomp_pgsql.Plo@am__quote@ # am--include-marker ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jobcomp_pgsql_la-pgsql_jobcomp_process.Plo@am__quote@ # am--include-marker ++ ++$(am__depfiles_remade): ++ @$(MKDIR_P) $(@D) ++ @echo '# dummy' >$@-t && $(am__mv) $@-t $@ ++ ++am--depfiles: $(am__depfiles_remade) ++ ++.c.o: ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< ++ ++.c.obj: ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` ++ ++.c.lo: ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< ++ ++jobcomp_pgsql_la-jobcomp_pgsql.lo: jobcomp_pgsql.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(jobcomp_pgsql_la_CFLAGS) $(CFLAGS) -MT jobcomp_pgsql_la-jobcomp_pgsql.lo -MD -MP -MF $(DEPDIR)/jobcomp_pgsql_la-jobcomp_pgsql.Tpo -c -o jobcomp_pgsql_la-jobcomp_pgsql.lo `test -f 'jobcomp_pgsql.c' || echo '$(srcdir)/'`jobcomp_pgsql.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/jobcomp_pgsql_la-jobcomp_pgsql.Tpo $(DEPDIR)/jobcomp_pgsql_la-jobcomp_pgsql.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='jobcomp_pgsql.c' object='jobcomp_pgsql_la-jobcomp_pgsql.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(jobcomp_pgsql_la_CFLAGS) $(CFLAGS) -c -o jobcomp_pgsql_la-jobcomp_pgsql.lo `test -f 'jobcomp_pgsql.c' || echo '$(srcdir)/'`jobcomp_pgsql.c ++ ++jobcomp_pgsql_la-pgsql_jobcomp_process.lo: pgsql_jobcomp_process.c ++@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(jobcomp_pgsql_la_CFLAGS) $(CFLAGS) -MT jobcomp_pgsql_la-pgsql_jobcomp_process.lo -MD -MP -MF $(DEPDIR)/jobcomp_pgsql_la-pgsql_jobcomp_process.Tpo -c -o jobcomp_pgsql_la-pgsql_jobcomp_process.lo `test -f 'pgsql_jobcomp_process.c' || echo '$(srcdir)/'`pgsql_jobcomp_process.c ++@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/jobcomp_pgsql_la-pgsql_jobcomp_process.Tpo $(DEPDIR)/jobcomp_pgsql_la-pgsql_jobcomp_process.Plo ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pgsql_jobcomp_process.c' object='jobcomp_pgsql_la-pgsql_jobcomp_process.lo' libtool=yes @AMDEPBACKSLASH@ ++@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ ++@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(jobcomp_pgsql_la_CFLAGS) $(CFLAGS) -c -o jobcomp_pgsql_la-pgsql_jobcomp_process.lo `test -f 'pgsql_jobcomp_process.c' || echo '$(srcdir)/'`pgsql_jobcomp_process.c ++ ++mostlyclean-libtool: ++ -rm -f *.lo ++ ++clean-libtool: ++ -rm -rf .libs _libs ++ ++ID: $(am__tagged_files) ++ $(am__define_uniq_tagged_files); mkid -fID $$unique ++tags: tags-am ++TAGS: tags ++ ++tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) ++ set x; \ ++ here=`pwd`; \ ++ $(am__define_uniq_tagged_files); \ ++ shift; \ ++ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ ++ test -n "$$unique" || unique=$$empty_fix; \ ++ if test $$# -gt 0; then \ ++ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ ++ "$$@" $$unique; \ ++ else \ ++ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ ++ $$unique; \ ++ fi; \ ++ fi ++ctags: ctags-am ++ ++CTAGS: ctags ++ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) ++ $(am__define_uniq_tagged_files); \ ++ test -z "$(CTAGS_ARGS)$$unique" \ ++ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ ++ $$unique ++ ++GTAGS: ++ here=`$(am__cd) $(top_builddir) && pwd` \ ++ && $(am__cd) $(top_srcdir) \ ++ && gtags -i $(GTAGS_ARGS) "$$here" ++cscopelist: cscopelist-am ++ ++cscopelist-am: $(am__tagged_files) ++ list='$(am__tagged_files)'; \ ++ case "$(srcdir)" in \ ++ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ ++ *) sdir=$(subdir)/$(srcdir) ;; \ ++ esac; \ ++ for i in $$list; do \ ++ if test -f "$$i"; then \ ++ echo "$(subdir)/$$i"; \ ++ else \ ++ echo "$$sdir/$$i"; \ ++ fi; \ ++ done >> $(top_builddir)/cscope.files ++ ++distclean-tags: ++ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags ++check-am: all-am ++check: check-am ++all-am: Makefile $(LTLIBRARIES) ++installdirs: ++ for dir in "$(DESTDIR)$(pkglibdir)"; do \ ++ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ ++ done ++install: install-am ++install-exec: install-exec-am ++install-data: install-data-am ++uninstall: uninstall-am ++ ++install-am: all-am ++ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am ++ ++installcheck: installcheck-am ++install-strip: ++ if test -z '$(STRIP)'; then \ ++ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ ++ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ ++ install; \ ++ else \ ++ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ ++ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ ++ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ ++ fi ++mostlyclean-generic: ++ ++clean-generic: ++ ++distclean-generic: ++ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) ++ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) ++ ++maintainer-clean-generic: ++ @echo "This command is intended for maintainers to use" ++ @echo "it deletes files that may require special tools to rebuild." ++clean: clean-am ++ ++clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \ ++ mostlyclean-am ++ ++distclean: distclean-am ++ -rm -f ./$(DEPDIR)/jobcomp_pgsql_la-jobcomp_pgsql.Plo ++ -rm -f ./$(DEPDIR)/jobcomp_pgsql_la-pgsql_jobcomp_process.Plo ++ -rm -f Makefile ++distclean-am: clean-am distclean-compile distclean-generic \ ++ distclean-tags ++ ++dvi: dvi-am ++ ++dvi-am: ++ ++html: html-am ++ ++html-am: ++ ++info: info-am ++ ++info-am: ++ ++install-data-am: ++ ++install-dvi: install-dvi-am ++ ++install-dvi-am: ++ ++install-exec-am: install-pkglibLTLIBRARIES ++ ++install-html: install-html-am ++ ++install-html-am: ++ ++install-info: install-info-am ++ ++install-info-am: ++ ++install-man: ++ ++install-pdf: install-pdf-am ++ ++install-pdf-am: ++ ++install-ps: install-ps-am ++ ++install-ps-am: ++ ++installcheck-am: ++ ++maintainer-clean: maintainer-clean-am ++ -rm -f ./$(DEPDIR)/jobcomp_pgsql_la-jobcomp_pgsql.Plo ++ -rm -f ./$(DEPDIR)/jobcomp_pgsql_la-pgsql_jobcomp_process.Plo ++ -rm -f Makefile ++maintainer-clean-am: distclean-am maintainer-clean-generic ++ ++mostlyclean: mostlyclean-am ++ ++mostlyclean-am: mostlyclean-compile mostlyclean-generic \ ++ mostlyclean-libtool ++ ++pdf: pdf-am ++ ++pdf-am: ++ ++ps: ps-am ++ ++ps-am: ++ ++uninstall-am: uninstall-pkglibLTLIBRARIES ++ ++.MAKE: install-am install-strip ++ ++.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ ++ clean-generic clean-libtool clean-pkglibLTLIBRARIES \ ++ cscopelist-am ctags ctags-am distclean distclean-compile \ ++ distclean-generic distclean-libtool distclean-tags dvi dvi-am \ ++ html html-am info info-am install install-am install-data \ ++ install-data-am install-dvi install-dvi-am install-exec \ ++ install-exec-am install-html install-html-am install-info \ ++ install-info-am install-man install-pdf install-pdf-am \ ++ install-pkglibLTLIBRARIES install-ps install-ps-am \ ++ install-strip installcheck installcheck-am installdirs \ ++ maintainer-clean maintainer-clean-generic mostlyclean \ ++ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ ++ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ ++ uninstall-pkglibLTLIBRARIES ++ ++.PRECIOUS: Makefile ++ ++ ++@WITH_ODBC_TRUE@force: ++@WITH_ODBC_TRUE@$(jobcomp_pgsql_la_LIBADD) : force ++@WITH_ODBC_TRUE@ @cd `dirname $@` && $(MAKE) `basename $@` ++ ++# Tell versions [3.59,3.63) of GNU make to not export all variables. ++# Otherwise a system limit (for SysV at least) may be exceeded. ++.NOEXPORT: +diff --git a/src/plugins/jobcomp/pgsql/jobcomp_pgsql.c b/src/plugins/jobcomp/pgsql/jobcomp_pgsql.c +new file mode 100755 +index 0000000..bd99dd5 +--- /dev/null ++++ b/src/plugins/jobcomp/pgsql/jobcomp_pgsql.c +@@ -0,0 +1,429 @@ ++/*****************************************************************************\ ++ * jobcomp_pgsql.c - Store/Get all information in a pgsql 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 ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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 "pgsql_jobcomp_process.h" ++#include ++#include ++#include ++#include "src/common/parse_time.h" ++#include "src/common/node_select.h" ++#include "src/common/uid.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 ++ * ++ * / ++ * ++ * where is a description of the intended application of ++ * the plugin (e.g., "jobacct" for Slurm job completion logging) and ++ * 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 PGSQL plugin"; ++const char plugin_type[] = "jobcomp/pgsql"; ++const uint32_t plugin_version = SLURM_VERSION_NUMBER; ++ ++pgsql_conn_t *jobcomp_pgsql_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} ++}; ++ ++/* File descriptor used for logging */ ++static pthread_mutex_t jobcomp_lock = PTHREAD_MUTEX_INITIALIZER; ++ ++ ++static int _pgsql_jobcomp_check_tables() ++{ ++ if (pgsql_db_create_table(jobcomp_pgsql_conn, jobcomp_table, ++ jobcomp_table_fields, ++ ", primary key (jobid, starttime, endtime))") ++ == SLURM_ERROR) ++ return SLURM_ERROR; ++ ++ return SLURM_SUCCESS; ++} ++ ++ ++/* get the user name for the give user_id */ ++static char *_get_user_name(uint32_t user_id) ++{ ++ static uint32_t cache_uid = 0; ++ static char cache_name[32] = "root", *uname; ++ char *ret_name = NULL; ++ ++ slurm_mutex_lock(&jobcomp_lock); ++ if (user_id != cache_uid) { ++ uname = uid_to_string((uid_t) user_id); ++ snprintf(cache_name, sizeof(cache_name), "%s", uname); ++ xfree(uname); ++ cache_uid = user_id; ++ } ++ ret_name = xstrdup(cache_name); ++ slurm_mutex_unlock(&jobcomp_lock); ++ ++ return ret_name; ++} ++ ++/* get the group name for the give group_id */ ++static char *_get_group_name(uint32_t group_id) ++{ ++ static uint32_t cache_gid = 0; ++ static char cache_name[32] = "root", *gname; ++ char *ret_name = NULL; ++ ++ slurm_mutex_lock(&jobcomp_lock); ++ if (group_id != cache_gid) { ++ gname = gid_to_string((gid_t) group_id); ++ snprintf(cache_name, sizeof(cache_name), "%s", gname); ++ xfree(gname); ++ cache_gid = group_id; ++ } ++ ret_name = xstrdup(cache_name); ++ slurm_mutex_unlock(&jobcomp_lock); ++ ++ return ret_name; ++} ++ ++/* ++ * init() is called when the plugin is loaded, before any other functions ++ * are called. Put global initialization here. ++ */ ++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 int fini ( void ) ++{ ++ if (jobcomp_pgsql_conn) { ++ destroy_pgsql_conn(jobcomp_pgsql_conn); ++ jobcomp_pgsql_conn = NULL; ++ } ++ return SLURM_SUCCESS; ++} ++ ++extern int jobcomp_p_set_location(char *location) ++{ ++ pgsql_db_info_t *db_info; ++ int rc = SLURM_SUCCESS; ++ char *db_name = NULL; ++ int i = 0; ++ ++ if (jobcomp_pgsql_conn && pgsql_db_ping(jobcomp_pgsql_conn) == 0) ++ return SLURM_SUCCESS; ++ ++ if (!location) ++ db_name = xstrdup(slurm_conf.job_comp_loc); ++ else { ++ while(location[i]) { ++ if (location[i] == '.' || location[i] == '/') { ++ debug("%s doesn't look like a database " ++ "name using %s", ++ location, DEFAULT_JOB_COMP_DB); ++ break; ++ } ++ i++; ++ } ++ if (location[i]) ++ db_name = xstrdup(DEFAULT_JOB_COMP_DB); ++ else ++ db_name = xstrdup(location); ++ } ++ ++ debug2("pgsql_connect() called for db %s", db_name); ++ /* Just make sure our connection is gone. */ ++ fini(); ++ jobcomp_pgsql_conn = create_pgsql_conn(0, 0, NULL); ++ ++ db_info = create_pgsql_db_info(SLURM_PGSQL_PLUGIN_JC); ++ ++ pgsql_db_get_db_connection(jobcomp_pgsql_conn, db_name, db_info); ++ xfree(db_name); ++ ++ rc = _pgsql_jobcomp_check_tables(); ++ ++ destroy_pgsql_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_log_record(job_record_t *job_ptr) ++{ ++ int rc = SLURM_SUCCESS; ++ char *usr_str = NULL, *grp_str = NULL, lim_str[32], *jname = NULL; ++ char *connect_type = NULL, *reboot = NULL, *rotate = NULL, ++ *geometry = NULL, *start = NULL, ++ *blockid = NULL; ++ uint32_t job_state; ++ char *query = NULL, *on_dup = NULL; ++ uint32_t time_limit, start_time, end_time; ++ ++ if (!jobcomp_pgsql_conn || pgsql_db_ping(jobcomp_pgsql_conn) != 0) { ++ if (jobcomp_p_set_location(slurm_conf.job_comp_loc)) ++ return SLURM_ERROR; ++ } ++ ++ usr_str = _get_user_name(job_ptr->user_id); ++ grp_str = _get_group_name(job_ptr->group_id); ++ ++ 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"); ++ ++ connect_type = select_g_select_jobinfo_xstrdup(job_ptr->select_jobinfo, ++ SELECT_PRINT_CONNECTION); ++ reboot = select_g_select_jobinfo_xstrdup(job_ptr->select_jobinfo, ++ SELECT_PRINT_REBOOT); ++ rotate = select_g_select_jobinfo_xstrdup(job_ptr->select_jobinfo, ++ SELECT_PRINT_ROTATE); ++ geometry = select_g_select_jobinfo_xstrdup(job_ptr->select_jobinfo, ++ SELECT_PRINT_GEOMETRY); ++ start = select_g_select_jobinfo_xstrdup(job_ptr->select_jobinfo, ++ SELECT_PRINT_START); ++ blockid = select_g_select_jobinfo_xstrdup(job_ptr->select_jobinfo, ++ SELECT_PRINT_RESV_ID); ++ 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 (connect_type) ++ xstrcat(query, ", connect_type"); ++ if (reboot) ++ xstrcat(query, ", reboot"); ++ if (rotate) ++ xstrcat(query, ", rotate"); ++ if (job_ptr->details && (job_ptr->details->max_cpus != NO_VAL)) ++ xstrcat(query, ", maxprocs"); ++ if (geometry) ++ xstrcat(query, ", geometry"); ++ if (start) ++ xstrcat(query, ", start"); ++ if (blockid) ++ xstrcat(query, ", blockid"); ++ xstrfmtcat(query, ") values (%u, %u, '%s', %u, '%s', '%s', %u, %u, " ++ "'%s', '%s', %u, %u, %u", ++ job_ptr->job_id, job_ptr->user_id, usr_str, ++ job_ptr->group_id, grp_str, jname, ++ job_state, job_ptr->total_cpus, job_ptr->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, job_ptr->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 (connect_type) { ++ xstrfmtcat(query, ", '%s'", connect_type); ++ xstrfmtcat(on_dup, ", connect_type='%s'", connect_type); ++ xfree(connect_type); ++ } ++ if (reboot) { ++ xstrfmtcat(query, ", '%s'", reboot); ++ xstrfmtcat(on_dup, ", reboot='%s'", reboot); ++ xfree(reboot); ++ } ++ if (rotate) { ++ xstrfmtcat(query, ", '%s'", rotate); ++ xstrfmtcat(on_dup, ", rotate='%s'", rotate); ++ xfree(rotate); ++ } ++ 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); ++ } ++ ++ if (geometry) { ++ xstrfmtcat(query, ", '%s'", geometry); ++ xstrfmtcat(on_dup, ", geometry='%s'", geometry); ++ xfree(geometry); ++ } ++ if (start) { ++ xstrfmtcat(query, ", '%s'", start); ++ xstrfmtcat(on_dup, ", start='%s'", start); ++ xfree(start); ++ } ++ if (blockid) { ++ xstrfmtcat(query, ", '%s'", blockid); ++ xstrfmtcat(on_dup, ", blockid='%s'", blockid); ++ xfree(blockid); ++ } ++ xstrfmtcat(query, ") ON DUPLICATE KEY UPDATE %s;", on_dup); ++ ++ debug3("(%s:%d) query\n%s", ++ THIS_FILE, __LINE__, query); ++ rc = pgsql_db_query(jobcomp_pgsql_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 jobcomp_p_get_jobs(slurmdb_job_cond_t *job_cond) ++{ ++ List job_list = NULL; ++ ++ if (!jobcomp_pgsql_conn || pgsql_db_ping(jobcomp_pgsql_conn) != 0) { ++ if (jobcomp_p_set_location(slurm_conf.job_comp_loc)) ++ return job_list; ++ } ++ ++ job_list = pgsql_jobcomp_process_get_jobs(job_cond); ++ ++ return job_list; ++} ++ ++/* ++ * expire old info from the storage ++ */ ++extern int jobcomp_p_archive(slurmdb_archive_cond_t *arch_cond) ++{ ++ if (!jobcomp_pgsql_conn || pgsql_db_ping(jobcomp_pgsql_conn) != 0) { ++ if (jobcomp_p_set_location(slurm_conf.job_comp_loc)) ++ return SLURM_ERROR; ++ } ++ ++ return pgsql_jobcomp_process_archive(arch_cond); ++} +diff --git a/src/plugins/jobcomp/pgsql/pgsql_jobcomp_process.c b/src/plugins/jobcomp/pgsql/pgsql_jobcomp_process.c +new file mode 100755 +index 0000000..3fdb6d4 +--- /dev/null ++++ b/src/plugins/jobcomp/pgsql/pgsql_jobcomp_process.c +@@ -0,0 +1,187 @@ ++/*****************************************************************************\ ++ * pgsql_jobcomp_process.c - functions the processing of ++ * information from the pgsql jobcomp ++ * storage. ++ ***************************************************************************** ++ * ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++ * ++ * This file is patterned after jobcomp_linux.c, written by Morris Jette and ++ * Copyright (C) 2002 The Regents of the University of California. ++\*****************************************************************************/ ++ ++#include ++#include "src/common/parse_time.h" ++#include "src/common/xstring.h" ++#include "pgsql_jobcomp_process.h" ++ ++extern List pgsql_jobcomp_process_get_jobs(slurmdb_job_cond_t *job_cond) ++{ ++ ++ char *query = NULL; ++ char *extra = NULL; ++ char *tmp = NULL; ++ char *selected_part = NULL; ++ slurm_selected_step_t *selected_step = NULL; ++ ListIterator itr = NULL; ++ int set = 0; ++ pgsql_res_t *result = NULL; ++ pgsql_row row; ++ int i; ++ int lc = 0; ++ jobcomp_job_rec_t *job = NULL; ++ char time_str[32]; ++ time_t temp_time; ++ List job_list = list_create(jobcomp_destroy_job); ++ ++ if (job_cond->step_list && list_count(job_cond->step_list)) { ++ set = 0; ++ xstrcat(extra, " where ("); ++ itr = list_iterator_create(job_cond->step_list); ++ while ((selected_step = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " || "); ++ tmp = xstrdup_printf("jobid=%u", ++ selected_step->step_id.job_id); ++ xstrcat(extra, tmp); ++ set = 1; ++ xfree(tmp); ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ if (job_cond->partition_list && list_count(job_cond->partition_list)) { ++ set = 0; ++ if (extra) ++ xstrcat(extra, " && ("); ++ else ++ xstrcat(extra, " where ("); ++ ++ itr = list_iterator_create(job_cond->partition_list); ++ while ((selected_part = list_next(itr))) { ++ if (set) ++ xstrcat(extra, " || "); ++ tmp = xstrdup_printf("`partition`='%s'", ++ selected_part); ++ xstrcat(extra, tmp); ++ set = 1; ++ xfree(tmp); ++ } ++ list_iterator_destroy(itr); ++ xstrcat(extra, ")"); ++ } ++ ++ i = 0; ++ while (jobcomp_table_fields[i].name) { ++ if (i) ++ xstrcat(tmp, ", "); ++ xstrfmtcat(tmp, "`%s`", jobcomp_table_fields[i].name); ++ i++; ++ } ++ ++ query = xstrdup_printf("select %s from %s", tmp, jobcomp_table); ++ xfree(tmp); ++ ++ if (extra) { ++ xstrcat(query, extra); ++ xfree(extra); ++ } ++ ++ //info("query = %s", query); ++ result =pgsql_db_query_ret(jobcomp_pgsql_conn, query, 0); ++ xfree(query); ++ if (!pgsql_db_match_state(pgsql_errno(result), SUCCESSFUL_COMPLETION)) { ++ pgsql_free_result(&result); ++ FREE_NULL_LIST(job_list); ++ return NULL; ++ } ++ ++ while ((row = pgsql_fetch_row(result))) { ++ lc++; ++ ++ job = xmalloc(sizeof(jobcomp_job_rec_t)); ++ if (row[JOBCOMP_REQ_JOBID]) ++ job->jobid = slurm_atoul(row[JOBCOMP_REQ_JOBID]); ++ job->partition = xstrdup(row[JOBCOMP_REQ_PARTITION]); ++ temp_time = atoi(row[JOBCOMP_REQ_STARTTIME]); ++ slurm_make_time_str(&temp_time, ++ time_str, ++ sizeof(time_str)); ++ ++ job->start_time = xstrdup(time_str); ++ temp_time = atoi(row[JOBCOMP_REQ_ENDTIME]); ++ slurm_make_time_str(&temp_time, ++ time_str, ++ sizeof(time_str)); ++ ++ job->elapsed_time = atoi(row[JOBCOMP_REQ_ENDTIME]) ++ - atoi(row[JOBCOMP_REQ_STARTTIME]); ++ ++ job->end_time = xstrdup(time_str); ++ if (row[JOBCOMP_REQ_UID]) ++ job->uid = slurm_atoul(row[JOBCOMP_REQ_UID]); ++ job->uid_name = xstrdup(row[JOBCOMP_REQ_USER_NAME]); ++ if (row[JOBCOMP_REQ_GID]) ++ job->gid = slurm_atoul(row[JOBCOMP_REQ_GID]); ++ job->gid_name = xstrdup(row[JOBCOMP_REQ_GROUP_NAME]); ++ job->jobname = xstrdup(row[JOBCOMP_REQ_NAME]); ++ job->nodelist = xstrdup(row[JOBCOMP_REQ_NODELIST]); ++ if (row[JOBCOMP_REQ_NODECNT]) ++ job->node_cnt = slurm_atoul(row[JOBCOMP_REQ_NODECNT]); ++ if (row[JOBCOMP_REQ_STATE]) { ++ i = atoi(row[JOBCOMP_REQ_STATE]); ++ job->state = xstrdup(job_state_string(i)); ++ } ++ job->timelimit = xstrdup(row[JOBCOMP_REQ_TIMELIMIT]); ++ if (row[JOBCOMP_REQ_MAXPROCS]) ++ job->max_procs = slurm_atoul(row[JOBCOMP_REQ_MAXPROCS]); ++ job->connection = xstrdup(row[JOBCOMP_REQ_CONNECTION]); ++ job->reboot = xstrdup(row[JOBCOMP_REQ_REBOOT]); ++ job->rotate = xstrdup(row[JOBCOMP_REQ_ROTATE]); ++ job->geo = xstrdup(row[JOBCOMP_REQ_GEOMETRY]); ++ job->bg_start_point = xstrdup(row[JOBCOMP_REQ_START]); ++ job->blockid = xstrdup(row[JOBCOMP_REQ_BLOCKID]); ++ list_append(job_list, job); ++ } ++ ++ pgsql_free_result(&result); ++ ++ return job_list; ++} ++ ++extern int pgsql_jobcomp_process_archive(slurmdb_archive_cond_t *arch_cond) ++{ ++ return SLURM_SUCCESS; ++} +diff --git a/src/plugins/jobcomp/pgsql/pgsql_jobcomp_process.h b/src/plugins/jobcomp/pgsql/pgsql_jobcomp_process.h +new file mode 100755 +index 0000000..446b421 +--- /dev/null ++++ b/src/plugins/jobcomp/pgsql/pgsql_jobcomp_process.h +@@ -0,0 +1,89 @@ ++/*****************************************************************************\ ++ * pgsql_jobcomp_process.h - functions the processing of ++ * information from the pgsql jobcomp ++ * storage. ++ ***************************************************************************** ++ * ++ * Copyright (C) 2004-2007 The Regents of the University of California. ++ * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). ++ * Written by Danny Auble ++ * ++ * This file is part of Slurm, a resource management program. ++ * For details, see . ++ * 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. ++ * ++ * This file is patterned after jobcomp_linux.c, written by Morris Jette and ++ * Copyright (C) 2002 The Regents of the University of California. ++\*****************************************************************************/ ++ ++#ifndef _HAVE_PGSQL_JOBCOMP_PROCESS_H ++#define _HAVE_PGSQL_JOBCOMP_PROCESS_H ++ ++#include "src/database/pgsql_common.h" ++#include "src/common/slurm_jobcomp.h" ++#include "src/common/slurm_jobacct_gather.h" ++#include "src/common/slurm_accounting_storage.h" ++ ++extern pgsql_conn_t *jobcomp_pgsql_conn; ++extern int jobcomp_db_init; ++ ++extern char *jobcomp_table; ++/* This variable and the following enum are related so if you change ++ the jobcomp_table_fields defined in pgsql_jobcomp.c you must update ++ this enum accordingly. ++*/ ++extern storage_field_t jobcomp_table_fields[]; ++enum { ++ JOBCOMP_REQ_JOBID, ++ JOBCOMP_REQ_UID, ++ JOBCOMP_REQ_USER_NAME, ++ JOBCOMP_REQ_GID, ++ JOBCOMP_REQ_GROUP_NAME, ++ JOBCOMP_REQ_NAME, ++ JOBCOMP_REQ_STATE, ++ JOBCOMP_REQ_PARTITION, ++ JOBCOMP_REQ_TIMELIMIT, ++ JOBCOMP_REQ_STARTTIME, ++ JOBCOMP_REQ_ENDTIME, ++ JOBCOMP_REQ_NODELIST, ++ JOBCOMP_REQ_NODECNT, ++ JOBCOMP_REQ_CONNECTION, ++ JOBCOMP_REQ_REBOOT, ++ JOBCOMP_REQ_ROTATE, ++ JOBCOMP_REQ_MAXPROCS, ++ JOBCOMP_REQ_GEOMETRY, ++ JOBCOMP_REQ_START, ++ JOBCOMP_REQ_BLOCKID, ++ JOBCOMP_REQ_COUNT ++}; ++ ++extern List pgsql_jobcomp_process_get_jobs(slurmdb_job_cond_t *job_cond); ++ ++extern int pgsql_jobcomp_process_archive(slurmdb_archive_cond_t *arch_cond); ++ ++#endif +-- +2.33.0 + diff --git a/slurm.spec b/slurm.spec index 3f68b4658c9d857b8548cecbf93be3950f303986..9304676d4f54f424a5110240197d3e759fa3deb1 100644 --- a/slurm.spec +++ b/slurm.spec @@ -1,6 +1,6 @@ Name: slurm Version: 21.08.8 -%define rel 3 +%define rel 4 Release: %{rel}%{?dist} Summary: Slurm Workload Manager @@ -17,6 +17,7 @@ URL: https://slurm.schedmd.com/ Source: %{slurm_source_dir}.tar.bz2 Patch1: 0001-support-oversubscribe-in-submit_job-restapi.patch +Patch2: 0002-add-pgsql-plugin.patch # build options .rpmmacros options change to default action # ==================== ==================== ======================== @@ -31,6 +32,7 @@ Patch1: 0001-support-oversubscribe-in-submit_job-restapi.patch # --with hwloc %_with_hwloc 1 require hwloc support # --with lua %_with_lua path build Slurm lua bindings # --with mysql %_with_mysql 1 require mysql/mariadb support +# --with pgsql %_with_pgsql 1 require unixODBC support # --with numa %_with_numa 1 require NUMA support # --without pam %_without_pam 1 don't require pam-devel RPM to be installed # --without x11 %_without_x11 1 disable internal X11 support @@ -52,6 +54,7 @@ Patch1: 0001-support-oversubscribe-in-submit_job-restapi.patch # If they are not set they will still be compiled if the packages exist. %bcond_with hwloc %bcond_with mysql +%bcond_with pgsql %bcond_with hdf5 %bcond_with lua %bcond_with numa @@ -70,7 +73,7 @@ Patch1: 0001-support-oversubscribe-in-submit_job-restapi.patch %global _hardened_cflags "-Wl,-z,lazy" %global _hardened_ldflags "-Wl,-z,lazy" -Requires: munge +Requires: munge %{?systemd_requires} BuildRequires: systemd @@ -79,6 +82,8 @@ BuildRequires: python3 BuildRequires: readline-devel BuildRequires: perl-devel BuildRequires: mariadb-devel +BuildRequires: glib2-devel +BuildRequires: gtk2-devel Obsoletes: slurm-lua slurm-munge slurm-plugins # fake systemd support when building rpms on other platforms @@ -94,6 +99,10 @@ BuildRequires: mariadb-devel >= 5.0.0 %endif %endif +%if %{with pgsql} +BuildRequires: unixODBC-devel >= 2.3.7 +%endif + %if %{with cray} BuildRequires: cray-libalpscomm_cn-devel BuildRequires: cray-libalpscomm_sn-devel @@ -335,6 +344,7 @@ notifies slurm about failed nodes. %prep # when the rel number is one, the tarball filename does not include it %autosetup -n %{slurm_source_dir} -p1 +autoreconf -ivf %build %configure \ @@ -342,6 +352,7 @@ notifies slurm about failed nodes. %{?_with_pam_dir} \ %{?_with_cpusetdir} \ %{?_with_mysql_config} \ + %{?_with_pgsql_config} \ %{?_with_ssl} \ %{?_without_cray:--enable-really-no-cray}\ %{?_with_cray_network:--enable-cray-network}\ @@ -530,6 +541,7 @@ rm -rf %{buildroot} %{_libdir}/slurm/src/* %{_libdir}/slurm/*.so %exclude %{_libdir}/slurm/accounting_storage_mysql.so +%exclude %{_libdir}/slurm/accounting_storage_pgsql.so %exclude %{_libdir}/slurm/job_submit_pbs.so %exclude %{_libdir}/slurm/spank_pbs.so %{_mandir} @@ -596,6 +608,9 @@ rm -rf %{buildroot} %defattr(-,root,root) %{_sbindir}/slurmdbd %{_libdir}/slurm/accounting_storage_mysql.so +%if %{with pgsql} +%{_libdir}/slurm/accounting_storage_pgsql.so +%endif %{_unitdir}/slurmdbd.service ############################################################################# @@ -694,6 +709,9 @@ rm -rf %{buildroot} %systemd_postun_with_restart slurmdbd.service %changelog +* Fri Apr 19 2024 Xing Liu - 21.08.8-4 +- add pgsql plugin. + * Fri Mar 15 2024 Xing Liu - 21.08.8-3 - support '--oversubscribe' attribute in submit_job restapi.