blob: 4818849b113514f9a39d53778faa22d1703d553d [file] [log] [blame] [edit]
/*****************************************************************************\
* layouts_mgr.c - layouts manager data structures and main functions
*****************************************************************************
* Initially written by Francois Chevallier <chevallierfrancois@free.fr>
* at Bull for slurm-2.6.
* Adapted by Matthieu Hautreux <matthieu.hautreux@cea.fr> for slurm-14.11.
* Enhanced by Matthieu Hautreux <matthieu.hautreux@cea.fr> for slurm-15.x.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include <ctype.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "layouts_mgr.h"
#include "slurm/slurm.h"
#include "slurm/slurm_errno.h"
#include "src/common/entity.h"
#include "src/common/layout.h"
#include "src/common/hostlist.h"
#include "src/common/list.h"
#include "src/common/node_conf.h"
#include "src/common/pack.h"
#include "src/common/plugin.h"
#include "src/common/read_config.h"
#include "src/common/slurm_protocol_api.h"
#include "src/common/strlcpy.h"
#include "src/common/timers.h"
#include "src/common/xstring.h"
#include "src/common/xtree.h"
#include "src/common/xmalloc.h"
#define PATHLEN 256
/* use to specify which layout callbacks to perform while loading data
* from conf files, state files or input buffers */
#define CONF_DONE 0x00000001
#define PARSE_ENTITY 0x00000002
#define UPDATE_DONE 0x00000004
#define PARSE_RELATIONS 0x00000008
/*****************************************************************************\
* STRUCTURES AND TYPES *
\*****************************************************************************/
/*
* layouts_conf_spec_t - structure used to keep track of layouts conf details
*/
typedef struct layouts_conf_spec_st {
char* whole_name;
char* name;
char* type;
} layouts_conf_spec_t;
static void layouts_conf_spec_free(void* x)
{
layouts_conf_spec_t* spec = (layouts_conf_spec_t*)x;
xfree(spec->whole_name);
xfree(spec->type);
xfree(spec->name);
xfree(spec);
}
/*
* layout ops - operations associated to layout plugins
*
* This struct is populated while opening the plugin and linking the
* associated symbols. See layout_syms description for the name of the "public"
* symbols associated to this structure fields.
*
* Notes : the layouts plugins are able to access the entities hashtable in order
* to read/create/modify entities as necessary during the load_entities and
* build_layout API calls.
*
*/
typedef struct layout_ops_st {
layouts_plugin_spec_t* spec;
int (*conf_done) (xhash_t* entities, layout_t* layout,
s_p_hashtbl_t* tbl);
void (*entity_parsing) (entity_t* e, s_p_hashtbl_t* etbl,
layout_t* layout);
int (*update_done) (layout_t* layout, entity_t** e_array,
int e_cnt);
} layout_ops_t;
/*
* layout plugin symbols - must be synchronized with ops structure definition
* as previously detailed, that's why though being a global constant,
* it is placed in this section.
*/
const char *layout_syms[] = {
"plugin_spec", /* holds constants, definitions, ... */
"layouts_p_conf_done", /* */
"layouts_p_entity_parsing",
"layouts_p_update_done",
};
/*
* layout_plugin_t - it is the structure holding the plugin context of the
* associated layout plugin as well as the ptr to the dlsymed calls.
* It is used by the layouts manager to operate on the different layouts
* loaded during the layouts framework initialization
*/
typedef struct layout_plugin_st {
plugin_context_t* context;
layout_t* layout;
char* name;
layout_ops_t* ops;
} layout_plugin_t;
static void _layout_plugins_destroy(layout_plugin_t *lp)
{
plugin_context_destroy(lp->context);
/* it might be interesting to also dlclose the ops here */
xfree(lp->name);
xfree(lp->ops);
}
/*
* layouts_keydef_t - entities similar keys share a same key definition
* in order to avoid loosing too much memory duplicating similar data
* like the key str itself and custom destroy/dump functions.
*
* The layouts manager keeps an hash table of the various keydefs and use
* the factorized details while parsing the configuration and creating the
* entity_data_t structs associated to the entities.
*
* Note custom_* functions are used if they are not NULL* and type equals
* L_T_CUSTOM
*/
typedef struct layouts_keydef_st {
char* key; /* lower case key prefixed by the
"%layout_type%." string */
char* shortkey; /* original key as defined in
the layout keys definition */
layouts_keydef_types_t type;
uint32_t flags;
void (*custom_destroy)(void* value);
char* (*custom_dump)(void* value);
layout_plugin_t* plugin;
char* ref_key; /* lower case reference key prefixed by
the "%layout_type%." might be NULL
if not defined. */
char* ref_shortkey; /* original ref key as defined in
the layout keys definition,
might be null too. */
} layouts_keydef_t;
/*
* layouts_keydef_idfunc - identity function to build an hash table of
* layouts_keydef_t
*/
static void layouts_keydef_idfunc(void* item, const char** key,
uint32_t* key_len)
{
layouts_keydef_t* keydef = (layouts_keydef_t*)item;
*key = keydef->key;
*key_len = strlen(keydef->key);
}
/*
* layouts_mgr_t - the main structure holding all the layouts, entities and
* shared keydefs as well as conf elements and plugins details.
*/
typedef struct layouts_mgr_st {
pthread_mutex_t lock;
bool init_done; /* Set if memory allocated for arrays/List */
layout_plugin_t *plugins;
uint32_t plugins_count;
List layouts_desc; /* list of the layouts requested in conf */
xhash_t *layouts; /* hash tbl of loaded layout structs (by type) */
xhash_t *entities; /* hash tbl of loaded entity structs (by name) */
xhash_t *keydefs; /* info on key types, how to free them etc */
} layouts_mgr_t;
/*****************************************************************************\
* GLOBALS *
\*****************************************************************************/
/** global structure holding layouts and entities */
static layouts_mgr_t layouts_mgr = {PTHREAD_MUTEX_INITIALIZER, false};
static layouts_mgr_t* mgr = &layouts_mgr;
/*****************************************************************************\
* HELPERS *
\*****************************************************************************/
/* entities added to the layouts mgr hash table come from the heap,
* this function will help to free them while freeing the hash table */
static void _entity_free(void* item)
{
entity_t* entity = (entity_t*) item;
entity_free(entity);
xfree(entity);
}
/* layouts added to the layouts mgr hash table come from the heap,
* this function will help to free them while freeing the hash table */
static void _layout_free(void* item)
{
layout_t* layout = (layout_t*) item;
layout_free(layout);
xfree(layout);
}
/* keydef added to the layouts mgr hash table come from the heap,
* this function will help to free them while freeing the hash table */
static void _layouts_keydef_free(void* x)
{
layouts_keydef_t* keydef = (layouts_keydef_t*)x;
xfree(keydef->key);
xfree(keydef->shortkey);
xfree(keydef->ref_key);
xfree(keydef->ref_shortkey);
xfree(keydef);
}
/* generic xfree callback */
static void xfree_as_callback(void* p)
{
xfree(p);
}
/* safer behavior than plain strncat */
static char* _cat(char* dest, const char* src, size_t n)
{
size_t len;
char* r;
if (n == 0)
return dest;
len = strlen(dest);
if (n - len - 1 <= 0) {
dest[n - 1] = 0;
return dest;
}
r = strncat(dest, src, n - len - 1);
dest[n - 1] = 0;
return r;
}
static char* _trim(char* str)
{
char* str_modifier;
if (!str)
return str;
while (*str && isspace(*str)) ++str;
str_modifier = str + strlen(str) - 1;
while (str_modifier >= str && isspace(*str_modifier)) {
*str_modifier = '\0';
--str_modifier;
}
return str;
}
/* check if str is in strings (null terminated string array) */
/* TODO: replace this with a xhash instead for next modification */
static int _string_in_array(const char* str, const char** strings)
{
xassert(strings); /* if etypes no specified in plugin, no new entity
should be created */
for (; *strings; ++strings) {
if (!xstrcmp(str, *strings))
return 1;
}
return 0;
}
static void _normalize_keydef_keycore(char* buffer, uint32_t size,
const char* key, const char* plugtype,
bool cat)
{
int i;
char keytmp[PATHLEN];
for (i = 0; plugtype[i] && i < PATHLEN - 1; ++i) {
keytmp[i] = tolower(plugtype[i]);
}
keytmp[i] = 0;
if (cat) {
_cat(buffer, keytmp, size);
} else {
strlcpy(buffer, keytmp, size);
}
_cat(buffer, ".", size);
for (i = 0; key[i] && i < PATHLEN - 1; ++i) {
keytmp[i] = tolower(key[i]);
}
keytmp[i] = 0;
_cat(buffer, keytmp, size);
}
static void _normalize_keydef_key(char* buffer, uint32_t size,
const char* key, const char* plugtype)
{
_normalize_keydef_keycore(buffer, size, key, plugtype, false);
}
static void _normalize_keydef_mgrkey(char* buffer, uint32_t size,
const char* key, const char* plugtype)
{
strlcpy(buffer, "mgr.", size);
_normalize_keydef_keycore(buffer, size, key, plugtype, true);
}
static void _entity_add_data(entity_t* e, const char* key, void* data)
{
layouts_keydef_t* hkey = xhash_get_str(mgr->keydefs, key);
xassert(hkey);
void (*freefunc)(void* p) = xfree_as_callback;
if (hkey && hkey->type == L_T_CUSTOM) {
freefunc = hkey->custom_destroy;
}
entity_set_data_ref(e, hkey->key, data, freefunc);
}
/*
* used in both automerge and autoupdate calls when dealing with
* advanced operations (SUM,MIN,MAX,AVG,...) while setting new key values
*/
#define _entity_update_kv_helper(type_t, operator) \
type_t* lvalue = (type_t*) oldvalue; \
type_t* rvalue = (type_t*) value; \
uint32_t* divider; \
switch (operator) { \
case S_P_OPERATOR_SET: \
*lvalue = *rvalue; \
break; \
case S_P_OPERATOR_ADD: \
*lvalue += *rvalue; \
break; \
case S_P_OPERATOR_SUB: \
*lvalue -= *rvalue; \
break; \
case S_P_OPERATOR_MUL: \
*lvalue *= *rvalue; \
break; \
case S_P_OPERATOR_DIV: \
if (*rvalue != (type_t) 0) \
*lvalue /= *rvalue; \
else { \
error("layouts: entity_update: " \
"key=%s val=0 operator=" \
"DIV !! skipping !!", \
keydef->key); \
} \
break; \
case S_P_OPERATOR_AVG: \
divider = (uint32_t*) value; \
if (*divider != (uint32_t) 0) \
*lvalue /= (type_t) *divider; \
else { \
error("layouts: entity_update: " \
"key=%s val=0 operator=" \
"AVG !! skipping !!", \
keydef->key); \
} \
break; \
case S_P_OPERATOR_SET_IF_MIN: \
if (*rvalue < *lvalue) \
*lvalue = *rvalue; \
break; \
case S_P_OPERATOR_SET_IF_MAX: \
if (*rvalue > *lvalue) \
*lvalue = *rvalue; \
break; \
default: \
break; \
}
static int _layouts_autoupdate_layout(layout_t* layout);
static int _layouts_autoupdate_layout_if_allowed(layout_t* layout);
/*****************************************************************************\
* LAYOUTS INTERNAL LOCKLESS API *
\*****************************************************************************/
layouts_keydef_t* _layouts_entity_get_kv_keydef(layout_t* l, entity_t* e,
char* key)
{
char keytmp[PATHLEN];
if (l == NULL || e == NULL || key == NULL)
return NULL;
_normalize_keydef_key(keytmp, PATHLEN, key, l->type);
return xhash_get_str(mgr->keydefs, keytmp);
}
int _layouts_entity_get_kv_type(layout_t* l, entity_t* e, char* key)
{
layouts_keydef_t* keydef;
keydef = _layouts_entity_get_kv_keydef(l, e, key);
if (keydef != NULL) {
return keydef->type;
}
return SLURM_ERROR;
}
int _layouts_entity_get_kv_flags(layout_t* l, entity_t* e, char* key)
{
layouts_keydef_t* keydef;
keydef = _layouts_entity_get_kv_keydef(l, e, key);
if (keydef != NULL) {
return keydef->flags;
}
return SLURM_ERROR;
}
int _layouts_entity_get_kv_size(layout_t* l, entity_t* e, char* key, size_t *size)
{
layouts_keydef_t* keydef;
keydef = _layouts_entity_get_kv_keydef(l, e, key);
if (keydef != NULL) {
switch(keydef->type) {
case L_T_ERROR:
return SLURM_ERROR;
case L_T_STRING:
*size = sizeof(void*);
break;
case L_T_CUSTOM:
*size = sizeof(void*);
break;
case L_T_LONG:
*size = sizeof(long);
break;
case L_T_UINT16:
*size = sizeof(uint16_t);
break;
case L_T_UINT32:
*size = sizeof(uint32_t);
break;
case L_T_BOOLEAN:
*size = sizeof(bool);
break;
case L_T_FLOAT:
*size = sizeof(float);
break;
case L_T_DOUBLE:
*size = sizeof(double);
break;
case L_T_LONG_DOUBLE:
*size = sizeof(long double);
break;
}
} else
return SLURM_ERROR;
return SLURM_SUCCESS;
}
bool _layouts_entity_check_kv_keytype(layout_t* l, entity_t* e, char* key,
layouts_keydef_types_t key_type)
{
layouts_keydef_types_t real_type;
if (l == NULL || e == NULL || key == NULL)
return SLURM_ERROR;
if (key_type) {
real_type = _layouts_entity_get_kv_type(l, e, key);
return (real_type == key_type);
}
/* no key type provided, consider that as a no-check request */
return true;
}
int _layouts_entity_push_kv(layout_t* l, entity_t* e, char* key)
{
/* a more advanced implementation should only pull what is necessary
* instead of forcing a full autoupdate */
return _layouts_autoupdate_layout_if_allowed(l);
}
int _layouts_entity_pull_kv(layout_t* l, entity_t* e, char* key)
{
/* a more advanced implementation should only pull what is necessary
* instead of forcing a full autoupdate */
return _layouts_autoupdate_layout_if_allowed(l);
}
int _layouts_entity_set_kv(layout_t* l, entity_t* e, char* key, void* value,
layouts_keydef_types_t key_type)
{
void* data;
size_t size;
layouts_keydef_types_t real_type;
char key_keydef[PATHLEN];
if (l == NULL || e == NULL || key == NULL || value == NULL)
return SLURM_ERROR;
real_type = _layouts_entity_get_kv_type(l, e, key);
if (key_type > 0 && real_type != key_type)
return SLURM_ERROR;
_normalize_keydef_key(key_keydef, PATHLEN, key, l->type);
switch(real_type) {
case L_T_ERROR:
return SLURM_ERROR;
case L_T_STRING:
data = xstrdup(value);
return entity_set_data_ref(e, key_keydef, data,
xfree_as_callback);
case L_T_CUSTOM:
/* TBD : add a custom_set call */
value = NULL;
return SLURM_ERROR;
case L_T_LONG:
size = sizeof(long);
break;
case L_T_UINT16:
size = sizeof(uint16_t);
break;
case L_T_UINT32:
size = sizeof(uint32_t);
break;
case L_T_BOOLEAN:
size = sizeof(bool);
break;
case L_T_FLOAT:
size = sizeof(float);
break;
case L_T_DOUBLE:
size = sizeof(double);
break;
case L_T_LONG_DOUBLE:
size = sizeof(long double);
break;
default:
value = NULL;
return SLURM_ERROR;
}
return entity_set_data(e, key_keydef, value, size);
}
int _layouts_entity_set_kv_ref(layout_t* l, entity_t* e, char* key, void* value,
layouts_keydef_types_t key_type)
{
int rc = SLURM_ERROR;
char key_keydef[PATHLEN];
if (l == NULL || e == NULL || key == NULL || value == NULL)
return rc;
if (!_layouts_entity_check_kv_keytype(l, e, key, key_type))
return rc;
_normalize_keydef_key(key_keydef, PATHLEN, key, l->type);
return entity_set_data_ref(e, key_keydef, value, xfree_as_callback);
}
int _layouts_entity_setpush_kv(layout_t* l, entity_t* e, char* key, void* value,
layouts_keydef_types_t key_type)
{
int rc = SLURM_ERROR;
if (_layouts_entity_set_kv(l, e, key, value, key_type) == SLURM_SUCCESS)
rc = _layouts_entity_push_kv(l, e, key);
return rc;
}
int _layouts_entity_setpush_kv_ref(layout_t* l, entity_t* e, char* key,
void* value, layouts_keydef_types_t key_type)
{
int rc = SLURM_ERROR;
if (_layouts_entity_set_kv_ref(l, e, key, value, key_type) ==
SLURM_SUCCESS)
rc = _layouts_entity_push_kv(l, e, key);
return rc;
}
int _layouts_entity_get_kv(layout_t* l, entity_t* e, char* key, void* value,
layouts_keydef_types_t key_type)
{
void* data;
size_t size;
layouts_keydef_types_t real_type;
char key_keydef[PATHLEN];
char ** pstr;
if (l == NULL || e == NULL || key == NULL || value == NULL)
return SLURM_ERROR;
real_type = _layouts_entity_get_kv_type(l, e, key);
if (key_type > 0 && real_type != key_type)
return SLURM_ERROR;
_normalize_keydef_key(key_keydef, PATHLEN, key, l->type);
data = entity_get_data_ref(e, key_keydef);
if (data == NULL) {
return SLURM_ERROR;
}
switch(real_type) {
case L_T_STRING:
pstr = (char**) value;
*pstr = xstrdup(data);
return SLURM_SUCCESS;
case L_T_CUSTOM:
/* TBD : add a custom_get call */
pstr = (char**) value;
*pstr = NULL;
return SLURM_ERROR;
case L_T_LONG:
size = sizeof(long);
break;
case L_T_UINT16:
size = sizeof(uint16_t);
break;
case L_T_UINT32:
size = sizeof(uint32_t);
break;
case L_T_BOOLEAN:
size = sizeof(bool);
break;
case L_T_FLOAT:
size = sizeof(float);
break;
case L_T_DOUBLE:
size = sizeof(double);
break;
case L_T_LONG_DOUBLE:
size = sizeof(long double);
break;
case L_T_ERROR:
default:
return SLURM_ERROR;
}
memcpy(value, data, size);
return SLURM_SUCCESS;
}
int _layouts_entity_get_mkv(layout_t* l, entity_t* e, char* keys, void* value,
size_t length, layouts_keydef_types_t key_type)
{
char *key = NULL;
hostlist_t kl;
size_t processed = 0;
size_t elt_size = sizeof(void*);;
int rc = 0;
/* expand in order the requested keys (in hostlist format)
* and iterate over each one of them, collecting the different
* values into the provided buffer.
* if no more space is available in the buffer, then just count
* the missing elements for the exit code.
* the first error encountered fakes a full buffer to just add
* the remaining keys to the missing elements count before
* exiting. */
kl = hostlist_create(keys);
while ((key = hostlist_shift(kl))) {
if (processed >= length) {
rc++;
} else if (_layouts_entity_get_kv_size(l, e, key, &elt_size) ||
(processed + elt_size) > length ||
_layouts_entity_get_kv(l, e, key, value, key_type)) {
rc++;
processed = length;
} else {
value += elt_size;
processed += elt_size;
}
free(key);
}
hostlist_destroy(kl);
return rc;
}
int _layouts_entity_get_kv_ref(layout_t* l, entity_t* e,
char* key, void** value,
layouts_keydef_types_t key_type)
{
int rc = SLURM_ERROR;
char key_keydef[PATHLEN];
void* data;
if (l == NULL || e == NULL || key == NULL || value == NULL)
return rc;
if (!_layouts_entity_check_kv_keytype(l, e, key, key_type))
return rc;
_normalize_keydef_key(key_keydef, PATHLEN, key, l->type);
data = entity_get_data_ref(e, key_keydef);
if (data != NULL) {
*value = data;
rc = SLURM_SUCCESS;
}
return rc;
}
int _layouts_entity_get_mkv_ref(layout_t* l, entity_t* e, char* keys,
void* value, size_t length,
layouts_keydef_types_t key_type)
{
char *key = NULL;
hostlist_t kl;
size_t processed = 0;
size_t elt_size = sizeof(void*);
int rc = 0;
/* expand in order the requested keys (in hostlist format)
* and iterate over each one of them, collecting the different
* references into the provided buffer.
* if no more space is available in the buffer, then just count
* the missing elements for the exit code.
* the first error encountered fakes a full buffer to just add
* the remaining keys to the missing elements count before
* exiting. */
kl = hostlist_create(keys);
while ((key = hostlist_shift(kl))) {
if (processed >= length) {
rc++;
} else if (_layouts_entity_get_kv_ref(l, e, key, value, key_type)) {
rc++;
processed = length;
} else {
value += elt_size;
processed += elt_size;
}
free(key);
}
hostlist_destroy(kl);
return rc;
}
int _layouts_entity_pullget_kv(layout_t* l, entity_t* e, char* key, void* value,
layouts_keydef_types_t key_type)
{
int rc = SLURM_ERROR;
if (!_layouts_entity_check_kv_keytype(l, e, key, key_type))
return rc;
if (_layouts_entity_pull_kv(l, e, key) == SLURM_SUCCESS)
rc = _layouts_entity_get_kv(l, e, key, value, key_type);
return rc;
}
int _layouts_entity_pullget_kv_ref(layout_t* l, entity_t* e,
char* key, void** value,
layouts_keydef_types_t key_type)
{
int rc = SLURM_ERROR;
if (!_layouts_entity_check_kv_keytype(l, e, key, key_type))
return rc;
if (_layouts_entity_pull_kv(l, e, key) == SLURM_SUCCESS)
rc = _layouts_entity_get_kv_ref(l, e, key, value, key_type);
return rc;
}
/*****************************************************************************\
* MANAGER INIT *
\*****************************************************************************/
static void _layouts_init_keydef(xhash_t* keydefs,
const layouts_keyspec_t* plugin_keyspec,
layout_plugin_t* plugin)
{
char keytmp[PATHLEN];
const layouts_keyspec_t* current;
layouts_keydef_t* nkeydef;
/* A layout plugin may have no data to store to entities but still
* being valid. */
if (!plugin_keyspec)
return;
/* iterate over the keys of the plugin */
for (current = plugin_keyspec; current->key; ++current) {
/* if not end of list, a keyspec key is mandatory */
_normalize_keydef_key(keytmp, PATHLEN, current->key,
plugin->layout->type);
xassert(xhash_get_str(keydefs, keytmp) == NULL);
nkeydef = (layouts_keydef_t*)
xmalloc(sizeof(layouts_keydef_t));
nkeydef->key = xstrdup(keytmp);
nkeydef->shortkey = xstrdup(current->key);
nkeydef->type = current->type;
nkeydef->flags = current->flags;
nkeydef->custom_destroy = current->custom_destroy;
nkeydef->custom_dump = current->custom_dump;
nkeydef->plugin = plugin;
if (current->ref_key != NULL) {
_normalize_keydef_key(keytmp, PATHLEN, current->ref_key,
plugin->layout->type);
nkeydef->ref_key = xstrdup(keytmp);
nkeydef->ref_shortkey = xstrdup(current->ref_key);
} else {
nkeydef->ref_key = NULL;
nkeydef->ref_shortkey = NULL;
}
xhash_add(keydefs, nkeydef);
}
/* then add keys managed by the layouts_mgr directly */
switch(plugin->layout->struct_type) {
case LAYOUT_STRUCT_TREE:
_normalize_keydef_mgrkey(keytmp, PATHLEN, "enclosed",
plugin->layout->type);
xassert(xhash_get_str(keydefs, keytmp) == NULL);
nkeydef = (layouts_keydef_t*)
xmalloc(sizeof(layouts_keydef_t));
nkeydef->key = xstrdup(keytmp);
nkeydef->shortkey = xstrdup("Enclosed");
nkeydef->type = L_T_STRING;
nkeydef->plugin = plugin;
xhash_add(keydefs, nkeydef);
break;
}
}
static void _debug_output_keydefs (void* item, void* args)
{
layouts_keydef_t* keydef = (layouts_keydef_t*) item;
debug3("layouts/keydefs: loaded: %s flags=0x%08lx refkey=%s",
keydef->key, (long unsigned int) keydef->flags,
(keydef->ref_key == NULL) ? "-":keydef->ref_key);
}
static int _layouts_init_layouts_walk_helper(void* x, void* arg)
{
layouts_conf_spec_t* spec = (layouts_conf_spec_t*)x;
int* i = (int*)arg;
layout_plugin_t* plugin = &mgr->plugins[*i];
const char* plugin_type = "layouts";
char plugin_name[PATHLEN];
void* inserted_item;
plugin_context_t* plugin_context;
snprintf(plugin_name, PATHLEN,
"layouts/%s_%s", spec->type, spec->name);
plugin->ops = (layout_ops_t*)xmalloc(sizeof(layout_ops_t));
debug2("layouts: loading %s...", spec->whole_name);
plugin->context = plugin_context = plugin_context_create(
plugin_type,
plugin_name,
(void**)plugin->ops,
layout_syms,
sizeof(layout_syms));
if (!plugin_context) {
error("layouts: error loading %s.", plugin_name);
return SLURM_ERROR;
}
if (!plugin->ops->spec) {
error("layouts: plugin_spec must be valid (%s plugin).",
plugin_name);
return SLURM_ERROR;
}
plugin->name = xstrdup(spec->whole_name);
plugin->layout = (layout_t*)xmalloc(sizeof(layout_t));
layout_init(plugin->layout, spec->name, spec->type, 0,
plugin->ops->spec->struct_type);
if ((inserted_item = xhash_add(mgr->layouts, plugin->layout)))
xassert(inserted_item == plugin->layout);
_layouts_init_keydef(mgr->keydefs,
plugin->ops->spec->keyspec,
plugin);
xhash_walk(mgr->keydefs, _debug_output_keydefs, NULL);
++*i;
return SLURM_SUCCESS;
}
static void _layouts_mgr_parse_global_conf(layouts_mgr_t* mgr)
{
char* layouts;
char* parser;
char* saveptr = NULL;
char* slash;
layouts_conf_spec_t* nspec;
mgr->layouts_desc = list_create(layouts_conf_spec_free);
layouts = slurm_get_layouts();
parser = strtok_r(layouts, ",", &saveptr);
while (parser) {
nspec = (layouts_conf_spec_t*)xmalloc(
sizeof(layouts_conf_spec_t));
nspec->whole_name = xstrdup(_trim(parser));
slash = strchr(parser, '/');
if (slash) {
*slash = 0;
nspec->type = xstrdup(_trim(parser));
nspec->name = xstrdup(_trim(slash+1));
} else {
nspec->type = xstrdup(_trim(parser));
nspec->name = xstrdup("default");
}
list_append(mgr->layouts_desc, nspec);
parser = strtok_r(NULL, ",", &saveptr);
}
xfree(layouts);
}
static void _layouts_mgr_free(layouts_mgr_t* mgr)
{
/* free the configuration details */
FREE_NULL_LIST(mgr->layouts_desc);
/* FIXME: can we do a faster free here? since each node removal will
* modify either the entities or layouts for back (or forward)
* references. */
xhash_free(mgr->layouts);
xhash_free(mgr->entities);
xhash_free(mgr->keydefs);
mgr->init_done = false;
}
static void _layouts_mgr_init(layouts_mgr_t* mgr)
{
if (mgr->init_done)
_layouts_mgr_free(mgr);
mgr->init_done = true;
_layouts_mgr_parse_global_conf(mgr);
mgr->layouts = xhash_init(layout_hashable_identify_by_type,
_layout_free);
mgr->entities = xhash_init(entity_hashable_identify,
_entity_free);
mgr->keydefs = xhash_init(layouts_keydef_idfunc,
_layouts_keydef_free);
}
/*****************************************************************************\
* CONFIGURATION *
\*****************************************************************************/
static char* _conf_get_filename(const char* type)
{
char path[PATHLEN];
char* final_path;
strlcpy(path, "layouts.d/", PATHLEN);
_cat(path, type, PATHLEN);
_cat(path, ".conf", PATHLEN);
final_path = get_extra_conf_path(path);
return final_path;
}
static char* _state_get_filename(const char* type)
{
return xstrdup_printf("%s/layouts_state_%s",
slurmctld_conf.state_save_location,
type);
}
static s_p_hashtbl_t* _conf_make_hashtbl(int struct_type,
const s_p_options_t* layout_options)
{
s_p_hashtbl_t* tbl = NULL;
s_p_hashtbl_t* tbl_relational = NULL;
s_p_hashtbl_t* tbl_layout = NULL;
s_p_options_t* relational_options = NULL;
/* generic line option */
static s_p_options_t global_options_entity[] = {
{"Entity", S_P_STRING},
{"Type", S_P_STRING},
{NULL}
};
static s_p_options_t global_options[] = {
{"Priority", S_P_UINT32},
{"Entity", S_P_EXPLINE, NULL, NULL, global_options_entity},
{NULL}
};
/* available for constructing a tree */
static s_p_options_t tree_options_entity[] = {
{"Enclosed", S_P_STRING},
{NULL}
};
static s_p_options_t tree_options[] = {
{"Root", S_P_STRING},
{"Entity", S_P_EXPLINE, NULL, NULL, tree_options_entity},
{NULL}
};
xassert(layout_options);
switch(struct_type) {
case LAYOUT_STRUCT_TREE:
relational_options = tree_options;
break;
default:
fatal("layouts: does not know what relation structure to "
"use for type %d", struct_type);
}
tbl = s_p_hashtbl_create(global_options);
tbl_relational = s_p_hashtbl_create(relational_options);
tbl_layout = s_p_hashtbl_create(layout_options);
s_p_hashtbl_merge_keys(tbl, tbl_relational);
s_p_hashtbl_merge_keys(tbl, tbl_layout);
s_p_hashtbl_destroy(tbl_relational);
s_p_hashtbl_destroy(tbl_layout);
return tbl;
}
#define _layouts_load_merge(type_t, s_p_get_type) { \
type_t rvalue; \
type_t* value = &rvalue; \
type_t* oldvalue; \
slurm_parser_operator_t operator = S_P_OPERATOR_SET; \
if (!s_p_get_type(&rvalue, option_key, etbl)) { \
/* no value to merge/create */ \
continue; \
} \
s_p_get_operator(&operator, option_key, etbl); \
oldvalue = (type_t*)entity_get_data_ref(e, key_keydef); \
if (oldvalue) { \
_entity_update_kv_helper(type_t, operator); \
} else { \
type_t* newalloc = (type_t*) \
xmalloc(sizeof(type_t)); \
*newalloc = *value; \
_entity_add_data(e, key_keydef, newalloc); \
} \
} \
#define _layouts_merge_check(type1, type2) \
(entity_option->type == type1 && keydef->type == type2)
static void _layouts_load_automerge(layout_plugin_t* plugin, entity_t* e,
s_p_hashtbl_t* etbl, uint32_t flags)
{
const s_p_options_t* layout_option;
const s_p_options_t* entity_option;
layouts_keydef_t* keydef;
char key_keydef[PATHLEN];
char* option_key;
for (layout_option = plugin->ops->spec->options;
layout_option && xstrcasecmp("Entity", layout_option->key);
++layout_option);
xassert(layout_option);
for (entity_option = layout_option->line_options;
entity_option->key;
++entity_option) {
option_key = entity_option->key;
_normalize_keydef_key(key_keydef, PATHLEN, option_key,
plugin->layout->type);
keydef = xhash_get_str(mgr->keydefs, key_keydef);
if (!keydef) {
/* key is not meant to be automatically handled,
* ignore it for this function */
continue;
}
/* do not perform automerge on updates for read-only keys */
if (flags & UPDATE_DONE &&
keydef->flags & KEYSPEC_RDONLY) {
debug4("layouts: do not try to merge RDONLY key '%s'",
keydef->key);
continue;
}
if (_layouts_merge_check(S_P_LONG, L_T_LONG)) {
_layouts_load_merge(long, s_p_get_long);
} else if (_layouts_merge_check(S_P_UINT16, L_T_UINT16)) {
_layouts_load_merge(uint16_t, s_p_get_uint16);
} else if (_layouts_merge_check(S_P_UINT32, L_T_UINT32)) {
_layouts_load_merge(uint32_t, s_p_get_uint32);
} else if (_layouts_merge_check(S_P_BOOLEAN, L_T_BOOLEAN)) {
bool newvalue;
if (s_p_get_boolean(&newvalue, option_key, etbl)) {
bool *newalloc = xmalloc(sizeof(bool));
*newalloc = newvalue;
_entity_add_data(e, key_keydef, newalloc);
}
} else if (_layouts_merge_check(S_P_LONG, L_T_LONG)) {
_layouts_load_merge(long, s_p_get_long);
} else if (_layouts_merge_check(S_P_FLOAT, L_T_FLOAT)) {
_layouts_load_merge(float, s_p_get_float);
} else if (_layouts_merge_check(S_P_DOUBLE, L_T_DOUBLE)) {
_layouts_load_merge(double, s_p_get_double);
} else if (_layouts_merge_check(S_P_LONG_DOUBLE,
L_T_LONG_DOUBLE)) {
_layouts_load_merge(long double, s_p_get_long_double);
} else if (_layouts_merge_check(S_P_STRING, L_T_STRING)) {
char* newvalue;
if (s_p_get_string(&newvalue, option_key, etbl)) {
_entity_add_data(e, key_keydef, newvalue);
}
}
}
}
/* extract Enlosed= attributes providing the relational structures info */
static void _layouts_parse_relations(layout_plugin_t* plugin, entity_t* e,
s_p_hashtbl_t* entity_tbl)
{
char* e_enclosed;
char* e_already_enclosed;
char* e_new_enclosed;
char key[PATHLEN];
switch(plugin->layout->struct_type) {
case LAYOUT_STRUCT_TREE:
if (s_p_get_string(&e_enclosed, "Enclosed", entity_tbl)) {
_normalize_keydef_mgrkey(key, PATHLEN, "enclosed",
plugin->layout->type);
e_already_enclosed = (char*)
entity_get_data_ref(e, key);
if (e_already_enclosed) {
e_new_enclosed = (char*) xmalloc(
strlen(e_already_enclosed) +
strlen(e_enclosed) + 2);
strcat(e_new_enclosed, e_already_enclosed);
strcat(e_new_enclosed, ",");
strcat(e_new_enclosed, e_enclosed);
xfree(e_enclosed);
e_enclosed = e_new_enclosed;
}
_entity_add_data(e, key, e_enclosed);
}
break;
}
}
static int _layouts_read_config_post(layout_plugin_t* plugin,
s_p_hashtbl_t* tbl)
{
char* root_nodename;
entity_t* e;
entity_node_t* enode;
xtree_node_t* root_node;
xtree_t* tree;
switch(plugin->layout->struct_type) {
case LAYOUT_STRUCT_TREE:
tree = layout_get_tree(plugin->layout);
xassert(tree);
if (!s_p_get_string(&root_nodename, "Root", tbl)) {
error("layouts: unable to construct the layout tree, "
"no root node specified");
xfree(root_nodename);
return SLURM_ERROR;
}
e = xhash_get_str(mgr->entities, _trim(root_nodename));
if (!e) {
error("layouts: unable to find specified root "
"entity `%s'", _trim(root_nodename));
xfree(root_nodename);
return SLURM_ERROR;
}
xfree(root_nodename);
if (!(enode = entity_add_node(e, plugin->layout)))
xassert(enode);
if (!(root_node = xtree_add_child(
tree, NULL, enode, XTREE_APPEND)))
xassert(root_node);
enode->node = (void*) root_node;
break;
}
return SLURM_SUCCESS;
}
/*
* _layouts_load_config_common - called by layouts_read_config,
* layouts_read_state or layouts_update_config with either a
* filename or a buffer as well as a flag to indicate if it
* is a full load or not (state save only)
*/
static int _layouts_load_config_common(layout_plugin_t* plugin,
char* filename, Buf buffer,
uint32_t flags)
{
s_p_hashtbl_t* tbl = NULL;
s_p_hashtbl_t** entities_tbl = NULL;
s_p_hashtbl_t* entity_tbl = NULL;
int entities_tbl_count = 0, i;
entity_t** updated_entities = NULL;
int rc = SLURM_SUCCESS;
uint32_t l_priority;
entity_t* e;
char* e_name = NULL;
char* e_type = NULL;
if (!plugin->ops->spec->options) {
/* no option in this layout plugin, nothing to parse */
return SLURM_SUCCESS;
}
tbl = _conf_make_hashtbl(plugin->layout->struct_type,
plugin->ops->spec->options);
if (filename) {
if (s_p_parse_file(tbl, NULL, filename, false) == SLURM_ERROR) {
info("layouts: something went wrong when opening/reading "
"'%s': %m", filename);
rc = SLURM_ERROR;
goto cleanup;
}
debug3("layouts: configuration file '%s' is loaded", filename);
} else if (buffer) {
if (s_p_parse_buffer(tbl, NULL, buffer, false) == SLURM_ERROR) {
error("layouts: something went wrong when parsing "
"buffer : %m");
rc = SLURM_ERROR;
goto cleanup;
}
debug3("layouts: buffer loaded");
} else {
error("layouts: invalid usage of _layouts_load_config_common");
rc = SLURM_ERROR;
goto cleanup;
}
if (s_p_get_uint32(&l_priority, "Priority", tbl)) {
plugin->layout->priority = l_priority;
}
/* get the config hash tables of the defined entities */
if (!s_p_get_expline(&entities_tbl, &entities_tbl_count,
"Entity", tbl)) {
error("layouts: no valid Entity found, can not append any "
"information nor construct relations for %s/%s",
plugin->layout->type, plugin->layout->name);
rc = SLURM_ERROR;
goto cleanup;
}
/* stage 0: xmalloc an array of entity_t* to save the updated entity_t
* and give their references in the update_done layout callback */
updated_entities = (entity_t**)
xmalloc(entities_tbl_count*sizeof(entity_t*));
/* stage 1: create the described entities or update them */
for (i = 0; i < entities_tbl_count; ++i) {
updated_entities[i] = NULL;
entity_tbl = entities_tbl[i];
xfree(e_name);
xfree(e_type);
if (!s_p_get_string(&e_name, "Entity", entity_tbl)) {
info("layouts: no name associated to entity[%d], "
"skipping...", i);
rc = SLURM_ERROR;
continue;
}
/* look for the entity in the entities hash table*/
e = xhash_get_str(mgr->entities, e_name);
if (!e) {
/* if the entity does not already exists, create it */
if (!s_p_get_string(&e_type, "Type", entity_tbl)) {
info("layouts: entity '%s' does not already "
"exists and no type was specified, "
"skipping", e_name);
rc = SLURM_ERROR;
continue;
}
if (!_string_in_array(e_type,
plugin->ops->spec->etypes)) {
info("layouts: entity '%s' type (%s) is "
"invalid, skipping", e_name, e_type);
rc = SLURM_ERROR;
continue;
}
e = (entity_t*)xmalloc(sizeof(entity_t));
entity_init(e, e_name, e_type);
xhash_add(mgr->entities, e);
} else if (s_p_get_string(&e_type, "Type", entity_tbl)) {
/* if defined, check that the type is consistent */
if (!_string_in_array(e_type,
plugin->ops->spec->etypes)) {
info("layouts: entity '%s' type (%s) is "
"invalid, skipping", e_name, e_type);
rc = SLURM_ERROR;
continue;
}
if (!e->type || xstrcmp(e_type, e->type)) {
info("layouts: entity '%s' type (%s) differs "
"from already registered entity type (%s)"
" skipping", e_name, e_type, e->type);
rc = SLURM_ERROR;
continue;
}
}
/* ** Full load config only (flags==0) **
* look for "Enclosed" pragmas identifying the relations
* among entities and kep that along with the entity for
* stage 2 */
if (flags & PARSE_RELATIONS)
_layouts_parse_relations(plugin, e, entity_tbl);
/*
* if the layout plugin requests automerge, try to automatically
* parse the conf hash table using the s_p_option_t description
* of the plugin, creating the key/vlaue with the right value
* type and adding them to the entity key hash table.
*/
if (plugin->ops->spec->automerge) {
_layouts_load_automerge(plugin, e, entity_tbl, flags);
}
/*
* in case the automerge was not sufficient, the layout parsing
* callback is called for further actions.
*/
if ((flags & PARSE_ENTITY) && plugin->ops->entity_parsing) {
plugin->ops->entity_parsing(e, entity_tbl,
plugin->layout);
}
/* add the entity ref to the array for further usage when
* calling the update_done layout callback */
updated_entities[i] = e;
}
xfree(e_name);
xfree(e_type);
/* ** Full load config only (flags==0) **
* post-read-and-build (post stage 1)
* ensure that a Root entity was defined and set it as the root of
* the relational structure of the layout.
* fails in case of error as a root is mandatory to walk the relational
* structure of the layout */
if ((flags & CONF_DONE) &&
_layouts_read_config_post(plugin, tbl) != SLURM_SUCCESS) {
goto cleanup;
}
/* ** Full load config only (flags==0) **
* call the layout plugin conf_done callback for further
* layout specific actions.
*/
if ((flags & CONF_DONE) && plugin->ops->conf_done) {
if (!plugin->ops->conf_done(mgr->entities, plugin->layout,
tbl)) {
error("layouts: plugin %s/%s has an error parsing its"
" configuration", plugin->layout->type,
plugin->layout->name);
rc = SLURM_ERROR;
goto cleanup;
}
}
/*
* In case we are processing an update (not a startup configuration)
* if the layout plugin requests autoupdate, call the autoupdate
* function on the current layout in order to set the inherited values
* according to the newly modified ones.
* (in startup configuration, the autoupdate is performed in stage 3
* when the relational structures are available)
*/
if ((flags & UPDATE_DONE) && plugin->ops->spec->autoupdate) {
_layouts_autoupdate_layout(plugin->layout);
}
/*
* Call the layout plugin update_done callback for further
* layout specific actions.
* Note : some entries of the updated_entities array might be NULL
* reflecting an issue while trying to analyze the corresponding
* parsed hash table.
*/
if ((flags & UPDATE_DONE) && plugin->ops->update_done) {
if (!plugin->ops->update_done(plugin->layout, updated_entities,
entities_tbl_count)) {
error("layouts: plugin %s/%s has an error reacting to"
" entities update", plugin->layout->type,
plugin->layout->name);
rc = SLURM_ERROR;
goto cleanup;
}
}
xfree(updated_entities);
cleanup:
s_p_hashtbl_destroy(tbl);
return rc;
}
/*
* _layouts_read_config - called after base entities are loaded successfully
*
* This function is the stage 1 of the layouts loading stage, where we collect
* info on all the entities and store them in a global hash table.
* Entities that do not already exist are created, otherwise updated.
*
* Information concerning the relations among entities provided by the
* 'Enclosed' conf pragma are also extracted here for further usage in stage 2.
*
* When layout plugins callbacks are called, relational structures among
* entities are not yet built.
*/
static int _layouts_read_config(layout_plugin_t* plugin)
{
int rc;
char* filename = _conf_get_filename(plugin->layout->type);
if (!filename) {
fatal("layouts: cannot find configuration file for "
"required layout '%s'", plugin->name);
}
rc = _layouts_load_config_common(plugin, filename, NULL,
CONF_DONE |
PARSE_ENTITY | PARSE_RELATIONS);
xfree(filename);
return rc;
}
/*
* _layouts_read_state - called to restore saved state of layout entities
*
* This function is the stage 1.1 of the layouts loading stage, where we collect
* info on all the entities stored in the state of the layout and store/update
* them in the global hash table.
*
* Information concerning the relations among entities provided by the
* 'Enclosed' conf pragma are not taken into account for now and will be those
* loaded during stage 1.
*
* No layout plugins callbacks are called when doing that for now.
*/
static int _layouts_read_state(layout_plugin_t* plugin)
{
int rc = SLURM_SUCCESS;
struct stat stat_buf;
char *filename = _state_get_filename(plugin->layout->type);
if (!filename) {
error("layouts: unable to build read state filename of layout"
" '%s/%s'", plugin->layout->type, plugin->layout->name);
return SLURM_ERROR;
}
/* check availability of the file otherwise it will later block
* waiting for a file to appear (in s_p_parse_file) */
if (stat(filename, &stat_buf) < 0) {
debug("layouts: skipping non existent state file for '%s/%s'",
plugin->layout->type, plugin->layout->name);
} else {
rc = _layouts_load_config_common(plugin, filename, NULL,
PARSE_ENTITY);
}
xfree(filename);
return rc;
}
static int _layouts_update_state(layout_plugin_t* plugin, Buf buffer)
{
return _layouts_load_config_common(plugin, NULL, buffer,
PARSE_ENTITY | UPDATE_DONE);
}
typedef struct _layouts_build_xtree_walk_st {
layout_t* layout;
char* enclosed_key;
xtree_t* tree;
} _layouts_build_xtree_walk_t;
uint8_t _layouts_build_xtree_walk(xtree_node_t* node,
uint8_t which,
uint32_t level,
void* arg)
{
_layouts_build_xtree_walk_t* p = (_layouts_build_xtree_walk_t*)arg;
entity_t* e;
entity_node_t* enode;
char* enclosed_str;
char* enclosed_name;
hostlist_t enclosed_hostlist;
entity_t* enclosed_e;
xtree_node_t* enclosed_node;
xassert(arg);
/* get the entity from the entity node associated with the tree node */
enode = (entity_node_t*) xtree_node_get_data(node);
xassert(enode);
e = enode->entity;
xassert(e);
/*
* FIXME: something goes wrong with the order...
* after a first growing, the first new child is called with preorder.
*
* for now, testing each time and use enclosed_str to know if it has
* been processed.
*/
if (which != XTREE_GROWING && which != XTREE_PREORDER)
return 1;
enclosed_str = (char*) entity_get_data_ref(e, p->enclosed_key);
if (enclosed_str) {
enclosed_hostlist = hostlist_create(enclosed_str);
entity_delete_data(e, p->enclosed_key);
while ((enclosed_name = hostlist_shift(enclosed_hostlist))) {
enclosed_e = xhash_get_str(mgr->entities,
enclosed_name);
if (!enclosed_e) {
error("layouts: entity '%s' specified in "
"enclosed entities of entity '%s' "
"not found, ignoring.",
enclosed_name, e->name);
free(enclosed_name);
continue;
}
free(enclosed_name);
/* create an entity node associated to the entity
* for this layout */
enode = entity_add_node(enclosed_e, p->layout);
xassert(enode);
/* add it to the tree, getting an xtree_node_t ref */
if (!(enclosed_node = xtree_add_child(
p->tree, node, enode, XTREE_APPEND)))
xassert(enclosed_node);
/* store the xtree_node_t ref in the entity node. It
* will be used to access this layout tree from the
* entity when necessary through the entity node */
enode->node = enclosed_node;
}
hostlist_destroy(enclosed_hostlist);
}
return 1;
}
/*
* _layouts_build_relations - called after _layouts_read_config to create the
* relational structure of the layout according to the topological
* details parsed in stage 1. This is the stage 2 of the layouts
* configuration load.
*
* This function is the stage 2 of the layouts loading stage, where we use
* the relational details extracted from the parsing stage (Enclosed pragmas
* and Root entity) to build the relational structure of the layout.
*
*/
static int _layouts_build_relations(layout_plugin_t* plugin)
{
xtree_t* tree;
xtree_node_t* root_node;
char key[PATHLEN];
switch(plugin->layout->struct_type) {
case LAYOUT_STRUCT_TREE:
tree = layout_get_tree(plugin->layout);
xassert(tree);
root_node = xtree_get_root(tree);
_normalize_keydef_mgrkey(key, PATHLEN, "enclosed",
plugin->layout->type);
_layouts_build_xtree_walk_t p = {
plugin->layout,
key,
tree
};
xtree_walk(tree,
root_node,
0,
XTREE_LEVEL_MAX,
_layouts_build_xtree_walk,
&p);
break;
}
return SLURM_SUCCESS;
}
/*****************************************************************************\
* STATE DUMP *
\*****************************************************************************/
/*
* _pack_args_t : helper struct/type used when passing args among the various
* functions used when packing layouts into a buffer as a set of strings.
*/
typedef struct _pack_args {
Buf buffer;
char *current_line;
layout_t *layout;
hostlist_t list_entities;
char *type;
uint32_t all;
uint32_t flags;
uint32_t record_count;
} _pack_args_t;
/*
* _pack_data_key : internal function used to get the key=val
* string representation of a particular entity data value
*/
static char* _pack_data_key(layouts_keydef_t* keydef, void* value)
{
char val;
if (!keydef) {
return NULL;
}
switch(keydef->type) {
case L_T_ERROR:
return NULL;
case L_T_STRING:
return xstrdup_printf("%s=%s", keydef->shortkey,
(char*)value);
case L_T_LONG:
return xstrdup_printf("%s=%ld", keydef->shortkey,
*(long*)value);
case L_T_UINT16:
return xstrdup_printf("%s=%u", keydef->shortkey,
*(uint16_t*)value);
case L_T_UINT32:
return xstrdup_printf("%s=%"PRIu32, keydef->shortkey,
*(uint32_t*)value);
case L_T_BOOLEAN:
val = *(bool*)value;
return xstrdup_printf("%s=%s", keydef->shortkey,
val ? "true" : "false");
case L_T_FLOAT:
return xstrdup_printf("%s=%f", keydef->shortkey,
*(float*)value);
case L_T_DOUBLE:
return xstrdup_printf("%s=%f", keydef->shortkey,
*(double*)value);
case L_T_LONG_DOUBLE:
return xstrdup_printf("%s=%Lf", keydef->shortkey,
*(long double*)value);
case L_T_CUSTOM:
if (keydef->custom_dump) {
return keydef->custom_dump(value);
} else
return NULL;
}
return NULL;
}
/*
* _pack_entity_layout_data : internal function used to append the
* key/value of a entity data element corresponding to the targeted
* layout when walking an entity list of entity data elements
*
* - append the " %key%=%val%" to the char* received as an input arg member
*
*/
static void _pack_entity_layout_data(void* item, void* arg)
{
entity_data_t* data;
_pack_args_t *pargs;
layouts_keydef_t* keydef;
char *data_dump;
xassert(item);
xassert(arg);
data = (entity_data_t*) item;
pargs = (_pack_args_t *) arg;
/* the pack args must contain a valid char* to append to */
xassert(pargs->current_line);
/* we must be able to get the keydef associated to the data key */
xassert(data);
keydef = xhash_get_str(mgr->keydefs, data->key);
xassert(keydef);
/* only dump keys related to the targeted layout */
if (!xstrncmp(keydef->plugin->layout->type, pargs->layout->type,
PATHLEN)) {
data_dump = _pack_data_key(keydef, data->value);
/* avoid printing any error in case of NULL pointer returned */
if (data_dump) {
xstrcat(pargs->current_line, " ");
xstrcat(pargs->current_line, data_dump);
xfree(data_dump);
}
}
return;
}
/*
* _pack_layout_tree : internal function used when walking a layout tree
*
* - print one line per entity with the following pattern :
* Entity=%name% [Type=%type%] [Enclosed=%childrens%] [key1=val1 ...]
*
* - potentially print an header line if the entity is the root like :
* Root=%name%
*
*/
static uint8_t _pack_layout_tree(xtree_node_t* node, uint8_t which,
uint32_t level, void* arg)
{
_pack_args_t *pargs;
xtree_node_t* child;
entity_node_t* enode;
hostlist_t enclosed;
char *enclosed_str = NULL, *e_name = NULL, *e_type = NULL;
Buf buffer;
char *strdump, *str = NULL;
/* only need to work for preorder and leaf cases */
if (which != XTREE_PREORDER && which != XTREE_LEAF) {
return 1;
}
/* get the buffer we need to pack the data too */
pargs = (_pack_args_t *) arg;
buffer = pargs->buffer;
/* aggregate children names to build the Enclosed=.. value */
if (which == XTREE_PREORDER) {
enclosed = hostlist_create(NULL);
child = node->start;
while (child) {
enode = (entity_node_t*) xtree_node_get_data(child);
if (!enode || !enode->entity) {
hostlist_push(enclosed, "NULL");
} else {
hostlist_push(enclosed, enode->entity->name);
}
child = child->next;
}
hostlist_uniq(enclosed);
if (hostlist_count(enclosed) > 0) {
enclosed_str = hostlist_ranged_string_xmalloc(enclosed);
}
hostlist_destroy(enclosed);
}
/* get the entity associated to this xtree node */
enode = (entity_node_t*) xtree_node_get_data(node);
if (!enode || !enode->entity) {
e_name = (char*) "NULL";
e_type = NULL;
} else {
e_name = enode->entity->name;
e_type = enode->entity->type;
}
/* print this entity as root if necessary */
if (level == 0 && !(pargs->flags & LAYOUTS_DUMP_NOLAYOUT)
&& pargs->type == NULL) {
if (pargs->all != 0 ||
pargs->list_entities == NULL ||
hostlist_find(pargs->list_entities, e_name) != -1) {
str = xstrdup_printf("Root=%s\n", e_name);
packstr(str, buffer);
pargs->record_count++;
xfree(str);
}
}
/* print entity name and type when possible */
str = xstrdup_printf("Entity=%s", e_name);
if (e_type) {
strdump = xstrdup_printf("%s Type=%s", str, e_type);
xfree(str);
str = strdump;
}
/* add entity keys matching the layout to the current str */
pargs->current_line = str;
if (enode && enode->entity) {
xhash_walk(enode->entity->data, _pack_entity_layout_data,
pargs);
}
/* the current line might have been extended/remalloced, so
* we need to sync it again in str for further actions */
str = pargs->current_line;
pargs->current_line = NULL;
/* don't print enclosed if no_relation option */
if ((pargs->flags & LAYOUTS_DUMP_NOLAYOUT)
&& enclosed_str != NULL
&& pargs->list_entities == NULL) {
xfree(enclosed_str);
xfree(str);
return 1;
}
/* don't print non enclosed if no "entities char*" option */
if (pargs->all == 0
&& pargs->list_entities == NULL
&& enclosed_str == NULL ) {
xfree(str);
return 1;
}
/* don't print entities if not named in "entities char*" */
if (pargs->all == 0
&& pargs->list_entities != NULL
&& hostlist_find(pargs->list_entities, e_name) == -1) {
xfree(str);
return 1;
}
/* don't print entities if not type of "type char*" */
if (pargs->type != NULL
&& (e_type == NULL || xstrcasecmp(e_type, pargs->type)!=0)) {
xfree(str);
return 1;
}
/* print enclosed entities if any */
if (!enclosed_str) {
xstrcat(str, "\n");
} else {
strdump = xstrdup_printf("%s Enclosed=%s\n", str, enclosed_str);
xfree(enclosed_str);
xfree(str);
str = strdump;
}
packstr(str, buffer);
pargs->record_count++;
xfree(str);
return 1;
}
/* helper function used by layouts_save_state when walking through
* the various layouts to save their state in Slurm state save location */
static void _state_save_layout(void* item, void* arg)
{
layout_t* layout = (layout_t*)item;
layouts_state_save_layout(layout->type);
}
/*****************************************************************************\
* ENTITIES KVs AUTOUPDATE *
\*****************************************************************************/
/*
* helper structure used when walking the tree of relational nodes in order
* to automatically update the entities KVs based on their inheritance
* relationships
*/
typedef struct _autoupdate_tree_args {
entity_node_t* enode;
uint8_t which;
uint32_t level;
} _autoupdate_tree_args_t;
/*
* helper function used to update a particular KV value of an entity according
* to a particular operator looking for the right type to apply during the
* operation
*/
static int _autoupdate_entity_kv(layouts_keydef_t* keydef,
layouts_keydef_t* ref_keydef,
slurm_parser_operator_t operator,
void* oldvalue, void* value)
{
int rc = SLURM_ERROR;
if (keydef->type != ref_keydef->type)
return rc;
if (keydef->type == L_T_LONG) {
_entity_update_kv_helper(long, operator);
} else if (keydef->type == L_T_UINT16) {
_entity_update_kv_helper(uint16_t, operator);
} else if (keydef->type == L_T_UINT32) {
_entity_update_kv_helper(uint32_t, operator);
} else if (keydef->type == L_T_FLOAT) {
_entity_update_kv_helper(float, operator);
} else if (keydef->type == L_T_DOUBLE) {
_entity_update_kv_helper(double, operator);
} else if (keydef->type == L_T_LONG_DOUBLE) {
_entity_update_kv_helper(long double, operator);
} else {
// L_T_BOOLEAN, L_T_STRING, L_T_CUSTOM not yet supported
return rc;
}
return SLURM_SUCCESS;
}
/*
* helper function used to update KVs of an entity using its xtree_node
* looking for known inheritance in the neighborhood (parents/children) */
static void _tree_update_node_entity_data(void* item, void* arg)
{
uint32_t action;
entity_data_t* data;
_autoupdate_tree_args_t *pargs;
layouts_keydef_t* keydef;
layouts_keydef_t* ref_keydef;
slurm_parser_operator_t operator;
xtree_node_t *node, *child;
entity_node_t *enode, *cnode;
void* oldvalue;
void* value;
uint32_t count;
int setter;
xassert(item);
xassert(arg);
data = (entity_data_t*) item;
pargs = (_autoupdate_tree_args_t *) arg;
cnode = pargs->enode;
/* we must be able to get the keydef associated to the data key */
xassert(data);
keydef = xhash_get_str(mgr->keydefs, data->key);
xassert(keydef);
/* only work on keys that depend of their neighborhood */
if (!(keydef->flags & KEYSPEC_UPDATE_CHILDREN_MASK) &&
!(keydef->flags & KEYSPEC_UPDATE_PARENTS_MASK)) {
return;
}
/* if children dependant and we are at leaf level, nothing to do */
if (keydef->flags & KEYSPEC_UPDATE_CHILDREN_MASK &&
pargs->which == XTREE_LEAF)
return;
/* only work on keys related to the targeted layout */
if (xstrncmp(keydef->plugin->layout->type, pargs->enode->layout->type,
PATHLEN)) {
return;
}
/* get ref_key (identical if not defined) */
if (keydef->ref_key != NULL) {
ref_keydef = xhash_get_str(mgr->keydefs, keydef->ref_key);
if (!ref_keydef) {
debug2("layouts: autoupdate: key='%s': invalid "
"ref_key='%s'", keydef->key, keydef->ref_key);
return;
}
} else {
ref_keydef = keydef;
}
/* process parents aggregation
* for now, xtree only provides one parent so any update op
* (MAX, MIN, FSHARE, ...) is a setter */
if ((action = keydef->flags & KEYSPEC_UPDATE_PARENTS_MASK) &&
(pargs->which == XTREE_PREORDER || pargs->which == XTREE_LEAF) &&
(node = ((xtree_node_t*)pargs->enode->node)->parent) != NULL ) {
/* get current node value reference */
oldvalue = entity_get_data_ref(cnode->entity, keydef->key);
if (!oldvalue)
return;
/* get siblings count */
child = node->start;
count = 0;
while (child) {
count++;
child = child->next;
}
/* get parent node KV data ref */
enode = (entity_node_t*) xtree_node_get_data(node);
value = entity_get_data_ref(enode->entity, ref_keydef->key);
if (!value)
return;
/* only set operation currently provided for parents except
* for fshare action */
_autoupdate_entity_kv(keydef, ref_keydef, S_P_OPERATOR_SET,
oldvalue, value);
if (action == KEYSPEC_UPDATE_PARENTS_FSHARE) {
_autoupdate_entity_kv(keydef, ref_keydef,
S_P_OPERATOR_AVG,
oldvalue, (void*) &count);
}
return;
}
/* process children aggregation */
if ((action = keydef->flags & KEYSPEC_UPDATE_CHILDREN_MASK) &&
pargs->which == XTREE_ENDORDER) {
/* get current node value reference */
oldvalue = entity_get_data_ref(cnode->entity, keydef->key);
if (!oldvalue)
return;
/* get children count */
node = (xtree_node_t*)cnode->node;
child = node->start;
count = 0;
while (child) {
count++;
child = child->next;
}
/* no action if no children */
if (count == 0)
return;
/* if count action, do what is necessary and return */
if (action == KEYSPEC_UPDATE_CHILDREN_COUNT) {
_autoupdate_entity_kv(keydef, ref_keydef,
S_P_OPERATOR_SET,
oldvalue, (void*) &count);
return;
}
/* iterate on the children */
setter = 1;
child = node->start;
while (child) {
/* get child node KV data ref */
enode = (entity_node_t*) xtree_node_get_data(child);
value = entity_get_data_ref(enode->entity,
ref_keydef->key);
if (!value) {
/* try next child */
child = child-> next;
continue;
}
switch (action) {
case KEYSPEC_UPDATE_CHILDREN_SUM:
case KEYSPEC_UPDATE_CHILDREN_AVG:
/* first child is a setter */
if (setter) {
operator = S_P_OPERATOR_SET;
setter = 0;
}
else
operator = S_P_OPERATOR_ADD;
break;
case KEYSPEC_UPDATE_CHILDREN_MIN:
operator = S_P_OPERATOR_SET_IF_MIN;
break;
case KEYSPEC_UPDATE_CHILDREN_MAX:
operator = S_P_OPERATOR_SET_IF_MAX;
break;
default:
/* should not be called! */
return;
}
/* update the value according to the operator */
_autoupdate_entity_kv(keydef, ref_keydef, operator,
oldvalue, value);
/* then next child */
child = child-> next;
}
/* if average action, do what is necessary before return */
if (action == KEYSPEC_UPDATE_CHILDREN_AVG) {
_autoupdate_entity_kv(keydef, ref_keydef,
S_P_OPERATOR_AVG,
oldvalue, (void*) &count);
return;
}
return;
}
}
/*
* _autoupdate_layout_tree : internal function used when automatically
* updating elements of a layout tree using _layouts_autoupdate_layout */
static uint8_t _autoupdate_layout_tree(xtree_node_t* node, uint8_t which,
uint32_t level, void* arg)
{
entity_node_t* cnode;
_autoupdate_tree_args_t sync_args;
/* only need to work for preorder, leaf and endorder cases */
if (which != XTREE_PREORDER &&
which != XTREE_LEAF &&
which != XTREE_ENDORDER) {
return 1;
}
/* extract current node entity_node to next browsing */
cnode = (entity_node_t*) xtree_node_get_data(node);
if (!cnode)
return 1;
/* prepare downcall args */
sync_args.enode = cnode;
sync_args.which = which;
sync_args.level = level;
/* iterate over the K/V of the entity, syncing them according
* to their autoupdate flags */
xhash_walk(cnode->entity->data, _tree_update_node_entity_data,
&sync_args);
return 1;
}
/* helper function used to automatically update a layout internal
* entities KVs based on inheritance relations (parents/children) */
static int _layouts_autoupdate_layout(layout_t* layout)
{
/* autoupdate according to the layout struct type */
switch(layout->struct_type) {
case LAYOUT_STRUCT_TREE:
xtree_walk(layout->tree, NULL, 0,
XTREE_LEVEL_MAX,
_autoupdate_layout_tree, NULL);
break;
}
return SLURM_SUCCESS;
}
/* helper function used to automatically update a layout internal
* entities KVs based on inheritance relations (parents/children)
* only when allowed by the associated plugin */
static int _layouts_autoupdate_layout_if_allowed(layout_t* layout)
{
int i, rc = SLURM_ERROR;
/* look if the corresponding layout plugin enables autoupdate */
for (i = 0; i < mgr->plugins_count; i++) {
if (mgr->plugins[i].layout == layout) {
/* no autoupdate allowed, return success */
if (!mgr->plugins[i].ops->spec->autoupdate)
rc = SLURM_SUCCESS;
else
rc = _layouts_autoupdate_layout(layout);
break;
}
}
return rc;
}
/*****************************************************************************\
* DEBUG DUMP *
\*****************************************************************************/
/*
* For debug purposes, dump functions helping to print the layout mgr
* internal states in a file after the load.
*/
#if 0
static char* _dump_data_key(layouts_keydef_t* keydef, void* value)
{
char val;
if (!keydef) {
return xstrdup("ERROR_bad_keydef");
}
switch(keydef->type) {
case L_T_ERROR:
return xstrdup("ERROR_keytype!");
case L_T_STRING:
return xstrdup((char*)value);
case L_T_LONG:
return xstrdup_printf("%ld", *(long*)value);
case L_T_UINT16:
return xstrdup_printf("%u", *(uint16_t*)value);
case L_T_UINT32:
return xstrdup_printf("%ul", *(uint32_t*)value);
case L_T_BOOLEAN:
val = *(bool*)value;
return xstrdup_printf("%s", val ? "true" : "false");
case L_T_FLOAT:
return xstrdup_printf("%f", *(float*)value);
case L_T_DOUBLE:
return xstrdup_printf("%f", *(double*)value);
case L_T_LONG_DOUBLE:
return xstrdup_printf("%Lf", *(long double*)value);
case L_T_CUSTOM:
if (keydef->custom_dump) {
return keydef->custom_dump(value);
}
return xstrdup_printf("custom_ptr(%p)", value);
}
return NULL;
}
static void _dump_entity_data(void* item, void* arg)
{
entity_data_t* data = (entity_data_t*)item;
FILE* fdump = (FILE*)arg;
layouts_keydef_t* keydef;
char* data_dump;
xassert(data);
keydef = xhash_get_str(mgr->keydefs, data->key);
xassert(keydef);
data_dump = _dump_data_key(keydef, data->value);
fprintf(fdump, "data %s (type: %d): %s\n",
data->key, keydef->type, data_dump);
xfree(data_dump);
}
static void _dump_entities(void* item, void* arg)
{
entity_t* entity = (entity_t*)item;
FILE* fdump = (FILE*)arg;
fprintf(fdump, "-- entity %s --\n", entity->name);
fprintf(fdump, "type: %s\nnode count: %d\nptr: %p\n",
entity->type, list_count(entity->nodes), entity->ptr);
xhash_walk(entity->data, _dump_entity_data, fdump);
}
static uint8_t _dump_layout_tree(xtree_node_t* node, uint8_t which,
uint32_t level, void* arg)
{
FILE* fdump = (FILE*)arg;
entity_t* e;
entity_node_t* enode;
if (which != XTREE_PREORDER && which != XTREE_LEAF) {
return 1;
}
enode = (entity_node_t*) xtree_node_get_data(node);
if (!enode || !enode->entity) {
fprintf(fdump, "NULL_entity\n");
}
else {
fprintf(fdump, "%*s%s\n", level, " ", enode->entity->name);
}
return 1;
}
static void _dump_layouts(void* item, void* arg)
{
layout_t* layout = (layout_t*)item;
FILE* fdump = (FILE*)arg;
fprintf(fdump, "-- layout %s --\n"
"type: %s\n"
"priority: %u\n"
"struct_type: %d\n"
"relational ptr: %p\n",
layout->name,
layout->type,
layout->priority,
layout->struct_type,
layout->tree);
switch(layout->struct_type) {
case LAYOUT_STRUCT_TREE:
fprintf(fdump, "struct_type(string): tree, count: %d\n"
"entities list:\n",
xtree_get_count(layout->tree));
xtree_walk(layout->tree, NULL, 0, XTREE_LEVEL_MAX,
_dump_layout_tree, fdump);
break;
}
}
#endif
/*****************************************************************************\
* SLURM LAYOUTS API *
\*****************************************************************************/
int layouts_init(void)
{
int i = 0;
uint32_t layouts_count;
debug3("layouts: layouts_init()...");
if (mgr->plugins) {
return SLURM_SUCCESS;
}
slurm_mutex_lock(&layouts_mgr.lock);
_layouts_mgr_init(&layouts_mgr);
layouts_count = list_count(layouts_mgr.layouts_desc);
if (layouts_count == 0)
info("layouts: no layout to initialize");
else
info("layouts: %d layout(s) to initialize", layouts_count);
mgr->plugins = xmalloc(sizeof(layout_plugin_t) * layouts_count);
list_for_each(layouts_mgr.layouts_desc,
_layouts_init_layouts_walk_helper,
&i);
mgr->plugins_count = i;
if (mgr->plugins_count != layouts_count) {
error("layouts: only %d/%d layouts loaded, aborting...",
mgr->plugins_count, layouts_count);
for (i = 0; i < mgr->plugins_count; i++) {
_layout_plugins_destroy(&mgr->plugins[i]);
}
xfree(mgr->plugins);
mgr->plugins = NULL;
} else if (layouts_count > 0) {
info("layouts: layouts_init done : %d layout(s) "
"initialized", layouts_count);
}
slurm_mutex_unlock(&layouts_mgr.lock);
return mgr->plugins_count == layouts_count ?
SLURM_SUCCESS : SLURM_ERROR;
}
int layouts_fini(void)
{
int i;
debug3("layouts: layouts_fini()...");
/* push layouts states to the state save location */
layouts_state_save();
slurm_mutex_lock(&mgr->lock);
/*
* free the layouts before destroying the plugins,
* otherwise we will get trouble xfreeing the layouts whose
* memory is owned by the plugins structs
*/
_layouts_mgr_free(mgr);
for (i = 0; i < mgr->plugins_count; i++) {
_layout_plugins_destroy(&mgr->plugins[i]);
}
xfree(mgr->plugins);
mgr->plugins = NULL;
mgr->plugins_count = 0;
slurm_mutex_unlock(&mgr->lock);
info("layouts: all layouts are now unloaded.");
return SLURM_SUCCESS;
}
int layouts_load_config(int recover)
{
int i, rc, inx;
node_record_t *node_ptr;
layout_t *layout;
uint32_t layouts_count;
entity_t *entity;
entity_node_t *enode;
void *ptr;
info("layouts: loading entities/relations information");
rc = SLURM_SUCCESS;
slurm_mutex_lock(&mgr->lock);
if (xhash_count(layouts_mgr.entities)) {
slurm_mutex_unlock(&mgr->lock);
return rc;
}
/*
* create a base layout to contain the configured nodes
* Notes : it might be moved to its own external layout in the
* slurm source layouts directory.
*/
layout = (layout_t*) xmalloc(sizeof(layout_t));
layout_init(layout, "slurm", "base", 0, LAYOUT_STRUCT_TREE);
if (xtree_add_child(layout->tree, NULL, NULL, XTREE_APPEND) == NULL) {
error("layouts: unable to create base layout tree root"
", aborting");
goto exit;
}
/*
* generate and store the slurm node entities,
* add them to the base layout at the same time
*/
for (inx = 0, node_ptr = node_record_table_ptr; inx < node_record_count;
inx++, node_ptr++) {
debug3("layouts: loading node %s", node_ptr->name);
xassert (node_ptr->magic == NODE_MAGIC);
xassert (node_ptr->config_ptr->magic == CONFIG_MAGIC);
/* init entity structure on the heap */
entity = (entity_t*) xmalloc(sizeof(struct entity_st));
entity_init(entity, node_ptr->name, "Node");
entity->ptr = node_ptr;
/* add to mgr entity hashtable */
if (xhash_add(layouts_mgr.entities,(void*)entity) == NULL) {
error("layouts: unable to add entity of node %s"
"in the hashtable, aborting", node_ptr->name);
entity_free(entity);
xfree(entity);
rc = SLURM_ERROR;
break;
}
/* add to the base layout (storing a callback ref to the
* layout node pointing to it) */
enode = entity_add_node(entity, layout);
ptr = xtree_add_child(layout->tree, layout->tree->root,
(void*)enode, XTREE_APPEND);
if (!ptr) {
error("layouts: unable to add entity of node %s"
"in the hashtable, aborting", node_ptr->name);
entity_free(entity);
xfree(entity);
rc = SLURM_ERROR;
break;
} else {
enode->node = ptr;
}
}
debug("layouts: %d/%d nodes in hash table, rc=%d",
xhash_count(layouts_mgr.entities), node_record_count, rc);
if (rc != SLURM_SUCCESS)
goto exit;
/* add the base layout to the layouts manager dedicated hashtable */
if (xhash_add(layouts_mgr.layouts, (void*)layout) == NULL) {
error("layouts: unable to add base layout into the hashtable");
layout_free(layout);
rc = SLURM_ERROR;
}
/* check that we get as many layouts as initiliazed plugins
* as layouts are added and referenced by type.
* do +1 in the operation as the base layout is currently managed
* separately.
* If this base layout is moved to a dedicated plugin and automatically
* added to the mgr layouts_desc at init, the +1 will have to be
* removed here as it will be counted as the other plugins in the sum
*/
layouts_count = xhash_count(layouts_mgr.layouts);
if ( layouts_count != mgr->plugins_count + 1) {
error("layouts: %d/%d layouts added to hashtable, aborting",
layouts_count, mgr->plugins_count+1);
rc = SLURM_ERROR;
}
exit:
if (rc != SLURM_SUCCESS) {
layout_free(layout);
xfree(layout);
} else {
debug("layouts: loading stage 1");
for (i = 0; i < mgr->plugins_count; ++i) {
debug3("layouts: reading config for %s",
mgr->plugins[i].name);
if (_layouts_read_config(&mgr->plugins[i]) !=
SLURM_SUCCESS) {
rc = SLURM_ERROR;
break;
}
}
if (recover) {
debug("layouts: loading stage 1.1 (restore state)");
for (i = 0; i < mgr->plugins_count; ++i) {
debug3("layouts: reading state of %s",
mgr->plugins[i].name);
_layouts_read_state(&mgr->plugins[i]);
}
}
debug("layouts: loading stage 2");
for (i = 0; i < mgr->plugins_count; ++i) {
debug3("layouts: creating relations for %s",
mgr->plugins[i].name);
if (_layouts_build_relations(&mgr->plugins[i]) !=
SLURM_SUCCESS) {
rc = SLURM_ERROR;
break;
}
}
debug("layouts: loading stage 3");
for (i = 0; i < mgr->plugins_count; ++i) {
debug3("layouts: autoupdating %s",
mgr->plugins[i].name);
if (mgr->plugins[i].ops->spec->autoupdate) {
if (_layouts_autoupdate_layout(mgr->plugins[i].
layout) !=
SLURM_SUCCESS) {
rc = SLURM_ERROR;
break;
}
}
}
}
/*
* For debug purposes, print the layout mgr internal states
* in a file after the load.
*/
#if 0
/* temporary section to test layouts */
FILE* fdump = fopen("/tmp/slurm-layouts-dump.txt", "wb");
xhash_walk(mgr->entities, _dump_entities, fdump);
xhash_walk(mgr->layouts, _dump_layouts, fdump);
if (fdump)
fclose(fdump);
#endif
slurm_mutex_unlock(&mgr->lock);
return rc;
}
layout_t* layouts_get_layout_nolock(const char* type)
{
return (layout_t*)xhash_get_str(mgr->layouts, type);
}
layout_t* layouts_get_layout(const char* type)
{
layout_t *layout = NULL;
slurm_mutex_lock(&mgr->lock);
layout = layouts_get_layout_nolock(type);
slurm_mutex_unlock(&mgr->lock);
return layout;
}
entity_t* layouts_get_entity_nolock(const char* name)
{
return (entity_t*)xhash_get_str(mgr->entities, name);
}
entity_t* layouts_get_entity(const char* name)
{
entity_t* e;
slurm_mutex_lock(&mgr->lock);
e = layouts_get_entity_nolock(name);
slurm_mutex_unlock(&mgr->lock);
return e;
}
int layouts_pack_layout(char *l_type, char *char_entities, char *type,
uint32_t flags, Buf buffer)
{
_pack_args_t pargs;
layout_t* layout;
int orig_offset, fini_offset;
char *str;
slurm_mutex_lock(&mgr->lock);
layout = layouts_get_layout_nolock(l_type);
if (layout == NULL) {
slurm_mutex_unlock(&mgr->lock);
info("unable to get layout of type '%s'", l_type);
return SLURM_ERROR;
}
/* initialize args for recursive packing */
pargs.buffer = buffer;
pargs.layout = layout;
pargs.current_line = NULL;
pargs.all = 0;
pargs.list_entities = NULL;
if (char_entities != NULL) {
if (xstrcmp(char_entities, "*") == 0)
pargs.all = 1;
else
pargs.list_entities = hostlist_create(char_entities);
}
pargs.type = type;
pargs.flags = flags;
pargs.record_count = 0;
orig_offset = get_buf_offset(buffer);
pack32(pargs.record_count, buffer);
/* start by packing the layout priority in case we are dumping state */
if (pargs.flags & LAYOUTS_DUMP_STATE) {
str = xstrdup_printf("Priority=%u\n", layout->priority);
packstr(str, buffer);
pargs.record_count++;
xfree(str);
}
/* pack according to the layout struct type */
switch (layout->struct_type) {
case LAYOUT_STRUCT_TREE:
xtree_walk(layout->tree, NULL, 0, XTREE_LEVEL_MAX,
_pack_layout_tree, &pargs);
break;
}
if (pargs.list_entities != NULL)
slurm_hostlist_destroy(pargs.list_entities);
fini_offset = get_buf_offset(buffer);
set_buf_offset(buffer, orig_offset);
pack32(pargs.record_count, buffer);
set_buf_offset(buffer, fini_offset);
slurm_mutex_unlock(&mgr->lock);
return SLURM_SUCCESS;
}
int layouts_update_layout(char *l_type, Buf buffer)
{
int i, rc;
slurm_mutex_lock(&mgr->lock);
for (i = 0; i < mgr->plugins_count; i++) {
if (!xstrcmp(mgr->plugins[i].name, l_type)) {
rc = _layouts_update_state((layout_plugin_t*)
&mgr->plugins[i],
buffer);
slurm_mutex_unlock(&mgr->lock);
return rc;
}
}
info("%s: no plugin matching layout=%s, skipping", __func__, l_type);
slurm_mutex_unlock(&mgr->lock);
return SLURM_ERROR;
}
int layouts_autoupdate_layout(char *l_type)
{
int rc = SLURM_ERROR;
layout_t* layout;
slurm_mutex_lock(&mgr->lock);
layout = layouts_get_layout_nolock(l_type);
if (layout == NULL) {
info("unable to get layout of type '%s'", l_type);
} else {
rc = _layouts_autoupdate_layout(layout);
}
slurm_mutex_unlock(&mgr->lock);
return rc;
}
int layouts_state_save_layout(char* l_type)
{
int error_code = 0, log_fd, offset;
char *old_file = NULL, *new_file = NULL, *reg_file = NULL;
static int high_buffer_size = (16 * 1024);
Buf buffer = init_buf(high_buffer_size);
FILE* fdump;
uint32_t utmp32, record_count = 0;
char *tmp_str = NULL;
DEF_TIMERS;
START_TIMER;
/* pack the targeted layout into a tmp buffer */
error_code = layouts_pack_layout(l_type, "*", NULL,
LAYOUTS_DUMP_STATE, buffer);
if (error_code != SLURM_SUCCESS) {
error("unable to save layout[%s] state", l_type);
return error_code;
}
/* rewind the freshly created buffer to unpack it into a file */
offset = get_buf_offset(buffer);
high_buffer_size = MAX(high_buffer_size, offset);
set_buf_offset(buffer, 0);
/* create working files */
reg_file = _state_get_filename(l_type);
old_file = xstrdup_printf("%s.old", reg_file);
new_file = xstrdup_printf("%s.new", reg_file);
log_fd = creat(new_file, 0600);
if (log_fd < 0 || !(fdump = fdopen(log_fd, "w"))) {
error("Can't save state, create file %s error %m",
new_file);
error_code = errno;
} else {
/* extract the amount of records and then proceed
* then dump packed strings into the temporary file */
safe_unpack32(&record_count, buffer);
debug("layouts/%s: dumping %u records into state file",
l_type, record_count);
while (get_buf_offset(buffer) < offset) {
safe_unpackstr_xmalloc(&tmp_str, &utmp32, buffer);
if (tmp_str != NULL) {
if (*tmp_str == '\0') {
xfree(tmp_str);
break;
}
fprintf(fdump, "%s", tmp_str);
xfree(tmp_str);
continue;
}
unpack_error:
break;
}
fflush(fdump);
fsync(log_fd);
fclose(fdump);
}
if (error_code)
(void) unlink(new_file);
else { /* file shuffle */
(void) unlink(old_file);
if (link(reg_file, old_file))
debug4("unable to create link for %s -> %s: %m",
reg_file, old_file);
(void) unlink(reg_file);
if (link(new_file, reg_file))
debug4("unable to create link for %s -> %s: %m",
new_file, reg_file);
(void) unlink(new_file);
}
xfree(old_file);
xfree(reg_file);
xfree(new_file);
free_buf(buffer);
END_TIMER2("layouts_state_save_layout");
return SLURM_SUCCESS;
}
int layouts_state_save(void)
{
DEF_TIMERS;
START_TIMER;
xhash_walk(mgr->layouts, _state_save_layout, NULL);
END_TIMER2("layouts_state_save");
return SLURM_SUCCESS;
}
#define _layouts_entity_wrapper(func, l, e, r...) \
layout_t* layout; \
entity_t* entity; \
int rc; \
slurm_mutex_lock(&mgr->lock); \
layout = layouts_get_layout_nolock(l); \
entity = layouts_get_entity_nolock(e); \
rc = func(layout, entity, ##r); \
slurm_mutex_unlock(&mgr->lock); \
return rc; \
int layouts_entity_get_kv_type(char* l, char* e, char* key)
{
_layouts_entity_wrapper(_layouts_entity_get_kv_type,l,e,key);
}
int layouts_entity_get_kv_flags(char* l, char* e, char* key)
{
_layouts_entity_wrapper(_layouts_entity_get_kv_flags, l, e, key);
}
int layouts_entity_push_kv(char* l, char* e, char* key)
{
_layouts_entity_wrapper(_layouts_entity_push_kv, l, e, key);
}
int layouts_entity_pull_kv(char* l, char* e, char* key)
{
_layouts_entity_wrapper(_layouts_entity_pull_kv, l, e, key);
}
int layouts_entity_set_kv(char* l, char* e, char* key, void* value,
layouts_keydef_types_t key_type)
{
_layouts_entity_wrapper(_layouts_entity_set_kv, l, e,
key, value, key_type);
}
int layouts_entity_set_kv_ref(char* l, char* e, char* key, void* value,
layouts_keydef_types_t key_type)
{
_layouts_entity_wrapper(_layouts_entity_set_kv_ref, l, e,
key, value, key_type);
}
int layouts_entity_setpush_kv(char* l, char* e, char* key, void* value,
layouts_keydef_types_t key_type)
{
_layouts_entity_wrapper(_layouts_entity_setpush_kv, l, e,
key, value, key_type);
}
int layouts_entity_setpush_kv_ref(char* l, char* e, char* key, void* value,
layouts_keydef_types_t key_type)
{
_layouts_entity_wrapper(_layouts_entity_setpush_kv_ref, l, e,
key, value, key_type);
}
int layouts_entity_get_kv(char* l, char* e, char* key, void* value,
layouts_keydef_types_t key_type)
{
_layouts_entity_wrapper(_layouts_entity_get_kv, l, e,
key, value, key_type);
}
int layouts_entity_get_mkv(char* l, char* e, char* keys, void* value,
size_t size, layouts_keydef_types_t key_type)
{
_layouts_entity_wrapper(_layouts_entity_get_mkv, l, e,
keys, value, size, key_type);
}
int layouts_entity_get_kv_ref(char* l, char* e, char* key, void** value,
layouts_keydef_types_t key_type)
{
_layouts_entity_wrapper(_layouts_entity_get_kv_ref, l, e,
key, value, key_type);
}
int layouts_entity_get_mkv_ref(char* l, char* e, char* keys, void* value,
size_t size, layouts_keydef_types_t key_type)
{
_layouts_entity_wrapper(_layouts_entity_get_mkv_ref, l, e,
keys, value, size, key_type);
}
int layouts_entity_pullget_kv(char* l, char* e, char* key, void* value,
layouts_keydef_types_t key_type)
{
_layouts_entity_wrapper(_layouts_entity_pullget_kv, l, e,
key, value, key_type);
}
int layouts_entity_pullget_kv_ref(char* l, char* e, char* key, void** value,
layouts_keydef_types_t key_type)
{
_layouts_entity_wrapper(_layouts_entity_pullget_kv_ref, l, e,
key, value, key_type);
}