/*
 * Copyright (C) 1994-2021 Altair Engineering, Inc.
 * For more information, contact Altair at www.altair.com.
 *
 * This file is part of both the OpenPBS software ("OpenPBS")
 * and the PBS Professional ("PBS Pro") software.
 *
 * Open Source License Information:
 *
 * OpenPBS is free software. You can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * OpenPBS 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 Affero General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Commercial License Information:
 *
 * PBS Pro is commercially licensed software that shares a common core with
 * the OpenPBS software.  For a copy of the commercial license terms and
 * conditions, go to: (http://www.pbspro.com/agreement.html) or contact the
 * Altair Legal Department.
 *
 * Altair's dual-license business model allows companies, individuals, and
 * organizations to create proprietary derivative works of OpenPBS and
 * distribute them - whether embedded or bundled with other software -
 * under a commercial license agreement.
 *
 * Use of Altair's trademarks, including but not limited to "PBS™",
 * "OpenPBS®", "PBS Professional®", and "PBS Pro™" and Altair's logos is
 * subject to Altair's trademark licensing policies.
 */

/**
 * @file	pbs_python_svr_internal.c
 * @brief
 * This are the internal helper functions that depend on a lot of PBS
 * Server Data structures.
 *
 */
#include <pbs_config.h> /* the master config generated by configure */
#include <pbs_python_private.h>

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <memory.h>
#include <stdlib.h>
#include <libpbs.h>
#include <pbs_ifl.h>
#include <errno.h>
#include <string.h>
#include <list_link.h>
#include <log.h>
#include <attribute.h>
#include <resource.h>
#include <server_limits.h>
#include <server.h>
#include <job.h>
#include <reservation.h>
#include <queue.h>
#include <pbs_error.h>
#include <batch_request.h>
#include <provision.h>
#include "hook.h"
#include "pbs_nodes.h"

#include "cmds.h"
#include "svrfunc.h"
#include "pbs_ecl.h"
#include "placementsets.h"
#include "pbs_reliable.h"

/* -----                        GLOBALS                        -----    */

/* pipe-separated string lists  */
#define runjob_modifiable_jobattrs ATTR_h "|" ATTR_a "|" ATTR_project "|" ATTR_e "|" ATTR_o "|" ATTR_l "|" ATTR_v "|" ATTR_depend "|" ATTR_create_resv_from_job
#define runjob_modifiable_vnattrs ATTR_NODE_state

#define FMT_RUNJOB_ERRMSG "Can only set job's (%s) attribute, or a vnode's (%s) attribute under RUNJOB event - got <%s>"

extern attribute_def que_attr_def[];
extern resource_def *svr_resc_def; /* the resource def structure */
extern int svr_resc_size;	   /* static + dynamic */
extern int svr_resc_unk;	   /* the last one */
extern char server_name[];
extern struct python_interpreter_data svr_interp_data;
extern pbs_list_head svr_queues;   /* list of queues                   */
extern pbs_list_head svr_alljobs;  /* list of all jobs in server       */
extern pbs_list_head svr_allresvs; /* all reservations in server */
extern char *msg_man_set;
extern char *path_hooks_workdir;

/* Global symbols */
extern char *vnode_state_to_str(int state_bit);
extern char *vnode_sharing_to_str(enum vnode_sharing share);
extern char *vnode_ntype_to_str(int vntype);

extern int str_to_vnode_state(char *vnstate);
extern enum vnode_sharing str_to_vnode_sharing(char *vn_str);
extern int str_to_vnode_ntype(char *vntype);
extern u_Long pps_size_to_kbytes(PyObject *l);
extern PyObject *svrattrl_list_to_pyobject(int rq_cmd, pbs_list_head *);
extern PyObject *svrattrl_to_server_attribute(int rq_cmd, svrattrl *);

static PyObject *
_pps_helper_get_resv(resc_resv *presv_o, const char *resvid, char *perf_label);

/* A dictionary for quick access to the pbs.v1 EMBEDDED_EXTENSION_TYPES */
static PyObject *PBS_PythonTypes = NULL; /* A dictionary maintaing name and type */

/*
 *  BEGIN Quick Access Types Table
 *    The below code is a convenience scheme, not really needed as they are
 *    available in the PBS_PythonTypes dict. But, since we do this for every
 *    aggregate type, comes in handy.
 */
typedef struct _pbs_python_types_entry {
	char *t_key;	   /* The key in EXPORTED TYPES DICTIONARY */
	PyObject *t_class; /* The actual type or class */
} pbs_python_types_entry;

#define PP_DESC_IDX 0
#define PP_GENERIC_IDX 1
#define PP_SIZE_IDX 2
#define PP_TIME_IDX 3
#define PP_ACL_IDX 4
#define PP_BOOL_IDX 5
#define PP_JOB_IDX 6
#define PP_QUE_IDX 7
#define PP_SVR_IDX 8
#define PP_RESV_IDX 9
#define PP_EVENT_IDX 10
#define PP_RESC_IDX 11
#define PP_ARST_IDX 12
#define PP_INT_IDX 13
#define PP_STR_IDX 14
#define PP_FLOAT_IDX 15
#define PP_EVENT_ERR_IDX 16
#define PP_UNSET_ATTR_NAME_ERR_IDX 17
#define PP_BADATTR_VTYPE_ERR_IDX 18
#define PP_BADATTR_VALUE_ERR_IDX 19
#define PP_UNSET_RESC_NAME_ERR_IDX 20
#define PP_BAD_RESC_VTYPE_ERR_IDX 21
#define PP_BAD_RESC_VALUE_ERR_IDX 22
#define PP_PBS_ITER_IDX 23
#define PP_VNODE_IDX 24
#define PP_ENTITY_IDX 25
#define PP_ENV_IDX 26
#define PP_MANAGEMENT_IDX 27
#define PP_SERVER_ATTRIBUTE_IDX 28

pbs_python_types_entry pbs_python_types_table[] = {
	{PY_TYPE_ATTR_DESCRIPTOR, NULL}, /* 0 Always first */
	{PY_TYPE_GENERIC, NULL},
	{PY_TYPE_SIZE, NULL},
	{PY_TYPE_TIME, NULL},
	{PY_TYPE_ACL, NULL},
	{PY_TYPE_BOOL, NULL},
	{PY_TYPE_JOB, NULL},
	{PY_TYPE_QUEUE, NULL},
	{PY_TYPE_SERVER, NULL},
	{PY_TYPE_RESV, NULL},
	{PY_TYPE_EVENT, NULL}, /* 10 */
	{PY_TYPE_RESOURCE, NULL},
	{PY_TYPE_LIST, NULL},
	{PY_TYPE_INT, NULL},
	{PY_TYPE_STR, NULL},
	{PY_TYPE_FLOAT, NULL},			   /* 15 */
	{PY_ERROR_EVENT_INCOMPATIBLE, NULL},	   /* 16 */
	{PY_ERROR_EVENT_UNSET_ATTRIBUTE, NULL},	   /* 17 */
	{PY_ERROR_BAD_ATTRIBUTE_VALUE_TYPE, NULL}, /* 18 */
	{PY_ERROR_BAD_ATTRIBUTE_VALUE, NULL},	   /* 19 */
	{PY_ERROR_UNSET_RESOURCE, NULL},	   /* 20 */
	{PY_ERROR_BAD_RESOURCE_VALUE_TYPE, NULL},  /* 21 */
	{PY_ERROR_BAD_RESOURCE_VALUE, NULL},	   /* 22 */
	{PY_TYPE_PBS_ITER, NULL},		   /* 23 */
	{PY_TYPE_VNODE, NULL},			   /* 24 */
	{PY_TYPE_ENTITY, NULL},			   /* 25 */
	{PY_TYPE_ENV, NULL},			   /* 26 */
	{PY_TYPE_MANAGEMENT, NULL},		   /* 27 */
	{PY_TYPE_SERVER_ATTRIBUTE, NULL},	   /* 28 */

	/* ADD ENTRIES ONLY BELOW, OR CHANGE THE PP_XXX_IDX above the table */

	{NULL, NULL} /* sentinel */
};

typedef struct _pbs_iter_item {
	PyObject *py_iter; /* *the* iterator */
	void *data;	   /* arbitrary pbs data */
	int data_index;	   /* index of data to some table */
	pbs_list_link all_iters;
} pbs_iter_item;

static pbs_list_head pbs_iter_list; /* list of PBS iterators */

typedef struct _vnode_set_req {
	char vnode_name[PBS_MAXNODENAME + 1];
	pbs_list_head rq_attr; /* list of attributes to set */
	pbs_list_link all_reqs;
} vnode_set_req;

static pbs_list_head pbs_vnode_set_list; /* list of vnode set requests */

/**
 * @brief
 * 	The pbs_resource_value structure holds the cached resource values for
 *      Python py_resource object.
 *
 * @param[in]	py_resource - the Python pbs_resource object that will later
 * 				be set with values in 'value_list'.
 * @param[in]	py_resource_str_value - a Python string object representing
 * 					the values in 'value_list'.
 * @param[in]	attr_def_p - the resource definition for the resource list
 * 			     represented by 'py_resource'.
 * @param[in]	value_list - list of values cached for the 'py_resource' object
 * @param[in]	all_resc - links various pbs_resource_value structures.
 */
typedef struct _pbs_resource_value {
	PyObject *py_resource;
	PyObject *py_resource_str_value;
	attribute_def *attr_def_p; /* corresponding resource definition */
	pbs_list_head value_list;  /* resource values to set */
	pbs_list_link all_rescs;
} pbs_resource_value;

static pbs_list_head pbs_resource_value_list; /* list of resource */
					      /* values to instantiate */

static PyObject *PyPbsV1Module_Obj = NULL; /* pbs.v1 module object */

/* an array holding all the vnode attribute descriptors (python pointers) */
static PyObject **py_vnode_attr_types = NULL;
/* an array holding all the resv attribute descriptors (python pointers) */
static PyObject **py_resv_attr_types = NULL;
/* an array holding all the server attribute descriptors (python pointers) */
static PyObject **py_svr_attr_types = NULL;
/* an array holding all the job attribute descriptors (python pointers) */
static PyObject **py_job_attr_types = NULL;
/* an array holding all the queue attribute descriptors (python pointers) */
static PyObject **py_que_attr_types = NULL;
/* an array of python objects holding all the resources (python pointers)*/
static PyObject **py_svr_resc_types = NULL;

/* The function object that  instantiates/populates a PBS object using */
static PyObject *py_pbs_statobj = NULL;

/* This is the current hook event object */
static PyObject *py_hook_pbsevent = NULL;
/* This is the cached local/server object */
static PyObject *py_hook_pbsserver = NULL;
/* An array of cached Python queue objects managed by the current server */
static PyObject **py_hook_pbsque = NULL;
static int py_hook_pbsque_max = 0;		  /* Max # of entries in py_hook_pbsque */
static int hook_pbsevent_accept = TRUE;		  /* flag to accept/reject event */
static int hook_pbsevent_stop_processing = FALSE; /* flag to stop */
/* processing event */
/* parameters */
static char hook_pbsevent_reject_msg[HOOK_MSG_SIZE];
static int hook_set_mode = C_MODE; /* in C_MODE, can set*/
/* anything */
static int hook_reboot_host = FALSE;		/* flag to reboot host or not */
static int hook_reboot_host_cmd[HOOK_BUF_SIZE]; /* cmdline to use */
/* to reboot host */
/* as alternate to */
/* reboot() call */
static int hook_scheduler_restart_cycle = FALSE; /* flag to tell local */
/* server to tell the */
/* scheduler to */
/* restart sched cycle*/

/*
 * The following limit and counter declarations are intended
 * to reduce the amount of memory consumed by the Python
 * interpreter so that the server process does not become
 * bloated. Python is able to garbage collect builtin
 * types (e.g. string, dict, etc.), but the PBS types are
 * not created such that their memory can be released.
 */
/* Max hook events to service before restarting the interpreter */
#define PBS_PYTHON_RESTART_MAX_HOOKS 100
/* Max objects created before restarting the interpreter */
#define PBS_PYTHON_RESTART_MAX_OBJECTS 1000
/* Minimum interval between interpreter restarts */
#define PBS_PYTHON_RESTART_MIN_INTERVAL 30
/* count of Python objects created */
static long object_counter = 0;

typedef struct hook_debug_t {
	FILE *input_fp;
	char input_file[MAXPATHLEN + 1];
	FILE *output_fp;
	char output_file[MAXPATHLEN + 1];
	FILE *data_fp;
	char data_file[MAXPATHLEN + 1];
	char objname[HOOK_BUF_SIZE + 1];
} hook_debug_t;

static int use_static_data = 0; /* use static server-related data */

static hook_debug_t hook_debug;

static pbs_list_head *server_data;

typedef struct server_jobs_t {
	pbs_list_head *data;
	pbs_list_head *ids;
} server_jobs_t;
static server_jobs_t server_jobs;

typedef struct server_queues_t {
	pbs_list_head *data;
	pbs_list_head *names;
} server_queues_t;
static server_queues_t server_queues;

typedef struct server_resvs_t {
	pbs_list_head *data;
	pbs_list_head *resvids;
} server_resvs_t;
static server_resvs_t server_resvs;

typedef struct server_vnodes_t {
	pbs_list_head *data;
	pbs_list_head *names;
} server_vnodes_t;
static server_vnodes_t server_vnodes;

/**
 * @brief
 * 	This is a function that logs the contents of the list
 *	headed by 'phead'.
 *
 * @param[in] head_str - some string that gets printed spearheading the list.
 * @param[in] phead - header of the list to be printed.
 *
 * @return	void
 *
 */
void
print_svrattrl_list(char *head_str, pbs_list_head *phead)
{
	svrattrl *plist = NULL;
	int i;

	if ((head_str == NULL) || (phead == NULL)) {
		return;
	}
	if (!will_log_event(PBSEVENT_DEBUG3))
		return;

	plist = (svrattrl *) GET_NEXT(*phead);
	i = 0;
	log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK, LOG_INFO, __func__, head_str);
	while (plist) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1,
#ifdef NAS /* localmod 005 */
			 "al_name=%s al_resc=%s al_value=%s al_flags=%ld",
			 plist->al_name, plist->al_resc ? plist->al_resc : "null",
			 plist->al_value, (long) plist->al_flags);
#else
			 "al_name=%s al_resc=%s al_value=%s al_flags=%d",
			 plist->al_name, plist->al_resc ? plist->al_resc : "null",
			 plist->al_value, plist->al_flags);
#endif /* localmod 005 */
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK, LOG_INFO, __func__,
			  log_buffer);

		plist = (svrattrl *) GET_NEXT(plist->al_link);
		i++;
	}
}

/**
 * @brief
 * 	This is a stub function that should be implemented to add any additional
 * 	elements to the global namespace dict.
 *
 * @param[in] globals - python object indicating reference to globals of a module
 *
 * @return	int
 * @retval	0	success
 * @retval	-1	error
 *
 */

int
pbs_python_setup_namespace_dict(PyObject *globals)
{
#if 0 /* enable later */
	/*
	 * First, insert our module object pbs.v1 as pbs so the hook scripts
	 * can access all the globals
	 */
	/* I believe our design REQUIRES hook write to issue a "import pbs" */
	if ((PyDict_SetItemString(globals, "pbs", PyPbsV1Module_Obj) == -1)) {
		goto ERROR_EXIT;
	}

	return 0;
ERROR_EXIT:
	pbs_python_write_error_to_log("pbs_python_setup_namespace_dict");
	return -1;
#endif
	return 0;
}

/**
 * @brief
 *      The C routine that will instantiate a PbsAttributeDescriptor
 *	class (see src/modules/_base_types.py), which wraps a
 *	PBS attribute into a descriptor.
 *
 * @param[in]	klass-the class attribute being wrapped into a data descriptor.
 * @param[in]	name - name of a PBS attribute.
 * @param[in]	default_value - given to the PBS attribute.
 * @param[in]	val_klass - the class/object type of the attribute's value.
 * @param[in]	resc_attr - if the attribute being wrapped is a pbs_resource,
 *			    then this is the name of its parent.
 *			    For example: name="ncpus", resc_attr="Resource_List"
 * @param[in]	is_entity - if the attribute being wrapped is also an
 *			    entity type.
 *			    For example: name="ncpus", resc_attr="max_run_res",
 *					is_entity=1.
 * @return      int
 * @retval      -1	Failed to create descriptor.
 * @retval 	0	Successfully created descriptor.
 * @retval 	1	Descriptor already present.
 *
 */
static int
_pps_getset_descriptor_object(PyObject *klass,
			      const char *name,
			      PyObject *default_value,
			      PyObject *val_klass,
			      const char *resc_attr,
			      int is_entity)
{
	/* this is for constructor call , see _base_types.py */
	static char *kwds[] = {"cls", "name", "default_value", "value_type",
			       "resc_attr", "is_entity"};
	PyObject *py_descr_class = NULL;
	PyObject *py_descr_args = PyTuple_New(0);
	PyObject *py_descr_kwds = NULL;
	PyObject *py_attr_descr = NULL;

	/* check we creation of tuple failed */
	if (!(py_descr_args)) {
		goto ERROR_EXIT;
	}
	/* check whether the attribute descriptor is already present */
	if (PyObject_HasAttrString(klass, name)) {
		Py_CLEAR(py_descr_args);
		return 1;
	}

	py_descr_class = pbs_python_types_table[PP_DESC_IDX].t_class;

	/* good, build the arument list NEW ref
	 *  class, name, default_value, value_class
	 */
	if (resc_attr) { /* ok we are creating a attribute for a resource */
		py_descr_kwds = Py_BuildValue("{s:O, s:s, s:O, s:(O), s:s, s:i}",
					      kwds[0], klass,
					      kwds[1], name,
					      kwds[2], default_value,
					      kwds[3], val_klass,
					      kwds[4], resc_attr,
					      kwds[5], is_entity);
	} else {
		py_descr_kwds = Py_BuildValue("{s:O, s:s, s:O, s:(O), s:i}",
					      kwds[0], klass,
					      kwds[1], name,
					      kwds[2], default_value,
					      kwds[3], val_klass,
					      kwds[5], is_entity);
	}

	if (!py_descr_kwds) {
		goto ERROR_EXIT;
	}
	py_attr_descr = PyObject_Call(py_descr_class, py_descr_args, py_descr_kwds);
	if (!py_attr_descr) {
		goto ERROR_EXIT;
	}
	Py_CLEAR(py_descr_args);
	Py_CLEAR(py_descr_kwds);
	/* now set the class attribute */
	if ((PyObject_SetAttrString(klass, name, py_attr_descr) == -1)) {
		goto ERROR_EXIT;
	}
	/* we don't need the descriptor any more */
	Py_CLEAR(py_attr_descr);
	return 0;

ERROR_EXIT:
	pbs_python_write_error_to_log(__func__);
	Py_CLEAR(py_descr_args);
	Py_CLEAR(py_descr_kwds);
	Py_CLEAR(py_attr_descr);
	return -1;
}

/*
 * pbs_python_get_python_type:
 *  This is the bulk where all the magic happens regarding the mapping from
 *  PBS to python types.
 *
 * returns:
 *   - A Borrowed Reference.
 */

/* TODO
 *  -big assumption, hopefully the DURATION/TIME encoding routine is
 *   not overloaded.
 *  -Make sure all types are there
 */
#define TYPE_DURATION(p) (((p) == encode_time))
#define TYPE_SIZE(p) (((p) == ATR_TYPE_SIZE))
#define TYPE_ACL(p) (((p) == ATR_TYPE_ACL))
#define TYPE_BOOL(p) (((p) == ATR_TYPE_BOOL))
#define TYPE_ARST(p) (((p) == ATR_TYPE_ARST))
#define TYPE_RESC(p) (((p) == ATR_TYPE_RESC))
#define TYPE_INT(p) ((((p) == ATR_TYPE_LONG) ||  \
		      ((p) == ATR_TYPE_SHORT) || \
		      ((p) == ATR_TYPE_CHAR)))
#define TYPE_STR(p) (((p) == ATR_TYPE_STR) || \
		     ((p) == ATR_TYPE_JINFOP))
#define TYPE_FLOAT(p) (((p) == ATR_TYPE_FLOAT))
#define TYPE_ENTITY(p) (((p) == ATR_TYPE_ENTITY))
#define ATTR_IS_RESC(a) (TYPE_RESC((a)->at_type) ||    \
			 (TYPE_ENTITY((a)->at_type) && \
			  ((a)->at_decode == decode_entlim_res)))
/*
 * TODO
 *  	- It is possible to combine pbs_python_setup_resc_get_value_type
 *    	and pbs_python_setup_attr_get_value_type into a macro.
 */

/*
 * NO exception raised
 */
/**
 * @brief
 *	given the resource definition return the corresponding python type.
 *
 * @param[in] resc_def_p - pointer to resource_def indicating resource list
 *
 * @return	pointer PyObject
 * @retval	resource table		success
 * @retval	return generic		failure
 *
 */
static PyObject *
pbs_python_setup_resc_get_value_type(resource_def *resc_def_p)
{
	PyObject *py_tmp = NULL; /* return value */

	/* check if we are special aka check PBS_PythonTypes first */
	py_tmp = PyDict_GetItemString(PBS_PythonTypes, resc_def_p->rs_name);
	if (py_tmp)
		return py_tmp; /* cool, we are special */

	/* careful long is overloadded so duration comes first */
	if (TYPE_DURATION(resc_def_p->rs_encode)) /* check for time type */
		return pbs_python_types_table[PP_TIME_IDX].t_class;

	if (TYPE_SIZE(resc_def_p->rs_type)) /* check for SIZE type */
		return pbs_python_types_table[PP_SIZE_IDX].t_class;

	if (TYPE_ACL(resc_def_p->rs_type)) /* check for ACL type */
		return pbs_python_types_table[PP_ACL_IDX].t_class;

	if (TYPE_BOOL(resc_def_p->rs_type)) /* check for BOOL type */
		return pbs_python_types_table[PP_BOOL_IDX].t_class;

	if (TYPE_ARST(resc_def_p->rs_type)) /* check for list of strings */
		return pbs_python_types_table[PP_ARST_IDX].t_class;

	if (TYPE_INT(resc_def_p->rs_type)) /* check for int,long,short type */
		return pbs_python_types_table[PP_INT_IDX].t_class;

	if (TYPE_STR(resc_def_p->rs_type)) /* check for str type */
		return pbs_python_types_table[PP_STR_IDX].t_class;

	if (TYPE_FLOAT(resc_def_p->rs_type)) /* check for float type */
		return pbs_python_types_table[PP_FLOAT_IDX].t_class;

	if (TYPE_ENTITY(resc_def_p->rs_type)) /* check for entity type */
		return pbs_python_types_table[PP_ENTITY_IDX].t_class;

	/* all else fails return generic */

	return pbs_python_types_table[PP_GENERIC_IDX].t_class;
}

/**
 * @brief
 *      Given an attribute defintion, return the corresponding Python type.
 *
 * @param[in]	attr_def_p	- an attribute_def type.
 * @param[in]	py_type		- a python type in string: "job", "server",
 *				"queue", "resv", or "vnode".
 * @return	PyObject *
 */

static PyObject *
pbs_python_setup_attr_get_value_type(attribute_def *attr_def_p, char *py_type)
{
	PyObject *py_tmp = NULL; /* return value */

	/* check if we are special aka check PBS_PythonTypes first */

	if ((strcmp(py_type, PY_TYPE_VNODE) != 0) ||
	    (strcmp(attr_def_p->at_name, ATTR_p) != 0)) {

		/* Only get the type from PBS_PythonTypes if not the vnode's Priority */
		/* attribute which is treated as a Python int instead of the */
		/* pbs.priority type mapped in PBS_Python_Types. */

		py_tmp = PyDict_GetItemString(PBS_PythonTypes, attr_def_p->at_name);
		if (py_tmp)
			return py_tmp; /* cool, we are special */
	}

	/* careful long is overloadded so duration comes first */
	if (TYPE_DURATION(attr_def_p->at_encode)) /* check for time type */
		return pbs_python_types_table[PP_TIME_IDX].t_class;

	if (ATTR_IS_RESC(attr_def_p))
		return pbs_python_types_table[PP_RESC_IDX].t_class;

	if (TYPE_SIZE(attr_def_p->at_type)) /* check for SIZE type */
		return pbs_python_types_table[PP_SIZE_IDX].t_class;

	if (TYPE_ACL(attr_def_p->at_type)) /* check for ACL type */
		return pbs_python_types_table[PP_ACL_IDX].t_class;

	if (TYPE_BOOL(attr_def_p->at_type)) /* check for BOOL type */
		return pbs_python_types_table[PP_BOOL_IDX].t_class;

	if (TYPE_ARST(attr_def_p->at_type)) /* check for list of strings */
		return pbs_python_types_table[PP_ARST_IDX].t_class;

	if (TYPE_INT(attr_def_p->at_type)) /* check for int,long,short */
		return pbs_python_types_table[PP_INT_IDX].t_class;

	if (TYPE_STR(attr_def_p->at_type)) /* check for str type */
		return pbs_python_types_table[PP_STR_IDX].t_class;

	if (TYPE_FLOAT(attr_def_p->at_type)) /* check for float type */
		return pbs_python_types_table[PP_FLOAT_IDX].t_class;

	if (TYPE_ENTITY(attr_def_p->at_type)) /* check for entity type */
		return pbs_python_types_table[PP_ENTITY_IDX].t_class;

	/* all else fails return generic */

	return pbs_python_types_table[PP_GENERIC_IDX].t_class;
}

/**
 * @brief
 * 	pbs_python_free_py_types_array
 *   	This frees up the global arrays
 *
 * @param[in] py_types_array - address reference to globals
 *
 * @return	Void
 */

void
pbs_python_free_py_types_array(PyObject ***py_types_array)
{
	PyObject **py_array_tmp = *py_types_array;
	PyObject *py_tmp = NULL;

	if (*py_types_array) {
		while ((py_tmp = *py_array_tmp)) {
			Py_CLEAR(py_tmp);
			py_array_tmp++;
		}
	}
	PyMem_Free(*py_types_array);
	*py_types_array = NULL; /* since we might be called again */
	return;
}

/**
 * @brief
 *	makes a call to given python object klass and maskes default value
 *
 * @param[in] klass - function
 * @param[in] args - arguments for function
 *
 * @return	PyObject *
 *
 */
PyObject *
pbs_python_make_default_value(PyObject *klass, PyObject *args)
{
	PyObject *py_default_value;

	py_default_value = PyObject_Call(klass, args, NULL);
	if (!py_default_value) {
		goto ERROR_EXIT;
	}
	return py_default_value;

ERROR_EXIT:
	pbs_python_write_error_to_log("could not make default value");
	return NULL;
}

/**
 * @brief
 *      Routine that actualizes *all* the attributes for a Python vnode object.
 *
 * @par Functionality:
 *	This takes input from node_attr_def[] and svr_resc_defm[] tables.
 *	Each attribute is setup as a descriptor for finer granularity of
 * 	control.
 *
 * @return      int
 * @retval       0 :    Successful execution of this function, with internal
 *			'py_vnode_attr_types' list populated.
 * @retval      -1 "    On failutre to populate 'py_vnode_attr_types' list.
 */
int
pbs_python_setup_vnode_class_attributes(void)
{
	int i = 0;
	attribute_def *attr_def_p = NULL; /* convenience pointer */
	PyObject *py_pbs_vnode_klass = pbs_python_types_table[PP_VNODE_IDX].t_class;
	PyObject *py_value_type = NULL;
	PyObject *py_default_value = NULL;
	PyObject *py_default_args = NULL;
	int num_entry = ND_ATR_LAST + 1; /* 1 for sentinel */
	int te;

	if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name))
		DEBUG3_ARG1("BEGIN setting up all vnode attributes %s", "");
	else
		DEBUG2_ARG1("BEGIN setting up all vnode attributes %s", "");
	py_vnode_attr_types = PyMem_New(PyObject *, num_entry);

	if (py_vnode_attr_types == NULL)
		goto ERROR_EXIT;

	memset(py_vnode_attr_types, 0, sizeof(PyObject *) * num_entry);

	/* ok now set all the node_attr_def types known to the server */
	attr_def_p = node_attr_def;
	for (i = 0; i < ND_ATR_LAST; i++) {
		/* get the value type for this attribute */
		py_value_type = pbs_python_setup_attr_get_value_type(attr_def_p,
								     PY_TYPE_VNODE);
		/* create a brand new default value from value type */
		if (ATTR_IS_RESC(attr_def_p)) {
			py_default_args = Py_BuildValue("(s)", attr_def_p->at_name);
			if (py_default_args == NULL) {
				log_err(-1, attr_def_p->at_name, "could not build args for default value");
				continue;
			}
			py_default_value = pbs_python_make_default_value(py_value_type, py_default_args);
			Py_DECREF(py_default_args);
			if (py_default_value == NULL) {
				log_err(-1, attr_def_p->at_name, "could not set default value");
				continue;
			}
			te = TYPE_ENTITY(attr_def_p->at_type);
		} else {
			py_default_value = Py_None;
			te = 0;
		}
		if (_pps_getset_descriptor_object(py_pbs_vnode_klass,
						  attr_def_p->at_name,
						  py_default_value,
						  py_value_type, NULL, te) == -1)

			goto ERROR_EXIT;
		Py_INCREF(py_value_type);
		if (py_default_value != Py_None)
			Py_CLEAR(py_default_value);
		py_vnode_attr_types[i] = py_value_type;
		attr_def_p++;
	}
	if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name))
		DEBUG3_ARG1("DONE setting up all vnode attributes, number set <%d>", i);
	else
		DEBUG2_ARG1("DONE setting up all vnode attributes, number set <%d>", i);
	return 0;

ERROR_EXIT:
	if (py_default_value != Py_None)
		Py_CLEAR(py_default_value);
	snprintf(log_buffer, LOG_BUF_SIZE - 1,
		 "could not set attribute <%s> for vnode python class", attr_def_p->at_name);
	log_buffer[LOG_BUF_SIZE - 1] = '\0';
	log_err(-1, __func__, log_buffer);
	return -1;
}
/**
 * @brief
 * 	pbs_python_setup_resv_class_attributes
 *   	routine that sets up *all* the attributes for a Python server Object
 *   	mapping directly to PBS Job object (struct job)
 *
 * @return	int
 * @retval	-1  	: 	failure
 * @retval	0  	: 	success
 */
int
pbs_python_setup_resv_class_attributes(void)
{
	int i = 0;
	attribute_def *attr_def_p = NULL; /* convenience pointer */
	PyObject *py_pbs_resv_klass = pbs_python_types_table[PP_RESV_IDX].t_class;
	PyObject *py_value_type = NULL;
	PyObject *py_default_value = NULL;
	PyObject *py_default_args = NULL;
	int num_entry = RESV_ATR_LAST + 1; /* 1 for sentinel */
	int te;

	if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name))
		DEBUG3_ARG1("BEGIN setting up all reservation attributes %s", "");
	else
		DEBUG2_ARG1("BEGIN setting up all reservation attributes %s", "");
	py_resv_attr_types = PyMem_New(PyObject *, num_entry);
	if (!py_resv_attr_types) {
		goto ERROR_EXIT;
	}
	memset(py_resv_attr_types, 0, sizeof(PyObject *) * num_entry);

	/* ok now set all the resv_attr_def types known to the server */
	attr_def_p = resv_attr_def;
	for (i = 0; i < RESV_ATR_LAST; i++) {
		/* get the value type for this attribute */
		py_value_type = pbs_python_setup_attr_get_value_type(attr_def_p,
								     PY_TYPE_RESV);
		/* create a brand new default value from value type */
		if (ATTR_IS_RESC(attr_def_p)) {
			py_default_args = Py_BuildValue("(s)", attr_def_p->at_name);
			if (!py_default_args) {
				/* TODO, continuing instead of fatal error */
				log_err(-1, attr_def_p->at_name, "could not build args for default value");
				continue;
			}
			py_default_value = pbs_python_make_default_value(py_value_type, py_default_args);
			Py_DECREF(py_default_args);
			if (!py_default_value) {
				/* TODO, continuing instead of fatal error */
				log_err(-1, attr_def_p->at_name, "could not set default value");
				continue;
			}
			te = TYPE_ENTITY(attr_def_p->at_type);
		} else {
			py_default_value = Py_None;
			te = 0;
		}
		if (_pps_getset_descriptor_object(py_pbs_resv_klass,
						  attr_def_p->at_name,
						  py_default_value,
						  py_value_type, NULL, te) == -1)

			goto ERROR_EXIT;
		Py_INCREF(py_value_type);
		if (py_default_value != Py_None)
			Py_CLEAR(py_default_value);
		py_resv_attr_types[i] = py_value_type;
		attr_def_p++;
	}
	if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name))
		DEBUG3_ARG1("DONE setting up all reservation attributes, number set <%d>", i);
	else
		DEBUG2_ARG1("DONE setting up all reservation attributes, number set <%d>", i);
	return 0;

ERROR_EXIT:
	if (py_default_value != Py_None)
		Py_CLEAR(py_default_value);
	snprintf(log_buffer, LOG_BUF_SIZE - 1,
		 "could not set attribute <%s> for reservation python class", attr_def_p->at_name);
	log_buffer[LOG_BUF_SIZE - 1] = '\0';
	log_err(-1, __func__, log_buffer);
	return -1;
}

/**
 * @brief
 *	pbs_python_setup_svr_class_attributes
 *  	 routine that sets up *all* the attributes for a Python server Object
 *  	 mapping directly to PBS Job object (struct job)
 *
 * @return      int
 * @retval      -1      :       failure
 * @retval      0       :       success
 */
int
pbs_python_setup_server_class_attributes(void)
{
	int i = 0;
	attribute_def *attr_def_p = NULL; /* convenience pointer */
	PyObject *py_pbs_svr_klass = pbs_python_types_table[PP_SVR_IDX].t_class;
	PyObject *py_value_type = NULL;
	PyObject *py_default_value = NULL;
	PyObject *py_default_args = NULL;
	int num_entry = SVR_ATR_LAST + 1; /* 1 for sentinel */
	int te;

	if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name))
		DEBUG3_ARG1("BEGIN setting up all server attributes %s", "");
	else
		DEBUG2_ARG1("BEGIN setting up all server attributes %s", "");
	py_svr_attr_types = PyMem_New(PyObject *, num_entry);
	if (!py_svr_attr_types) {
		goto ERROR_EXIT;
	}
	memset(py_svr_attr_types, 0, sizeof(PyObject *) * num_entry);

	/* ok now set all the svr_attr_def types known to the server */
	attr_def_p = svr_attr_def;
	for (i = 0; i < SVR_ATR_LAST; i++) {
		/* get the value type for this attribute */
		py_value_type = pbs_python_setup_attr_get_value_type(attr_def_p,
								     PY_TYPE_SERVER);
		/* create a brand new default value from value type */
		if (ATTR_IS_RESC(attr_def_p)) {
			py_default_args = Py_BuildValue("(s)", attr_def_p->at_name);
			if (!py_default_args) {
				/* TODO, continuing instead of fatal error */
				log_err(-1, attr_def_p->at_name, "could not build args for default value");
				continue;
			}
			py_default_value = pbs_python_make_default_value(py_value_type, py_default_args);
			Py_DECREF(py_default_args);
			if (!py_default_value) {
				/* TODO, continuing instead of fatal error */
				log_err(-1, attr_def_p->at_name, "could not set default value");
				continue;
			}
			te = TYPE_ENTITY(attr_def_p->at_type);
		} else {
			py_default_value = Py_None;
			te = 0;
		}
		if (_pps_getset_descriptor_object(py_pbs_svr_klass,
						  attr_def_p->at_name,
						  py_default_value,
						  py_value_type, NULL, te) == -1)

			goto ERROR_EXIT;
		Py_INCREF(py_value_type);
		if (py_default_value != Py_None)
			Py_CLEAR(py_default_value);
		py_svr_attr_types[i] = py_value_type;
		attr_def_p++;
	}
	if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name))
		DEBUG3_ARG1("DONE setting up all server attributes, number set <%d>", i);
	else
		DEBUG2_ARG1("DONE setting up all server attributes, number set <%d>", i);
	return 0;

ERROR_EXIT:
	if (py_default_value != Py_None)
		Py_CLEAR(py_default_value);
	snprintf(log_buffer, LOG_BUF_SIZE - 1,
		 "could not set attribute <%s> for <server> python class", attr_def_p->at_name);
	log_buffer[LOG_BUF_SIZE - 1] = '\0';
	log_err(-1, __func__, log_buffer);
	return -1;
}

/**
 * @brief
 * 	pbs_python_setup_job_class_attributes
 *   	routine that sets up *all* the attributes for a Python Job Object
 *   	mapping directly to PBS Job object (struct job)
 *
 * @return      int
 * @retval      -1      :       failure
 * @retval      0       :       success
 */
int
pbs_python_setup_job_class_attributes(void)
{
	int i = 0;
	attribute_def *attr_def_p = NULL; /* convenience pointer */
	PyObject *py_pbs_job_klass = pbs_python_types_table[PP_JOB_IDX].t_class;
	PyObject *py_value_type = NULL;
	PyObject *py_default_value = NULL;
	PyObject *py_default_args = NULL;
	int num_entry = JOB_ATR_LAST + 1; /* 1 for sentinel */
	int te;

	if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name))
		DEBUG3_ARG1("BEGIN setting up all job attributes %s", "");
	else
		DEBUG2_ARG1("BEGIN setting up all job attributes %s", "");
	py_job_attr_types = PyMem_New(PyObject *, num_entry);
	if (!py_job_attr_types) {
		goto ERROR_EXIT;
	}
	memset(py_job_attr_types, 0, sizeof(PyObject *) * num_entry);

	/* ok now set all the job_attr_def types known to the server */
	attr_def_p = job_attr_def;
	for (i = 0; i < JOB_ATR_LAST; i++) {
		/* get the value type for this attribute */
		py_value_type = pbs_python_setup_attr_get_value_type(attr_def_p,
								     PY_TYPE_JOB);
		/* create a brand new default value from value type */
		if (ATTR_IS_RESC(attr_def_p)) {
			py_default_args = Py_BuildValue("(s)", attr_def_p->at_name);
			if (!py_default_args) {
				/* TODO, continuing instead of fatal error */
				log_err(-1, attr_def_p->at_name, "could not build args for default value");
				continue;
			}
			py_default_value = pbs_python_make_default_value(py_value_type, py_default_args);
			Py_DECREF(py_default_args);
			if (!py_default_value) {
				/* TODO, continuing instead of fatal error */
				log_err(-1, attr_def_p->at_name, "could not set default value");
				continue;
			}
			te = TYPE_ENTITY(attr_def_p->at_type);
		} else {
			py_default_value = Py_None;
			te = 0;
		}
		/* default to None */
		if (_pps_getset_descriptor_object(py_pbs_job_klass,
						  attr_def_p->at_name,
						  py_default_value,
						  py_value_type, NULL, te) == -1)

			goto ERROR_EXIT;
		Py_INCREF(py_value_type);
		if (py_default_value != Py_None)
			Py_CLEAR(py_default_value);
		py_job_attr_types[i] = py_value_type;
		attr_def_p++;
	}
	if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name))
		DEBUG3_ARG1("DONE setting up all job attributes, number set <%d>", i);
	else
		DEBUG2_ARG1("DONE setting up all job attributes, number set <%d>", i);
	return 0;

ERROR_EXIT:
	if (py_default_value != Py_None)
		Py_CLEAR(py_default_value);
	snprintf(log_buffer, LOG_BUF_SIZE - 1,
		 "could not set attribute <%s> for <job> python class", attr_def_p->at_name);
	log_buffer[LOG_BUF_SIZE - 1] = '\0';
	log_err(-1, __func__, log_buffer);
	return -1;
}

/**
 * @brief
 * 	pbs_python_setup_queue_class_attributes
 *   	routine that sets up *all* the attributes for a Python Queue Object
 *   	mapping directly to PBS Queue object (pbs_queue)
 *
 * @return      int
 * @retval      -1      :       failure
 * @retval      0       :       success
 */
int
pbs_python_setup_queue_class_attributes(void)
{
	int i = 0;
	attribute_def *attr_def_p = NULL; /* convenience pointer */
	PyObject *py_pbs_que_klass = pbs_python_types_table[PP_QUE_IDX].t_class;
	PyObject *py_value_type = NULL;
	PyObject *py_default_value = NULL;
	PyObject *py_default_args = NULL;
	int num_entry = QA_ATR_LAST + 1; /* 1 for sentinel */
	int te;

	if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name))
		DEBUG3_ARG1("BEGIN setting up all queue attributes %s", "");
	else
		DEBUG2_ARG1("BEGIN setting up all queue attributes %s", "");
	py_que_attr_types = PyMem_New(PyObject *, num_entry);
	if (!py_que_attr_types) {
		goto ERROR_EXIT;
	}
	memset(py_que_attr_types, 0, sizeof(PyObject *) * num_entry);

	/* ok now set all the resources known to the server */
	attr_def_p = que_attr_def;
	for (i = 0; i < QA_ATR_LAST; i++) {
		/* get the value type for this attribute */
		py_value_type = pbs_python_setup_attr_get_value_type(attr_def_p,
								     PY_TYPE_QUEUE);
		/* create a brand new default value from value type */
		if (ATTR_IS_RESC(attr_def_p)) {
			py_default_args = Py_BuildValue("(s)", attr_def_p->at_name);
			if (!py_default_args) {
				/* TODO, continuing instead of fatal error */
				log_err(-1, attr_def_p->at_name, "could not build args for default value");
				attr_def_p++;
				continue;
			}
			py_default_value = pbs_python_make_default_value(py_value_type, py_default_args);
			Py_DECREF(py_default_args);
			if (!py_default_value) {
				/* TODO, continuing instead of fatal error */
				log_err(-1, attr_def_p->at_name, "could not set default value");
				attr_def_p++;
				continue;
			}
			te = TYPE_ENTITY(attr_def_p->at_type);
		} else {
			py_default_value = Py_None;
			te = 0;
		}
		if (_pps_getset_descriptor_object(py_pbs_que_klass,
						  attr_def_p->at_name,
						  py_default_value,
						  py_value_type, NULL, te) == -1)

			goto ERROR_EXIT;
		Py_INCREF(py_value_type);
		if (py_default_value != Py_None)
			Py_CLEAR(py_default_value);
		py_que_attr_types[i] = py_value_type;
		attr_def_p++;
	}
	if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name))
		DEBUG3_ARG1("DONE setting up all queue attributes, number set <%d>", i);
	else
		DEBUG2_ARG1("DONE setting up all queue attributes, number set <%d>", i);
	return 0;

ERROR_EXIT:
	if (py_default_value != Py_None)
		Py_CLEAR(py_default_value);
	snprintf(log_buffer, LOG_BUF_SIZE - 1,
		 "could not set attribute <%s> for <queue> python class", attr_def_p->at_name);
	log_buffer[LOG_BUF_SIZE - 1] = '\0';
	log_err(-1, __func__, log_buffer);
	return -1;
}

/**
 * @brief
 * 	pbs_python_setup_python_resource_type
 *	routine that sets up *all* the resources for a Python resource Object
 *
 * @return      int
 * @retval      -1      :       failure
 * @retval      0       :       success
 */
int
pbs_python_setup_python_resource_type(void)
{
	int i = 0, j;
	resource_def *resc_def_p = NULL; /* convenience pointer */
	PyObject *py_pbs_resc_klass = pbs_python_types_table[PP_RESC_IDX].t_class;
	PyObject *py_value_type = NULL;
	int num_entry = svr_resc_size + 1; /* 1 for sentinel */

	if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name))
		DEBUG3_ARG1("BEGIN setting up all resource attributes %s", "");
	else
		DEBUG2_ARG1("BEGIN setting up all resource attributes %s", "");
	py_svr_resc_types = PyMem_New(PyObject *, num_entry);
	if (!py_svr_resc_types) {
		goto ERROR_EXIT;
	}
	memset(py_svr_resc_types, 0, sizeof(PyObject *) * num_entry);

	/* ok now set all the resources known to the server */
	resc_def_p = svr_resc_def;
	i = 0;
	j = svr_resc_size;
	while (j--) {

		/* get the value type for this resource */
		py_value_type = pbs_python_setup_resc_get_value_type(resc_def_p);
		/* default to None */

		if (_pps_getset_descriptor_object(py_pbs_resc_klass,
						  resc_def_p->rs_name,
						  Py_None,
						  py_value_type, PY_RESOURCE_GENERIC_VALUE, 0) == -1) {
			goto ERROR_EXIT;
		}
		Py_INCREF(py_value_type);
		py_svr_resc_types[i] = py_value_type;
		resc_def_p = resc_def_p->rs_next;
		i++;
	}
	if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name))
		DEBUG3_ARG1("DONE setting up all resource attributes, number set <%d>", i);
	else
		DEBUG2_ARG1("DONE setting up all resource attributes, number set <%d>", i);
	return 0;

ERROR_EXIT:
	snprintf(log_buffer, LOG_BUF_SIZE - 1,
		 "could not set attribute <%s> for <pbs_resource> python class", resc_def_p->rs_name);
	log_buffer[LOG_BUF_SIZE - 1] = '\0';
	log_err(-1, __func__, log_buffer);
	return -1;
}

/**
 * @brief
 * 	pbs_python_clear_types_table clear the python pointers
 *
 * exceptions:
 *   	None
 */

static void
pbs_python_clear_types_table(void)
{
	pbs_python_types_entry *pp_type = pbs_python_types_table;

	while (pp_type->t_key) {
		Py_CLEAR(pp_type->t_class);
		pp_type++;
	}
	return;
}

/**
 * @brief
 * 	pbs_python_types_table setup routine, called at initialization of interp.
 *
 * @return      int
 * @retval      -1      :       failure
 * @retval      0       :       success
 *
 * exceptions:
 *   None
 * Assumptions:
 *   PBS_PythonTypes must be setup.
 */

static int
pbs_python_setup_types_table(void)
{
	pbs_python_types_entry *pp_type = pbs_python_types_table;

	while (pp_type->t_key) {
		pp_type->t_class = PyDict_GetItemString(PBS_PythonTypes, pp_type->t_key);
		if (!(pp_type->t_class)) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "could not find key <%s> in PBS_PythonTypes",
				 pp_type->t_key);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			log_err(-1, __func__, log_buffer);
			goto ERROR_EXIT;
		}
		/* since we are borrowed incref */
		Py_INCREF(pp_type->t_class);
		pp_type++;
	}

	return 0;

ERROR_EXIT:
	/* on error, the load_python_type ... calls the unload for globals */
	return -1;
}

/**
 * @brief
 *    Unload all the PBS python types initialized, as well as static Python
 *    objects created during the initial loading of the interpreter.
 *
 * @param[in/out]  interp_data	- data representing the interpeter that will
 *				be filled with new information.
 *
 * @return none
 *
 */

void
pbs_python_unload_python_types(struct python_interpreter_data *interp_data)
{
	Py_CLEAR(PyPbsV1Module_Obj);
	Py_CLEAR(PBS_PythonTypes);
	pbs_python_clear_types_table();
	pbs_python_free_py_types_array(&py_svr_resc_types);   /* all resources */
	pbs_python_free_py_types_array(&py_que_attr_types);   /* pbs.queue attrs */
	pbs_python_free_py_types_array(&py_job_attr_types);   /* pbs.job attrs */
	pbs_python_free_py_types_array(&py_svr_attr_types);   /* pbs.server attrs */
	pbs_python_free_py_types_array(&py_resv_attr_types);  /* pbs.resv attrs */
	pbs_python_free_py_types_array(&py_vnode_attr_types); /* pbs.vnode attrs */
	Py_CLEAR(py_pbs_statobj);

	interp_data->pbs_python_types_loaded = 0;
	return;
}

/**
 * @brief
 * 	This routine is called at the embedded intialization time, to load all
 * 	the Python types into a dictionary. Mapping the name to a python type.
 * 	The dictionary items are:
 *     	Key  = <an attribute name>
 *    	Value = <the attribute type object from python module (pure)>
 *
 * NOTES:
 *     The Dictionary overwrites if a key already exists!
 *
 * @return      int
 * @retval      -1      :       failure
 * @retval      0       :       success
 *
 * side effects:
 *      Any python exception raised is cleared via the call to
 *          pbs_python_write_error_to_log
 */

int
pbs_python_load_python_types(struct python_interpreter_data *interp_data)
{
	PyObject *py_import = NULL;	 /* new */
	PyObject *py_sys_modules = NULL; /* Borrowed ref */

	if ((PBS_PythonTypes)) { /* ok already loaded */
		return 0;
	}

	interp_data->pbs_python_types_loaded = 0; /* just to be safe */

	if (!(py_import =
		      PyImport_ImportModuleEx((char *) PBS_PYTHON_V1_MODULE, NULL, NULL,
					      NULL))) {
		goto ERROR_EXIT;
	}
	/* we don't need it any more, see next for explanation */
	Py_CLEAR(py_import);

	pbs_python_write_object_to_log(PyImport_GetModuleDict(), "sys.modules=", LOG_DEBUG);

	/* At this point the sys.modules loads module from __init__.py of
	 * PBS_PYTHON_V1_MODULE, which may as a side effect load a bunch of other
	 * modules. So now we get the actual module from sys.modules
	 */
	py_sys_modules = PyImport_GetModuleDict(); /* get sys.modules */
	if (!(PyPbsV1Module_Obj = PyDict_GetItemString(py_sys_modules, PBS_PYTHON_V1_MODULE))) {
		goto ERROR_EXIT;
	}
	Py_INCREF(PyPbsV1Module_Obj); /* since dict.get returns borrowed ref*/

	if (!(PBS_PythonTypes =
		      PyObject_GetAttrString(PyPbsV1Module_Obj,
					     PBS_PYTHON_V1_TYPES_DICTIONARY))) {
		goto ERROR_EXIT;
	}

	/* check and make sure it is a mapping */
	if (!PyDict_Check(PBS_PythonTypes)) {
		log_err(-1, pbs_python_daemon_name,
			"FATAL: PBS_PythonTypes object does not support mapping protocol");
		goto ERROR_EXIT;
	}

	if ((pbs_python_setup_types_table() == -1)) {
		goto ERROR_EXIT;
	}

	/* setup all attrs for pbs_resource class */
	if ((pbs_python_setup_python_resource_type() == -1))
		goto ERROR_EXIT;

	/* setup all attrs for queue class */
	if ((pbs_python_setup_queue_class_attributes() == -1))
		goto ERROR_EXIT;

	/* setup all attrs for job class */
	if ((pbs_python_setup_job_class_attributes() == -1))
		goto ERROR_EXIT;

	/* setup all attrs for server class */
	if ((pbs_python_setup_server_class_attributes() == -1))
		goto ERROR_EXIT;

	/* setup all attrs for reservation (resv) class */
	if ((pbs_python_setup_resv_class_attributes() == -1))
		goto ERROR_EXIT;

	/* setup all attrs for vnode class */
	if ((pbs_python_setup_vnode_class_attributes() == -1))
		goto ERROR_EXIT;

	interp_data->pbs_python_types_loaded = 1;
	return 0;

ERROR_EXIT:
	pbs_python_write_error_to_log(__func__);
	Py_CLEAR(py_import); /* just in case */
	pbs_python_unload_python_types(interp_data);
	return -1;
}

/**
 * @brief
 * 	A convenience function where given a Python object 'py_resource'
 * 	representing 'reslist_name', set its 'resc' to 'value'.
 *
 * @param[in]	py_resource - Python resource list object to set
 * @param[in]	reslist_name - name of resource list
 * @param[in]	resc - name of resource to set
 * @param[in]	value - value of resource being set
 *
 * @return 	int
 * @retval	!= -1	- success
 * @retval	-1	- failure
 *
 */
static int
set_in_python(PyObject *py_resource,
	      char *reslist_name, char *resc, char *value)
{
	int rc;
	int hook_set_mode_orig;

	hook_set_mode_orig = hook_set_mode;

	/* C_MODE is permissive mode unlike PY_MODE, which would enable */
	/* extra checks. PY_MODE is needed only if we're loading data */
	/* from outside (i.e. specified by hook writer). Here, */
	/* set_in_python() is an internal function that is called when */
	/* loading data from PBS data structures. */
	hook_set_mode = C_MODE;
	rc = pbs_python_object_set_attr_string_value(py_resource, resc, value);
	hook_set_mode = hook_set_mode_orig;

	if (rc == -1) {
		LOG_ERROR_ARG2("%s:failed to set resource <%s>",
			       reslist_name, resc);
	}
	return (rc);
}

/** @brief
 *	Given a list of entity resource values (limits) in 'resc_value_list',
 *	if py_resource' is not NULL, then set the Python resource object
 *	represented by 'py_resource' to the values given in 'resc_value_list'.
 *	Otherwise, if 'py_resource' is NULL, and then return a
 *	comma-separated string of resource values in 'resc_value_list' into
 *	'p_strbuf'.
 *  @note
 *	The returned string in 'p_strbuf' is in a privately malloced area, and
 *	should not be freed outside this function.
 *
 * @param[in]	resc_value_list - list of resource values (<resc>=<val).
 * @param[in]	reslist_name - name of the resource list (e.g. resources_used).
 * @param[in]	py_resource - the Python pbs resource object being set.
 * @param[in]	p_strbuf - pointer to a string buffer to hold return string
 *			value if 'py_resource' is NULL.
 * @return int
 * @retval 0	- for success
 * @retval -1   - for failure
 *
 */
static int
set_entity_resource_or_return_value(pbs_list_head *resc_value_list,
				    char *reslist_name, PyObject *py_resource, char **p_strbuf)
{
	static char *ret_str_value = NULL;
	static size_t ret_len = STRBUF;
	static size_t nlen = 0;
	svrattrl *svrattr_val_tmp = NULL; /* tmp pointer for traversal*/
	svrattrl *svrattr_val_next = NULL;
	svrattrl *plist = NULL;
	pbs_list_head entity_head;
	char *bef_resc = NULL;
	char *cur_resc = NULL;
	char *next_resc = NULL;
	char *tmp_str = NULL;
	int rc = 0; /* will be set to -1 if there's at least one failure */

	if (ret_str_value == NULL) {
		ret_str_value = (char *) malloc(ret_len);
		if (ret_str_value == NULL) {
			log_err(-1, __func__, "failed to malloc string buffer!");
			return (-1);
		}
	}
	ret_str_value[0] = '\0';

	/*
	 * The 'resc_value_list' for entity resource say 'max_queued_res'
	 * would look like:
	 *
	 * resc_value_list->
	 * al_name		al_resc		al_value
	 * ---------		--------	----------
	 *  max_queued_res	file		[u:alpha=110gb]
	 *  max_queued_res	ncpus		[u:bayucan=9]
	 *  max_queued_res	file		[u:fatir=120gb]
	 *  max_queued_res	file		[u:mega=115gb]
	 *
	 *  So we store these in an intermediate list (entity_head)
	 *  in a sorted way, so as to group them according to resource names.
	 *  By comparing adjacent entries, we can determine which values
	 *  belong to a particular resource name.
	 *
	 *  entity_head->
	 * al_name		al_resc		al_value
	 * ---------		--------	----------
	 *  max_queued_res	file		[u:alpha=110gb]
	 *  max_queued_res	file		[u:fatir=120gb]
	 *  max_queued_res	file		[u:omega=115gb]
	 *  max_queued_res	ncpus		[u:bayucan=9]
	 *
	 * And process 'entity_head' in a way that the following is stored in
	 * 'ret_str_value':
	 *	max_queued_res=file="[u:alpha=110gb],[u:fatir=120gb],[u:omega]",ncpus=[u:bayucan=9]
	 *	     <- note: there's an enclosing quote in multi-valued limits
	 */

	CLEAR_HEAD(entity_head);

	/* need to append values in a sorted way */
	nlen = 1; /* 1 for \0 */
	plist = (svrattrl *) GET_NEXT(*resc_value_list);
	while (plist) {
		if (add_to_svrattrl_list_sorted(&entity_head,
						plist->al_name, plist->al_resc,
						plist->al_value, plist->al_op,
						plist->al_resc) == -1) {
			free_attrlist(&entity_head);
			log_err(-1, __func__,
				"failed populating entity limits value");
			return (-1);
		}
		nlen += 1 + /*  for ',' */
			strlen(plist->al_resc) +
			1 + /* for '=' */
			strlen(plist->al_value) +
			3; /* allocate for each possible */
		/* '"' and ',' in case we have a */
		/* multi-valued resource limit. Ex. */
		/* file="[u:alpha=50gb],[u:fap=60gb]" */

		plist = (svrattrl *) GET_NEXT(plist->al_link);
	}
	if (nlen > ret_len) {

		nlen += BUF_SIZE; /* malloc a larger size */
		tmp_str = (char *) realloc(ret_str_value, nlen);
		if (tmp_str == NULL) {
			log_err(-1, __func__,
				"failed to realloc string buffer!");
			return (-1);
		}
		ret_str_value = tmp_str;
		ret_len = nlen;
	}

	/* at this point, we have enough buffer space in */
	/* ret_str_value */
	/*

	 Algorithm:

	 3 variables are used to keep track of the individual entries
	 in the sorted 'entity_head' list.

	 1.	cur_resc is the current resource/value line being looked at.
	 2.	bef_resc is the resource/value from the previous line.
	 3.	next_resc is the resource/value on the next line.

	 Then there's a buffer 'ret_str_value' that accumulates values
	 associated with a given resource name. <resource> and <value>
	 are those associated with 'cur_resc'.

	 bef_resc == NULL, next_resc == NULL
	 -> 	if returning string value, then
	 ret_str_value=<resource>=<value>
	 else
	 set <py_resource>.<resource>=<value>

	 bef_resc == NULL, next_resc != cur_resc
	 -> 	if returning string value, then
	 ret_str_value=<resource>=<value>
	 else
	 set <py_resource>.<resource>=<value>

	 bef_resc == NULL, next_resc == cur_resc
	 ->	if returning string value, then
	 start accumulating values:
	 ret_str_value="<resource>=\"<value>"
	 else
	 ret_str_value="<value>"

	 bef_resc == cur_resc, cur_resc != next_resc
	 ->	if returning string value,
	 terminate the current string value:

	 ret_str_value += ",<value>\""
	 else
	 set <py_resource>.<resource>=<ret_str_value>,<value>

	 bef_resc == cur_resc, cur_resc == next_resc
	 ->	continue on the string value:
	 ret_str_value  += ",<value>"

	 bef_resc != cur_resc, cur_resc != next_resc
	 ->	if returning string value,
	 start a new set of resource/value pairs:
	 ret_str_value += ",<resource>=<value>"
	 else
	 set <py_resource>.<resource>=<value>

	 bef_resc != cur_resc, cur_resc == next_resc
	 if returning string value,
	 ret_str_value += "=\"<value>"
	 else
	 ret_str_value = "<value>"
	 */

	svrattr_val_tmp = (svrattrl *) GET_NEXT(entity_head);
	bef_resc = NULL;
	while (svrattr_val_tmp) {

		svrattr_val_next =
			(svrattrl *) GET_NEXT(svrattr_val_tmp->al_link);

		cur_resc = svrattr_val_tmp->al_resc;
		if (cur_resc == NULL) {
			bef_resc = cur_resc;
			svrattr_val_tmp = svrattr_val_next;
			continue;
		}
		if (svrattr_val_next != NULL) {
			if (svrattr_val_next->al_resc == NULL) {
				bef_resc = cur_resc;
				svrattr_val_tmp = svrattr_val_next;
				continue;
			}
			next_resc = svrattr_val_next->al_resc;
		} else {
			next_resc = NULL;
		}

		if (bef_resc == NULL) {
			if (next_resc == NULL) {

				if (py_resource == NULL) {
					sprintf(ret_str_value, "%s=%s",
						svrattr_val_tmp->al_resc,
						svrattr_val_tmp->al_value);
				} else {
					if (set_in_python(py_resource,
							  reslist_name,
							  svrattr_val_tmp->al_resc,
							  svrattr_val_tmp->al_value) == -1)
						rc = -1;
				}

				if (hook_debug.data_fp != NULL) {
					fprintf(hook_debug.data_fp,
						"%s.%s[%s]=%s\n",
						(char *) hook_debug.objname,
						reslist_name,
						svrattr_val_tmp->al_resc,
						svrattr_val_tmp->al_value);
				}
			} else {
				if (strcmp(cur_resc, next_resc) != 0) {
					if (py_resource == NULL) {
						sprintf(ret_str_value, "%s=%s",
							svrattr_val_tmp->al_resc,
							svrattr_val_tmp->al_value);
					} else {
						if (set_in_python(py_resource,
								  reslist_name,
								  svrattr_val_tmp->al_resc,
								  svrattr_val_tmp->al_value) == -1)
							rc = -1;
					}
					if (hook_debug.data_fp != NULL) {
						fprintf(hook_debug.data_fp,
							"%s.%s[%s]=%s\n",
							(char *) hook_debug.objname,
							reslist_name,
							svrattr_val_tmp->al_resc,
							svrattr_val_tmp->al_value);
					}
				} else {
					if (py_resource == NULL) {
						sprintf(ret_str_value,
							"%s=\"%s",
							svrattr_val_tmp->al_resc,
							svrattr_val_tmp->al_value);
					} else {
						/* use ret_str_value */
						/* as value buffer */
						strcpy(ret_str_value,
						       svrattr_val_tmp->al_value);
					}

					if (hook_debug.data_fp != NULL) {
						fprintf(hook_debug.data_fp,
							"%s.%s[%s]=%s",
							(char *) hook_debug.objname,
							reslist_name,
							svrattr_val_tmp->al_resc,
							svrattr_val_tmp->al_value);
					}
				}
			}

		} else {

			if (strcmp(bef_resc, cur_resc) == 0) {
				strcat(ret_str_value, ",");
				strcat(ret_str_value,
				       svrattr_val_tmp->al_value);
				if (hook_debug.data_fp != NULL) {
					fprintf(hook_debug.data_fp,
						",%s",
						svrattr_val_tmp->al_value);
				}
				if ((next_resc != NULL) &&
				    (strcmp(cur_resc, next_resc) != 0)) {
					if (py_resource == NULL) {
						/* terminate entity val  */
						strcat(ret_str_value, "\"");
					} else {
						if (set_in_python(py_resource,
								  reslist_name,
								  svrattr_val_tmp->al_resc,
								  ret_str_value) == -1)
							rc = -1;
					}
					if (hook_debug.data_fp != NULL) {
						fprintf(hook_debug.data_fp,
							"\n");
					}
				}
			} else { /* start new */
				if (py_resource == NULL) {
					strcat(ret_str_value, ",");
				} else {
					ret_str_value[0] = '\0';
				}
				if ((next_resc == NULL) ||
				    (strcmp(cur_resc, next_resc) != 0)) {
					if (py_resource == NULL) {
						strcat(ret_str_value,
						       svrattr_val_tmp->al_resc);
						strcat(ret_str_value, "=");
						strcat(ret_str_value,
						       svrattr_val_tmp->al_value);
					} else {
						if (set_in_python(py_resource,
								  reslist_name,
								  svrattr_val_tmp->al_resc,
								  svrattr_val_tmp->al_value) == -1)
							rc = -1;
					}

					if (hook_debug.data_fp !=
					    NULL) {
						fprintf(hook_debug.data_fp,
							"%s.%s[%s]=%s\n",
							(char *) hook_debug.objname,
							reslist_name,
							svrattr_val_tmp->al_resc,
							svrattr_val_tmp->al_value);
					}
				} else { /* cur_resc == next_resc */
					if (py_resource == NULL) {
						strcat(ret_str_value,
						       svrattr_val_tmp->al_resc);
						strcat(ret_str_value, "=\"");
						strcat(ret_str_value,
						       svrattr_val_tmp->al_value);
					} else {
						/* using as value string */
						strcpy(ret_str_value,
						       svrattr_val_tmp->al_value);
					}
					if (hook_debug.data_fp !=
					    NULL) {
						fprintf(hook_debug.data_fp,
							"%s.%s[%s]=%s",
							(char *) hook_debug.objname,
							reslist_name,
							svrattr_val_tmp->al_resc,
							svrattr_val_tmp->al_value);
					}
				}
			}
		}

		bef_resc = cur_resc;
		svrattr_val_tmp = svrattr_val_next;
	}
	free_attrlist(&entity_head);

	if (py_resource == NULL)
		*p_strbuf = ret_str_value;

	return (rc);
}

/**
 * @brief
 *	Given a list of resource values in 'resc_value_list', if
 *	py_resource' is not NULL, then set the Python resource object
 *	represented by 'py_resource' to the values given in 'resc_value_list'.
 *	If 'py_resource' is NULL, and then return a comma-separated string
 *	into 'p_strbuf', which represent the values given in 'resc_value_list'.
 * @note
 *	The returned string in 'p_strbuf' is in a private malloced area that
 *	should not be freed outside this function.
 *
 * @param[in]	resc_value_list - list of resource values (<resc>=<val).
 * @param[in]	py_resource - the Python pbs resource object being set.
 * @param[in]	p_strbuf - pointer to a string buffer to hold return string
 *			value if 'py_resource' is NULL.
 * @return 	int
 * @retval 	0    -	for success
 * @retval 	-1   - 	for failure
 *
 */
static int
set_resource_or_return_value(pbs_list_head *resc_value_list, char *reslist_name,
			     PyObject *py_resource, char **p_strbuf)
{
	static char *ret_str_value = NULL;
	static size_t ret_len = STRBUF;
	static size_t nlen = 0;
	svrattrl *svrattr_val_tmp = NULL; /* tmp pointer for traversal*/
	char *tmp_str = NULL;
	int rc = 0; /* set to 1 if there's at least 1 failure */

	if (ret_str_value == NULL) {
		ret_str_value = (char *) malloc(ret_len);
		if (ret_str_value == NULL) {
			log_err(-1, __func__, "failed to malloc string buffer!");
			return (-1);
		}
	}
	ret_str_value[0] = '\0';

	svrattr_val_tmp = (svrattrl *) GET_NEXT(*resc_value_list);
	while (svrattr_val_tmp) {

		if (py_resource == NULL) {
			nlen = strlen(ret_str_value) +
			       1 + /*  for ',' */
			       strlen(svrattr_val_tmp->al_resc) +
			       1 + /* for '=' */
			       strlen(svrattr_val_tmp->al_value) +
			       1; /* for '\0' */

			if (nlen > ret_len) {
				nlen += BUF_SIZE; /* malloc a larger size */
				tmp_str = (char *) realloc(ret_str_value, nlen);
				if (tmp_str == NULL) {
					log_err(-1, __func__,
						"failed to realloc string buffer!");
					return (-1);
				}
				ret_str_value = tmp_str;
				ret_len = nlen;
			}

			if (*ret_str_value == '\0') {

				sprintf(ret_str_value, "%s=%s",
					svrattr_val_tmp->al_resc,
					svrattr_val_tmp->al_value);
			} else {
				strcat(ret_str_value, ",");
				strcat(ret_str_value, svrattr_val_tmp->al_resc);
				strcat(ret_str_value, "=");
				strcat(ret_str_value, svrattr_val_tmp->al_value);
			}
		} else {
			if (set_in_python(py_resource,
					  reslist_name,
					  svrattr_val_tmp->al_resc,
					  svrattr_val_tmp->al_value) == -1) {
				rc = -1;
			}
		}
		if (hook_debug.data_fp != NULL) {
			fprintf(hook_debug.data_fp, "%s.%s[%s]=%s\n",
				(char *) hook_debug.objname,
				reslist_name, svrattr_val_tmp->al_resc,
				svrattr_val_tmp->al_value);
		}
		svrattr_val_tmp =
			(svrattrl *) GET_NEXT(svrattr_val_tmp->al_link);
	}

	if (py_resource == NULL)
		*p_strbuf = ret_str_value;
	return (rc);
}

/**
 * @brief
 *	Returns tthe Python string value of 'resc_val' which is of type
 *	'pbs_resource_value' representing a pbs_resource object.
 *
 * @param[in]	resc_val  a 'pbs_resource_value' input type.
 *
 * @return	PyObject *
 * @retval	NULL	- error getting string value.
 * @retval	<python string value> - a Python object representing the
 * @retval	"<resc1>=<val1>,"<entity_resc2>=<val2,val3",...<rescN>=<valN>"
 *		- a concatenation of string values for py_resource.
 *
 */
PyObject *
py_resource_string_value(pbs_resource_value *resc_val)
{
	char *ret_string = NULL;

	if (resc_val == NULL) {
		Py_RETURN_NONE;
	}

	if (TYPE_ENTITY(resc_val->attr_def_p->at_type)) {
		set_entity_resource_or_return_value(&(resc_val->value_list),
						    resc_val->attr_def_p->at_name,
						    NULL, &ret_string);
	} else { /* a regular resource */
		set_resource_or_return_value(&(resc_val->value_list),
					     resc_val->attr_def_p->at_name,
					     NULL, &ret_string);
	}
	/* New reference returned below */
	return PyUnicode_FromString(ret_string);
}

/*
 * ---------- ATTRIBUTE CONVERSION HELPER METHODS ------------
 */

/**
 * @brief
 *
 * 	Populates a Python instance 'py_instance' with values found in
 *	an attributes data array.
 *
 * @param[in] py_instance -  a Python object/class to populate
 * @param[in] attr_py_array - list of Python types to map attributes with
 * @param[in] attr_data_array - array of actual attribute names/resources/values
 * @param[in] attr_def_array - array of attribute definitions (ex. job_attr_def)
 * @param[in] attr_def_array_size - size of attr_def_array.
 * @param[in]	perf_label - passed on to hook_perf_stat* call.
 * @param[in]	perf_action - passed on to hook_perf_stat* call.
 *
 * @return indication of whether or not 'py_instance' was completely
 *	   populated or not
 * @return 0	- completely populated
 * @return -1	- incompletely populated
 *
 * @note
 *		This function calls a single hook_perf_stat_start()
 *		that has some malloc-ed data that are freed in the
 *		hook_perf_stat_stop() call, which is done at the end of
 *		this function.
 *		Ensure that after the hook_perf_stat_start(), all
 *		program execution path lead to hook_perf_stat_stop()
 *		call.
 */

int
pbs_python_populate_attributes_to_python_class(PyObject *py_instance,
					       PyObject **attr_py_array,
					       attribute *attr_data_array,
					       attribute_def *attr_def_array,
					       int attr_def_array_size, char *perf_label, char *perf_action)
{
	int i = 0;	   /* index */
	int encode_rv = 0; /* at_encode functions return value */
	int rc = -1;
	int ret_rc = 0;
	svrattrl *svrattr_val = NULL;	  /* tmp pointer */
	svrattrl *svrattr_val_tmp = NULL; /* tmp pointer for traversal*/
	pbs_list_head pheadp;
	attribute *attr_p = NULL;
	attribute_def *attr_def_p = NULL;
	PyObject *py_attr_resc = NULL; /* for resource types */
	char *value_str = NULL;
	char *new_value_str = NULL;
	pbs_resource_value *resc_val;

	hook_perf_stat_start(perf_label, perf_action, 0);
	for (i = 0; i < attr_def_array_size; i++) {
		attr_p = attr_data_array + i;
		attr_def_p = attr_def_array + i;

		memset(&pheadp, 0, sizeof(pheadp));
		CLEAR_HEAD(pheadp);

		svrattr_val = NULL;
		encode_rv = attr_def_p->at_encode(attr_p,
						  /* linked list */ &pheadp,
						  /* name        */ attr_def_p->at_name,
						  /* resource    */ NULL,
						  /* Encoding type */ ATR_ENCODE_HOOK,
						  /* returned svrattrl */ &svrattr_val);

		if ((encode_rv == 0) && (svrattr_val != NULL)) {
			encode_rv = 1;
		}
		if (encode_rv == 0) {
			/* not set or no value */
			continue;
		} else if (encode_rv >= 1) { /* good, single value */
			/* we could be a resource list */
			if (ATTR_IS_RESC(attr_def_p)) {
				if (!PyObject_HasAttrString(py_instance, attr_def_p->at_name)) {
					free_attrlist(&pheadp);
					continue;
				}

				/* NOTE the below is a new reference */
				py_attr_resc =
					PyObject_GetAttrString(py_instance,
							       attr_def_p->at_name);
				if (py_attr_resc == NULL) {
					pbs_python_write_error_to_log(__func__);
					free_attrlist(&pheadp);
					continue;
				}
				/* Mark resource currently has no value */
				/* loaded, but the value will be set later */
				/* as needed, by saving the value in */
				/* pbs_resource_value_list */
				rc = pbs_python_object_set_attr_integral_value(
					py_attr_resc,
					PY_RESOURCE_HAS_VALUE, FALSE);
				if (rc == -1) {
					LOG_ERROR_ARG2("%s:failed to set resource <%s> to False",
						       attr_def_p->at_name,
						       PY_RESOURCE_HAS_VALUE);
					ret_rc = -1;
				} else {
					sprintf(log_buffer, "set py_resource %s %s to FALSE",
						attr_def_p->at_name,
						PY_RESOURCE_HAS_VALUE);
					resc_val =
						(pbs_resource_value *) malloc(
							sizeof(pbs_resource_value));
					if (resc_val ==
					    NULL) {
						free_attrlist(&pheadp);
						continue;
					}

					(void) memset((char *) resc_val, (int) 0,
						      (size_t) sizeof(pbs_resource_value));
					CLEAR_LINK(resc_val->all_rescs);
					/* no need to incref py_attr_resc */
					/* since that's already done */
					/* with the PyObject_GetAttrString() */
					/* call earlier. */
					resc_val->py_resource = py_attr_resc;
					resc_val->attr_def_p = attr_def_p;

					CLEAR_HEAD(resc_val->value_list);
					list_move(&pheadp,
						  &resc_val->value_list);

					append_link(&pbs_resource_value_list,
						    &resc_val->all_rescs,
						    (pbs_resource_value *) resc_val);
					resc_val->py_resource_str_value =
						py_resource_string_value(resc_val);
				}
			} else { /* attribute */
				/* PBS' ATTR_inter/ATTR_block/ATTR_X11_port can either have a boolean-like */
				/* value for client (i.e. "True" or "False"), or an int-like */
				/* value for others (e.g. "2274" for port number)            */
				/* Python's version of these attributes are defined as ints, */
				/* and are not modifiable in a hook script. So we need to    */
				/* map the values into something consistent.                 */

				if ((strcmp(attr_def_p->at_name, ATTR_inter) == 0) ||
				    (strcmp(attr_def_p->at_name, ATTR_block) == 0) ||
				    (strcmp(attr_def_p->at_name, ATTR_X11_port) == 0)) {
					char inter_val[2];

					if (strcasecmp(svrattr_val->al_value, ATR_FALSE) == 0) {
						strcpy(inter_val, "0");
					} else {
						strcpy(inter_val, "1");
					}
					rc = pbs_python_object_set_attr_string_value(py_instance,
										     attr_def_p->at_name,
										     inter_val);
					if ((rc != -1) && (hook_debug.data_fp != NULL)) {
						fprintf(hook_debug.data_fp, "%s.%s=%s\n", (char *) hook_debug.objname,
							attr_def_p->at_name, inter_val);
					}
				} else if ((strcmp(attr_def_p->at_name,
						   ATTR_NODE_state) == 0) ||
					   (strcmp(attr_def_p->at_name,
						   ATTR_NODE_ntype) == 0)) {
					/* ignore these attributes, dealt with externally */
					free_attrlist(&pheadp);
					continue;

				} else if ((strcmp(attr_def_p->at_name,
						   ATTR_NODE_Sharing) == 0)) {

					attribute lattr;
					char nshare_str[HOOK_BUF_SIZE];

					rc = decode_sharing(&lattr, attr_def_p->at_name, 0,
							    svrattr_val->al_value);

					if (rc == 0) {
						snprintf(nshare_str, sizeof(nshare_str), "%ld",
							 lattr.at_val.at_long);

						rc = pbs_python_object_set_attr_string_value(py_instance,
											     attr_def_p->at_name, nshare_str);
						if ((rc != -1) && (hook_debug.data_fp != NULL)) {
							fprintf(hook_debug.data_fp, "%s.%s=%s\n", (char *) hook_debug.objname,
								attr_def_p->at_name, nshare_str);
						}
					}

				} else if (TYPE_ENTITY(attr_def_p->at_type)) {
					/* an entity attribute - can have a list of values */

					svrattr_val_tmp = svrattr_val;
					while (svrattr_val_tmp) {

						new_value_str = NULL;
						value_str = pbs_python_object_get_attr_string_value(
							py_instance, svrattr_val_tmp->al_name);

						if (value_str != NULL) {

							new_value_str = malloc(strlen(value_str) +
									       strlen(svrattr_val_tmp->al_value) + 2);
							/* +2 for: "," and "\0" */
							if (new_value_str == NULL) {
								LOG_ERROR_ARG2(
									"%s:malloc failed extending entity <%s>",
									attr_def_p->at_name,
									svrattr_val_tmp->al_name);
								ret_rc = -1;
							} else {
								sprintf(new_value_str, "%s,%s",
									value_str, svrattr_val_tmp->al_value);
							}
						}
						rc = pbs_python_object_set_attr_string_value(
							py_instance,
							attr_def_p->at_name,
							new_value_str ? new_value_str : svrattr_val->al_value);
						if ((rc != -1) && (hook_debug.data_fp != NULL)) {
							fprintf(hook_debug.data_fp, "%s.%s=%s\n", (char *) hook_debug.objname,
								attr_def_p->at_name,
								new_value_str ? new_value_str : svrattr_val->al_value);
						}

						if (new_value_str != NULL) {
							free(new_value_str);
						}
						svrattr_val_tmp = (svrattrl *) GET_NEXT(
							svrattr_val_tmp->al_link);

					} /* while */

				} else {
					rc = pbs_python_object_set_attr_string_value(py_instance,
										     attr_def_p->at_name,
										     svrattr_val->al_value);

					if ((rc != -1) && (hook_debug.data_fp != NULL)) {
						fprintf(hook_debug.data_fp, "%s.%s=%s\n", (char *) hook_debug.objname,
							attr_def_p->at_name, svrattr_val->al_value);
					}
				}

				if (rc == -1) {
					LOG_ERROR_ARG2("%s:failed to set attribute <%s>",
						       "", attr_def_p->at_name);
					ret_rc = -1;
				}
			}

			free_attrlist(&pheadp);
		} else { /* error */
			continue;
		}
	} /* for */
	hook_perf_stat_stop(perf_label, perf_action, 0);
	return ret_rc;
}

/**
 * @brief
 *
 * 	Populates a Python instance 'py_instance' with values found in
 *	a svrattrl list.
 *
 * @param[in] py_instance -  a Python object/class to populate
 * @param[in] svrattrl_list - a pbs_list_head with svrattrl entries whose
 *				values will be used to populate 'py_instance'
 * @param[in]	perf_label - data passed on to hook_perf_stat* call
 * @param[in]	perf_action - dat passed on to hook_perf_stat* call
 *
 * @return indication of whether or not 'py_instance' was completely
 *	   populated or not
 * @return 0	- completely populated
 * @return -1	- incompletely populated
 *
 * @note
 *		This function calls a single hook_perf_stat_start()
 *		that has some malloc-ed data that are freed in the
 *		hook_perf_stat_stop() call, which is done at the end of
 *		this function.
 *		Ensure that after the hook_perf_stat_start(), all
 *		program execution path lead to hook_perf_stat_stop()
 *		call.
 */
int
pbs_python_populate_python_class_from_svrattrl(PyObject *py_instance, pbs_list_head *svrattrl_list, char *perf_label, char *perf_action)
{
	svrattrl *plist = NULL;

	int rc = 0;
	int ret_rc = 0;
	PyObject *py_attr_resc = NULL; /* for resource types */
	char *objname = NULL;

	if (hook_debug.input_fp != NULL) {

		if (PyObject_IsInstance(py_instance,
					pbs_python_types_table[PP_JOB_IDX].t_class))
			objname = EVENT_JOB_OBJECT;
		else if (PyObject_IsInstance(py_instance,
					     pbs_python_types_table[PP_RESV_IDX].t_class))
			objname = EVENT_RESV_OBJECT;
		else if (PyObject_IsInstance(py_instance,
					     pbs_python_types_table[PP_VNODE_IDX].t_class))
			objname = EVENT_VNODE_OBJECT;
		else
			objname = EVENT_OBJECT;
	}

	print_svrattrl_list("pbs_python_populate_python_class_from_svrattrl==>",
			    svrattrl_list);
	hook_perf_stat_start(perf_label, perf_action, 0);
	plist = (svrattrl *) GET_NEXT(*svrattrl_list);

	while (plist) {

		if (plist->al_resc) {
			if (!PyObject_HasAttrString(py_instance, plist->al_name)) {
				plist = (svrattrl *) GET_NEXT(plist->al_link);
				continue;
			}

			py_attr_resc = PyObject_GetAttrString(py_instance,
							      plist->al_name);

			if (!py_attr_resc) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1,
					 "Could not find %s", plist->al_name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				pbs_python_write_error_to_log(log_buffer);
				ret_rc = -1;
			} else {
				rc = pbs_python_object_set_attr_string_value(py_attr_resc,
									     plist->al_resc, plist->al_value);
				Py_DECREF(py_attr_resc);
				if (rc == -1) {
					LOG_ERROR_ARG2("%s:failed to set resource <%s>",
						       plist->al_resc, plist->al_name);
					ret_rc = -1;
				} else if (hook_debug.input_fp != NULL) {
					fprintf(hook_debug.input_fp, "%s.%s[%s]=%s\n", objname,
						plist->al_name, plist->al_resc, plist->al_value);
				}
			}
		} else {

			if (PyObject_IsInstance(py_instance,
						pbs_python_types_table[PP_VNODE_IDX].t_class) &&
			    (strcmp(plist->al_name, VNATTR_HOOK_REQUESTOR) == 0)) {
				/* an special value not be Python set */
				plist = (svrattrl *) GET_NEXT(plist->al_link);
				continue;
			}

			rc = pbs_python_object_set_attr_string_value(py_instance,
								     plist->al_name, return_internal_value(plist->al_name, plist->al_value));
			if (rc == -1) {
				LOG_ERROR_ARG2("%s:failed to set attribute <%s>",
					       "", plist->al_name);
				ret_rc = -1;
			} else if (hook_debug.input_fp != NULL)
				fprintf(hook_debug.input_fp, "%s.%s=%s\n", objname, plist->al_name, plist->al_value);
		}

		plist = (svrattrl *) GET_NEXT(plist->al_link);
	}

	hook_perf_stat_stop(perf_label, perf_action, 0);
	return (ret_rc);
}

/**
 * @brief
 * 	Returns the # of seconds equivalent to the given 'time_str' which is of
 * 	the form [hh:[mm:]]ss[.ms]
 *
 * @return	long
 * @retval	-1 or -2				for error, each filling
 *							a differnt log_buffer message
 * @retval	time in[hh:[mm:]]ss[.ms] format		success
 */
static long
duration_to_secs(char *time_str)
{

	char *value_tmp = NULL;
	attribute attr;
	int rc;

	/*  The *decode* functions below "munges" the value argument, so will use */
	/*  a copy */
	value_tmp = strdup(time_str);
	if (value_tmp == NULL) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "strdup failed! (errno %d)", errno);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		return -1;
	}

	clear_attr(&attr, job_attr_def);
	/* just a dummy attribute representing "walltime" to get the # of secs */
	/* of duration time */
	rc = decode_time(&attr, WALLTIME_RESC, NULL, value_tmp);

	if (value_tmp) {
		free(value_tmp);
	}

	if (rc != 0) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "input value %s not of the right format'",
			 time_str);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		return (-2);
	}

	return (attr.at_val.at_long);
}

/**
 *
 * @brief
 *	Compares 2 pbs variables string lists (i.e. comma-separated lists of
 *	var=value entries).
 *
 * @param[in]	varl1	- variable string list #1
 * @param[in]	varl2	- variable string list #2
 *
 * @return int
 * @retval 1	if the variable string lists are the same
 * @retval 0	otherwise
 */
int
varlist_same(char *varl1, char *varl2)
{
	pbs_list_head list1; /* Caution: list maintained in sorted order */
	pbs_list_head list2; /* Caution: list maintained in sorted order */
	char *pc, *pc1;
	char *env_var;
	char *env_val;
	char *varl1_dup = NULL;
	char *varl2_dup = NULL;
	svrattrl *pal1 = NULL;
	svrattrl *pal2 = NULL;
	int rc;

	if ((varl1 == NULL) || (varl2 == NULL))
		return 0;

	/* quick test */
	if (strcmp(varl1, varl2) == 0) {
		return 1;
	}

	varl1_dup = strdup(varl1);
	if (varl1_dup == NULL)
		return 0;
	varl2_dup = strdup(varl2);
	if (varl2_dup == NULL) {
		free(varl1_dup);
		return 0;
	}

	CLEAR_HEAD(list1);
	CLEAR_HEAD(list2);

	pc = strtok(varl1_dup, ",");
	while (pc != NULL) {
		env_var = pc;
		env_val = NULL;

		pc1 = strchr(pc, '=');
		if (pc1) {
			*pc1 = '\0';
			env_val = pc1 + 1;
		}
		(void) add_to_svrattrl_list_sorted(&list1, env_var, NULL,
						   env_val ? env_val : "", 0, NULL);

		pc = strtok(NULL, ",");
	}

	pc = strtok(varl2_dup, ",");
	while (pc != NULL) {
		env_var = pc;
		env_val = NULL;

		pc1 = strchr(pc, '=');
		if (pc1) {
			*pc1 = '\0';
			env_val = pc1 + 1;
		}
		(void) add_to_svrattrl_list_sorted(&list2, env_var, NULL,
						   env_val ? env_val : "", 0, NULL);
		pc = strtok(NULL, ",");
	}

	/* now compare the 2 sorted lists, which means if the 2 lists are */
	/* the same, then there should be a line by line match. */
	pal1 = (svrattrl *) GET_NEXT(list1);
	pal2 = (svrattrl *) GET_NEXT(list2);
	rc = 1;
	while ((pal1 != NULL) && (pal2 != NULL)) {
		if ((strcmp(pal1->al_name, pal2->al_name) != 0) ||
		    (strcmp(pal1->al_value, pal2->al_value) != 0)) {
			rc = 0;
			goto varlist_same_end;
		}
		pal1 = (svrattrl *) GET_NEXT(pal1->al_link);
		pal2 = (struct svrattrl *) GET_NEXT(pal2->al_link);
	}
	/* in the end, if they both match, both pointers must not be pointing anywhere */
	if ((pal1 != NULL) || (pal2 != NULL)) {
		rc = 0;
	}

varlist_same_end:
	free_attrlist(&list1);
	free_attrlist(&list2);
	free(varl1_dup);
	free(varl2_dup);

	return (rc);
}

/**
 * @brief
 *	Load the cached values found 'pbs_resource_value_list' into the
 *	Python resource list type object, py_resource_match.
 *
 * @param[in]	py_resource_match - the Resource list type object.
 *
 * @return int
 * @retval 0	- for success.
 * @retval != 0 - if some failure occurred.
 *
 */
static int
load_cached_resource_value(PyObject *py_resource_match)
{
	pbs_resource_value *resc_val = NULL;
	int rc;

	resc_val = (pbs_resource_value *) GET_NEXT(pbs_resource_value_list);
	while (resc_val != NULL) {

		if ((resc_val->py_resource != NULL) &&
		    (py_resource_match == resc_val->py_resource)) {
			break;
		}

		resc_val = (pbs_resource_value *) GET_NEXT(resc_val->all_rescs);
	}

	if (resc_val == NULL) {
		/* no match */
		return (0); /* no cached value found */
	}

	if (TYPE_ENTITY(resc_val->attr_def_p->at_type)) {
		rc = set_entity_resource_or_return_value(
			&(resc_val->value_list), resc_val->attr_def_p->at_name,
			resc_val->py_resource, NULL);
	} else { /* a regular resource */
		rc = set_resource_or_return_value(&(resc_val->value_list),
						  resc_val->attr_def_p->at_name,
						  resc_val->py_resource, NULL);
	}

	if (rc == 0) {

		hook_set_mode = C_MODE;
		rc = pbs_python_object_set_attr_integral_value(
			resc_val->py_resource, PY_RESOURCE_HAS_VALUE, TRUE);
		hook_set_mode = PY_MODE;

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set resource <%s>",
				       resc_val->attr_def_p->at_name,
				       PY_RESOURCE_HAS_VALUE);
		}
		Py_DECREF(resc_val->py_resource);
		free_attrlist(&resc_val->value_list);
		delete_link(&resc_val->all_rescs);
		free(resc_val);
	}

	return (rc);
}

/**
 *
 * @brief
 *	Repopulates the 'svrattrl_list' (pbs_list_head) with values found from
 * 	py_instance's attributes  and resources.
 *
 * @param[in]	  py_instance	- Python object to get input from.
 * @param[in/out] svrattrl_list  - the list to populate with data, as well
 *				   as the input list containing the
 *				   attribute flags to retain for each attribute
 *				   name.
 * @param[in]	  name_prefix - If not NULL, then the value for attribute
 *				name in svrattr_list elements is prefixed
 *				with this string.
 * @param[in]	  append   - if set to 0, then 'svrattrl_list' will get
 *			   initialized and populated with
 *			   data(name, resource, value, attribute_flag)
 *			   coming from 'py_instance''s
 *			   attribute name, resource name, value, and
 *			   'attribute_flag' will retain the previous flag value
 *			   if appearing on the original 'svrattrl_list'.
 * 			   - if set to 1, then 'svrattrl_list' will accumulate
 *			   (get appended to) data(name, resource, value,
 *			   attribute_flag) from 'py_instance', with the
 *			   'attribute_flag' not be taken from the original
 *			   svrattrl_list.
 * @note
 *	'append' mode to 1 is usually done if calling this function multiple
 *	times to accumulate the 'svrattrl_list'. For instance, this can be
 * 	useful in periodic hook when trying to get all the vnode values in
 *	a vnode_list.
 *
 * @return int
 * @retval 0	for success
 * @retval != 0 for error
 *
 */
int
pbs_python_populate_svrattrl_from_python_class(PyObject *py_instance,
					       pbs_list_head *svrattrl_list, char *name_prefix, int append)
{
	PyObject *py_attr_dict = NULL;
	PyObject *py_attr_hookset_dict = NULL;
	PyObject *py_resc_hookset_dict = NULL;
	PyObject *py_attr_keys = NULL;
	PyObject *py_val = NULL;
	PyObject *py_keys = NULL;
	PyObject *py_keys_dict = NULL;
	PyObject *py_keys_dict2 = NULL;
	char *name_str_dup = NULL;
	char *val_str_dup = NULL;
	int num_attrs, i;
	pbs_list_head svrattrl_list2;
	int rc = -1;
	int hook_set_flag = 0;
	int has_resv_duration;
	char the_resc[HOOK_BUF_SIZE];
	static char *the_val = NULL;
	static int val_buf_size = HOOK_BUF_SIZE;
	PyObject *py_resc = NULL;
	long val_sec;
	char *objname = NULL;

	if (the_val == NULL) {
		the_val = (char *) malloc(val_buf_size);
		if (the_val == NULL) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1, "malloc failure (errno %d)",
				 errno);
			log_err(PBSE_SYSTEM, __func__, log_buffer);
			return rc;
		}
	}

	if (hook_debug.output_fp != NULL) {

		if (PyObject_IsInstance(py_instance,
					pbs_python_types_table[PP_JOB_IDX].t_class))
			objname = EVENT_JOB_OBJECT;
		else if (PyObject_IsInstance(py_instance,
					     pbs_python_types_table[PP_RESV_IDX].t_class))
			objname = EVENT_RESV_OBJECT;
		else if (PyObject_IsInstance(py_instance,
					     pbs_python_types_table[PP_VNODE_IDX].t_class))
			objname = EVENT_VNODE_OBJECT;
		else
			objname = EVENT_OBJECT;
	}

	CLEAR_HEAD(svrattrl_list2);
	/* py_attr_dict = <py_instance>.attributes[] */
	if (!PyObject_HasAttrString(py_instance, PY_ATTRIBUTES)) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "python object does not have '%s'", PY_ATTRIBUTES);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		return rc;
	}

	py_attr_dict = PyObject_GetAttrString(py_instance, PY_ATTRIBUTES); /* NEW*/
	if (!py_attr_dict) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "Failed to obtain event job's '%s'", PY_ATTRIBUTES);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto svrattrl_exit;
	}

	if (!PyDict_Check(py_attr_dict)) {
		log_err(PBSE_INTERNAL, __func__, "object's attributes not a dictionary!");
		goto svrattrl_exit;
	}

	/* py_attr_keys = keys of <py_instance>.attributes[] */
	py_attr_keys = PyDict_Keys(py_attr_dict); /* NEW ref */
	if (!py_attr_keys) {
		log_err(PBSE_INTERNAL, __func__,
			"Failed to obtain event job's attributes keys");
		goto svrattrl_exit;
	}

	if (!PyList_Check(py_attr_keys)) {
		log_err(PBSE_INTERNAL, __func__, "object's attributes keys not a list!");
		goto svrattrl_exit;
	}

	/* Get the attributes that have been set in the hook script */
	py_attr_hookset_dict = PyObject_GetAttrString(
		py_instance, PY_ATTRIBUTES_HOOK_SET); /* new ref */
	if (py_attr_hookset_dict == NULL || !PyDict_Check(py_attr_hookset_dict)) {
		LOG_ERROR_ARG2("%s: <%s> does not exist or is not a dict", objname,
			       PY_ATTRIBUTES_HOOK_SET);
		Py_CLEAR(py_attr_hookset_dict); /* don't use if not dict */
	}

	/* Reservation specific, let's see if our  object already contains */
	/* a non-NULL, non-None ATTR_resv_duration attribute */
	has_resv_duration = 0;
	if (PyObject_IsInstance(py_instance,
				pbs_python_types_table[PP_RESV_IDX].t_class) &&
	    PyObject_HasAttrString(py_instance, ATTR_resv_duration)) {
		py_val = PyObject_GetAttrString(py_instance, ATTR_resv_duration); /*NEW*/
		if (py_val && (py_val != Py_None)) {
			has_resv_duration = 1;
		}
		Py_CLEAR(py_val);
	}

	num_attrs = PyList_Size(py_attr_keys);

	if (!append) {
		/* svrattrl_list2 is a copy of the original list */
		/* svrattrl_list2 must be freed at the end of this func */
		if (copy_svrattrl_list(svrattrl_list, &svrattrl_list2) == -1) {
			log_err(errno, __func__, "failed to save svrattrl_list");
			goto svrattrl_exit;
		}
		free_attrlist(svrattrl_list);
	}

	for (i = 0; i < num_attrs; i++) {
		char *name_str = NULL;
		resource_def *rescdef = NULL;

		name_str_dup = strdup(pbs_python_list_get_item_string_value(py_attr_keys, i));
		if (!name_str_dup) {
			log_err(errno, __func__, "strdup error");
			goto svrattrl_exit;
		}
		name_str = name_str_dup;

		/* these don't have entries in svrattrl */
		if (!name_str || (name_str[0] == '\0') ||
		    (strcmp(name_str, ATTR_queue) == 0) ||
		    in_string_list(name_str, ',', PY_PYTHON_DEFINED_ATTRIBUTES)) {
			if (name_str_dup) {
				free(name_str_dup);
				name_str_dup = NULL;
			}
			continue;
		}

		if (!PyObject_HasAttrString(py_instance, name_str)) {
			if (name_str_dup) {
				free(name_str_dup);
				name_str_dup = NULL;
			}
			continue;
		}

		py_val = PyObject_GetAttrString(py_instance, name_str);
		/* must be Py_CLEAR(-)ed or
		 * Py_DECREF()-ed later, so as to not leak memory
		 */

		if (!py_val || (py_val == Py_None)) {

			if (name_str_dup) {
				free(name_str_dup);
				name_str_dup = NULL;
			}
			continue;
		}

		if (PyObject_IsInstance(py_val,
					pbs_python_types_table[PP_RESC_IDX].t_class)) { /* a resource */
			char *resc, *val;
			int k, num_keys;
			PyObject *py_class = pbs_python_types_table[PP_RESC_IDX].t_class;
			PyObject *py_tmp = NULL;

			/* code snippet below mimics what's done in the
			 * __str__ method of class pbs_resource under
			 * /pbs/v1/_base_types.py for accessing entries
			 * in resource list.
			 */
			if (PyObject_HasAttrString(py_val, PY_RESOURCE_HAS_VALUE)) {
				if (pbs_python_object_get_attr_integral_value(py_val, PY_RESOURCE_HAS_VALUE) == 0) { /* no value yet */
					if (load_cached_resource_value(py_val) != 0) {
						log_err(PBSE_INTERNAL, __func__,
							"Failed to to load cached value for resource list");
						goto svrattrl_exit;
					}
				}
			}

			/* Obtain list of resource names from
			 *  <class pbs_resource>._attributes  dictionary
			 */
			if (PyObject_HasAttrString(py_class, PY_ATTRIBUTES)) {
				py_keys_dict = PyObject_GetAttrString(py_class,
								      PY_ATTRIBUTES);
			}

			if (!py_keys_dict || !PyDict_Check(py_keys_dict)) {
				log_err(PBSE_INTERNAL, __func__,
					"Failed to obtain event job's resource list dictionary");
				goto svrattrl_exit;
			}
			py_tmp = py_keys_dict;
			py_keys_dict = PyDict_Copy(py_tmp);
			Py_CLEAR(py_tmp);
			if (!py_keys_dict) {
				log_err(PBSE_INTERNAL, __func__,
					"Failed to duplicate resource list dictionary");
				goto svrattrl_exit;
			}

			/* look into <class pbs_resource>._attributes_unknown
			 * for custom resource names defined in a hook but
			 * not yet in resource table.
			 */
			py_keys_dict2 = PyObject_GetAttrString(
				py_val, "_attributes_unknown"); /* new ref */
			if (py_keys_dict2) {
				/* Merge resource list dictionary with the dictionary of
				 * unknown resources
				 */
				if (PyDict_Check(py_keys_dict2)) {
					PyDict_Update(py_keys_dict, py_keys_dict2);
				}
			}

			py_keys = PyDict_Keys(py_keys_dict);
			if (!py_keys || !PyList_Check(py_keys)) {
				log_err(PBSE_INTERNAL, __func__,
					"Failed to obtain event job's resource list keys");
				goto svrattrl_exit;
			}

			/* Get the resources that have been set in the hook script */
			py_resc_hookset_dict = PyObject_GetAttrString(
				py_val, PY_ATTRIBUTES_HOOK_SET); /* new ref */
			if (py_resc_hookset_dict != NULL && !PyDict_Check(py_resc_hookset_dict)) {
				Py_CLEAR(py_resc_hookset_dict); /* don't use if not dict */
			}

			num_keys = PyList_Size(py_keys);
			for (k = 0; k < num_keys; k++) {
				char *tmpstr = NULL;
				tmpstr = pbs_python_list_get_item_string_value(py_keys, k);
				if (tmpstr == NULL)
					continue;

				resc = strdup(tmpstr);
				if (resc == NULL) {
					log_err(errno, __func__, "strdup error");
					goto svrattrl_exit;
				}
				tmpstr = pbs_python_object_get_attr_string_value(py_val, resc);
				if (tmpstr == NULL) {
					free(resc);
					continue;
				}

				val = strdup(tmpstr);
				if (val == NULL) {
					log_err(errno, __func__, "strdup error");
					free(resc);
					goto svrattrl_exit;
				}

				if ((strcmp(resc, PY_RESOURCE_NAME) == 0) ||
				    (strcmp(resc, PY_RESOURCE_HAS_VALUE) == 0)) {
					free(resc);
					free(val);
					continue;
				}

				hook_set_flag = 0;
				if (py_resc_hookset_dict != NULL &&
				    PyDict_GetItemString(py_resc_hookset_dict, resc) != NULL) {
					hook_set_flag = 1; /* resource set/unset in hook script */
				}

				if ((strcmp(resc, WALLTIME_RESC) == 0) && has_resv_duration) {
					snprintf(log_buffer, LOG_BUF_SIZE - 1,
						 "Ignoring reservation resource '%s' since '%s' "
						 "already specified",
						 resc, ATTR_resv_duration);
					log_buffer[LOG_BUF_SIZE - 1] = '\0';
					log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK, LOG_INFO,
						  __func__, log_buffer);
					free(resc);
					free(val);
					continue;
				}

				the_val[0] = '\0';
				if (pbs_strcat(&the_val, &val_buf_size, val) == NULL) {
					snprintf(log_buffer, LOG_BUF_SIZE - 1, "malloc failure (errno %d)",
						 errno);
					log_err(PBSE_SYSTEM, __func__, log_buffer);
					goto svrattrl_exit;
				}

				strncpy(the_resc, resc, sizeof(the_resc) - 1);
				if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name)) {

					if ((rescdef = find_resc_def(svr_resc_def, resc)) == NULL) {
						/* not a builtin or previously defined resource */
						py_resc = PyObject_GetAttrString(py_val, resc); /* NEW */

						if (PyBool_Check(py_resc)) {
							snprintf(the_resc, sizeof(the_resc), "%s,boolean", resc);
						} else if (PyObject_IsInstance(py_resc,
									       pbs_python_types_table[PP_TIME_IDX].t_class)) {
							/* this must appear */
							/* before check to */
							/* Int instance */
							/* for TIME object */
							/* is derived from an */
							/*   int */
							snprintf(the_resc, sizeof(the_resc), "%s,long", resc);
							if (val != NULL) {
								val_sec = duration_to_secs(val);
								snprintf(the_val, val_buf_size, "%ld", val_sec);
							}
						} else if (PyLong_Check(py_resc) || PyLong_Check(py_resc)) {
							snprintf(the_resc, sizeof(the_resc), "%s,long", resc);
						} else if (PyFloat_Check(py_resc)) {
							snprintf(the_resc, sizeof(the_resc), "%s,float", resc);
						} else if (PyUnicode_Check(py_resc)) {
							/* this check should come first before the test of PP_ARST_IDX instance */
							/* for a regular string is also an instance/subset of PP_ARST_IDX type */
							snprintf(the_resc, sizeof(the_resc), "%s,string", resc);
						} else if (PyObject_IsInstance(py_resc,
									       pbs_python_types_table[PP_SIZE_IDX].t_class)) {
							snprintf(the_resc, sizeof(the_resc), "%s,size", resc);
						} else if (PyObject_IsInstance(py_resc,
									       pbs_python_types_table[PP_ARST_IDX].t_class)) {
							snprintf(the_resc, sizeof(the_resc), "%s,string_array", resc);
						} else {
							snprintf(the_resc, sizeof(the_resc), "%s,string", resc);
						}

						Py_CLEAR(py_resc);
					} else {
						if (TYPE_BOOL(rescdef->rs_type)) {
							snprintf(the_resc, sizeof(the_resc), "%s,boolean", resc);
						} else if (TYPE_INT(rescdef->rs_type)) {
							snprintf(the_resc, sizeof(the_resc), "%s,long", resc);
						} else if (TYPE_FLOAT(rescdef->rs_type)) {
							snprintf(the_resc, sizeof(the_resc), "%s,float", resc);
						} else if (TYPE_STR(rescdef->rs_type)) {
							/* this check should come first before the test of PP_ARST_IDX instance */
							/* for a regular string is also an instance/subset of PP_ARST_IDX type */
							snprintf(the_resc, sizeof(the_resc), "%s,string", resc);
						} else if (TYPE_SIZE(rescdef->rs_type)) {
							snprintf(the_resc, sizeof(the_resc), "%s,size", resc);
						} else if (TYPE_ARST(rescdef->rs_type)) {
							snprintf(the_resc, sizeof(the_resc), "%s,string_array", resc);
						} else if (TYPE_DURATION(rescdef->rs_encode)) {
							snprintf(the_resc, sizeof(the_resc), "%s,long", resc);
							if (val != NULL) {
								val_sec = duration_to_secs(val);
								snprintf(the_val, val_buf_size, "%ld", val_sec);
							}
						} else {
							snprintf(the_resc, sizeof(the_resc), "%s,string", resc);
						}
					}
				}

				if (add_to_svrattrl_list(svrattrl_list, name_str, the_resc, the_val,
							 get_svrattrl_flag(name_str, the_resc, the_val,
									   &svrattrl_list2, hook_set_flag),
							 name_prefix) == -1) {
					snprintf(log_buffer, LOG_BUF_SIZE - 1, "failed to add_to_svrattrl_list(%s,%s,%s",
						 name_str, resc, (val ? val : ""));
					log_buffer[LOG_BUF_SIZE - 1] = '\0';
					log_err(errno, __func__, log_buffer);
					free(resc);
					free(val);
					goto svrattrl_exit;
				}

				if (hook_debug.output_fp != NULL)
					fprintf(hook_debug.output_fp, "%s.%s[%s]=%s\n", objname, name_str, the_resc,
						return_external_value(name_str, the_val));
				free(resc);
				free(val);
			}
		} else {

			char *val_str2;	  /* what gets sent to the server */
			char val_buf[40]; /* holds JOB_NAME_UNSET_VALUE and */
			char *val_str;	  /* what gets sent to the server */
			/* 'long' value string */
			long nsecs;

			val_str = pbs_python_object_str(py_val); /* does not return NULL */

			val_str2 = val_str;

			/* For Job_Name attribute, if it got an "" value (meaning None    */
			/* was specified in a hook script), then mimic what PBS server    */
			/* does which is set it to "none".  We cannot have a NULL or ""   */
			/*  value for Job_Name as this is used for constructing job       */
			/* output files and accounting_logs entry and may cause the       */
			/* server to  crash later.                                        */
			if ((strcmp(name_str, ATTR_N) == 0) && (val_str[0] == '\0')) {
				strcpy(val_buf, JOB_NAME_UNSET_VALUE);
				val_str2 = val_buf;
			} else if ((strcmp(name_str, ATTR_resv_duration) == 0) &&
				   (val_str[0] != '\0')) {
				nsecs = duration_to_secs(val_str);
				if (nsecs < 0) {
					log_err(errno, __func__, log_buffer);
					goto svrattrl_exit;
				}
				sprintf(val_buf, "%ld", nsecs);
				val_str2 = val_buf;
			}

			hook_set_flag = 0;
			if (py_attr_hookset_dict != NULL &&
			    PyDict_GetItemString(py_attr_hookset_dict, name_str) != NULL) {
				hook_set_flag = 1; /* attribute set/unset in a hook script */
			}
			if (strcmp(name_str, ATTR_v) == 0) {
				svrattrl *svrattrl_e;

				/* if there's a change in Variables_List value, 	*/
				/* (svrattrl_list2 (orig) vs svrattrl_list (new)),    	*/
				/* then flag as hook set */
				svrattrl_e = find_svrattrl_list_entry(&svrattrl_list2, ATTR_v, NULL);
				if ((svrattrl_e == NULL) ||
				    !varlist_same(svrattrl_e->al_value, val_str))
					hook_set_flag = 1;
			}

			if (add_to_svrattrl_list(svrattrl_list, name_str, NULL, val_str2,
						 get_svrattrl_flag(name_str, NULL, val_str,
								   &svrattrl_list2, hook_set_flag),
						 name_prefix) == -1) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1, "failed to add_to_svrattrl_list(%s,null,%s)",
					 name_str, val_str);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				log_err(errno, __func__, log_buffer);
				goto svrattrl_exit;
			}
			if (hook_debug.output_fp != NULL)
				fprintf(hook_debug.output_fp, "%s.%s=%s\n", objname, name_str, return_external_value(name_str, val_str2));
		}
		/* must be cleared as they take on different values on each iteration */
		Py_CLEAR(py_val);
		Py_CLEAR(py_keys);
		Py_CLEAR(py_keys_dict);
		Py_CLEAR(py_keys_dict2);
		Py_CLEAR(py_resc_hookset_dict);
		free(name_str_dup);
		name_str_dup = NULL;

	} /* for loop */
	rc = 0;
svrattrl_exit:
	Py_CLEAR(py_attr_dict);
	Py_CLEAR(py_attr_hookset_dict);
	Py_CLEAR(py_resc_hookset_dict);
	Py_CLEAR(py_attr_keys);
	Py_CLEAR(py_val);
	Py_CLEAR(py_resc);
	Py_CLEAR(py_keys);
	Py_CLEAR(py_keys_dict);
	Py_CLEAR(py_keys_dict2);

	if (name_str_dup) {
		free(name_str_dup);
	}
	if (val_str_dup) {
		free(val_str_dup);
	}
	/* safe to call because start of func clears this list */
	free_attrlist(&svrattrl_list2);

	return (rc);
}

/**
 * @brief
 *  	Causes the 'py_instance' object's attributes to be unsettable.
 *
 * @param[in] py_instance - PyObject with attributes
 *
 * @return	int
 * @retval	0 	for success;
 * @retval	-1 	otherwise.
 *
 */
int
pbs_python_mark_object_readonly(PyObject *py_instance)
{
	PyObject *py_attr_dict = NULL;
	PyObject *py_attr_keys = NULL;
	PyObject *py_val = NULL;
	int num_attrs, i;
	int rc = -1;

	/* mark the owning object readonly */
	rc = pbs_python_object_set_attr_integral_value(py_instance,
						       PY_READONLY_FLAG, TRUE);
	if (rc == -1) {

		snprintf(log_buffer, LOG_BUF_SIZE - 1, "Failed set object's '%s' flag", PY_READONLY_FLAG);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		return (-1);
	}

	/* Now mark attached resources read-only */

	/* py_attr_dict = <py_instance>.attributes[] */
	if (!PyObject_HasAttrString(py_instance, PY_ATTRIBUTES)) {

		snprintf(log_buffer, LOG_BUF_SIZE - 1, "encountered an object that has no '%s'",
			 PY_ATTRIBUTES);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		return (-1);
	}

	py_attr_dict = PyObject_GetAttrString(py_instance, PY_ATTRIBUTES); /* NEW*/
	if (!py_attr_dict) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "failed to obtain object's '%s'", PY_ATTRIBUTES);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		return (-1);
	}

	if (!PyDict_Check(py_attr_dict)) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "object's '%s' is not a dictionary!",
			 PY_ATTRIBUTES);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		rc = -1;
		goto mark_readonly_exit;
	}

	/* py_attr_keys = keys of <py_instance>.attributes[] */
	py_attr_keys = PyDict_Keys(py_attr_dict); /* NEW ref */

	if (!py_attr_keys) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "Failed to obtain object's '%s' keys",
			 PY_ATTRIBUTES);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		rc = -1;
		goto mark_readonly_exit;
	}
	if (!PyList_Check(py_attr_keys)) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "object's '%s' keys is not a list!", PY_ATTRIBUTES);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, PY_ATTRIBUTES);
		rc = -1;
		goto mark_readonly_exit;
	}

	num_attrs = PyList_Size(py_attr_keys);
	for (i = 0; i < num_attrs; i++) {
		char *name_str = NULL;

		name_str = pbs_python_list_get_item_string_value(py_attr_keys, i);

		if (!name_str || (name_str[0] == '\0'))
			continue;

		if (!PyObject_HasAttrString(py_instance, name_str))
			continue;

		py_val = PyObject_GetAttrString(py_instance, name_str); /* NEW */

		if (!py_val) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1, "failed to get attribute '%s' value", name_str);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			rc = -1;
			goto mark_readonly_exit;
		}

		if (PyObject_IsInstance(py_val,
					pbs_python_types_table[PP_RESC_IDX].t_class)) { /* a resource */

			rc = pbs_python_object_set_attr_integral_value(py_val,
								       PY_READONLY_FLAG, TRUE);
			if (rc == -1) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1,
					 "failed to set %s '%s'", name_str, PY_READONLY_FLAG);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto mark_readonly_exit;
			}
		}
		/* must be cleared as they take on different values on each iteration */
		Py_CLEAR(py_val);

	} /* for loop */

	rc = 0;

mark_readonly_exit:
	Py_CLEAR(py_attr_dict);
	Py_CLEAR(py_attr_keys);
	Py_CLEAR(py_val);
	return (rc);
}

/*
 * --------------------- MODULE HELPER METHODS  ----------------------------
 */

/**
 *
 * @brief
 *     Helper method returning PBS Python queue object mapping the given 'pque'
 *     (a pbs_queue struct) if set; otherwise, look into the list of pbs_queue
 *     structures managed by the local server, which  matches 'que_name'.
 *
 * @param[in]	pque - if set, this is the pbs_queue structure whose values
 *		       will be mapped into a PBS Python queue object.
 * @param[in]   queue_name - if 'pque' is not set, then create a PBS Python
 *			      queue object mapping a pbs_queue structure in
 *			      the system that matches 'queue_name'.
 * @param[in]	perf_label - passed on to hook_perf_stat* call.
 * @note
 *	This first returns any cached Python queue object found in
 *	'py_hook_pbsque[]' matching 'que_name' or pque's que_name.
 *	Otherwise, the Python queue object returned is cached in
 *	'py_hook_pbsque[]' array.
 *
 * @return	PyObject *	pointer to a Python queue object to map the
 *				queue.
 */
static PyObject *
_pps_helper_get_queue(pbs_queue *pque, const char *que_name, char *perf_label)
{
	PyObject *py_que_class = NULL;
	PyObject *py_que = NULL;
	PyObject *py_qargs = NULL;
	pbs_queue *que;
	int tmp_rc = -1;
	int i;
	char perf_action[MAXBUFLEN];
	long total_jobs;
	attribute *qattr;

	if (pque != NULL) {
		que = pque;
	} else {
		if ((que_name == NULL) || (que_name[0] == '\0')) {
			log_err(PBSE_INTERNAL, __func__,
				"Unable to populate python queue object");
			return NULL;
		}
		que = find_queuebyname((char *) que_name);
	}

	/* make sure que is not null */
	if (!que) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1,
			 "could not find queue '%s'", que_name);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		return py_que;
	}

	if (py_hook_pbsque != NULL) {

		for (i = 0; (i < py_hook_pbsque_max) && (py_hook_pbsque[i] != NULL);
		     i++) {
			char *qn;

			qn = pbs_python_object_get_attr_string_value(py_hook_pbsque[i],
								     "name");
			if ((qn != NULL) && (qn[0] != '\0') &&
			    (strcmp(qn, que->qu_qs.qu_name) == 0)) {
				Py_INCREF(py_hook_pbsque[i]);
				return py_hook_pbsque[i];
			}
		}
	}

	/*
	 * First things first create a Python queue  object.
	 *  - Borrowed reference
	 *  - Exception is *NOT* set
	 */
	py_que_class = pbs_python_types_table[PP_QUE_IDX].t_class;

	py_qargs = Py_BuildValue("(s)", que->qu_qs.qu_name); /* NEW ref */
	if (!py_qargs) {
		log_err(PBSE_INTERNAL, __func__, "could not build args list for queue");
		goto ERROR_EXIT;
	}
	py_que = PyObject_Call(py_que_class, py_qargs, NULL);
	if (!py_que) {
		log_err(PBSE_INTERNAL, __func__, "failed to create a python queue object");
		goto ERROR_EXIT;
	}
	if (py_qargs)
		Py_CLEAR(py_qargs);
	/*
	 * OK, At this point we need to start populating the que class.
	 */
	/* As done is statque update the state count */
	if (!svr_chk_history_conf()) {
		total_jobs = que->qu_numjobs;
	} else {
		total_jobs = que->qu_numjobs - (que->qu_njstate[JOB_STATE_MOVED] + que->qu_njstate[JOB_STATE_FINISHED] + que->qu_njstate[JOB_STATE_EXPIRED]);
	}
	set_qattr_l_slim(que, QA_ATR_TotalJobs, total_jobs, SET);

	qattr = get_qattr(que, QA_ATR_JobsByState);
	update_state_ct(qattr, que->qu_njstate, &que_attr_def[QA_ATR_JobsByState]);
	/* stuff all the attributes */
	snprintf((char *) hook_debug.objname, HOOK_BUF_SIZE - 1, "%s(%s)", SERVER_QUEUE_OBJECT, que->qu_qs.qu_name);
	snprintf(perf_action, sizeof(perf_action), "%s:%s", HOOK_PERF_POPULATE, hook_debug.objname);
	tmp_rc = pbs_python_populate_attributes_to_python_class(py_que,
								py_que_attr_types,
								que->qu_attr,
								que_attr_def,
								QA_ATR_LAST, perf_label, perf_action);
	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__,
			"partially populated python queue object");
	}
	free_attr(que_attr_def, qattr, QA_ATR_JobsByState);

	tmp_rc = pbs_python_mark_object_readonly(py_que);

	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__, "Failed to mark queue readonly!");
		goto ERROR_EXIT;
	}

	object_counter++;

	if (server.sv_qs.sv_numque > 0) {

		if (py_hook_pbsque == NULL) {
			py_hook_pbsque = (PyObject **) calloc(server.sv_qs.sv_numque,
							      sizeof(PyObject *));
			if (py_hook_pbsque == NULL) {
				log_err(errno, __func__,
					"Failed to calloc array of cached pbs queue objects");
				goto ERROR_EXIT;
			}
			py_hook_pbsque_max = server.sv_qs.sv_numque;
		} else if (server.sv_qs.sv_numque > py_hook_pbsque_max) {
			PyObject **py_hook_pbsque_tmp;
			py_hook_pbsque_tmp = (PyObject **) realloc(py_hook_pbsque,
								   server.sv_qs.sv_numque * sizeof(PyObject *));
			if (py_hook_pbsque_tmp == NULL) {
				log_err(errno, __func__,
					"Failed to realloc array of cached pbs queue objects");
				for (i = 0; (i < py_hook_pbsque_max) &&
					    (py_hook_pbsque[i] != NULL);
				     i++) {
					Py_CLEAR(py_hook_pbsque[i]);
				}
				free(py_hook_pbsque);
				py_hook_pbsque = NULL;
				goto ERROR_EXIT;
			}
			py_hook_pbsque = py_hook_pbsque_tmp;
			for (i = py_hook_pbsque_max; i < server.sv_qs.sv_numque; i++) {
				py_hook_pbsque[i] = NULL;
			}

			py_hook_pbsque_max = server.sv_qs.sv_numque;
		}
	}

	if (py_hook_pbsque != NULL) {
		for (i = 0; i < py_hook_pbsque_max; i++) {
			if (py_hook_pbsque[i] == NULL) {
				Py_INCREF(py_que);
				py_hook_pbsque[i] = py_que;
				break;
			}
		}
	}

	return py_que;
ERROR_EXIT:
	if (PyErr_Occurred())
		pbs_python_write_error_to_log(__func__);

	if (py_qargs)
		Py_CLEAR(py_qargs);
	if (py_que)
		Py_CLEAR(py_que);
	PyErr_SetString(PyExc_AssertionError, "Failed to create queue object");

	return NULL;
}

/**
 *
 * @brief
 * 	Helper method returning a server Python Object representing the local
 *	(current) server.
 *
 * @param[in]	perf_label - passed on to hook_perf_stat* call.
 *
 *  @note
 *	This marks the server object "read-only" in Python mode.
 *	Also, this first returns the cached 'py_hook_pbsserver' object.
 *	Otherwise, the obtained PBS Python server object is cached in
 *	'py_hook_pbsserver'.
 *
 * @return	PyObject *	pointer to a Python server object to map the
 *				local server values.
 */
static PyObject *
_pps_helper_get_server(char *perf_label)
{
	PyObject *py_svr_class = NULL;
	PyObject *py_svr = NULL;
	PyObject *py_sargs = NULL;
	int tmp_rc = -1;
	char perf_action[MAXBUFLEN];

	if (py_hook_pbsserver != NULL) {
		Py_INCREF(py_hook_pbsserver);
		return py_hook_pbsserver;
	}

	/*
	 * First things first create a Python queue  object.
	 *  - Borrowed reference
	 *  - Exception is *NOT* set
	 */
	py_svr_class = pbs_python_types_table[PP_SVR_IDX].t_class;

	py_sargs = Py_BuildValue("(s)", server_name); /* NEW ref */
	if (!py_sargs) {
		log_err(-1, pbs_python_daemon_name, "could not build args list for server");
		goto ERROR_EXIT;
	}

	py_svr = PyObject_Call(py_svr_class, py_sargs, NULL);
	if (!py_svr) {
		log_err(-1, pbs_python_daemon_name, "failed to create a python server object");
		goto ERROR_EXIT;
	}
	if (py_sargs)
		Py_CLEAR(py_sargs);
	/*
	 * OK, At this point we need to start populating the server class.
	 */
	/* As done is stat_svr update the state count */

	/* update count and state counts from sv_numjobs and sv_jobstates */
	set_sattr_l_slim(SVR_ATR_TotalJobs, server.sv_qs.sv_numjobs, SET);
	update_state_ct(get_sattr(SVR_ATR_JobsByState), server.sv_jobstates, &svr_attr_def[SVR_ATR_JobsByState]);

	update_license_ct();

	/* stuff all the attributes */
	strncpy((char *) hook_debug.objname, SERVER_OBJECT, HOOK_BUF_SIZE - 1);
	snprintf(perf_action, sizeof(perf_action), "%s:%s", HOOK_PERF_POPULATE, hook_debug.objname);
	tmp_rc = pbs_python_populate_attributes_to_python_class(py_svr,
								py_svr_attr_types,
								server.sv_attr,
								svr_attr_def,
								SVR_ATR_LAST, perf_label, perf_action);

	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__,
			"partially populated python server object");
	}

	tmp_rc = pbs_python_mark_object_readonly(py_svr);

	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__, "Failed to mark server readonly!");
		goto ERROR_EXIT;
	}

	object_counter++;
	Py_INCREF(py_svr);
	py_hook_pbsserver = py_svr;
	return py_svr;
ERROR_EXIT:
	if (PyErr_Occurred())
		pbs_python_write_error_to_log(__func__);
	if (py_sargs)
		Py_CLEAR(py_sargs);
	if (py_svr)
		Py_CLEAR(py_svr);

	PyErr_SetString(PyExc_AssertionError, "Failed to create server object");

	return NULL;
}

/**
 * @brief
 * 	Helper method returning a job Python Object from a job struct
 * 	This marks the job object "read-only" in Python mode.
 * 	If  'qname' is not NULL or "", then the job object is returned if
 * 	it is queued in 'qname'.
 *
 * @param[in] pjob_o - job info
 * @param[in] jobid - job identifier
 * @param[in] qname - queuename
 * @param[in]	perf_label - data passed on to hook_perf_stat* call
 *
 * @return	PyObject*
 * @retval	job python object	success
 * @retval	NULL			error
 *
 */
static PyObject *
_pps_helper_get_job(job *pjob_o, const char *jobid, const char *qname, char *perf_label)
{
	PyObject *py_job_class = NULL;
	PyObject *py_job = NULL;
	PyObject *py_jargs = NULL;
	PyObject *py_que = NULL;
	PyObject *py_resv = NULL;
	PyObject *py_server = NULL;
	job *pjob;
	int tmp_rc = -1;
	int t;
	char perf_action[MAXBUFLEN];

	if (pjob_o != NULL) {
		pjob = pjob_o;
	} else {
		if ((jobid == NULL) || (jobid[0] == '\0')) {
			log_err(PBSE_INTERNAL, __func__,
				"Unable to populate python job object");
			return NULL;
		}
		t = is_job_array((char *) jobid);

		if (t == IS_ARRAY_Single) {
			pjob = find_job((char *) jobid); /* has data if instantiated */
			if (pjob == NULL) {		 /* otherwise, return parent */
				pjob = find_arrayparent((char *) jobid);
			}
		} else if ((t == IS_ARRAY_NO) || (t == IS_ARRAY_ArrayJob)) {
			pjob = find_job((char *) jobid); /* regular or ArrayJob itself */
		} else {
			pjob = find_arrayparent((char *) jobid); /* subjob(s) */
		}
	}

	/* make sure pjob is not null */
	if (!pjob) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "could not find job '%s'", jobid);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		return py_job;
	}
	if (qname && (qname[0] != '\0') &&
	    (strcmp(pjob->ji_qs.ji_queue, qname) != 0)) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "job '%s' not in '%s'", jobid, qname);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		return py_job;
	}

	/*
	 * First things first create a Python queue  object.
	 *  - Borrowed reference
	 *  - Exception is *NOT* set
	 */
	py_job_class = pbs_python_types_table[PP_JOB_IDX].t_class;

	py_jargs = Py_BuildValue("(s)", pjob->ji_qs.ji_jobid); /* NEW ref */
	if (!py_jargs) {
		log_err(-1, pbs_python_daemon_name, "could not build args list for job");
		goto ERROR_EXIT;
	}
	py_job = PyObject_Call(py_job_class, py_jargs, NULL);
	if (!py_job) {
		log_err(-1, pbs_python_daemon_name, "failed to create a python job object");
		goto ERROR_EXIT;
	}
	if (py_jargs)
		Py_CLEAR(py_jargs);
	/*
	 * OK, At this point we need to start populating the job class.
	 */
	snprintf((char *) hook_debug.objname, HOOK_BUF_SIZE - 1, "%s(%s)", SERVER_JOB_OBJECT, pjob->ji_qs.ji_jobid);
	snprintf(perf_action, sizeof(perf_action), "%s:%s", HOOK_PERF_POPULATE, hook_debug.objname);
	tmp_rc = pbs_python_populate_attributes_to_python_class(py_job,
								py_job_attr_types,
								pjob->ji_wattr,
								job_attr_def,
								JOB_ATR_LAST, perf_label, perf_action);

	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__,
			"partially populated python job object");
	}

	/* set job.queue to actual queue object */
	if (pjob->ji_qs.ji_queue) {
		py_que = _pps_helper_get_queue(NULL, pjob->ji_qs.ji_queue, perf_label); /* NEW ref */
		if (py_que) {
			if (PyObject_HasAttrString(py_job, ATTR_queue)) {
				/* py_que ref ct incremented as part of py_job */
				(void) PyObject_SetAttrString(py_job, ATTR_queue, py_que);
			}
			Py_DECREF(py_que); /* we no longer need to reference */
		}
	}

	if (pjob->ji_myResv) {
		/* set job.resv to actual reservation object */
		py_resv = _pps_helper_get_resv(pjob->ji_myResv,
					       pjob->ji_myResv->ri_qs.ri_resvID, perf_label); /* NEW ref */
		if (py_resv) {
			if (PyObject_HasAttrString(py_job, ATTR_resv)) {
				/* py_resv ref ct incremented as part of py_job */
				(void) PyObject_SetAttrString(py_job, ATTR_resv, py_resv);
			}
			Py_DECREF(py_resv); /* we no longer need to reference */
		}
	}

	/* set job.server to actual server object */
	py_server = _pps_helper_get_server(perf_label); /* NEW Ref */

	if (py_server) {
		if (PyObject_HasAttrString(py_job, ATTR_server)) {
			/* py_server ref ct incremented as part of py_job */
			(void) PyObject_SetAttrString(py_job, ATTR_server, py_server);
		}
		Py_DECREF(py_server);
	}

	tmp_rc = pbs_python_mark_object_readonly(py_job);

	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__, "Failed to mark job readonly!");
		goto ERROR_EXIT;
	}

	object_counter++;
	return py_job;
ERROR_EXIT:
	if (PyErr_Occurred())
		pbs_python_write_error_to_log(__func__);
	Py_CLEAR(py_jargs);
	Py_CLEAR(py_job);
	PyErr_SetString(PyExc_AssertionError, "Failed to create job object");

	return NULL;
}

/**
 * @brief
 * 	Helper method returning a resv Python Object from a a resc_resv struct.
 *
 * @param[in] presv_o - reservation structure
 * @param[in] resvid - reservation name
 * @param[in] perf_label - passed on to hook_perf_stat* call.
 *
 * @return	PyObject *
 * @retval	This returns a Python object that maps
 * 		to a resc_resv struct taken directly from presv_o if non-NULL,
 * 		or to the struct returned by find_resv(<resvid>).
 */
static PyObject *
_pps_helper_get_resv(resc_resv *presv_o, const char *resvid, char *perf_label)
{
	PyObject *py_resv_class = NULL;
	PyObject *py_resv = NULL;
	PyObject *py_rargs = NULL;
	PyObject *py_que = NULL;
	PyObject *py_server = NULL;
	resc_resv *presv;
	int tmp_rc = -1;
	char resvid_out[PBS_MAXCLTJOBID];
	char server_out[MAXSERVERNAME];
	char perf_action[MAXBUFLEN];

	if (presv_o != NULL) {
		presv = presv_o;
	} else {
		if ((resvid == NULL) || (resvid[0] == '\0')) {
			log_err(PBSE_INTERNAL, __func__,
				"Unable to populate python reservation object");
			return NULL;
		}

		if (get_server((char *) resvid, (char *) resvid_out,
			       (char *) server_out)) {

			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "illegally formed reservation identifier %s", resvid);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			return NULL;
		}
		presv = find_resv((char *) resvid_out);
	}

	if (presv == NULL) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "%s: no such reservation", resvid);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		/* this takes care of incrementing ref cnt of Py_None */
		Py_RETURN_NONE;
	}

	/*
	 * First things first create a Python resv object.
	 *  - Borrowed reference
	 *  - Exception is *NOT* set
	 */
	py_resv_class = pbs_python_types_table[PP_RESV_IDX].t_class;

	py_rargs = Py_BuildValue("(s)", presv->ri_qs.ri_resvID); /* NEW ref */
	if (py_rargs == NULL) {
		log_err(-1, pbs_python_daemon_name,
			"could not build args list for resv");
		goto GR_ERROR_EXIT;
	}
	py_resv = PyObject_Call(py_resv_class, py_rargs, NULL);
	if (py_resv == NULL) {
		log_err(-1, pbs_python_daemon_name,
			"failed to create a python resv object");
		goto GR_ERROR_EXIT;
	}

	Py_CLEAR(py_rargs);

	/*
	 * OK, At this point we need to start populating the resv class.
	 */
	snprintf((char *) hook_debug.objname, HOOK_BUF_SIZE - 1, "%s(%s)", SERVER_RESV_OBJECT, presv->ri_qs.ri_resvID);
	snprintf(perf_action, sizeof(perf_action), "%s:%s", HOOK_PERF_POPULATE, hook_debug.objname);
	tmp_rc = pbs_python_populate_attributes_to_python_class(py_resv,
								py_resv_attr_types,
								presv->ri_wattr,
								resv_attr_def,
								RESV_ATR_LAST, perf_label, perf_action);

	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__,
			"partially populated python resv object");
	}

	/* set resv.queue to actual queue object */
	if (presv->ri_qs.ri_queue && PyObject_HasAttrString(py_resv, ATTR_queue)) {
		py_que = _pps_helper_get_queue(NULL, presv->ri_qs.ri_queue, perf_label); /* NEW */
		if (py_que) {
			/* py_que ref ct incremented as part of py_resv */
			(void) PyObject_SetAttrString(py_resv, ATTR_queue, py_que);
			Py_DECREF(py_que); /* we no longer need to reference */
		}
	}

	/* set resv.server to actual server object */
	py_server = _pps_helper_get_server(perf_label); /* NEW */

	if (py_server) {
		if (PyObject_HasAttrString(py_resv, ATTR_server)) {
			/* py_server ref ct incremented as part of py_resv */
			(void) PyObject_SetAttrString(py_resv, ATTR_server, py_server);
		}
		Py_DECREF(py_server);
	}

	tmp_rc = pbs_python_mark_object_readonly(py_resv);

	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__, "Failed to mark resv readonly!");
		goto GR_ERROR_EXIT;
	}

	object_counter++;
	return py_resv;

GR_ERROR_EXIT:
	if (PyErr_Occurred())
		pbs_python_write_error_to_log(__func__);
	Py_CLEAR(py_rargs);
	Py_CLEAR(py_resv);
	PyErr_SetString(PyExc_AssertionError, "Failed to create resv object");

	return NULL;
}

/**
 * @brief
 *	Returns a Python object that maps to a struct pbsnodes * taken directly
 *	from pvnode_o if non-NULL, or to the struct returned by
 *	find_nodebyname(<vname>).
 *
 * @param[in]   pvnode_o	- the "struct pbsnode *" that will be used to
 *				  populate a Python vnode object.
 * @param[in]	vname		- name of a vnode to obtain "struct pbsnode *"
 *				  content to populate a Python vnode object.
 * @param[in]	perf_label	- passed on to hook_perf_stat* call.
 *
 * @return      PyObject *	- the Python vnode object corresponding to
 *				  'pvnode_o' or 'vname'.
 */
static PyObject *
_pps_helper_get_vnode(struct pbsnode *pvnode_o, const char *vname, char *perf_label)
{
	PyObject *py_vnode_class = NULL;
	PyObject *py_vnode = NULL;
	PyObject *py_rargs = NULL;
	PyObject *py_que = NULL;
	struct pbsnode *pvnode;
	int tmp_rc = -1;
	char buf[512];
	char perf_action[MAXBUFLEN];

	if (pvnode_o != NULL) {
		pvnode = pvnode_o;
	} else {
		if ((vname == NULL) || (vname[0] == '\0')) {
			log_err(PBSE_INTERNAL, __func__,
				"Unable to populate python vnode object");
			return NULL;
		}

		pvnode = find_nodebyname((char *) vname);
	}

	if (pvnode == NULL) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "%s: no such vnode", vname);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		Py_RETURN_NONE;
	}

	/*
	 * First things first create a Python vnode object.
	 *  - Borrowed reference
	 *  - Exception is *NOT* set
	 */
	py_vnode_class = pbs_python_types_table[PP_VNODE_IDX].t_class;
	py_rargs = Py_BuildValue("(s)", pvnode->nd_name); /* NEW ref */
	if (py_rargs == NULL) {
		log_err(-1, pbs_python_daemon_name,
			"could not build args list for vnode");
		goto GR_ERROR_EXIT;
	}
	py_vnode = PyObject_Call(py_vnode_class, py_rargs, NULL);
	if (py_vnode == NULL) {
		log_err(-1, pbs_python_daemon_name,
			"failed to create a python vnode object");
		goto GR_ERROR_EXIT;
	}

	Py_CLEAR(py_rargs);

	/*
	 * OK, At this point we need to start populating the vnode class.
	 */
	snprintf((char *) hook_debug.objname, HOOK_BUF_SIZE - 1, "%s(%s)", SERVER_VNODE_OBJECT, pvnode->nd_name);
	snprintf(perf_action, sizeof(perf_action), "%s:%s", HOOK_PERF_POPULATE, hook_debug.objname);
	tmp_rc = pbs_python_populate_attributes_to_python_class(py_vnode,
								py_vnode_attr_types,
								pvnode->nd_attr,
								node_attr_def,
								ND_ATR_LAST, perf_label, perf_action);

	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__,
			"partially populated python vnode object");
	}

	/* set vnode.queue to actual queue object */
	if (pvnode->nd_pque && PyObject_HasAttrString(py_vnode, ATTR_queue)) {
		py_que = _pps_helper_get_queue(pvnode->nd_pque, NULL, perf_label); /* NEW */
		if (py_que) {
			/* py_que ref ct incremented as part of py_vnode */
			(void) PyObject_SetAttrString(py_vnode, ATTR_queue, py_que);
			Py_DECREF(py_que); /* we no longer need to reference */
		}
	}

	snprintf(buf, sizeof(buf), "%ld", pvnode->nd_state);
	tmp_rc = pbs_python_object_set_attr_string_value(py_vnode, ATTR_NODE_state,
							 buf);
	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__, "Failed to set vnode's state.");
		goto GR_ERROR_EXIT;
	}

	snprintf(buf, sizeof(buf), "%d", pvnode->nd_ntype);

	tmp_rc = pbs_python_object_set_attr_string_value(py_vnode, ATTR_NODE_ntype,
							 buf);
	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__, "Failed to set vnode's type.");
		goto GR_ERROR_EXIT;
	}

	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__, "Failed to mark vnode readonly.");
		goto GR_ERROR_EXIT;
	}

	object_counter++;
	return py_vnode;

GR_ERROR_EXIT:
	if (PyErr_Occurred())
		pbs_python_write_error_to_log(__func__);
	Py_CLEAR(py_rargs);
	Py_CLEAR(py_vnode);
	PyErr_SetString(PyExc_AssertionError, "Failed to create vnode object");

	return NULL;
}
/*
 * ---------- EVENT RELATED FUNCTIONS ------------
 */

/**
 * @brief
 * 	Returns the event param's item corresponding to key 'name'.
 *
 * @param[in] name - key
 *
 * @return 	PyObject *
 * @retval	a borrowed reference.
 */
PyObject *
_pbs_python_event_get_param(char *name)
{
	PyObject *py_param = NULL;
	PyObject *py_p = NULL;

	if (!py_hook_pbsevent) {
		log_err(PBSE_INTERNAL, __func__, "No hook event found!");
		return NULL;
	}

	/* py_param = event()._param[] */
	if (!PyObject_HasAttrString(py_hook_pbsevent, PY_EVENT_PARAM)) {
		log_err(PBSE_INTERNAL, __func__, "Failed to obtain event's param");
		return NULL;
	}

	py_param = PyObject_GetAttrString(py_hook_pbsevent,
					  PY_EVENT_PARAM); /* NEW */
	if (!py_param) {
		log_err(PBSE_INTERNAL, __func__, "Failed to obtain event's param");
		return NULL;
	}

	if (!PyDict_Check(py_param)) {
		log_err(PBSE_INTERNAL, __func__, "event's param is not a dictionary");
		Py_CLEAR(py_param);
		return NULL;
	}

	/* ex. py_job = event().param["job"] */
	py_p = PyDict_GetItemString(py_param, name);
	Py_DECREF(py_param);

	return (py_p);
}
/**
 * @brief
 * 	Makes the Python PBS event object read-only, meaning none of its
 * 	could be modified in a hook script.
 *
 * @return	int
 * @retval	0 	for sucess;
 * @retval	-1 	otherwise
 */
int
_pbs_python_event_mark_readonly(void)
{
	int rv;

	if (!py_hook_pbsevent) {
		log_err(PBSE_INTERNAL, __func__, "event not found!");
		return (-1);
	}

	rv = pbs_python_mark_object_readonly(py_hook_pbsevent);

	if (rv == -1) {
		log_err(PBSE_INTERNAL, __func__, "Failed to mark event readonly!");
		return (-1);
	}
	return (rv);
}

/**
 * @brief
 * 	Sets the "operation" mode of Python: if 'mode' is PY_MODE, then we're
 * 	inside the hook script; if 'mode' is C_MODE, then we're inside some
 * 	internal C helper function.
 * 	Setting 'mode' to C_MODE usually means we don't have any restriction
 * 	as to which attributes we can or cannot set.
 */
void
_pbs_python_set_mode(int mode)
{
	if ((mode != PY_MODE) && (mode != C_MODE)) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "unexpected mode %d", mode);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		return;
	}

	hook_set_mode = mode;
}

/**
 *
 * @brief
 *	Given a list of vnode attributes/resources/values in 'vnlist',
 *	return a Python dictionary object to represent 'vnlist' as an array of
 *	vnode objects.
 *
 * @param[in]	vnlist - list of vnode attributes/resources/values.
 *			format: a list of plist entries:
 * @param[in]	plist->al_name = <node_name>.<attribute_name>
 * @param[in]	plist->al_resc =  <resource_name>,<type>
 * @param[in]	plist->al_value = <value>
 * @param[in]	perf_label - data passed on to hook_perf_stat* call
 * @param[in]	perf_action - dat passed on to hook_perf_stat* call
 *
 * @return 	PyObject *
 * @retval	<object>	- the Python dictionary object holding
 *			           the individual vnode objects, indexed by
 *				   vnode names.
 * @retval	NULL		- if an error occured.
 *
 * @note
 *		This function calls a single hook_perf_stat_start()
 *		that has some malloc-ed data that are freed in the
 *		hook_perf_stat_stop() call, which is done at the end of
 *		this function.
 *		Ensure that after the hook_perf_stat_start(), all
 *		program execution path lead to hook_perf_stat_stop()
 *		call.
 */
static PyObject *
create_py_vnodelist(pbs_list_head *vnlist, char *perf_label, char *perf_action)
{
	svrattrl *plist, *plist_next;
	PyObject *py_vn = NULL; /* class vnode arg list */
	PyObject *py_va = NULL; /* instantiated vnode object */
	PyObject *py_vnodelist = NULL;
	PyObject *py_vnode_class = NULL;
	struct rq_node {
		char rq_id[PBS_MAXNODENAME * 2];
		pbs_list_head rq_attr;
	} rqs;
	char *p = NULL;
	char *pn = NULL;
	char *p1 = NULL;
	char *attr_name = NULL;
	PyObject *py_vnlist_ret = NULL;
	int rc;

	if (vnlist == NULL) {
		log_err(PBSE_INTERNAL, __func__, "bad input parameter");
		return (NULL);
	}

	py_vnodelist = PyDict_New(); /* NEW - empty dict */
	if (py_vnodelist == NULL) {
		log_err(PBSE_INTERNAL, __func__,
			"failed to create a Vnodes list dictionary!");
		return NULL;
	}

	hook_perf_stat_start(perf_label, perf_action, 0);

	py_vnode_class = pbs_python_types_table[PP_VNODE_IDX].t_class;

	rqs.rq_id[0] = '\0';
	CLEAR_HEAD(rqs.rq_attr);

	plist = (svrattrl *) GET_NEXT(*vnlist);
	do {
		if (plist == NULL)
			break;

		plist_next = (svrattrl *) GET_NEXT(plist->al_link);

		/* look for last dot as the name could be dotted like a node name */
		p = strrchr(plist->al_name, '.');
		if (p == NULL) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "warning: encountered an attribute %s without a node name...ignoring", plist->al_name);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			plist = plist_next;
			continue;
		}
		*p = '\0';	   /* now plist->al_name would be the node name */
		attr_name = p + 1; /* p will be the actual attribute name */
		if (plist->al_resc != NULL) {
			p1 = strchr(plist->al_resc, ',');
			if (p1 != NULL) {
				*p1 = '\0';
			}
		}

		/* at this point, we have:
		 * 	plist->al_name: <node_name><p><attribute_name>
		 * 				where <p> = \0
		 * 	plist->al_resc: <resc_name><p1><type>
		 * 				where <p1> = \0
		 */

		if (add_to_svrattrl_list(&rqs.rq_attr, attr_name,
					 plist->al_resc, plist->al_value, 0, NULL) != 0) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "warning: failed to add_to_svrattrl_list(%s,%s,%s)",
				 plist->al_name,
				 plist->al_resc ? plist->al_resc : "", plist->al_value);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			goto create_py_vnodelist_exit;
		}
		pn = NULL;

		/* Check if we're done processing the attributes/resources */
		/* of the current node. 				    */
		if (plist_next != NULL) {

			/* look at last dot for "dotted" node names */
			pn = strrchr(plist_next->al_name, '.');
			if (pn == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "warning: encountered the next attribute %s without a node name...ignoring", plist_next->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				plist = (svrattrl *) GET_NEXT(plist_next->al_link);
				continue;
			}
			*pn = '\0'; /* now plist_next->al_name would be the */
				    /* node name */
				    /* at this point, we have:
			 * 	plist_next->al_name:
			 * 		<node_name><pn><attribute_name>
			 * 				where <pn> = \0
			 */
		}

		/* The next vnlist entry is for a different node name */
		/* or we've reached the end of the line */
		if ((plist_next == NULL) ||
		    (strcmp(plist->al_name, plist_next->al_name) != 0)) {

			strncpy(rqs.rq_id, plist->al_name,
				sizeof(rqs.rq_id) - 1);

			py_va = Py_BuildValue("(s)", rqs.rq_id); /* NEW ref */
			if (py_va == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "could not build args list for vnode %s",
					 plist->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto create_py_vnodelist_exit;
			}

			py_vn = PyObject_Call(py_vnode_class, py_va,
					      NULL); /* NEW ref */
			if (py_vn == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "failed to create a python vnode %s object",
					 plist->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto create_py_vnodelist_exit;
			}

			rc = pbs_python_populate_python_class_from_svrattrl(py_vn, &rqs.rq_attr, NULL, NULL);

			if (rc == -1) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "failed to fully populate Python"
					 " vnode %s object",
					 plist->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto create_py_vnodelist_exit;
			}

			/* set vnode : now py_vn ref count auto incremented*/
			rc = PyDict_SetItemString(py_vnodelist, plist->al_name,
						  py_vn);
			if (rc == -1) {
				LOG_ERROR_ARG2("%s: partially set remaining param['%s'] attributes",
					       PY_TYPE_EVENT, PY_EVENT_PARAM_VNODELIST);
				goto create_py_vnodelist_exit;
			}

			rqs.rq_id[0] = '\0';
			free_attrlist(&rqs.rq_attr);
			CLEAR_HEAD(rqs.rq_attr);

			Py_CLEAR(py_va);
			Py_CLEAR(py_vn);
		}

		plist = plist_next;

		if (p != NULL) {
			*p = '.'; /* restore prev plist->al_name to contain node name */
			p = NULL;
		}

		if (p1 != NULL) { /* restore prev "<resc>,<resc_type>"  value for plist->al_resc */
			*p1 = ',';
			p1 = NULL;
		}

		if (pn != NULL) {
			*pn = '.'; /* restore prev plist_next->al_name to contain node name */
			pn = NULL;
		}

	} while (plist);

	py_vnlist_ret = py_vnodelist;

create_py_vnodelist_exit:
	rqs.rq_id[0] = '\0';
	free_attrlist(&rqs.rq_attr);
	CLEAR_HEAD(rqs.rq_attr);
	if (py_vnlist_ret != py_vnodelist) {
		Py_CLEAR(py_vnodelist);
	}
	Py_CLEAR(py_va);
	Py_CLEAR(py_vn);

	if (p != NULL) {
		*p = '.'; /* restore plist->al_name to contain node name */
		p = NULL;
	}

	if (p1 != NULL) { /* restore prev "<resc>,<resc_type>"  value for plist->al_resc */
		*p1 = ',';
		p1 = NULL;
	}

	if (pn != NULL) {
		*pn = '.'; /* restore prev plist_next->al_name to contain node name */
		pn = NULL;
	}

	hook_perf_stat_stop(perf_label, perf_action, 0);
	return (py_vnlist_ret);
}

/**
 * @brief
 *	Given a list of job attributes/resources/values in 'joblist',
 *	return a Python dictionary object to represent 'joblist' as an array of
 *	job objects.
 *
 * @param[in]	joblist - list of job attributes/resources/values.
 *			format: a list of svrattrl entries (plist):
 * @param[in]	plist->al_name:	<job_name>.<attribute_name>
 * @param[in]	plist->al_resc: <resource_name>,<type>
 * @param[in]	plist->al_value: <value>
 * @param[in]	perf_label - data passed on to hook_perf_stat* call
 * @param[in]	perf_action - dat passed on to hook_perf_stat* call
 *
 * @return 	PyObject *
 * @retval	<object>	- the Python dictionary object holding
 *			           the individual job objects, indexed by
 *				   job names.
 * @retval	NULL		- if an error occured.
 *
 * @note
 *		This function calls a single hook_perf_stat_start()
 *		that has some malloc-ed data that are freed in the
 *		hook_perf_stat_stop() call, which is done at the end of
 *		this function.
 *		Ensure that after the hook_perf_stat_start(), all
 *		program execution path lead to hook_perf_stat_stop()
 *		call.
 */
static PyObject *
create_py_joblist(pbs_list_head *joblist, char *perf_label, char *perf_action)
{
	svrattrl *plist, *plist_next;
	PyObject *py_jn = NULL; /* class job arg list */
	PyObject *py_ja = NULL; /* instantiated job object */
	PyObject *py_joblist = NULL;
	PyObject *py_job_class = NULL;
	struct rq_job {
		char rq_id[PBS_MAXNODENAME * 2];
		pbs_list_head rq_attr;
	} rqs;
	char *p = NULL;
	char *pn = NULL;
	char *p1 = NULL;
	char *attr_name = NULL;
	PyObject *py_joblist_ret = NULL;
	int rc;

	py_joblist = PyDict_New(); /* NEW - empty dict */
	if (py_joblist == NULL) {
		log_err(PBSE_INTERNAL, __func__,
			"failed to create a jobs list dictionary!");
		return NULL;
	}

	hook_perf_stat_start(perf_label, perf_action, 0);
	py_job_class = pbs_python_types_table[PP_JOB_IDX].t_class;

	rqs.rq_id[0] = '\0';
	CLEAR_HEAD(rqs.rq_attr);

	plist = (svrattrl *) GET_NEXT(*joblist);
	do {
		if (plist == NULL)
			break;

		plist_next = (svrattrl *) GET_NEXT(plist->al_link);

		/* look for last dot as the name could be dotted like a job name */
		p = strrchr(plist->al_name, '.');
		if (p == NULL) { /* did not detect entry <job_name>.<atr_name> */
			snprintf(log_buffer, sizeof(log_buffer),
				 "warning: encountered an attribute %s without a job name...ignoring", plist->al_name);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			plist = plist_next;
			continue;
		}
		*p = '\0';	   /* now plist->al_name would be the job name */
		attr_name = p + 1; /* p will be the actual attribute name */
		if (plist->al_resc != NULL) {
			/* looking for resource entry "<resc>,<resc_type>" */
			p1 = strchr(plist->al_resc, ',');
			if (p1 != NULL) {
				*p1 = '\0';
			}
		}
		/* at this point we have:
		 * plist->al_name = <job_name><p><attribute_name>
		 * 				where <p> = \0
		 * plist->al_resc = <resource_name><p1><type>
		 * 				where <p1> = \0
		 */

		if (add_to_svrattrl_list(&rqs.rq_attr, attr_name,
					 plist->al_resc, plist->al_value, 0, NULL) != 0) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "warning: failed to add_to_svrattrl_list(%s,%s,%s)",
				 plist->al_name,
				 plist->al_resc ? plist->al_resc : "", plist->al_value);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			goto create_py_joblist_exit;
		}

		/* Check if we're done processing the attributes/resources */
		/* of the current job. 				    */
		if (plist_next != NULL) {

			/* looking for the form: <job_name>.<attrib_name> */
			pn = strrchr(plist_next->al_name, '.');
			if (pn == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "warning: encountered the next attribute %s without a job name...ignoring", plist_next->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				plist = (svrattrl *) GET_NEXT(plist_next->al_link);
				if (p != NULL) {
					*p = '.'; /* restore plist->al_name to contain job name */
					p = NULL;
				}
				continue;
			}
			*pn = '\0'; /* now plist_next->al_name would be the */
				    /* job name */
				    /* at this point, we have
			 * plist_next->al_name: <job_name><pn><attrib_name>
			 * 			where <pn> = \0
			 */
		}

		/* The next joblist entry is for a different job name */
		/* or we've reached the end of the line */
		if ((plist_next == NULL) ||
		    (strcmp(plist->al_name, plist_next->al_name) != 0)) {

			strncpy(rqs.rq_id, plist->al_name,
				sizeof(rqs.rq_id) - 1);

			py_ja = Py_BuildValue("(s)", rqs.rq_id); /* NEW ref */
			if (py_ja == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "could not build args list for job %s",
					 plist->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto create_py_joblist_exit;
			}

			py_jn = PyObject_Call(py_job_class, py_ja,
					      NULL); /* NEW ref */
			if (py_jn == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "failed to create a python job %s object",
					 plist->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto create_py_joblist_exit;
			}

			rc = pbs_python_populate_python_class_from_svrattrl(py_jn, &rqs.rq_attr, NULL, NULL);

			if (rc == -1) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "failed to fully populate Python"
					 " job %s object",
					 plist->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto create_py_joblist_exit;
			}

			/* set job : now py_jn ref count auto incremented*/
			rc = PyDict_SetItemString(py_joblist, plist->al_name,
						  py_jn);
			if (rc == -1) {
				LOG_ERROR_ARG2("%s: partially set remaining param['%s'] attributes",
					       PY_TYPE_EVENT, PY_EVENT_PARAM_JOBLIST);
				goto create_py_joblist_exit;
			}

			rqs.rq_id[0] = '\0';
			free_attrlist(&rqs.rq_attr);
			CLEAR_HEAD(rqs.rq_attr);

			Py_CLEAR(py_ja);
			Py_CLEAR(py_jn);
		}

		plist = plist_next;

		if (p != NULL) {
			/* restore prev plist->al_name to contain job name */
			*p = '.';
			p = NULL;
		}

		if (p1 != NULL) {
			/* restore prev "<resc>,<resc_type>"  value for plist->al_resc */
			*p1 = ',';
			p1 = NULL;
		}

		if (pn != NULL) {
			/* restore prev plist_next->al_name to contain job name */
			*pn = '.';
			pn = NULL;
		}

	} while (plist);

	py_joblist_ret = py_joblist;

create_py_joblist_exit:
	free_attrlist(&rqs.rq_attr);
	CLEAR_HEAD(rqs.rq_attr);
	if (py_joblist_ret != py_joblist) {
		Py_CLEAR(py_joblist);
	}
	Py_CLEAR(py_ja);
	Py_CLEAR(py_jn);

	if (p != NULL) {
		/* restore prev plist->al_name to contain job name */
		*p = '.';
		p = NULL;
	}

	if (p1 != NULL) {
		/* restore prev "<resc>,<resc_type>"  value for plist->al_resc */
		*p1 = ',';
		p1 = NULL;
	}

	if (pn != NULL) {
		/* restore prev plist_next->al_name to contain job name */
		*pn = '.';
		pn = NULL;
	}

	hook_perf_stat_stop(perf_label, perf_action, 0);
	return (py_joblist_ret);
}

/*
 * @brief
 *	Given a list of reservation attributes/resources/values in 'resvlist',
 *	return a Python dictionary object to represent 'resvlist' as an array of
 *	reservation objects.
 *
 * @param[in]	resvlist - list of reservation attributes/resources/values.
 *			format: a list of svrattrl entries (plist):
 *	plist->al_name:	<resv_name>.<attribute_name>
 *	plist->al_resc: <resource_name>.<type>
 *	plist->al_value: <value>
 *
 * @param[in]	perf_label - data passed on to hook_perf_stat* call
 * @param[in]	perf_action - dat passed on to hook_perf_stat* call
 *
 * @return 	PyObject *
 * @retval	<object>	- the Python dictionary object holding
 *			           the individual reservation objects, indexed by
 *				   reservation names.
 * @retval	NULL		- if an error occured.
 *
 * @note
 *		This function calls a single hook_perf_stat_start()
 *		that has some malloc-ed data that are freed in the
 *		hook_perf_stat_stop() call, which is done at the end of
 *		this function.
 *		Ensure that after the hook_perf_stat_start(), all
 *		program execution path lead to hook_perf_stat_stop()
 *		call.
 */
static PyObject *
create_py_resvlist(pbs_list_head *resvlist, char *perf_label, char *perf_action)
{
	svrattrl *plist, *plist_next;
	PyObject *py_rn = NULL; /* class reservation arg list */
	PyObject *py_ra = NULL; /* instantiated reservation object */
	PyObject *py_resvlist = NULL;
	PyObject *py_resv_class = NULL;
	struct rq_resv {
		char rq_id[PBS_MAXNODENAME * 2];
		pbs_list_head rq_attr;
	} rqs;
	char *p = NULL;
	char *pn = NULL;
	char *p1 = NULL;
	char *attr_name = NULL;
	PyObject *py_resvlist_ret = (PyObject *) NULL;
	int rc;

	py_resvlist = PyDict_New(); /* NEW - empty dict */
	if (py_resvlist == NULL) {
		log_err(PBSE_INTERNAL, __func__,
			"failed to create a reservation list dictionary!");
		return (NULL);
	}

	hook_perf_stat_start(perf_label, perf_action, 0);
	py_resv_class = pbs_python_types_table[PP_RESV_IDX].t_class;

	memset(rqs.rq_id, 0, sizeof(rqs.rq_id));
	CLEAR_HEAD(rqs.rq_attr);

	for (plist = (svrattrl *) GET_NEXT(*resvlist); plist; plist = plist_next) {

		plist_next = (svrattrl *) GET_NEXT(plist->al_link);

		/* look for last dot as the name could be dotted like a resv name */
		p = strrchr(plist->al_name, '.');
		if (p == NULL) { /* did not detect entry <resv_name>.<atr_name> */
			snprintf(log_buffer, sizeof(log_buffer),
				 "warning: encountered an attribute %s without a resv name...ignoring", plist->al_name);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			continue;
		}
		*p = '\0';	   /* now plist->al_name would be the resv name */
		attr_name = p + 1; /* p will be the actual attribute name */
		if (plist->al_resc != NULL) {
			/* looking for resource entry "<resc>,<resc_type>" */
			p1 = strchr(plist->al_resc, ',');
			if (p1 != NULL) {
				*p1 = '\0';
			}
		}
		/* at this point we have:
		 * plist->al_name = <resv_name><p><attribute_name>
		 * 				where <p> = \0
		 * plist->al_resc = <resource_name><p1><type>
		 * 				where <p1> = \0
		 */

		if (add_to_svrattrl_list(&rqs.rq_attr, attr_name,
					 plist->al_resc, plist->al_value, 0, NULL) != 0) {
			snprintf(log_buffer, LOG_BUF_SIZE,
				 "warning: failed to add_to_svrattrl_list(%s,%s,%s)",
				 plist->al_name,
				 plist->al_resc ? plist->al_resc : "", plist->al_value);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			goto create_py_resvlist_exit;
		}

		/* Check if we're done processing the attributes/resources */
		/* of the current resv. 				    */
		if (plist_next != NULL) {

			/* looking for the form: <resv_name>.<attrib_name> */
			pn = strrchr(plist_next->al_name, '.');
			if (pn == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "warning: encountered the next attribute %s without a resv name...ignoring", plist_next->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				plist = (svrattrl *) GET_NEXT(plist_next->al_link);
				if (p != NULL) {
					*p = '.'; /* restore plist->al_name to contain resv name */
					p = NULL;
				}
				continue;
			}
			*pn = '\0'; /* now plist_next->al_name would be the */
				    /* resv name */
				    /* at this point, we have
			 * plist_next->al_name: <resv_name><pn><attrib_name>
			 * 			where <pn> = \0
			 */
		}

		/* The next resvlist entry is for a different resv name */
		/* or we've reached the end of the line */
		if ((plist_next == NULL) ||
		    (strcmp(plist->al_name, plist_next->al_name) != 0)) {

			snprintf(rqs.rq_id, sizeof(rqs.rq_id), "%s", plist->al_name);

			py_ra = Py_BuildValue("(s)", rqs.rq_id); /* NEW ref */
			if (py_ra == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "could not build args list for resv %s",
					 plist->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto create_py_resvlist_exit;
			}

			py_rn = PyObject_Call(py_resv_class, py_ra,
					      (PyObject *) NULL); /* NEW ref */
			if (py_rn == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "failed to create a python resv %s object",
					 plist->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto create_py_resvlist_exit;
			}

			rc = pbs_python_populate_python_class_from_svrattrl(py_rn, &rqs.rq_attr, NULL, NULL);

			if (rc == -1) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "failed to fully populate Python"
					 " resv %s object",
					 plist->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto create_py_resvlist_exit;
			}

			/* set resv : now py_jn ref count auto incremented*/
			rc = PyDict_SetItemString(py_resvlist, plist->al_name,
						  py_rn);
			if (rc == -1) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "%s: partially set remaining param['%s'] attributes",
					 PY_TYPE_EVENT, PY_EVENT_PARAM_RESVLIST);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto create_py_resvlist_exit;
			}

			rqs.rq_id[0] = '\0';
			free_attrlist(&rqs.rq_attr);
			CLEAR_HEAD(rqs.rq_attr);

			Py_CLEAR(py_ra);
			Py_CLEAR(py_rn);
		}

		if (p != NULL) {
			/* restore prev plist->al_name to contain resv name */
			*p = '.';
			p = NULL;
		}

		if (p1 != NULL) {
			/* restore prev "<resc>,<resc_type>"  value for plist->al_resc */
			*p1 = ',';
			p1 = NULL;
		}

		if (pn != NULL) {
			/* restore prev plist_next->al_name to contain resv name */
			*pn = '.';
			pn = NULL;
		}
	}

	py_resvlist_ret = py_resvlist;

create_py_resvlist_exit:
	free_attrlist(&rqs.rq_attr);
	CLEAR_HEAD(rqs.rq_attr);
	if (py_resvlist_ret != py_resvlist) {
		Py_CLEAR(py_resvlist);
	}
	Py_CLEAR(py_ra);
	Py_CLEAR(py_rn);

	if (p != NULL) {
		/* restore prev plist->al_name to contain resv name */
		*p = '.';
		p = NULL;
	}

	if (p1 != NULL) {
		/* restore prev "<resc>,<resc_type>"  value for plist->al_resc */
		*p1 = ',';
		p1 = NULL;
	}

	if (pn != NULL) {
		/* restore prev plist_next->al_name to contain resv name */
		*pn = '.';
		pn = NULL;
	}

	hook_perf_stat_stop(perf_label, perf_action, 0);
	return (py_resvlist_ret);
}

/**
 *
 * @brief
 *	Given a list of string values in 'str_list',
 *	return a Python list object to represent 'str_list'.
 *
 * @param[in]	str_list - an array of strings.
 *
 * @return 	PyObject *
 * @retval	<object>	- the Python list object holding
 *			           the individual strings.
 * @retval	NULL		- if an error occured.
 */
static PyObject *
create_py_strlist(char **str_list)
{
	int i;
	PyObject *py_str = NULL; /* a string value */
	PyObject *py_strlist = NULL;
	PyObject *py_strlist_ret = NULL;

	if (str_list == NULL)
		return NULL;

	py_strlist = PyList_New(0); /* NEW - empty list */
	if (py_strlist == NULL) {
		log_err(PBSE_INTERNAL, __func__,
			"failed to create an array of strings list!");
		return NULL;
	}
	i = 0;
	while (str_list[i]) {
		py_str = Py_BuildValue("s", str_list[i]); /* NEW ref */
		if (py_str == NULL) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "could not create python object for %s",
				 str_list[i]);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			goto create_py_strlist_exit;
		}
		/* py_str's reference count incremented inside PyList */
		if (PyList_Append(py_strlist, py_str) != 0) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "Failed to append %s to python string list",
				 str_list[i]);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			Py_DECREF(py_str);
			goto create_py_strlist_exit;
		}
		Py_DECREF(py_str);
		i++;
	}
	py_strlist_ret = py_strlist;

create_py_strlist_exit:
	if (py_strlist_ret != py_strlist) {
		Py_CLEAR(py_strlist);
	}

	return (py_strlist_ret);
}

/**
 *
 * @brief
 *	Given a pbs_list_head 'phead', convert just the attribute names
 *      (plist->al_name) into a Python string list.
 *	return a Python list object to represent names of the given
 *	pbs list..
 *
 * @param[in]	phead - head of svrattrl entries..
 *
 * @return 	PyObject *
 * @retval	<object>	- the Python list object holding
 *			           the individual strings of svrattrl names.
 * @retval	NULL		- if an error occured.
 */
static PyObject *
create_py_strlist_from_svrattrl_names(pbs_list_head *phead)
{
	PyObject *py_str = NULL; /* a string value */
	PyObject *py_strlist = NULL;
	PyObject *py_strlist_ret = NULL;
	svrattrl *plist;

	if (phead == NULL)
		return NULL;

	py_strlist = PyList_New(0); /* NEW - empty list */
	if (py_strlist == NULL) {
		log_err(PBSE_INTERNAL, __func__,
			"failed to create a strings list!");
		return NULL;
	}

	for (plist = (svrattrl *) GET_NEXT(*phead); plist;
	     plist = (svrattrl *) GET_NEXT(plist->al_link)) {

		if (plist->al_name == NULL) {
			continue;
		}

		py_str = Py_BuildValue("s", plist->al_name); /* NEW ref */
		if (py_str == NULL) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "could not create python object for %s",
				 plist->al_name);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			goto create_py_strlist_from_svrattrl_names_exit;
		}
		/* py_str's reference count incremented inside PyList */
		if (PyList_Append(py_strlist, py_str) != 0) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "Failed to append %s to python string list",
				 plist->al_name);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			Py_DECREF(py_str);
			goto create_py_strlist_from_svrattrl_names_exit;
		}
		Py_DECREF(py_str);
	}
	py_strlist_ret = py_strlist;

create_py_strlist_from_svrattrl_names_exit:
	if (py_strlist_ret != py_strlist) {
		Py_CLEAR(py_strlist);
	}

	return (py_strlist_ret);
}

/**
 *
 * @brief
 *	Given a Python list of string values,
 *	dump its contents into a svrattrl list.
 *
 * @param[in]	py_strlist - the input Python list
 * @param[in]	to_head - destination svrattrl list
 * @param[in]	name_str - attribute name to associate each entry
 *
 * @return 	int
 * @retval	0	- success
 * @retval	-1	- error
 */
static int
py_strlist_to_svrattrl(PyObject *py_strlist, pbs_list_head *to_head, char *name_str)
{
	char *str;
	int i;
	int len;
	char index_str[20];

	if ((py_strlist == NULL) || (to_head == NULL) || (name_str == NULL))
		return (-1);

	len = PyList_Size(py_strlist);
	if (len == 0)
		return (0);

	CLEAR_HEAD((*to_head));
	for (i = 0; (i < len) && ((str = pbs_python_list_get_item_string_value(py_strlist, i)) != NULL); i++) {
		snprintf(index_str, sizeof(index_str), "%d", i);
		if (add_to_svrattrl_list(to_head, name_str, index_str, str, ATR_VFLAG_HOOK, NULL) == -1) {
			free_attrlist(to_head);
			return (-1);
		}
	}
	return (0);
}

/**
 *
 * @brief
 *	Given a Python list of string values,
 *	dump its contents into a reliable_job_node list.
 *
 * @param[in]	py_strlist - the input Python list
 * @param[in]	to_head - destination reliable_job_node list
 *
 * @return 	int
 * @retval	0	- success
 * @retval	-1	- error
 */
static int
py_strlist_to_reliable_job_node_list(PyObject *py_strlist, pbs_list_head *to_head)
{
	char *str;
	int i;
	int len;

	if ((py_strlist == NULL) || (to_head == NULL))
		return (-1);

	len = PyList_Size(py_strlist);
	if (len == 0)
		return (0);

	CLEAR_HEAD((*to_head));
	for (i = 0; (i < len) && ((str = pbs_python_list_get_item_string_value(py_strlist, i)) != NULL); i++) {
		if (reliable_job_node_add(to_head, str) == -1) {
			free_attrlist(to_head);
			return (-1);
		}
	}
	return (0);
}

/**
 * @brief
 *	Read data from /proc/self/statm if available.
 *
 * @return char *
 * @retval NULL: No data
 * @retval !NULL: Memory usage data
 */
static char *
read_statm(void)
{
	static char buf[128] = {'\0'};
	long vmsize, vmrss;
	int rc;
	FILE *fp;

	fp = fopen("/proc/self/statm", "r");
	if (!fp)
		return NULL;
	/* Only fetch the first two entries. */
	rc = fscanf(fp, "%ld %ld", &vmsize, &vmrss);
	fclose(fp);
	if (rc != 2)
		return NULL;
	/* Convert to KB. */
	vmsize *= 4;
	vmrss *= 4;
	snprintf(buf, sizeof(buf), "VmSize=%ldkB, VmRSS=%ldkB", vmsize, vmrss);
	return (buf);
}

/**
 *
 * @brief
 *	Helper function to create a vnode_list[] type of parameter
 *	named 'param_name' under python object 'py_event_param' of
 * 	type 'event_type', with data coming from 'vnlist'.
 *
 * @param[in]	py_event_param - event parameter object
 * @param[in]	event_type - the event type requesting for this
 * @param[in]	param_name - name of the vnode_list parameter
 * @param[in]	vnlist - data for the vnode_list parameter
 * @param[in]	perf_label - passed on to hook_perf_stat* call.
 * @param[in]	perf_action - passed on to hook_perf_stat* call.
 *
 * @return PyObject *
 * @retval <python_object>	- the Python object representing the
 *				  vnode_list parameter
 * @retval NULL		- if failure is encountered
 *
 */
static PyObject *
create_hook_vnode_list_param(PyObject *py_event_param,
			     char *event_type, char *param_name, pbs_list_head *vnlist,
			     char *perf_label, char *perf_action)
{
	PyObject *py_vnlist = NULL;
	int rc;

	if ((py_event_param == NULL) || (param_name == NULL) || (vnlist == NULL)) {
		log_err(-1, __func__, "bad function parameter");
		return (NULL);
	}

	(void) PyDict_SetItemString(py_event_param, param_name, Py_None);

	py_vnlist = create_py_vnodelist(vnlist, perf_label, perf_action);
	if (py_vnlist == NULL) {
		return (NULL);
	}

	/* set vnode list: py_vnlist given to py_event_param so ref count auto incremented */
	rc = PyDict_SetItemString(py_event_param, param_name, py_vnlist);
	if (rc == -1) {
		Py_CLEAR(py_vnlist);
		LOG_ERROR_ARG2("%s: partially set remaining param['%s'] attributes", event_type, param_name);
		return (NULL);
	}
	return (py_vnlist);
}

/**
 *
 * @brief
 *	Function which will clear python objects after the processing
 *  hooks
 *
 */
void
pbs_python_clear_attributes()
{
	pbs_iter_item *iter_entry = NULL;
	pbs_iter_item *nxp_iter_entry;

	vnode_set_req *vn_set_req = NULL;
	vnode_set_req *nxp_vn_set_req;

	pbs_resource_value *resc_val = NULL;
	pbs_resource_value *nxp_resc_val;
	int i;

	/* Initialize the list of PBS iterators for new runs of hooks */
	/* servicing a given event (e.g. runjob event).               */
	if (pbs_iter_list.ll_next != NULL)
		iter_entry = (pbs_iter_item *) GET_NEXT(pbs_iter_list);
	while (iter_entry != NULL) {
		/* save the next iterator item */
		nxp_iter_entry = (pbs_iter_item *) GET_NEXT(iter_entry->all_iters);

		if (iter_entry->py_iter)
			Py_CLEAR(iter_entry->py_iter);

		delete_link(&iter_entry->all_iters);
		free(iter_entry);
		iter_entry = nxp_iter_entry;
	}

	/* Initialize the list of PBS vnode set operations for new runs of hooks */
	/* servicing a given event (e.g. runjob event).               */
	if (pbs_vnode_set_list.ll_next != NULL)
		vn_set_req = (vnode_set_req *) GET_NEXT(pbs_vnode_set_list);
	while (vn_set_req != NULL) {
		/* save the next vnode_set_req item  */
		nxp_vn_set_req = (vnode_set_req *) GET_NEXT(vn_set_req->all_reqs);

		free_attrlist(&vn_set_req->rq_attr);

		delete_link(&vn_set_req->all_reqs);
		free(vn_set_req);
		vn_set_req = nxp_vn_set_req;
	}

	/* Initialize the list of PBS resource values to set for new runs of hooks */
	/* servicing a given event (e.g. runjob event).               */
	if (pbs_resource_value_list.ll_next != NULL)
		resc_val = (pbs_resource_value *) GET_NEXT(pbs_resource_value_list);
	while (resc_val != NULL) {
		/* save the next vnode_set_req item  */
		nxp_resc_val = (pbs_resource_value *) GET_NEXT(resc_val->all_rescs);

		Py_CLEAR(resc_val->py_resource);
		Py_CLEAR(resc_val->py_resource_str_value);
		free_attrlist(&resc_val->value_list);

		delete_link(&resc_val->all_rescs);
		free(resc_val);
		resc_val = nxp_resc_val;
	}

	/* py_hook_pbsevent is instantiated in C_MODE so I own it */
	if (py_hook_pbsevent != NULL)
		Py_CLEAR(py_hook_pbsevent);

	/* py_hook_pbsserver is instantiated in C_MODE so I own it */
	if (py_hook_pbsserver != NULL)
		Py_CLEAR(py_hook_pbsserver);

	if (py_hook_pbsque != NULL) {
		for (i = 0; (i < py_hook_pbsque_max) && (py_hook_pbsque[i] != NULL); i++) {
			Py_CLEAR(py_hook_pbsque[i]);
		}
	}
}


/**
 * @brief
 *      Creates a PBS Python event object that can be accessed in a hook
 *	script as: pbs.event().
 *
 * @param[in]	hook_event	- the hook event name for the event object.
 *				  (e.g. HOOK_EVENT_QUEUEJOB)
 * @param[in]	req_user	- the requesting user
 * @param[in]	req_host	- the requesting host
 * @param[in]	req_params	- a structure containing the input parameters.
 * @param[in]	perf_label - data passed on to hook_perf_stat* call
 *
 * @return int
 * @retval	0	- success
 * @retval	-1	- error
 * @retval	-2	- function failed to complete execution due to a
 *			   a keyboard interrupt. This maybe caused by the
 *			   calling process getting a SIGINT. In this case,
 *			   just rerun this call.
 */
int
_pbs_python_event_set(unsigned int hook_event, char *req_user, char *req_host,
		      hook_input_param_t *req_params, char *perf_label)
{
	PyObject *py_event = NULL;
	PyObject *py_eargs = NULL;
	PyObject *py_jargs = NULL;
	PyObject *py_rargs = NULL;
	PyObject *py_job = NULL;
	PyObject *py_job_o = NULL;
	PyObject *py_que = NULL;
	PyObject *py_resv = NULL;
	PyObject *py_resv_o = NULL;
	PyObject *py_margs = NULL;
	PyObject *py_management = NULL;
	PyObject *py_event_param = NULL;
	PyObject *py_event_class = NULL;
	PyObject *py_job_class = NULL;
	PyObject *py_management_class = NULL;
	PyObject *py_resv_class = NULL;
	PyObject *py_env_class = NULL;
	PyObject *py_varlist = NULL;
	PyObject *py_varlist_o = NULL;
	PyObject *py_vnodelist = NULL;
	PyObject *py_vnodelist_fail = NULL;
	PyObject *py_joblist = NULL;
	PyObject *py_resvlist = NULL;
	PyObject *py_exec_vnode = NULL;
	PyObject *py_vnode = NULL;
	PyObject *py_vnode_o = NULL;
	PyObject *py_aoe = NULL;
	PyObject *py_resclist = NULL;
	PyObject *py_progname = NULL;
	PyObject *py_arglist = NULL;
	PyObject *py_env = NULL;
	PyObject *py_pid = NULL;
	PyObject *py_node_list = (PyObject *) NULL;
	PyObject *py_failed_node_list = (PyObject *) NULL;
	char perf_action[MAXBUFLEN];

	static long hook_counter = 0;	      /* for tracking interpreter restart */
	static long min_restart_interval = 0; /* prevents frequent restarts */
	static int init_iters = 0;	      /* 1 to initialize the PBS iterarators list */
	static int init_vnode_set = 0;	      /* 1 to initialize the vnode set opers */

	static int init_resource_values = 0; /* 1 to initialize the */
					     /* list of pbs_resource values */
					     /* to instantiate. */
	static long max_hooks = 0;
	static long max_objects = 0;
	static time_t previous_restart = (time_t) 0;

	long lval;
	int restart_python;
	int rc = -1;

	pbs_list_head *vnlist;
	pbs_list_head *joblist;
	pbs_list_head *resvlist;

	if (!init_iters) {
		CLEAR_HEAD(pbs_iter_list);
		init_iters = 1;
	}

	if (!init_vnode_set) {
		CLEAR_HEAD(pbs_vnode_set_list);
		init_vnode_set = 1;
	}

	if (!init_resource_values) {
		CLEAR_HEAD(pbs_resource_value_list);
		init_resource_values = 1;
	}

	lval = max_hooks;
	if (is_sattr_set(SVR_ATR_PythonRestartMaxHooks))
		max_hooks = get_sattr_long(SVR_ATR_PythonRestartMaxHooks);
	else
		max_hooks = PBS_PYTHON_RESTART_MAX_HOOKS;
	if (lval != max_hooks) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "python_restart_max_hooks is now %ld", max_hooks);
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK, LOG_INFO, __func__, log_buffer);
	}

	lval = max_objects;
	if (is_sattr_set(SVR_ATR_PythonRestartMaxObjects))
		max_objects = get_sattr_long(SVR_ATR_PythonRestartMaxObjects);
	else
		max_objects = PBS_PYTHON_RESTART_MAX_OBJECTS;
	if (lval != max_objects) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "python_restart_max_objects is now %ld", max_objects);
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK, LOG_INFO, __func__, log_buffer);
	}

	lval = min_restart_interval;
	if (is_sattr_set(SVR_ATR_PythonRestartMinInterval))
		min_restart_interval = get_sattr_long(SVR_ATR_PythonRestartMinInterval);
	else
		min_restart_interval = PBS_PYTHON_RESTART_MIN_INTERVAL;
	if (lval != min_restart_interval) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "python_restart_min_interval is now %ld", min_restart_interval);
		log_event(PBSEVENT_DEBUG3, PBS_EVENTCLASS_HOOK, LOG_INFO, __func__, log_buffer);
	}

	hook_counter++;
	restart_python = 0;
	if (hook_counter >= max_hooks)
		restart_python = 1;
	if (object_counter >= max_objects)
		restart_python = 1;
	if ((time(NULL) - previous_restart) < min_restart_interval)
		restart_python = 0;
	if (restart_python) {
		char *line;
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK, LOG_INFO, __func__,
			  "Restarting Python interpreter to reduce mem usage");
		pbs_python_ext_shutdown_interpreter(&svr_interp_data);
		if (pbs_python_ext_start_interpreter(&svr_interp_data) != 0) {
			log_err(PBSE_INTERNAL, __func__, "Failed to restart Python interpreter");
			goto event_set_exit;
		}
		/* Reset counters for the next interpreter restart. */
		hook_counter = 0;
		object_counter = 0;
		previous_restart = time(NULL);
		/* Log current memory usage. */
		line = read_statm();
		snprintf(log_buffer, sizeof(log_buffer),
			 "Current memory usage: %s",
			 (line ? line : "unknown"));
		log_event(PBSEVENT_DEBUG2, PBS_EVENTCLASS_HOOK, LOG_INFO, __func__, log_buffer);
	}

	hook_set_mode = C_MODE;

	/*
	 * First things first create a Python event object.
	 *  - Borrowed reference
	 *  - Exception is *NOT* set
	 */
	py_event_class = pbs_python_types_table[PP_EVENT_IDX].t_class;

	py_eargs = Py_BuildValue("(iss)", hook_event, req_user, req_host); /*NEW ref*/

	if (!py_eargs) {
		log_err(-1, __func__, "could not build args list for event");
		goto event_set_exit;
	}
	py_event = PyObject_Call(py_event_class, py_eargs, NULL); /*NEW*/

	if (!py_event) {
		log_err(-1, __func__, "failed to create a python event object");
		goto event_set_exit;
	}

	if (!PyObject_HasAttrString(py_event, PY_EVENT_PARAM)) {
		log_err(PBSE_INTERNAL, __func__, "event has no param structure!");
		goto event_set_exit;
	}

	py_event_param = PyObject_GetAttrString(py_event,
						PY_EVENT_PARAM); /* NEW ref*/
	if (!py_event_param) {
		log_err(PBSE_INTERNAL, __func__,
			"failed to get param attribute of event object");
		goto event_set_exit;
	}

	if (!PyDict_Check(py_event_param)) {
		log_err(PBSE_INTERNAL, __func__,
			"attribute of event object not a dictionary!");
		goto event_set_exit;
	}

	if (hook_event == HOOK_EVENT_QUEUEJOB) {
		struct rq_queuejob *rqj = req_params->rq_job;

		/* initialize event param to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
					    Py_None);
		/*
		 * First things first create a Python job object.
		 *  - Borrowed reference
		 *  - Exception is *NOT* set
		 */
		py_job_class = pbs_python_types_table[PP_JOB_IDX].t_class;

		py_jargs = Py_BuildValue("(s)", rqj->rq_jid); /* NEW ref */
		if (!py_jargs) {
			log_err(PBSE_INTERNAL, __func__, "could not build args list for job");
			goto event_set_exit;
		}
		py_job = PyObject_Call(py_job_class, py_jargs, NULL); /*NEW*/

		if (!py_job) {
			log_err(PBSE_INTERNAL, __func__, "failed to create a python job object");
			(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
						    Py_None);
			goto event_set_exit;
		}

		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB, py_job);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		rc = pbs_python_object_set_attr_string_value(py_job, ATTR_queue,
							     rqj->rq_destin);
		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set attribute <%s>",
				       "", ATTR_queue);
			goto event_set_exit;
		}

		snprintf(perf_action, sizeof(perf_action), "%s:%s(%s)", HOOK_PERF_POPULATE, EVENT_JOB_OBJECT, rqj->rq_jid);
		rc = pbs_python_populate_python_class_from_svrattrl(py_job,
								    &rqj->rq_attr, perf_label, perf_action);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s: partially set remaining param['%s'] attributes",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}
	} else if (hook_event == HOOK_EVENT_POSTQUEUEJOB) {
		struct rq_postqueuejob *rqj = req_params->rq_postqueuejob;

		/* initialize event param to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
					    Py_None);
		if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name)) {
			if (py_pbs_statobj != NULL) {
				Py_XDECREF(py_jargs); /* discard previously used value */
				py_jargs = Py_BuildValue("(sss)", "job", rqj->rq_jid,
							 pbs_conf.pbs_server_name); /* NEW ref */
				py_job = PyObject_Call(py_pbs_statobj, py_jargs,
						       NULL); /*NEW*/
				hook_set_mode = C_MODE;	      /* ensure still in C mode */
			}
		} else {
			py_job = _pps_helper_get_job(NULL, rqj->rq_jid, NULL, perf_label);
		}
		/* NEW - we own ref */

		if (!py_job || (py_job == Py_None)) {
			LOG_ERROR_ARG2("%s:failed to get job %s's python "
				       "job object",
				       PY_TYPE_EVENT, rqj->rq_jid);
			goto event_set_exit;
		}

		/* py_job given to py_event_parm...so ref. count auto incremented */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB, py_job);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		/* py_job is not read-only but only ATTR_h, ATTR_a are modifiable and */
		/* checked under is_attrib_val_settable(). Resource-type attributes   */
		/* such as ATTR_l, which is part of py_job,  go through a different   */
		/* processing mechanism.        				      */
		/* It is fatal if py_job's readonly flag could not be set to False    */
		/* as it could prevent all job attributes including ATTR_l to be      */
		/* not settable.                                                      */
		rc = pbs_python_object_set_attr_integral_value(py_job,
							       PY_READONLY_FLAG, FALSE);
		if (rc == -1) {

			log_err(PBSE_INTERNAL, __func__,
				"Failed set object's readonly flag");
			goto event_set_exit;
		}

		py_resclist = PyObject_GetAttrString(py_job, ATTR_l); /* NEW */
		if ((py_resclist != NULL) && (py_resclist != Py_None)) {
			/* Don't mark pbs.event().job.Resource_List[] as readonly */
			rc = pbs_python_object_set_attr_integral_value(py_resclist,
								       PY_READONLY_FLAG, FALSE);
			if (rc == -1) {

				log_err(PBSE_INTERNAL, __func__,
					"Failed set object's readonly flag");
				LOG_ERROR_ARG2("%s: warning - failed to set object's '%s' readonly flag", __func__, "Resource_List[]");
			}
		}
	} else if (hook_event == HOOK_EVENT_RESVSUB) {
		struct rq_queuejob *rqj = req_params->rq_job;

		/* initialize event param to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_RESV,
					    Py_None);
		/*
		 * First things first create a Python job object.
		 *  - Borrowed reference
		 *  - Exception is *NOT* set
		 */
		py_resv_class = pbs_python_types_table[PP_RESV_IDX].t_class;

		py_rargs = Py_BuildValue("(s)", rqj->rq_jid); /* NEW ref */

		if (!py_rargs) {
			log_err(PBSE_INTERNAL, __func__, "could not build args list for resv");
			goto event_set_exit;
		}

		py_resv = PyObject_Call(py_resv_class, py_rargs,
					NULL); /*NEW*/

		if (!py_resv) {
			log_err(PBSE_INTERNAL, __func__, "failed to create a python resv object");
			goto event_set_exit;
		}

		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_RESV, py_resv);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_RESV);
			goto event_set_exit;
		}
		snprintf(perf_action, sizeof(perf_action), "%s:%s(%s)", HOOK_PERF_POPULATE, EVENT_RESV_OBJECT, rqj->rq_jid);
		rc = pbs_python_populate_python_class_from_svrattrl(py_resv,
								    &rqj->rq_attr, perf_label, perf_action);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:partially set remaining param['%s'] attributes",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_RESV);
			goto event_set_exit;
		}
	} else if (hook_event == HOOK_EVENT_MODIFYRESV) {
		struct rq_manage *rqr = req_params->rq_manage;

		/* initialize event params to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
					    Py_None);
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB_O,
					    Py_None);
		/*
		 * First things first create a Python job object.
		 *  - Borrowed reference
		 *  - Exception is *NOT* set
		 */
		py_resv_class = pbs_python_types_table[PP_RESV_IDX].t_class;

		py_rargs = Py_BuildValue("(s)", rqr->rq_objname); /* NEW ref */

		if (!py_rargs) {
			log_err(PBSE_INTERNAL, __func__, "could not build args list for reservation");
			goto event_set_exit;
		}

		py_resv = PyObject_Call(py_resv_class, py_rargs, NULL); /*NEW*/

		if (!py_resv) {
			log_err(PBSE_INTERNAL, __func__, "failed to create a python reservation object");
			goto event_set_exit;
		}

		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_RESV, py_resv);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_RESV);
			goto event_set_exit;
		}

		snprintf(perf_action, sizeof(perf_action), "%s:%s(%s)", HOOK_PERF_POPULATE, EVENT_JOB_OBJECT, rqr->rq_objname);
		rc = pbs_python_populate_python_class_from_svrattrl(py_resv,
								    &rqr->rq_attr, perf_label, perf_action);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s: partially set remaining param['%s'] attributes",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_RESV);
			goto event_set_exit;
		}

		if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name)) {
			if (py_pbs_statobj != NULL) {
				Py_XDECREF(py_rargs); /* discard previously used value   */
				/* NOTE: *XDECREF() is safe where  */
				/* if py_jargs is NULL, then */
				/* nothing is released. */
				/* Current value of py_jargs is */
				/* released at the end of this  */
				/* function (at event_set_exit:) */
				py_rargs = Py_BuildValue("(sss)", "resv", rqr->rq_objname,
							 pbs_conf.pbs_server_name); /* NEW ref */
				py_resv_o = PyObject_Call(py_pbs_statobj, py_rargs,
							  NULL); /*NEW*/
				hook_set_mode = C_MODE;		 /* ensure still in C mode */
			}
		} else {
			/* we own this reference */
			py_resv_o = _pps_helper_get_resv(NULL, rqr->rq_objname, perf_label);
		}

		if (!py_resv_o || (py_resv_o == Py_None)) {
			LOG_ERROR_ARG2("%s:failed to create original reservation %s's python resv object",
				       PY_TYPE_EVENT, rqr->rq_objname);
			rc = -1;
			goto event_set_exit;
		}
		/* handed off to py_event_parm...reference count incremented again */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_RESV_O,
					  py_resv_o);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_RESV_O);
			goto event_set_exit;
		}
		/* Need to send a Variable_List of the original reservation, so that */
		/* in a hook script, we'll only allow to modify or add to existing */
		/* Variable_List */
		py_varlist_o = PyObject_GetAttrString(py_resv_o, ATTR_v); /* NEW */
		if ((py_varlist_o == NULL) || (py_varlist_o == Py_None)) {

			py_varlist = PyDict_New(); /* NEW - empty dict */
		} else {
			/* important to have a copy, so that changes in py_job's */
			/* Variable_List does not reflect in py_job_o's.	 */
			py_varlist = PyDict_Copy(py_varlist_o); /* NEW */
		}

		if (py_varlist == NULL) {
			log_err(PBSE_INTERNAL, __func__,
				"failed to create a Variable_List dictionary!");
			rc = -1;
			goto event_set_exit;
		}

		/* upon success, py_resv adds a reference count to py_varlist */
		if (PyObject_SetAttrString(py_resv, ATTR_v, py_varlist) == -1) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "failed to set dictionary for %s", ATTR_v);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			rc = -1;
			goto event_set_exit;
		}
		/* end of Variable_List setting */

	} else if (hook_event == HOOK_EVENT_MODIFYJOB) {
		struct rq_manage *rqj = req_params->rq_manage;

		/* initialize event params to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
					    Py_None);
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB_O,
					    Py_None);
		/*
		 * First things first create a Python job object.
		 *  - Borrowed reference
		 *  - Exception is *NOT* set
		 */
		py_job_class = pbs_python_types_table[PP_JOB_IDX].t_class;

		py_jargs = Py_BuildValue("(s)", rqj->rq_objname); /* NEW ref */

		if (!py_jargs) {
			log_err(PBSE_INTERNAL, __func__, "could not build args list for job");
			goto event_set_exit;
		}

		py_job = PyObject_Call(py_job_class, py_jargs, NULL); /*NEW*/

		if (!py_job) {
			log_err(PBSE_INTERNAL, __func__, "failed to create a python job object");
			goto event_set_exit;
		}

		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB, py_job);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		snprintf(perf_action, sizeof(perf_action), "%s:%s(%s)", HOOK_PERF_POPULATE, EVENT_JOB_OBJECT, rqj->rq_objname);
		rc = pbs_python_populate_python_class_from_svrattrl(py_job,
								    &rqj->rq_attr, perf_label, perf_action);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s: partially set remaining param['%s'] attributes",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name)) {
			if (py_pbs_statobj != NULL) {
				Py_XDECREF(py_jargs); /* discard previously used value   */
				/* NOTE: *XDECREF() is safe where  */
				/* if py_jargs is NULL, then */
				/* nothing is released. */
				/* Current value of py_jargs is */
				/* released at the end of this  */
				/* function (at event_set_exit:) */
				py_jargs = Py_BuildValue("(sss)", "job", rqj->rq_objname,
							 pbs_conf.pbs_server_name); /* NEW ref */
				py_job_o = PyObject_Call(py_pbs_statobj, py_jargs,
							 NULL); /*NEW*/
				hook_set_mode = C_MODE;		/* ensure still in C mode */
			}
		} else {
			/* we own this reference */
			py_job_o = _pps_helper_get_job(NULL, rqj->rq_objname, NULL, perf_label);
		}

		if (!py_job_o || (py_job_o == Py_None)) {
			LOG_ERROR_ARG2("%s:failed to create original job %s's python job object",
				       PY_TYPE_EVENT, rqj->rq_objname);
			rc = -1;
			goto event_set_exit;
		}
		/* handed off to py_event_parm...reference count incremented again */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB_O,
					  py_job_o);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB_O);
			goto event_set_exit;
		}
		/* Need to send a Variable_List of the original job, so that */
		/* in a hook script, we'll only allow to modify or add to existing */
		/* Variable_List */
		py_varlist_o = PyObject_GetAttrString(py_job_o, ATTR_v); /* NEW */
		if ((py_varlist_o == NULL) || (py_varlist_o == Py_None)) {

			py_varlist = PyDict_New(); /* NEW - empty dict */
		} else {
			/* important to have a copy, so that changes in py_job's */
			/* Variable_List does not reflect in py_job_o's.	 */
			py_varlist = PyDict_Copy(py_varlist_o); /* NEW */
		}

		if (py_varlist == NULL) {
			log_err(PBSE_INTERNAL, __func__,
				"failed to create a Variable_List dictionary!");
			rc = -1;
			goto event_set_exit;
		}

		/* upon success, py_job adds a reference count to py_varlist */
		if (PyObject_SetAttrString(py_job, ATTR_v, py_varlist) == -1) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "failed to set dictionary for %s", ATTR_v);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			rc = -1;
			goto event_set_exit;
		}
		/* end of Variable_List setting */

	} else if (hook_event == HOOK_EVENT_MOVEJOB) {
		struct rq_move *rqj = req_params->rq_move;

		/* initialize params to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_SRC_QUEUE,
					    Py_None);
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
					    Py_None);

		if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name)) {
			if (py_pbs_statobj != NULL) {
				Py_XDECREF(py_jargs); /* discard previously used value */
				py_jargs = Py_BuildValue("(sss)", "job", rqj->rq_jid,
							 pbs_conf.pbs_server_name); /* NEW ref */
				py_job = PyObject_Call(py_pbs_statobj, py_jargs,
						       NULL); /*NEW*/
				hook_set_mode = C_MODE;	      /* ensure still in C mode */
			}
		} else {
			py_job = _pps_helper_get_job(NULL, rqj->rq_jid, NULL, perf_label);
			/* NEW - we own ref */
		}

		if (!py_job || (py_job == Py_None)) {
			LOG_ERROR_ARG2("%s:failed to create job %s's python "
				       "job object",
				       PY_TYPE_EVENT, rqj->rq_jid);
			goto event_set_exit;
		}

		/* py_job handed off to py_event_parm...reference count incremented */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB, py_job);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		if (!PyObject_HasAttrString(py_job, ATTR_queue)) {
			LOG_ERROR_ARG2("%s: does not have attribute <%s>",
				       PY_TYPE_JOB, ATTR_queue);
			rc = -1;
			goto event_set_exit;
		}

		/* save the current value of job's queue attribute */
		py_que = PyObject_GetAttrString(py_job, ATTR_queue); /* NEW */

		if (py_que == NULL || (py_que == Py_None)) {
			LOG_ERROR_ARG2("movejob %s has a bad value for attribute <%s>",
				       rqj->rq_jid, ATTR_queue);
			rc = -1;
			goto event_set_exit;
		}

		/* handed off to py_event_parm...reference count incremented again */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_SRC_QUEUE,
					  py_que);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_SRC_QUEUE);
			goto event_set_exit;
		}

		/* reset the movejob's queue attribute to the *new* queue to move to */
		rc = pbs_python_object_set_attr_string_value(py_job, ATTR_queue,
							     rqj->rq_destin);
		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set attribute <%s>",
				       PY_TYPE_JOB, ATTR_queue);
			goto event_set_exit;
		}

		/* py_job is not read-only but only ATTR_queue is modifiable and */
		/* checked under is_attrib_val_settable() 			 */
		rc = pbs_python_object_set_attr_integral_value(py_job,
							       PY_READONLY_FLAG, FALSE);
		if (rc == -1) {

			log_err(PBSE_INTERNAL, __func__,
				"Failed set object's readonly flag");
			goto event_set_exit;
		}

	} else if (hook_event == HOOK_EVENT_PROVISION) {
		struct prov_vnode_info *prov_vnode_info = req_params->rq_prov;

		/* initialize event params to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_VNODE,
					    Py_None);
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_AOE,
					    Py_None);
		py_vnode = PyUnicode_FromString(prov_vnode_info->pvnfo_vnode);
		/* set vnode */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_VNODE, py_vnode);
		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_VNODE);
			goto event_set_exit;
		}

		py_aoe = PyUnicode_FromString(prov_vnode_info->pvnfo_aoe_req); /* NEW ref */
		/* set aoe */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_AOE, py_aoe);
		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_AOE);
			goto event_set_exit;
		}
	} else if (hook_event == HOOK_EVENT_PERIODIC) {
		vnlist = (pbs_list_head *) req_params->vns_list;

		/* SET VNODE_LIST param */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_VNODELIST,
					    Py_None);
		py_vnodelist = create_py_vnodelist(vnlist, perf_label, HOOK_PERF_POPULATE_VNODELIST);
		if (py_vnodelist == NULL) {
			LOG_ERROR_ARG2("%s: failed to create a Python vnodelist object for param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_VNODELIST);
			goto event_set_exit;
		}

		/* set vnode list: py_vnodelist given to py_event_param so ref count */
		/* auto incremented */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_VNODELIST,
					  py_vnodelist);
		if (rc == -1) {
			LOG_ERROR_ARG2("%s: partially set remaining param['%s'] attributes",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_VNODELIST);
			goto event_set_exit;
		}

		/* SET RESV_LIST param */
		resvlist = (pbs_list_head *) req_params->resv_list;

		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_RESVLIST,
					    Py_None);
		py_resvlist = create_py_resvlist(resvlist, perf_label, HOOK_PERF_POPULATE_RESVLIST);
		if (py_resvlist == NULL) {
			LOG_ERROR_ARG2("%s: failed to create a Python resvlist object for param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_RESVLIST);
			goto event_set_exit;
		}
		/* set resv list: py_resvlist given to py_event_param so ref count */
		/* auto incremented */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_RESVLIST,
					  py_resvlist);
		if (rc == -1) {
			LOG_ERROR_ARG2("%s: partially set remaining param['%s'] attributes",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_RESVLIST);
			goto event_set_exit;
		}
	} else if (hook_event == HOOK_EVENT_RUNJOB) {
		struct rq_runjob *rqj = req_params->rq_run;

		/* initialize event param to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
					    Py_None);
		if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name)) {
			if (py_pbs_statobj != NULL) {
				Py_XDECREF(py_jargs); /* discard previously used value */
				py_jargs = Py_BuildValue("(sss)", "job", rqj->rq_jid,
							 pbs_conf.pbs_server_name); /* NEW ref */
				py_job = PyObject_Call(py_pbs_statobj, py_jargs,
						       NULL); /*NEW*/
				hook_set_mode = C_MODE;	      /* ensure still in C mode */
			}
		} else {
			py_job = _pps_helper_get_job(NULL, rqj->rq_jid, NULL, perf_label);
		}
		/* NEW - we own ref */

		if (!py_job || (py_job == Py_None)) {
			LOG_ERROR_ARG2("%s:failed to get job %s's python "
				       "job object",
				       PY_TYPE_EVENT, rqj->rq_jid);
			goto event_set_exit;
		}

		/* py_job given to py_event_parm...so ref. count auto incremented */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB, py_job);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		/* py_job is not read-only but only ATTR_h, ATTR_a are modifiable and */
		/* checked under is_attrib_val_settable(). Resource-type attributes   */
		/* such as ATTR_l, which is part of py_job,  go through a different   */
		/* processing mechanism.        				      */
		/* It is fatal if py_job's readonly flag could not be set to False    */
		/* as it could prevent all job attributes including ATTR_l to be      */
		/* not settable.                                                      */
		rc = pbs_python_object_set_attr_integral_value(py_job,
							       PY_READONLY_FLAG, FALSE);
		if (rc == -1) {

			log_err(PBSE_INTERNAL, __func__,
				"Failed set object's readonly flag");
			goto event_set_exit;
		}

		py_resclist = PyObject_GetAttrString(py_job, ATTR_l); /* NEW */
		if ((py_resclist != NULL) && (py_resclist != Py_None)) {
			/* Don't mark pbs.event().job.Resource_List[] as readonly */
			rc = pbs_python_object_set_attr_integral_value(py_resclist,
								       PY_READONLY_FLAG, FALSE);
			if (rc == -1) {

				log_err(PBSE_INTERNAL, __func__,
					"Failed set object's readonly flag");
				LOG_ERROR_ARG2("%s: warning - failed to set object's '%s' readonly flag", __func__, "Resource_List[]");
			}
		}

		if (!PyObject_HasAttrString(py_job, ATTR_execvnode)) {
			LOG_ERROR_ARG2("%s: does not have attribute <%s>",
				       PY_TYPE_JOB,
				       ATTR_execvnode);
			rc = -1;
			goto event_set_exit;
		}

		/* set value of job's exec_vnode attribute if not already set */
		py_exec_vnode = PyObject_GetAttrString(py_job, ATTR_execvnode); /* NEW */

		if ((rqj->rq_destin != NULL) && (*rqj->rq_destin != '\0') &&
		    ((py_exec_vnode == NULL) || (py_exec_vnode == Py_None))) {
			/* set "exec_vnodes" attribute if not set */
			rc = pbs_python_object_set_attr_string_value(py_job,
								     ATTR_execvnode, rqj->rq_destin);
			if (rc == -1) {
				LOG_ERROR_ARG2("%s:failed to set attribute <%s>",
					       PY_TYPE_JOB, ATTR_execvnode);
				goto event_set_exit;
			}
		}
	} else if (hook_event == HOOK_EVENT_JOBOBIT) {
		struct rq_jobobit *rqj = req_params->rq_obit;
		/* initialize params to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
					    Py_None);

		if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name)) {
			if (py_pbs_statobj != NULL) {
				Py_XDECREF(py_jargs); /* discard previously used value */
				py_jargs = Py_BuildValue("(sss)", "job", rqj->rq_pjob->ji_qs.ji_jobid,
							 pbs_conf.pbs_server_name); /* NEW ref */
				py_job = PyObject_Call(py_pbs_statobj, py_jargs,
						       NULL); /*NEW*/
				hook_set_mode = C_MODE;	      /* ensure still in C mode */
			}
		} else {
			py_job = _pps_helper_get_job(NULL, rqj->rq_pjob->ji_qs.ji_jobid, NULL, perf_label);
			/* NEW - we own ref */
		}

		if (!py_job || (py_job == Py_None)) {
			LOG_ERROR_ARG2("%s:failed to create job %s's python "
				       "job object",
				       PY_TYPE_EVENT, rqj->rq_pjob->ji_qs.ji_jobid);
			goto event_set_exit;
		}

		/* py_job handed off to py_event_parm...reference count incremented */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB, py_job);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}
	} else if (hook_event == HOOK_EVENT_MANAGEMENT) {
		PyObject *py_attr = (PyObject *) NULL;
		struct rq_management *rqj = req_params->rq_manage;
		py_management_class = pbs_python_types_table[PP_MANAGEMENT_IDX].t_class;
		if (!py_management_class) {
			log_err(PBSE_INTERNAL, __func__, "failed to acquire management class");
			(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_MANAGEMENT,
						    Py_None);
			goto event_set_exit;
		}

		py_attr = svrattrl_list_to_pyobject(rqj->rq_manager.rq_cmd, &rqj->rq_manager.rq_attr);
		if (!py_attr) {
			log_err(PBSE_INTERNAL, __func__, "could not build the list of server attributes");
			goto event_set_exit;
		}

		py_margs = Py_BuildValue("(iisliiisO)",
					 rqj->rq_manager.rq_cmd,
					 rqj->rq_manager.rq_objtype,
					 rqj->rq_manager.rq_objname,
					 rqj->rq_time,
					 rqj->rq_reply->brp_code,
					 rqj->rq_reply->brp_auxcode,
					 rqj->rq_reply->brp_choice,
					 (rqj->rq_reply->brp_choice == BATCH_REPLY_CHOICE_Text) ? rqj->rq_reply->brp_un.brp_txt.brp_str : NULL,
					 py_attr); /* NEW ref */
		Py_CLEAR(py_attr);

		if (!py_margs) {
			log_err(PBSE_INTERNAL, __func__, "could not build args list for management");
			goto event_set_exit;
		}
		py_management = PyObject_CallObject(py_management_class, py_margs);

		if (!py_management) {
			pbs_python_write_error_to_log(__func__);
			log_err(PBSE_INTERNAL, __func__, "failed to create a python management object");
			(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_MANAGEMENT,
						    Py_None);
			goto event_set_exit;
		}

		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_MANAGEMENT,
					  py_management);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_MANAGEMENT);
			goto event_set_exit;
		}
	} else if (hook_event == HOOK_EVENT_MODIFYVNODE) {
		struct rq_modifyvnode *rqmvn = req_params->rq_modifyvnode;
		struct pbsnode *vnode_o = rqmvn->rq_vnode_o;
		struct pbsnode *vnode = rqmvn->rq_vnode;
		int tmpv_rc;

		/* initialize event params to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_VNODE,
					    Py_None);
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_VNODE_O,
					    Py_None);

		/* Retrieve the vnode_o data */
		py_vnode_o = _pps_helper_get_vnode(vnode_o, NULL, HOOK_PERF_POPULATE_VNODE_O);
		if (py_vnode_o == NULL) {
			log_err(PBSE_INTERNAL, __func__, "failed to create a python vnode_o object");
			goto event_set_exit;
		}

		/* Set the vnode_o object to readonly to prevent hook writers from modifying values */
		tmpv_rc = pbs_python_mark_object_readonly(py_vnode_o);
		if (tmpv_rc == -1) {
			log_err(PBSE_INTERNAL, __func__, "Failed to mark python vnode_o object readonly");
			goto event_set_exit;
		}

		/* Retrieve the vnode data */
		py_vnode = _pps_helper_get_vnode(vnode, NULL, HOOK_PERF_POPULATE_VNODE);
		if (py_vnode == NULL) {
			log_err(PBSE_INTERNAL, __func__, "failed to create a python vnode object");
			goto event_set_exit;
		}

		/* Set the vnode object to readonly to prevent hook writers from modifying values */
		tmpv_rc = pbs_python_mark_object_readonly(py_vnode);
		if (tmpv_rc == -1) {
			log_err(PBSE_INTERNAL, __func__, "Failed to mark python vnode object readonly");
			goto event_set_exit;
		}

		/* Set the vnode_o event param */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_VNODE_O, py_vnode_o);
		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_VNODE_O);
			goto event_set_exit;
		}

		/* Set the vnode event param */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_VNODE, py_vnode);
		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_VNODE);
			goto event_set_exit;
		}
	} else if ((hook_event == HOOK_EVENT_RESV_END) ||
		   (hook_event == HOOK_EVENT_RESV_BEGIN)) {
		struct rq_manage *rqj = req_params->rq_manage;

		/* initialize event param to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_RESV, Py_None);

		if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name)) {
			if (py_pbs_statobj != NULL) {
				Py_XDECREF(py_rargs); /* discard previously used value */
				py_rargs = Py_BuildValue("(sss)", "resv", rqj->rq_objname,
							 pbs_conf.pbs_server_name); /* NEW ref */
				py_resv = PyObject_Call(py_pbs_statobj, py_rargs,
							NULL); /*NEW Reservation object*/
				hook_set_mode = C_MODE;	       /* ensure still in C mode */
			}
		} else {
			py_resv = _pps_helper_get_resv(NULL, rqj->rq_objname, perf_label);
		}

		if (!py_resv || (py_resv == Py_None)) {
			LOG_ERROR_ARG2("%s:failed to create resv %s's python resv object", PY_TYPE_EVENT, rqj->rq_objname);
			goto event_set_exit;
		}
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_RESV, py_resv);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>", PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}
	} else if (hook_event == HOOK_EVENT_RESV_CONFIRM) {
		/* Confirm uses rq_runjob, not rq_manage and sticks the reservation id in rq_jid */
		struct rq_runjob *rqj = req_params->rq_run;

		/* initialize event param to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_RESV, Py_None);

		if (IS_PBS_PYTHON_CMD(pbs_python_daemon_name)) {
			if (py_pbs_statobj != NULL) {
				Py_XDECREF(py_rargs); /* discard previously used value */
				py_rargs = Py_BuildValue("(sss)", "resv", rqj->rq_jid,
							 pbs_conf.pbs_server_name); /* NEW ref */
				py_resv = PyObject_Call(py_pbs_statobj, py_rargs,
							NULL); /*NEW Reservation object*/
				hook_set_mode = C_MODE;	       /* ensure still in C mode */
			}
		} else {
			py_resv = _pps_helper_get_resv(NULL, rqj->rq_jid, perf_label);
		}

		if (!py_resv || (py_resv == Py_None)) {
			LOG_ERROR_ARG2("%s:failed to create resv %s's python resv object", PY_TYPE_EVENT, rqj->rq_jid);
			goto event_set_exit;
		}
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_RESV, py_resv);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>", PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}
	} else if ((hook_event == HOOK_EVENT_EXECJOB_BEGIN) ||
		   (hook_event == HOOK_EVENT_EXECJOB_RESIZE) ||
		   (hook_event == HOOK_EVENT_EXECJOB_PROLOGUE) ||
		   (hook_event == HOOK_EVENT_EXECJOB_EPILOGUE) ||
		   (hook_event == HOOK_EVENT_EXECJOB_END) ||
		   (hook_event == HOOK_EVENT_EXECJOB_ABORT) ||
		   (hook_event == HOOK_EVENT_EXECJOB_POSTSUSPEND) ||
		   (hook_event == HOOK_EVENT_EXECJOB_PRERESUME) ||
		   (hook_event == HOOK_EVENT_EXECJOB_PRETERM)) {
		struct rq_queuejob *rqj = req_params->rq_job;

		/* initialize event param to None */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
					    Py_None);
		/*
		 * First things first create a Python job object.
		 *  - Borrowed reference
		 *  - Exception is *NOT* set
		 */
		py_job_class = pbs_python_types_table[PP_JOB_IDX].t_class;

		py_jargs = Py_BuildValue("(s)", rqj->rq_jid); /* NEW ref */
		if (!py_jargs) {
			log_err(PBSE_INTERNAL, __func__, "could not build args list for job");
			goto event_set_exit;
		}
		py_job = PyObject_Call(py_job_class, py_jargs, NULL); /*NEW*/

		if (!py_job) {
			log_err(PBSE_INTERNAL, __func__, "failed to create a python job object");
			(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
						    Py_None);
			goto event_set_exit;
		}

		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB, py_job);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s:failed to set param attribute <%s>",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		snprintf((char *) perf_action, sizeof(perf_action), "%s:%s(%s)", HOOK_PERF_POPULATE, EVENT_JOB_OBJECT, rqj->rq_jid);
		rc = pbs_python_populate_python_class_from_svrattrl(py_job, &rqj->rq_attr, perf_label, perf_action);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s: partially set remaining param['%s'] attributes",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		py_vnodelist = create_hook_vnode_list_param(py_event_param,
							    PY_TYPE_EVENT, PY_EVENT_PARAM_VNODELIST,
							    (pbs_list_head *) req_params->vns_list,
							    perf_label, HOOK_PERF_POPULATE_VNODELIST);

		if (py_vnodelist == NULL) {
			rc = -1;
			goto event_set_exit;
		}

		if (hook_event == HOOK_EVENT_EXECJOB_PROLOGUE) {

			py_vnodelist_fail = create_hook_vnode_list_param(py_event_param,
									 PY_TYPE_EVENT, PY_EVENT_PARAM_VNODELIST_FAIL,
									 (pbs_list_head *) req_params->vns_list_fail, perf_label, HOOK_PERF_POPULATE_VNODELIST_FAIL);

			if (py_vnodelist_fail == NULL) {
				rc = -1;
				goto event_set_exit;
			}
			py_failed_node_list = create_py_strlist_from_svrattrl_names(req_params->failed_mom_list);
			if (py_failed_node_list == NULL) {
				rc = -1;
				goto event_set_exit;
			}

			/* set failed_mom_list: py_vnlist given to py_job so ref count auto incremented */
			rc = PyObject_SetAttrString(py_job, PY_JOB_FAILED_MOM_LIST, py_failed_node_list);
			if (rc == -1)
				goto event_set_exit;

			/* set succeeded_mom_list: py_vnlist given to py_job so ref count auto incremented */
			py_node_list = create_py_strlist_from_svrattrl_names((pbs_list_head *) req_params->succeeded_mom_list);
			if (py_node_list == NULL) {
				rc = -1;
				goto event_set_exit;
			}

			/* set vnode list: py_vnlist given to py_job so ref count auto incremented */
			rc = PyObject_SetAttrString(py_job, PY_JOB_SUCCEEDED_MOM_LIST, py_node_list);
			if (rc == -1)
				goto event_set_exit;
		}

	} else if (hook_event == HOOK_EVENT_EXECJOB_LAUNCH) {
		struct rq_queuejob *rqj;
		char *progname = NULL;
		char **arg_list = NULL;
		char *env_str = NULL;

		rqj = (struct rq_queuejob *) req_params->rq_job;

		/* SET JOB param */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
					    Py_None);
		/*
		 * First things first create a Python job object.
		 *  - Borrowed reference
		 *  - Exception is *NOT* set
		 */
		py_job_class = pbs_python_types_table[PP_JOB_IDX].t_class;

		py_jargs = Py_BuildValue("(s)", rqj->rq_jid); /* NEW ref */
		if (py_jargs == NULL) {
			LOG_ERROR_ARG2("%s: could not build job args list for param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		py_job = PyObject_Call(py_job_class, py_jargs, NULL); /*NEW*/
		if (py_job == NULL) {
			LOG_ERROR_ARG2("%s: failed to create a python job object for param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
						    Py_None);
			goto event_set_exit;
		}

		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB, py_job);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s: failed to set param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		snprintf((char *) perf_action, sizeof(perf_action), "%s:%s(%s)", HOOK_PERF_POPULATE, EVENT_JOB_OBJECT, rqj->rq_jid);
		rc = pbs_python_populate_python_class_from_svrattrl(py_job, &rqj->rq_attr, perf_label, perf_action);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s: partially set remaining param['%s'] attributes",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		py_vnodelist =
			create_hook_vnode_list_param(py_event_param,
						     PY_TYPE_EVENT, PY_EVENT_PARAM_VNODELIST,
						     (pbs_list_head *) req_params->vns_list, perf_label, HOOK_PERF_POPULATE_VNODELIST);

		if (py_vnodelist == NULL) {
			rc = -1;
			goto event_set_exit;
		}

		py_vnodelist_fail =
			create_hook_vnode_list_param(py_event_param,
						     PY_TYPE_EVENT, PY_EVENT_PARAM_VNODELIST_FAIL,
						     (pbs_list_head *) req_params->vns_list_fail, perf_label, HOOK_PERF_POPULATE_VNODELIST_FAIL);

		if (py_vnodelist_fail == NULL) {
			rc = -1;
			goto event_set_exit;
		}

		/* SET job's failed_mom_list param *vnlist */
		py_failed_node_list = create_py_strlist_from_svrattrl_names(req_params->failed_mom_list);
		if (py_failed_node_list == NULL) {
			rc = -1;
			goto event_set_exit;
		}

		/* set failed_mom_list: py_failed_node_list given to py_job so ref count auto incremented */
		rc = PyObject_SetAttrString(py_job, PY_JOB_FAILED_MOM_LIST, py_failed_node_list);
		if (rc == -1)
			goto event_set_exit;

		/* SET job's succeeded_mom_list param *vnlist */
		py_node_list = create_py_strlist_from_svrattrl_names((pbs_list_head *) req_params->succeeded_mom_list);
		if (py_node_list == NULL) {
			rc = -1;
			goto event_set_exit;
		}

		/* set succeeded_mom_list: py_node_list given to py_job so ref count auto incremented */
		rc = PyObject_SetAttrString(py_job, PY_JOB_SUCCEEDED_MOM_LIST, py_node_list);
		if (rc == -1)
			goto event_set_exit;

		/* SET PROGNAME param */
		progname = req_params->progname;
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_PROGNAME,
					    Py_None);

		py_progname = Py_BuildValue("s", progname); /* NEW ref */
		if (py_progname == NULL) {
			LOG_ERROR_ARG2("%s:failed to create a Python string object for param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_PROGNAME);
			goto event_set_exit;
		}

		/* set progname: py_progname given to py_event_param so ref count */
		/* auto incremented */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_PROGNAME,
					  py_progname);
		if (rc == -1) {
			LOG_ERROR_ARG2("%s: failed to set param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_PROGNAME);
			goto event_set_exit;
		}

		/* SET ARGV param */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_ARGLIST,
					    Py_None);

		arg_list = svrattrl_to_str_array(req_params->argv_list);
		if (arg_list == NULL) {
			LOG_ERROR_ARG2("%s: failed to build a string array for setting param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_ARGLIST);
			goto event_set_exit;
		}
		py_arglist = create_py_strlist(arg_list);

		free_str_array(arg_list);
		arg_list = NULL;

		if (py_arglist == NULL) {
			LOG_ERROR_ARG2("%s: failed to create a Python string list for setting param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_ARGLIST);
			goto event_set_exit;
		}

		/* set arg_list: py_arglist given to py_event_param so ref count */
		/* auto incremented */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_ARGLIST,
					  py_arglist);
		if (rc == -1) {
			LOG_ERROR_ARG2("%s: failed to set param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_ARGLIST);
			goto event_set_exit;
		}

		/* SET ENV param */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_ENV,
					    Py_None);
		env_str = req_params->env;
		if (env_str == NULL) {
			LOG_ERROR_ARG2("%s: failed to build a string array for setting param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_ENV);
			goto event_set_exit;
		}

		py_env_class = pbs_python_types_table[PP_ENV_IDX].t_class;

		py_eargs = Py_BuildValue("(si)", env_str, 1); /* NEW ref */
		if (!py_eargs) {
			log_err(PBSE_INTERNAL, __func__, "could not build env list for job");
			goto event_set_exit;
		}
		py_env = PyObject_Call(py_env_class, py_eargs, NULL); /*NEW*/

		if (!py_env) {
			log_err(PBSE_INTERNAL, __func__, "failed to create a python env object");
			goto event_set_exit;
		}

		/* set env: py_env given to py_event_param so ref count */
		/* auto incremented */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_ENV,
					  py_env);
		if (rc == -1) {
			LOG_ERROR_ARG2("%s: failed to set param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_ENV);
			goto event_set_exit;
		}

	} else if ((hook_event == HOOK_EVENT_EXECHOST_PERIODIC) ||
		   (hook_event == HOOK_EVENT_EXECHOST_STARTUP)) {

		/* SET VNODELIST param */
		py_vnodelist = create_hook_vnode_list_param(py_event_param,
							    PY_TYPE_EVENT,
							    PY_EVENT_PARAM_VNODELIST,
							    (pbs_list_head *) req_params->vns_list,
							    perf_label, HOOK_PERF_POPULATE_VNODELIST);

		if (py_vnodelist == NULL) {
			rc = -1;
			goto event_set_exit;
		}

		/* SET JOB_LIST param */
		if (hook_event == HOOK_EVENT_EXECHOST_PERIODIC) {
			/* initialize event param to None */
			joblist = (pbs_list_head *) req_params->jobs_list;

			(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOBLIST,
						    Py_None);
			py_joblist = create_py_joblist(joblist, perf_label, HOOK_PERF_POPULATE_JOBLIST);
			if (py_joblist == NULL) {
				LOG_ERROR_ARG2("%s: failed to create a Python joblist object for param['%s']",
					       PY_TYPE_EVENT, PY_EVENT_PARAM_JOBLIST);
				goto event_set_exit;
			}
			/* set job list: py_vnodelist given to py_event_param so ref count */
			/* auto incremented */
			rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOBLIST,
						  py_joblist);
			if (rc == -1) {
				LOG_ERROR_ARG2("%s: partially set remaining param['%s'] attributes",
					       PY_TYPE_EVENT, PY_EVENT_PARAM_JOBLIST);
				goto event_set_exit;
			}
		}

	} else if (hook_event == HOOK_EVENT_EXECJOB_ATTACH) {
		struct rq_queuejob *rqj;
		pid_t pid;

		rqj = (struct rq_queuejob *) req_params->rq_job;

		/* SET JOB param */
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
					    Py_None);
		/*
		 * First things first create a Python job object.
		 *  - Borrowed reference
		 *  - Exception is *NOT* set
		 */
		py_job_class = pbs_python_types_table[PP_JOB_IDX].t_class;

		py_jargs = Py_BuildValue("(s)", rqj->rq_jid); /* NEW ref */
		if (py_jargs == NULL) {
			LOG_ERROR_ARG2("%s: could not build job args list for param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		py_job = PyObject_Call(py_job_class, py_jargs, NULL); /*NEW*/
		if (py_job == NULL) {
			LOG_ERROR_ARG2("%s: failed to create a python job object for param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB,
						    Py_None);
			goto event_set_exit;
		}

		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_JOB, py_job);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s: failed to set param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		snprintf((char *) perf_action, sizeof(perf_action), "%s:%s(%s)", HOOK_PERF_POPULATE, EVENT_JOB_OBJECT, rqj->rq_jid);
		rc = pbs_python_populate_python_class_from_svrattrl(py_job, &rqj->rq_attr, perf_label, perf_action);

		if (rc == -1) {
			LOG_ERROR_ARG2("%s: partially set remaining param['%s'] attributes",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
			goto event_set_exit;
		}

		/* SET PID param */
		pid = req_params->pid;
		(void) PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_PID,
					    Py_None);

		py_pid = Py_BuildValue("i", (int) pid); /* NEW ref */
		if (py_pid == NULL) {
			LOG_ERROR_ARG2("%s:failed to create a Python int object for param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_PID);
			goto event_set_exit;
		}

		/* set progname: py_progname given to py_event_param so ref count */
		/* auto incremented */
		rc = PyDict_SetItemString(py_event_param, PY_EVENT_PARAM_PID,
					  py_pid);
		if (rc == -1) {
			LOG_ERROR_ARG2("%s: failed to set param['%s']",
				       PY_TYPE_EVENT, PY_EVENT_PARAM_PID);
			goto event_set_exit;
		}

		py_vnodelist = create_hook_vnode_list_param(py_event_param,
							    PY_TYPE_EVENT,
							    PY_EVENT_PARAM_VNODELIST,
							    (pbs_list_head *) req_params->vns_list,
							    perf_label, HOOK_PERF_POPULATE_VNODELIST);

		if (py_vnodelist == NULL) {
			rc = -1;
			goto event_set_exit;
		}

	} else {
		LOG_ERROR_ARG2("%s:got unknown hook event %s",
			       PY_TYPE_EVENT, hook_event_as_string(hook_event));
		goto event_set_exit;
	}

	rc = 0;

event_set_exit:

	py_hook_pbsevent = py_event;

	if (py_hook_pbsevent != NULL) {
		Py_INCREF(py_hook_pbsevent); /* don't lose reference to event object */

		/* only applies to event being accessed, set  in a Python script */
		if (_pbs_python_event_mark_readonly() == -1) {
			log_err(PBSE_INTERNAL, __func__, "Failed to mark event readonly.");
			rc = -1;
		}
	}

	if (PyErr_Occurred()) {

		if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
			rc = -2;
		pbs_python_write_error_to_log(__func__);
	}

	Py_CLEAR(py_eargs);
	Py_CLEAR(py_event);
	Py_CLEAR(py_jargs);
	Py_CLEAR(py_job);
	Py_CLEAR(py_job_o);
	Py_CLEAR(py_que);
	Py_CLEAR(py_rargs);
	Py_CLEAR(py_resv);
	Py_CLEAR(py_event_param);
	Py_CLEAR(py_varlist);
	Py_CLEAR(py_varlist_o);
	Py_CLEAR(py_vnodelist);
	Py_CLEAR(py_vnodelist_fail);
	Py_CLEAR(py_failed_node_list);
	Py_CLEAR(py_node_list);
	Py_CLEAR(py_resclist);
	Py_CLEAR(py_exec_vnode);
	Py_CLEAR(py_vnode);
	Py_CLEAR(py_vnode_o);
	Py_CLEAR(py_aoe);
	Py_CLEAR(py_progname);
	Py_CLEAR(py_arglist);
	Py_CLEAR(py_env);
	Py_CLEAR(py_joblist);
	Py_CLEAR(py_pid);
	Py_CLEAR(py_resvlist);
	Py_CLEAR(py_margs);
	Py_CLEAR(py_management);
	return (rc);
}


/**
 *
 * @brief
 *	Helper function to populate the svrattrl list 'vnlist' with
 *	data taken from the individual vnodes in event parameter
 *	'vnodelist_name'.
 * @param[in]	vnodelist_name  - name of vnode_list[] in pbs.event().
 * @param[in,out] vnlist 	- the pbs list to populate.
 *
 * @return int
 * @retval 0	- success
 * @retval -1	- if error occurred.
 *
 */
static int
populate_svrattrl_from_vnodelist_param(char *vnodelist_name,
				       pbs_list_head *vnlist)
{
	PyObject *py_vnlist = NULL;
	PyObject *py_attr_keys = NULL;
	PyObject *py_vnode = NULL;
	int num_attrs;
	int i;

	if ((vnodelist_name == NULL) || (vnlist == NULL)) {
		log_err(PBSE_INTERNAL, __func__, "bad input param");
		return -1;
	}

	py_vnlist = _pbs_python_event_get_param(vnodelist_name);

	if (py_vnlist == NULL) {
		log_err(PBSE_INTERNAL, __func__,
			"No vnode list parameter found for event!");
		return -1;
	}

	if (!PyDict_Check(py_vnlist)) {
		log_err(PBSE_INTERNAL, __func__,
			"vnode list parameter not a dictionary!");
		return -1;
	}
	py_attr_keys = PyDict_Keys(py_vnlist); /* NEW ref */

	if (py_attr_keys == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "Failed to obtain object's '%s' keys",
			 vnodelist_name);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		return -1;
	}

	if (!PyList_Check(py_attr_keys)) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "object's '%s' keys is not a list!",
			 vnodelist_name);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		Py_CLEAR(py_attr_keys);
		return -1;
	}

	num_attrs = PyList_Size(py_attr_keys);
	for (i = 0; i < num_attrs; i++) {
		char *key_str;

		key_str = strdup(pbs_python_list_get_item_string_value(py_attr_keys, i));
		if ((key_str == NULL) || (key_str[0] == '\0')) {
			if (key_str != NULL) {
				free(key_str);
				key_str = NULL;
			}
			continue;
		}

		py_vnode = PyDict_GetItemString(py_vnlist, key_str); /* no need to Py_CLEAR() later since this returns a borrowed reference */

		if (py_vnode == NULL) {
			snprintf(log_buffer, sizeof(log_buffer) - 1,
				 "failed to get attribute '%s' value",
				 key_str);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			Py_CLEAR(py_attr_keys);
			free(key_str);
			key_str = NULL;
			return -1;
		}

		if (pbs_python_populate_svrattrl_from_python_class(
			    py_vnode, vnlist, key_str, 1) == -1) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "failed to populate svrattrl with key '%s' value", key_str);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			Py_CLEAR(py_attr_keys);
			free(key_str);
			return -1;
		}
		free(key_str);
	}
	Py_CLEAR(py_attr_keys);

	return (0);
}

/**
 *
 * @brief
 * 	Recreates 'req_params' (structure of batch requests, ex. rq_queuejob, rq_manage,
 * 	rq_move) consulting the parameter values obtained from current
 * 	PBS Python event object representing 'hook_event'.
 *
 * @param[in]		hook_event 	- event in question
 * @param[in/out]	req_params	- results parameter
 * @param[in]		perf_label - passed on to hook_perf_stat* call.
 * @param[in]		perf_action - passed on to hook_perf_stat* call.
 * @note
 * 	Care must be taken to malloc free up allocated entries in 'req_params'
 * 	after use. The 'req_params' entries could be partially allocated upon
 * 	a failure from this function.
 *
 * @return int
 * @retval	0 	for success
 * @retval	-1	otherwise, for failure.
 *
 * @note
 *		This function calls a single hook_perf_stat_start()
 *		that has some malloc-ed data that are freed in the
 *		hook_perf_stat_stop() call, which is done at the end of
 *		this function.
 *		Ensure that after the hook_perf_stat_start(), all
 *		program execution path lead to hook_perf_stat_stop()
 *		call.
 */
int
_pbs_python_event_to_request(unsigned int hook_event, hook_output_param_t *req_params, char *perf_label, char *perf_action)
{
	PyObject *py_job = NULL;
	PyObject *py_vnode = NULL;
	PyObject *py_vnodelist = NULL;
	PyObject *py_joblist = NULL;
	PyObject *py_resvlist = NULL;
	PyObject *py_job_o = NULL;
	PyObject *py_resv = NULL;
	PyObject *py_resv_o = NULL;
	char *queue;
	PyObject *py_varlist = NULL;
	PyObject *py_varlist_o = NULL;
	int i, num_attrs;
	char *key_str = NULL;
	PyObject *py_attr_keys = NULL;
	PyObject *py_progname = NULL;
	PyObject *py_arglist = NULL;
	PyObject *py_env = NULL;
	char *progname;
	char *env_str;
	int deletejob_flag;
	int rerunjob_flag;
	char val_str[HOOK_BUF_SIZE];
	int rc = -1;

	hook_perf_stat_start(perf_label, perf_action, 0);
	switch (hook_event) {
		case HOOK_EVENT_QUEUEJOB:
		case HOOK_EVENT_POSTQUEUEJOB:

			py_job = _pbs_python_event_get_param(PY_EVENT_PARAM_JOB);
			if (!py_job) {
				log_err(PBSE_INTERNAL, __func__,
					"No job parameter found for event!");
				goto event_to_request_exit;
			}

			queue = pbs_python_object_get_attr_string_value(py_job,
									ATTR_queue);
			if (queue) {
				if (hook_event == HOOK_EVENT_QUEUEJOB)
					strcpy(((struct rq_queuejob *) (req_params->rq_job))->rq_destin, queue);
				else
					strcpy(((struct rq_postqueuejob *) (req_params->rq_postqueuejob))->rq_destin, queue);
			}

			if (hook_event == HOOK_EVENT_QUEUEJOB) {
				if (pbs_python_populate_svrattrl_from_python_class(py_job,
										   &((struct rq_queuejob *) (req_params->rq_job))->rq_attr, NULL, 0) == -1) {
					goto event_to_request_exit;
				}
				print_svrattrl_list("pbs_populate_svrattrl_from_python_class==>", &((struct rq_queuejob *) (req_params->rq_job))->rq_attr);
			} else {
				if (pbs_python_populate_svrattrl_from_python_class(py_job,
										   &((struct rq_postqueuejob *) (req_params->rq_postqueuejob))->rq_attr, NULL, 0) == -1) {
					goto event_to_request_exit;
				}
				print_svrattrl_list("pbs_populate_svrattrl_from_python_class==>", &((struct rq_postqueuejob *) (req_params->rq_job))->rq_attr);
			}
			break;
		case HOOK_EVENT_EXECJOB_LAUNCH:
		case HOOK_EVENT_EXECJOB_BEGIN:
		case HOOK_EVENT_EXECJOB_PROLOGUE:
		case HOOK_EVENT_EXECJOB_EPILOGUE:
		case HOOK_EVENT_EXECJOB_PRETERM:
		case HOOK_EVENT_EXECJOB_END:
		case HOOK_EVENT_EXECJOB_ABORT:
		case HOOK_EVENT_EXECJOB_POSTSUSPEND:
		case HOOK_EVENT_EXECJOB_PRERESUME:

			py_job = _pbs_python_event_get_param(PY_EVENT_PARAM_JOB);
			if (!py_job) {
				log_err(PBSE_INTERNAL, __func__,
					"No job parameter found for event!");
				goto event_to_request_exit;
			}

			if (pbs_python_populate_svrattrl_from_python_class(py_job,
									   &((struct rq_queuejob *) (req_params->rq_job))->rq_attr, NULL, 0) == -1) {
				log_err(PBSE_INTERNAL, __func__,
					"Failed to populate request structure!");
				goto event_to_request_exit;
			}
			print_svrattrl_list("pbs_populate_svrattrl_from_python_class==>", &((struct rq_queuejob *) (req_params->rq_job))->rq_attr);

			if (hook_event == HOOK_EVENT_EXECJOB_PROLOGUE) {
				/* populate vnodelist_fail event param */
				if (populate_svrattrl_from_vnodelist_param(PY_EVENT_PARAM_VNODELIST_FAIL, (pbs_list_head *) (req_params->vns_list_fail))) {
					goto event_to_request_exit;
				}
			} else if (hook_event == HOOK_EVENT_EXECJOB_LAUNCH) {
				int ret;

				py_progname = _pbs_python_event_get_param(PY_EVENT_PARAM_PROGNAME);
				if (py_progname == NULL) {
					log_err(PBSE_INTERNAL, __func__,
						"No progname parameter found for event!");
					goto event_to_request_exit;
				}
				progname = strdup(pbs_python_object_str(py_progname));
				if (progname == NULL) {
					log_err(PBSE_INTERNAL, __func__,
						"Failed to strdup progname parameter!");
					goto event_to_request_exit;
				}
				*((char **) (req_params->progname)) = progname;

				py_arglist = _pbs_python_event_get_param(PY_EVENT_PARAM_ARGLIST);
				if (py_arglist == NULL) {
					log_err(PBSE_INTERNAL, __func__,
						"No argv parameter found for event!");
					goto event_to_request_exit;
				}

				ret = py_strlist_to_svrattrl(py_arglist,
							     (pbs_list_head *) (req_params->argv_list),
							     PY_EVENT_PARAM_ARGLIST);
				if (ret == -1) {
					snprintf(log_buffer, sizeof(log_buffer),
						 "%s: Failed to dump Python string list values into a svrattrl list!",
						 PY_EVENT_PARAM_ARGLIST);
					log_err(PBSE_INTERNAL, __func__, log_buffer);
					goto event_to_request_exit;
				}

				py_env = _pbs_python_event_get_param(PY_EVENT_PARAM_ENV);
				if (py_env == NULL) {
					log_err(PBSE_INTERNAL, __func__,
						"No env parameter found for event!");
					goto event_to_request_exit;
				}

				env_str = strdup(pbs_python_object_str(py_env));
				if (env_str == NULL) {
					log_err(PBSE_INTERNAL, __func__,
						"Failed to strdup progname parameter!");
					goto event_to_request_exit;
				}
				*((char **) (req_params->env)) = env_str;

				/* populate vnodelist_fail event param */
				if (populate_svrattrl_from_vnodelist_param(PY_EVENT_PARAM_VNODELIST_FAIL, (pbs_list_head *) (req_params->vns_list_fail))) {
					goto event_to_request_exit;
				}
			}

			/* fall through here */
		case HOOK_EVENT_EXECHOST_PERIODIC:
		case HOOK_EVENT_EXECHOST_STARTUP:

			/* populate vnodelist event param */
			if (populate_svrattrl_from_vnodelist_param(PY_EVENT_PARAM_VNODELIST, (pbs_list_head *) (req_params->vns_list))) {
				goto event_to_request_exit;
			}
			print_svrattrl_list("pbs_populate_svrattrl_from_python_class==>", (pbs_list_head *) (req_params->vns_list));
			Py_CLEAR(py_attr_keys);

			if (hook_event == HOOK_EVENT_EXECHOST_PERIODIC) {

				py_joblist = _pbs_python_event_get_param(PY_EVENT_PARAM_JOBLIST);
				if (!py_joblist) {
					log_err(PBSE_INTERNAL, __func__,
						"No job list parameter found for event!");
					goto event_to_request_exit;
				}

				if (!PyDict_Check(py_joblist)) {
					log_err(PBSE_INTERNAL, __func__,
						"job list parameter not a dictionary!");
					goto event_to_request_exit;
				}

				py_attr_keys = PyDict_Keys(py_joblist); /* NEW ref */

				if (py_attr_keys == NULL) {
					snprintf(log_buffer, sizeof(log_buffer),
						 "Failed to obtain object's '%s' keys",
						 PY_EVENT_PARAM_JOBLIST);
					log_err(PBSE_INTERNAL, __func__, log_buffer);
					goto event_to_request_exit;
				}

				if (!PyList_Check(py_attr_keys)) {
					snprintf(log_buffer, sizeof(log_buffer),
						 "object's '%s' keys is not a list!",
						 PY_EVENT_PARAM_JOBLIST);
					log_err(PBSE_INTERNAL, __func__, log_buffer);
					Py_CLEAR(py_attr_keys);
					goto event_to_request_exit;
				}

				num_attrs = PyList_Size(py_attr_keys);

				for (i = 0; i < num_attrs; i++) {

					key_str = strdup(pbs_python_list_get_item_string_value(
						py_attr_keys, i));

					if ((key_str == NULL) || (key_str[0] == '\0')) {
						if (key_str != NULL) {
							free(key_str);
						}
						continue;
					}

					py_job = PyDict_GetItemString(py_joblist,
								      key_str); /* borrowed */

					if (py_job == NULL) {
						snprintf(log_buffer, sizeof(log_buffer) - 1,
							 "failed to get attribute '%s' value", key_str);
						log_err(PBSE_INTERNAL, __func__, log_buffer);
						Py_CLEAR(py_attr_keys);
						free(key_str);
						goto event_to_request_exit;
					}

					if (pbs_python_populate_svrattrl_from_python_class(py_job,
											   (pbs_list_head *) (req_params->jobs_list), key_str, 1) == -1) {
						snprintf(log_buffer, sizeof(log_buffer) - 1,
							 "failed to populate svrattrl with key '%s' value", key_str);
						log_err(PBSE_INTERNAL, __func__, log_buffer);
						Py_CLEAR(py_attr_keys);
						free(key_str);
						goto event_to_request_exit;
					}

					deletejob_flag = pbs_python_object_get_attr_integral_value(py_job,
												   PY_DELETEJOB_FLAG);
					if (deletejob_flag != -1) {
						if (deletejob_flag == 1) {
							strcpy(val_str, "True");
						} else {
							strcpy(val_str, "False");
						}
						if (add_to_svrattrl_list(
							    (pbs_list_head *) (req_params->jobs_list),
							    PY_DELETEJOB_FLAG, NULL, val_str,
							    ATR_VFLAG_HOOK, key_str) == -1) {
							snprintf(log_buffer, LOG_BUF_SIZE - 1, "failed to add_to_svrattrl_list(%s.%s,null,%s)",
								 key_str, PY_DELETEJOB_FLAG, val_str);
							log_buffer[LOG_BUF_SIZE - 1] = '\0';
							log_err(errno, __func__, log_buffer);
							Py_CLEAR(py_attr_keys);
							free(key_str);
							goto event_to_request_exit;
						}
					}

					rerunjob_flag = pbs_python_object_get_attr_integral_value(py_job,
												  PY_RERUNJOB_FLAG);
					if (rerunjob_flag != -1) {
						if (rerunjob_flag == 1) {
							strcpy(val_str, "True");
						} else {
							strcpy(val_str, "False");
						}
						if (add_to_svrattrl_list(
							    (pbs_list_head *) (req_params->jobs_list),
							    PY_RERUNJOB_FLAG, NULL,
							    val_str, ATR_VFLAG_HOOK, key_str) == -1) {
							snprintf(log_buffer, LOG_BUF_SIZE - 1, "failed to add_to_svrattrl_list(%s.%s,null,%s)",
								 key_str, PY_RERUNJOB_FLAG, val_str);
							log_buffer[LOG_BUF_SIZE - 1] = '\0';
							log_err(errno, __func__, log_buffer);
							Py_CLEAR(py_attr_keys);
							free(key_str);
							goto event_to_request_exit;
						}
					}

					if (key_str != NULL) {
						free(key_str);
					}
				}
				Py_CLEAR(py_attr_keys);
				print_svrattrl_list("pbs_populate_svrattrl_from_python_class==>", (pbs_list_head *) (req_params->jobs_list));
			}

			break;
		case HOOK_EVENT_RESVSUB:

			py_resv = _pbs_python_event_get_param(PY_EVENT_PARAM_RESV);
			if (!py_resv) {
				log_err(PBSE_INTERNAL, __func__,
					"No resv parameter found for event!");
				goto event_to_request_exit;
			}
			if (pbs_python_populate_svrattrl_from_python_class(py_resv,
									   &((struct rq_queuejob *) (req_params->rq_job))->rq_attr, NULL, 0) == -1) {
				goto event_to_request_exit;
			}

			print_svrattrl_list("pbs_populate_svrattrl_from_python_class==>", &((struct rq_queuejob *) (req_params->rq_job))->rq_attr);
			break;
		case HOOK_EVENT_MODIFYRESV:

			py_resv = _pbs_python_event_get_param(PY_EVENT_PARAM_RESV);
			if (!py_resv) {
				log_err(PBSE_INTERNAL, __func__,
					"No resv parameter found for event!");
				goto event_to_request_exit;
			}

			py_resv_o = _pbs_python_event_get_param(PY_EVENT_PARAM_RESV_O);
			if (!py_resv_o) {
				log_err(PBSE_INTERNAL, __func__,
					"No resv_o parameter found for event!");
				goto event_to_request_exit;
			}

			/* Need to check if ATTR_v (i.e. Variable_list) changed, and if */
			/* so, needs to be sent with the MODIFYRESV request.	            */

			if (PyObject_HasAttrString(py_resv, ATTR_v))
				py_varlist = PyObject_GetAttrString(py_resv, ATTR_v); /* NEW */

			if (PyObject_HasAttrString(py_resv_o, ATTR_v))
				py_varlist_o = PyObject_GetAttrString(py_resv_o, ATTR_v);
			/* NEW */

			if ((py_varlist != NULL) && (py_varlist_o != NULL) &&
			    (PyObject_RichCompareBool(py_varlist, py_varlist_o, Py_EQ))) {
				/* upon success, py_resv decreases ref count to py_varlist */
				(void) PyObject_SetAttrString(py_resv, ATTR_v, Py_None);
			}

			Py_CLEAR(py_varlist);
			Py_CLEAR(py_varlist_o);

			if (pbs_python_populate_svrattrl_from_python_class(py_resv,
									   &((struct rq_manage *) (req_params->rq_manage))->rq_attr, NULL, 0) == -1) {
				goto event_to_request_exit;
			}
			print_svrattrl_list("pbs_populate_svrattrl_from_python_class==>", &((struct rq_manage *) (req_params->rq_manage))->rq_attr);
			break;

		case HOOK_EVENT_MODIFYJOB:

			py_job = _pbs_python_event_get_param(PY_EVENT_PARAM_JOB);
			if (!py_job) {
				log_err(PBSE_INTERNAL, __func__,
					"No job parameter found for event!");
				goto event_to_request_exit;
			}

			py_job_o = _pbs_python_event_get_param(PY_EVENT_PARAM_JOB_O);
			if (!py_job_o) {
				log_err(PBSE_INTERNAL, __func__,
					"No job_o parameter found for event!");
				goto event_to_request_exit;
			}

			/* Need to check if ATTR_v (i.e. Variable_list) changed, and if */
			/* so, needs to be sent with the MODIFYJOB request.	            */

			if (PyObject_HasAttrString(py_job, ATTR_v))
				py_varlist = PyObject_GetAttrString(py_job, ATTR_v); /* NEW */

			if (PyObject_HasAttrString(py_job_o, ATTR_v))
				py_varlist_o = PyObject_GetAttrString(py_job_o, ATTR_v);
			/* NEW */

			if ((py_varlist != NULL) && (py_varlist_o != NULL) &&
			    (PyObject_RichCompareBool(py_varlist, py_varlist_o, Py_EQ))) {
				/* upon success, py_job decreases ref count to py_varlist */
				(void) PyObject_SetAttrString(py_job, ATTR_v, Py_None);
			}

			Py_CLEAR(py_varlist);
			Py_CLEAR(py_varlist_o);

			if (pbs_python_populate_svrattrl_from_python_class(py_job,
									   &((struct rq_manage *) (req_params->rq_manage))->rq_attr, NULL, 0) == -1) {
				goto event_to_request_exit;
			}
			print_svrattrl_list("pbs_populate_svrattrl_from_python_class==>", &((struct rq_manage *) (req_params->rq_manage))->rq_attr);
			break;
		case HOOK_EVENT_MOVEJOB:

			py_job = _pbs_python_event_get_param(PY_EVENT_PARAM_JOB);
			if (!py_job) {
				log_err(PBSE_INTERNAL, __func__,
					"No job parameter found for event!");
				goto event_to_request_exit;
			}
			queue = pbs_python_object_get_attr_string_value(py_job,
									ATTR_queue);
			if (queue)
				strcpy(((struct rq_move *) (req_params->rq_move))->rq_destin, queue);

			break;
		case HOOK_EVENT_PERIODIC:
			py_vnodelist = _pbs_python_event_get_param(PY_EVENT_PARAM_VNODELIST);
			if (!py_vnodelist) {
				log_err(PBSE_INTERNAL, __func__,
					"No vnode list parameter found for event!");
				goto event_to_request_exit;
			}

			if (!PyDict_Check(py_vnodelist)) {
				log_err(PBSE_INTERNAL, __func__,
					"vnode list parameter not a dictionary!");
				goto event_to_request_exit;
			}

			py_attr_keys = PyDict_Keys(py_vnodelist); /* NEW ref */

			if (py_attr_keys == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "Failed to obtain object's '%s' keys",
					 PY_EVENT_PARAM_VNODE);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto event_to_request_exit;
			}

			if (!PyList_Check(py_attr_keys)) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "object's '%s' keys is not a list!",
					 PY_EVENT_PARAM_VNODE);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				Py_CLEAR(py_attr_keys);
				goto event_to_request_exit;
			}

			num_attrs = PyList_Size(py_attr_keys);
			for (i = 0; i < num_attrs; i++) {

				key_str = strdup(pbs_python_list_get_item_string_value(
					py_attr_keys, i));

				if ((key_str == NULL) || (key_str[0] == '\0')) {
					if (key_str != NULL) {
						free(key_str);
						key_str = NULL;
					}
					continue;
				}

				py_vnode = PyDict_GetItemString(py_vnodelist, key_str);

				if (py_vnode == NULL) {
					snprintf(log_buffer, sizeof(log_buffer),
						 "failed to get attribute '%s' value", key_str);
					log_err(PBSE_INTERNAL, __func__, log_buffer);
					Py_CLEAR(py_attr_keys);
					free(key_str);
					key_str = NULL;
					goto event_to_request_exit;
				}

				if (pbs_python_populate_svrattrl_from_python_class(py_vnode,
										   (pbs_list_head *) (req_params->vns_list), key_str, 1) == -1) {
					snprintf(log_buffer, sizeof(log_buffer),
						 "failed to populate svrattrl with key '%s' value", key_str);
					log_err(PBSE_INTERNAL, __func__, log_buffer);
					Py_CLEAR(py_attr_keys);
					free(key_str);
					key_str = NULL;
					goto event_to_request_exit;
				}
				free(key_str);
			}
			Py_CLEAR(py_attr_keys);

			py_resvlist = _pbs_python_event_get_param(PY_EVENT_PARAM_RESVLIST);
			if (!py_resvlist) {
				log_err(PBSE_INTERNAL, __func__,
					"No reservation list parameter found for event!");
				goto event_to_request_exit;
			}

			if (!PyDict_Check(py_resvlist)) {
				log_err(PBSE_INTERNAL, __func__,
					"reservation list parameter not a dictionary!");
				goto event_to_request_exit;
			}

			py_attr_keys = PyDict_Keys(py_resvlist); /* NEW ref */

			if (py_attr_keys == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "Failed to obtain object's '%s' keys",
					 PY_EVENT_PARAM_RESVLIST);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				goto event_to_request_exit;
			}

			if (!PyList_Check(py_attr_keys)) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "object's '%s' keys is not a list!",
					 PY_EVENT_PARAM_RESVLIST);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				Py_CLEAR(py_attr_keys);
				goto event_to_request_exit;
			}

			num_attrs = PyList_Size(py_attr_keys);

			for (i = 0; i < num_attrs; i++) {

				key_str = strdup(pbs_python_list_get_item_string_value(
					py_attr_keys, i));

				if ((key_str == NULL) || (key_str[0] == '\0')) {
					if (key_str != NULL) {
						free(key_str);
					}
					continue;
				}

				py_job = PyDict_GetItemString(py_resvlist,
							      key_str); /* borrowed */

				if (py_job == NULL) {
					snprintf(log_buffer, sizeof(log_buffer) - 1,
						 "failed to get attribute '%s' value", key_str);
					log_err(PBSE_INTERNAL, __func__, log_buffer);
					Py_CLEAR(py_attr_keys);
					free(key_str);
					goto event_to_request_exit;
				}

				if (pbs_python_populate_svrattrl_from_python_class(py_job,
										   (pbs_list_head *) (req_params->resv_list), key_str, 1) == -1) {
					snprintf(log_buffer, sizeof(log_buffer) - 1,
						 "failed to populate svrattrl with key '%s' value", key_str);
					log_err(PBSE_INTERNAL, __func__, log_buffer);
					Py_CLEAR(py_attr_keys);
					free(key_str);
					goto event_to_request_exit;
				}

				free(key_str);
			}
			Py_CLEAR(py_attr_keys);

			break;
		default:
			log_err(PBSE_INTERNAL, __func__,
				"unexpected hook event type");
			goto event_to_request_exit;
	}
	rc = 0;
event_to_request_exit:
	hook_perf_stat_stop(perf_label, perf_action, 0);
	return rc;
}

/**
 * @brief
 *  	Allows the current PBS event request to proceed.
 */
void
_pbs_python_event_accept(void)
{

	hook_pbsevent_accept = TRUE;
}

/**
 * @brief
 *  	Reject the current PBS event request.
 */
void
_pbs_python_event_reject(char *msg)
{

	hook_pbsevent_accept = FALSE;
	memset(hook_pbsevent_reject_msg, '\0', HOOK_MSG_SIZE);
	if (msg) {
		snprintf(hook_pbsevent_reject_msg, HOOK_MSG_SIZE - 1, "%s", msg);
	}
}

/**
 * @brief
 * 	Returns the message string supplied in the hook script when it rejected
 * 	an event request.
 */
char *
_pbs_python_event_get_reject_msg(void)
{
	if (hook_pbsevent_reject_msg[0] != '\0')
		return ((char *) hook_pbsevent_reject_msg);
	return NULL;
}

/**
 * @brief
 * 	Returns the value of the event accept flag (1 for TRUE or 0 for FALSE).
 */
int
_pbs_python_event_get_accept_flag(void)
{
	return (hook_pbsevent_accept);
}

/**
 * @brief
 * 	Sets a global flag that says modifications to the PBS Python
 * 	attributes are allowed.
 */
void
_pbs_python_event_param_mod_allow(void)
{

	hook_pbsevent_stop_processing = FALSE;
}

/**
 * @brief
 * 	Sets a global flag that says any more modifications to the PBS Python
 * 	attributes would be disallowed.
 */
void
_pbs_python_event_param_mod_disallow(void)
{

	hook_pbsevent_stop_processing = TRUE;
}

/**
 * @brief
 * 	Returns the value (0 or 1) of the global flag that says whether or not
 * 	modifications to the PBS Python attributes is allowed.
 */
int
_pbs_python_event_param_get_mod_flag(void)
{
	return (hook_pbsevent_stop_processing);
}

/**
 * @brief
 * 	Sets the value of the attribute 'name' of the current Python Object event
 * 	to a string 'value'. The descriptor for the attribute will take care of
 * 	converting to an actual type.
 *
 * @param[in] name - attribute name
 * @param[in] value - attr value
 *
 * @param[in]
 * @return	int
 * @retval	0	success
 * @retval	-1	error
 */
int
_pbs_python_event_set_attrval(char *name, char *value)
{
	int rc;

	if ((name == NULL) || (value == NULL)) {
		log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER,
			  LOG_ERR, __func__, "Got a NULL 'name' or 'value'");
		return -1;
	}

	if (py_hook_pbsevent == NULL) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1,
			 "can't set event attribute %s = %s: event is unset", name, value);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER,
			  LOG_ERR, __func__, log_buffer);
		return -1;
	}

	rc = pbs_python_object_set_attr_string_value(py_hook_pbsevent, name, value);

	if (rc == -1) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1,
			 "failed to set event attribute %s = %s", name, value);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER,
			  LOG_ERR, __func__, log_buffer);
		return -1;
	}

	return (0);
}
/**
 * @brief
 * 	Gets the value of the attribute 'name' of the current Python Object event
 * 	as a string.
 *
 * @param[in] name - attr name
 *
 * @return	char *
 * @retval	attr name	success
 * @retval	NULL  		if it doesn't find one.
 */
char *
_pbs_python_event_get_attrval(char *name)
{
	PyObject *py_attrval = NULL;
	char *attrval_str = NULL;

	if (name == NULL) {
		log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER,
			  LOG_ERR, __func__, "Got a NULL 'name'");
		return NULL;
	}

	if (py_hook_pbsevent == NULL) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1,
			 "can't get event attribute %s: event is unset", name);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';

		log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER,
			  LOG_ERR, __func__, log_buffer);
		return NULL;
	}

	if (!PyObject_HasAttrString(py_hook_pbsevent, name)) {
		return NULL;
	}

	py_attrval = PyObject_GetAttrString(py_hook_pbsevent, name); /* NEW */

	if (py_attrval) {
		PyArg_Parse(py_attrval, "s", &attrval_str);
		Py_DECREF(py_attrval);
	}
	return (attrval_str);
}

/*
 * --------------------- MODULE METHODS ---------------------------------
 */
/*
 * Create a queue object and stuff the attributes
 */
/* pbs_v1_module method get_queue */

const char pbsv1mod_meth_get_queue_doc[] =
	"get_queue(strName)\n\
  where:\n\
\n\
   strName:  name of the queue to retrieve\n\
\n\
  returns:\n\
         instance of _queue type representing queue 'strName'\n\
";

/**
 * @brief
 *	Create a queue object and stuff the attributes.
 *
 * @par	strName:  name of the queue to retrieve.
 *
 * @return	PyObject*
 * @retval	instance of _queue type representing queue 'strName'	success
 * @retval	NULL							error
 */
PyObject *
pbsv1mod_meth_get_queue(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {"name", NULL};

	char *name = NULL;
	PyObject *py_que = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "s:get_queue",
					 kwlist,
					 &name)) {
		return NULL;
	}

	hook_set_mode = C_MODE;
	py_que = _pps_helper_get_queue(NULL, name, HOOK_PERF_FUNC);
	hook_set_mode = PY_MODE;

	if (py_que != NULL)
		return py_que;
	else
		Py_RETURN_NONE;
}

/*
 * Create a job object and stuff the attributes
 */
/* pbs_v1_module method get_job */

const char pbsv1mod_meth_get_job_doc[] =
	"get_job(strName, strQueue)\n\
  where:\n\
\n\
   strName:  name of the job to retrieve\n\
\n\
   strQueue:  name of the queue where job belongs to\n\
\n\
  returns:\n\
         instance of _job type representing job 'strName' in 'strQueue'; or\n\
         None if no job was found or job 'strName' is not part of 'strQueue'\n\
";

/**
 * @brief
 *	Create a job object and stuff the attributes
 *
 * @par Note:
 *	strName:  name of the job to retrieve\n\
 *	strQueue:  name of the queue where job belongs to\n\
 *
 * @return	PyObject*
 * @retval	instance of _job type representing job 'strName' in 'strQueue'		success
 * @retval	None if no job was found or job 'strName' is not part of 'strQueue'	error
 */
PyObject *
pbsv1mod_meth_get_job(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {PY_TYPE_JOB, PY_TYPE_QUEUE, NULL};

	char *jname = NULL;
	char *qname = NULL;
	PyObject *py_job = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "s|s:get_job",
					 kwlist,
					 &jname,
					 &qname)) {
		return NULL;
	}

	hook_set_mode = C_MODE;
	py_job = _pps_helper_get_job(NULL, jname, qname, HOOK_PERF_FUNC);
	hook_set_mode = PY_MODE;

	if (py_job != NULL)
		return py_job;
	else
		Py_RETURN_NONE;
}

/*
 * Create a resv object and stuff the attributes
 */
/* pbs_v1_module method get_resv */

const char pbsv1mod_meth_get_resv_doc[] =
	"get_resv(strName)\n\
  where:\n\
\n\
   strName:  name of the resv to retrieve\n\
\n\
  returns:\n\
         instance of _resv type representing resv 'strName'; or\n\
         None if no resv was found.\n\
";

/**
 * @brief
 *	Create a resv object and stuff the attributes
 *
 * @par	Note:
 *	strName:  name of the resv to retrieve.
 *
 * @return	PyObject *
 * @retval	instance of _resv type representing resv 'strName'	success
 * @retval	None							if no resv was found.
 *
 */
PyObject *
pbsv1mod_meth_get_resv(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {PY_TYPE_RESV, NULL};

	char *rname = NULL;
	PyObject *py_resv = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "s:get_resv",
					 kwlist,
					 &rname)) {
		return NULL;
	}

	hook_set_mode = C_MODE;
	py_resv = _pps_helper_get_resv(NULL, rname, HOOK_PERF_FUNC);
	hook_set_mode = PY_MODE;

	if (py_resv != NULL)
		return py_resv;
	else
		Py_RETURN_NONE;
}

/*
 * Create a vnode object and stuff the attributes
 */
/* pbs_v1_module method get_vnode */

const char pbsv1mod_meth_get_vnode_doc[] =
	"get_vnode(strVname)\n\
  where:\n\
\n\
   strVname:  name of the vnode to retrieve\n\
\n\
  returns:\n\
         instance of _vnode type representing vnode 'strVname'; or\n\
         None if no vnode was found.\n\
";
/**
 * @brief
 *	This is the C->Python wrapper program to _pps_helper_get_vnode()
 *	that is callable in Python script.
 *
 * @param[in]	args[1]	- the vnode name passed by the Python function invoking
 *			this function.
 *
 * @return	PyObject *	- the Python vnode object corresponding
 *				to args[1].
 *
 */
PyObject *
pbsv1mod_meth_get_vnode(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {PY_TYPE_VNODE, NULL};

	char *vname = NULL;
	PyObject *py_vnode = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "s:get_vnode",
					 kwlist,
					 &vname)) {
		return NULL;
	}

	hook_set_mode = C_MODE;
	py_vnode = _pps_helper_get_vnode(NULL, vname, HOOK_PERF_FUNC);
	hook_set_mode = PY_MODE;

	if (py_vnode != NULL)
		return py_vnode;
	else
		Py_RETURN_NONE;
}

/*
 * Create a server object and stuff the attributes
 */
/* pbs_v1_module method server */

const char pbsv1mod_meth_server_doc[] =
	"server([strName])\n\
      [strName] is an optional argument referring the server host name to\n\
             query. Use of this argument is currently not implemented.\n\
    returns:\n\
         instance of _server type representing the local server if\n\
	 'strName' is not given.\n\
 Methods:\n\
   Obtain information about the local server:\n\
      s = pbs.server()\n\
      s.pbs_version	 -> returns the PBS version\n\
      s.job(22.fest)	 -> returns a job in server\n\
\n\
   Obtain information about a queue in the local server:\n\
      q = s.queue(workq)\n\
      q.total_jobs	 -> returns the # of jobs on workq\n\
      q.job(22.fest)	 -> returns a job in the queue\n\
";

/**
 * @brief
 *	Create a server object and stuff the attributes.
 *
 * @par	Note:
 *	[strName] is an optional argument referring the server host name to
 *	query. Use of this argument is currently not implemented.
 *
 * @return	PyObject *
 * @retval	instance of _server type representing the local server if strname not given
 *
 */

PyObject *
pbsv1mod_meth_server(void)
{
	PyObject *py_svr = NULL;
	hook_set_mode = C_MODE;
	py_svr = _pps_helper_get_server(HOOK_PERF_FUNC);
	hook_set_mode = PY_MODE;
	return (py_svr);
}

const char pbsv1mod_meth_in_python_mode_doc[] =
	"in_python_mode()\n\
\n\
  returns:\n\
         True if hook_set_mode is PY_MODE; False, otherwise.\n\
  	 This is an internal function.\n\
";

/**
 * @brief
 *	check hook_set_mode.
 *
 * @return	PyObject *
 * @retval	Py_True	if hook_set_mode is PY_MODE
 * @retval	Py_False	otherwise
 *
 * @par	This is an internal function
 */
PyObject *
pbsv1mod_meth_in_python_mode(void)
{
	PyObject *ret;
	ret = (hook_set_mode == PY_MODE) ? Py_True : Py_False;
	Py_INCREF(ret);
	return (ret);
}

const char pbsv1mod_meth_in_site_hook_doc[] =
	"in_site_hook()\n\
\n\
  returns:\n\
         True if executing under a HOOK_SITE hook; False, otherwise.\n\
	 This is an internal function.\n\
";

/**
 * @brief
 *	check whether hook_site.
 *
 * @return	PyObject *
 * @retval	Py_True	if executing under a HOOK_SITE hook
 * @retval	Py_False	otherwise
 */
PyObject *
pbsv1mod_meth_in_site_hook(void)
{
	PyObject *ret;
	char *hook_type;

	hook_type = _pbs_python_event_get_attrval(PY_EVENT_HOOK_TYPE);

	if ((hook_type != NULL) && (strcmp(hook_type, HOOKSTR_SITE) == 0))
		ret = Py_True;
	else
		ret = Py_False;

	Py_INCREF(ret);
	return (ret);
}

const char pbsv1mod_meth_is_attrib_val_settable_doc[] =
	"is_attrib_val_settable(self,owner,value)\n\
  where:\n\
\n\
   self    :  obj attribute name (e.g. Resource_List or Priority)\n\
   owner   :  obj resource name of self, if it is a resource (e.g. ncpus)\n\
   value   :  obj value being set to\n\
\n\
  returns:\n\
         True, False, or an exception\n\
";

/**
 *
 * @brief
 *	Returns Python True if some object attribute name/resource name
 *	is allowed to set its value.
 *
 * @param[in]	self	- owning object
 * @param[in]	args[1]	- object attribute name (ex. Resource_List/Priority)
 * 		args[2]	- object resource name (ex. ncpus)
 * 		args[3]	- object value being set to
 * @param[in]	kwds	- keywords to objects mappings
 *
 * @return	PyObject *
 * @retval	Python True	- if settable
 * @retval	Python False	- if not settable
 * @retval	NULL		- error
 *
 */
PyObject *
pbsv1mod_meth_is_attrib_val_settable(PyObject *self, PyObject *args, PyObject *kwds)
{
	PyObject *ret;
	static char *kwlist[] = {"self", "owner", "value", NULL};

	PyObject *py_self = NULL;
	PyObject *py_owner = NULL;
	PyObject *py_value = NULL;

	char *name = NULL;     /* malloc   */
	char *resource = NULL; /* malloced */
	char *pstr;

	PyObject *py_value_type = NULL;
	PyObject *py_value_type_0 = NULL;
	PyObject *py_value_type_0_derived = NULL;
	int readonly = 0;
	int is_resource = 0;
	unsigned int event;
	int attr_idx = -1;
	resource_def *rscdef = NULL;

	int rc = 1;

	if (hook_set_mode == C_MODE) { /* can set anything */
		ret = Py_True;
		Py_INCREF(ret);
		return (ret);
	}

	memset((char *) log_buffer, '\0', LOG_BUF_SIZE);

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "OOO:is_attrib_val_settable",
					 kwlist,
					 &py_self,
					 &py_owner,
					 &py_value)) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1,
			 "in func %s, PyArg_ParseTupleAndKeywords failed!",
			 __func__);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		PyErr_SetString(PyExc_SyntaxError, log_buffer);
		goto IAVS_ERROR_EXIT;
	}

	/* at this point, we can have an unset attribute (without _is_resource) */
	/* or a set attribute; or an unset resource (without is_resource with  */
	/* 'name" as attribute), or a set resource */

	readonly = 0;
	if (PyObject_HasAttrString(py_owner, "_readonly")) {
		readonly = pbs_python_object_get_attr_integral_value(py_owner,
								     PY_READONLY_FLAG);
	}

	is_resource = 0;
	if (PyObject_HasAttrString(py_self, PY_DESCRIPTOR_IS_RESOURCE)) {
		is_resource = pbs_python_object_get_attr_integral_value(py_self,
									PY_DESCRIPTOR_IS_RESOURCE);
	}

	if (is_resource == 1) {

		if (PyObject_HasAttrString(py_owner, PY_RESOURCE_NAME)) {
			/* e.g. Resource_List */
			pstr = pbs_python_object_get_attr_string_value(py_owner,
								       PY_RESOURCE_NAME);
			if (pstr) {
				if ((name = strdup(pstr)) == NULL) {
					snprintf(log_buffer, LOG_BUF_SIZE - 1, "in func %s, Unable to allocate Memory!\n", __func__);
					PyErr_SetString(PyExc_MemoryError, log_buffer);
					goto IAVS_ERROR_EXIT;
				}
			}
		}

		if (PyObject_HasAttrString(py_self, PY_DESCRIPTOR_NAME)) {
			/* e.g. ncpus */
			pstr = pbs_python_object_get_attr_string_value(py_self,
								       PY_DESCRIPTOR_NAME);
			if (pstr) {
				if ((resource = strdup(pstr)) == NULL) {
					snprintf(log_buffer, LOG_BUF_SIZE - 1, "in func %s, Unable to allocate Memory!\n", __func__);
					PyErr_SetString(PyExc_MemoryError, log_buffer);
					goto IAVS_ERROR_EXIT;
				}
			}
		}
	} else { /* 0 or -1 (not yet set, assume attribute) */
		if (PyObject_HasAttrString(py_self, PY_DESCRIPTOR_NAME)) {
			/* e.g. ncpus */
			pstr = pbs_python_object_get_attr_string_value(py_self,
								       PY_DESCRIPTOR_NAME);
			if (pstr) {
				if ((name = strdup(pstr)) == NULL) {
					snprintf(log_buffer, LOG_BUF_SIZE - 1, "in func %s, Unable to allocate Memory!\n", __func__);
					PyErr_SetString(PyExc_MemoryError, log_buffer);
					goto IAVS_ERROR_EXIT;
				}
			}
		}
		if ((resource = strdup("")) == NULL) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1, "in func %s, Unable to allocate Memory!\n", __func__);
			PyErr_SetString(PyExc_MemoryError, log_buffer);
			goto IAVS_ERROR_EXIT;
		}
	}

	if (!name || !resource) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1,
			 "name and/or resource is NULL: name=%s resource=%s",
			 (name ? name : "null"), (resource ? resource : "null"));
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		PyErr_SetString(PyExc_AssertionError, log_buffer);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto IAVS_ERROR_EXIT;
	}

	/* do some sanity checking */

	if (name[0] == '\0') {
		PyErr_SetString(PyExc_AssertionError, "No attribute name found");
		goto IAVS_ERROR_EXIT;
	}

	/* specia case: bypass a pbs_resource with default value */
	if ((is_resource == 1) && (strcmp(name, PY_RESOURCE_GENERIC_VALUE) == 0) && (strcmp(resource, PY_RESOURCE_NAME) == 0)) {
		rc = 0; /* return True */
		goto IAVS_OK_EXIT;
	}

	if (hook_pbsevent_stop_processing) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1,
			 "Not permitted to modify attributes (name=%s,res=%s): an event.accept() or event.reject() already called.", name, resource);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		PyErr_SetString(PyExc_AssertionError, log_buffer);
		/* throw an exception */
		goto IAVS_ERROR_EXIT;
	}

	if (readonly) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1,
			 "attribute '%s' is part of a readonly object", name);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		PyErr_SetString(
			pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
			log_buffer);
		/* throw an exception */
		goto IAVS_ERROR_EXIT;
	}
	if (!py_hook_pbsevent) {
		PyErr_SetString(PyExc_AssertionError, "Event not found");
		goto IAVS_ERROR_EXIT;
	}
	/* Variable_List can not be set in a python script, only individual */
	/* environment=variable settings can via dictionary setitem. */
	if (strcmp(name, ATTR_v) == 0) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1,
			 "attribute '%s' cannot be directly set.", name);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		PyErr_SetString(
			pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
			log_buffer);
		goto IAVS_ERROR_EXIT;
	}

	event = pbs_python_object_get_attr_integral_value(py_hook_pbsevent, "type");
	switch (event) {
		case HOOK_EVENT_QUEUEJOB:
		case HOOK_EVENT_POSTQUEUEJOB:
		case HOOK_EVENT_MODIFYJOB:
			if (!PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_JOB_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_RESC_IDX].t_class)) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1,
					 "Can only set job,resource attributes under %s event.", ((event == HOOK_EVENT_QUEUEJOB) ? "queuejob" : "modifyjob"));
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_AssertionError, log_buffer);
				goto IAVS_ERROR_EXIT;
			}

			if (in_string_list(name, ',', PY_PYTHON_DEFINED_ATTRIBUTES)) {
				if ((strcmp(name, PY_RESOURCE_NAME) == 0) || (strcmp(name, PY_RESOURCE_HAS_VALUE) == 0)) {
					/* matched a special, internal-only attribute */
					/* holding the resc name, has_value (e.g. "Resource_List") */
					goto IAVS_OK_EXIT;
				}

				snprintf(log_buffer, LOG_BUF_SIZE - 1, "attribute '%s' is readonly", name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class, log_buffer);
				goto IAVS_ERROR_EXIT;
			}

			if ((event == HOOK_EVENT_QUEUEJOB) &&
			    strcmp(name, ATTR_queue) == 0) {
				break; /* ok to modify queue under qsub */
			}

			attr_idx = find_attr(job_attr_idx, job_attr_def, name);
			if (attr_idx == -1) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1, "job attribute '%s' not found", name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_LookupError, log_buffer);
				goto IAVS_ERROR_EXIT;
			}

			if ((job_attr_def[attr_idx].at_flags & ATR_DFLAG_HOOK_SET) == 0) {
				/* ATTR_J, ATTR_cred override any read-only permission seen */
				if ((strcmp(name, ATTR_J) != 0) &&
				    (strcmp(name, ATTR_cred) != 0)) {
					snprintf(log_buffer, LOG_BUF_SIZE - 1, "job attribute '%s' is readonly", name);
					log_buffer[LOG_BUF_SIZE - 1] = '\0';
					PyErr_SetString(pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class, log_buffer);
					goto IAVS_ERROR_EXIT;
				}
			}
			if (resource && (resource[0] != '\0')) {
				rscdef = find_resc_def(svr_resc_def, resource);
				if (!rscdef) {
					snprintf(log_buffer, LOG_BUF_SIZE - 1, "resource attribute '%s' not found", resource);
					log_buffer[LOG_BUF_SIZE - 1] = '\0';
					PyErr_SetString(PyExc_LookupError, log_buffer);
					goto IAVS_ERROR_EXIT;
				}
				if ((rscdef->rs_flags & ATR_DFLAG_HOOK_SET) == 0) {
					snprintf(log_buffer, LOG_BUF_SIZE - 1, "resource attribute '%s' is readonly", name);
					log_buffer[LOG_BUF_SIZE - 1] = '\0';
					PyErr_SetString(pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class, log_buffer);
					goto IAVS_ERROR_EXIT;
				}
			} else if (ATTR_IS_RESC(&job_attr_def[attr_idx])) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1, "can't set the head resource '%s' directly ", name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_AssertionError, log_buffer);
				goto IAVS_ERROR_EXIT;
			}

			break;
		case HOOK_EVENT_EXECJOB_BEGIN:
		case HOOK_EVENT_EXECJOB_PROLOGUE:
		case HOOK_EVENT_EXECJOB_EPILOGUE:
		case HOOK_EVENT_EXECJOB_END:
		case HOOK_EVENT_EXECJOB_ABORT:
		case HOOK_EVENT_EXECJOB_POSTSUSPEND:
		case HOOK_EVENT_EXECJOB_PRERESUME:
		case HOOK_EVENT_EXECJOB_PRETERM:
			if (!PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_JOB_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_RESC_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_VNODE_IDX].t_class)) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1, "Can only set job,resource,vnode attributes under %s event.", "mom hook");
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_AssertionError, log_buffer);
				goto IAVS_ERROR_EXIT;
			}
			break;

		case HOOK_EVENT_EXECJOB_LAUNCH:
			if (!PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_EVENT_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_JOB_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_RESC_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_VNODE_IDX].t_class)) {
				snprintf(log_buffer, LOG_BUF_SIZE,
					 "Can only set progname, argv, env event parameters as well as job, resource, vnode under %s hook.", HOOKSTR_EXECJOB_LAUNCH);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_AssertionError, log_buffer);
				goto IAVS_ERROR_EXIT;
			}
			break;

		case HOOK_EVENT_EXECJOB_ATTACH:
			PyErr_SetString(PyExc_AssertionError, "nothing is settable inside an execjob_attach hook!");
			goto IAVS_ERROR_EXIT;

		case HOOK_EVENT_EXECJOB_RESIZE:
			PyErr_SetString(PyExc_AssertionError, "nothing is settable inside an execjob_resize hook!");
			goto IAVS_ERROR_EXIT;

		case HOOK_EVENT_EXECHOST_PERIODIC:
			if (!PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_VNODE_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_JOB_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_RESC_IDX].t_class)) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1,
					 "Can only set node,resource,job attributes under %s event.", (event == HOOK_EVENT_EXECHOST_PERIODIC) ? HOOKSTR_EXECHOST_PERIODIC : HOOKSTR_EXECHOST_STARTUP);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_AssertionError, log_buffer);
				goto IAVS_ERROR_EXIT;
			}

			break;
		case HOOK_EVENT_EXECHOST_STARTUP:
			if (!PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_VNODE_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_RESC_IDX].t_class)) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1,
					 "Can only set node,resource attributes under %s event.", (event == HOOK_EVENT_EXECHOST_PERIODIC) ? HOOKSTR_EXECHOST_PERIODIC : HOOKSTR_EXECHOST_STARTUP);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_AssertionError, log_buffer);
				goto IAVS_ERROR_EXIT;
			}

			break;
		case HOOK_EVENT_RESVSUB:
		case HOOK_EVENT_MODIFYRESV:
			if (!PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_RESV_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_RESC_IDX].t_class)) {
				PyErr_SetString(PyExc_AssertionError, "Can only set job attributes under resvsub event.");
				goto IAVS_ERROR_EXIT;
			}
			if (in_string_list(name, ',', PY_PYTHON_DEFINED_ATTRIBUTES)) {
				if ((strcmp(name, PY_RESOURCE_NAME) == 0) || (strcmp(name, PY_RESOURCE_HAS_VALUE) == 0)) {
					/* matched a special, internal-only attribute */
					/* holding the resc name, has_value (e.g. "Resource_List") */
					goto IAVS_OK_EXIT;
				}
				snprintf(log_buffer, LOG_BUF_SIZE - 1, "attribute '%s' is readonly", name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class, log_buffer);
				goto IAVS_ERROR_EXIT;
			}
			attr_idx = find_attr(resv_attr_idx, resv_attr_def, name);
			if (attr_idx == -1) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1, "resv attribute '%s' not found", name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_LookupError, log_buffer);
				goto IAVS_ERROR_EXIT;
			}

			if ((resv_attr_def[attr_idx].at_flags & ATR_DFLAG_HOOK_SET) == 0) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1, "resv attribute '%s' is readonly", name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class, log_buffer);
				goto IAVS_ERROR_EXIT;
			}
			if (resource && (resource[0] != '\0')) {
				rscdef = find_resc_def(svr_resc_def, resource);

				if (!rscdef) {
					snprintf(log_buffer, LOG_BUF_SIZE - 1, "resv resource attribute '%s' not found", resource);
					log_buffer[LOG_BUF_SIZE - 1] = '\0';
					PyErr_SetString(PyExc_LookupError, log_buffer);
					goto IAVS_ERROR_EXIT;
				}

				if ((rscdef->rs_flags & ATR_DFLAG_HOOK_SET) == 0) {
					snprintf(log_buffer, LOG_BUF_SIZE - 1, "resv resource attribute '%s' is readonly", resource);
					log_buffer[LOG_BUF_SIZE - 1] = '\0';
					PyErr_SetString(pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class, log_buffer);
					goto IAVS_ERROR_EXIT;
				}
			} else if (ATTR_IS_RESC(&resv_attr_def[attr_idx])) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1, "can't set the head resv resource '%s' directly ", name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_AssertionError, log_buffer);
				goto IAVS_ERROR_EXIT;
			}

			break;
		case HOOK_EVENT_MOVEJOB:

			if (!PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_JOB_IDX].t_class)) {
				PyErr_SetString(PyExc_AssertionError, "Can only set job attributes under MOVEJOB event.");
				goto IAVS_ERROR_EXIT;
			}

			if (strcmp(name, ATTR_queue) != 0) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1, "Can only set job's 'queue' attribute under MOVEJOB event - "
								       "got <%s>",
					 name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_AssertionError, log_buffer);
				goto IAVS_ERROR_EXIT;
			}
			break;
		case HOOK_EVENT_RUNJOB:

			if (!PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_JOB_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_RESC_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_VNODE_IDX].t_class)) {
				PyErr_SetString(PyExc_AssertionError, "Can only set job,vnode attributes under RUNJOB event.");
				goto IAVS_ERROR_EXIT;
			}

			/*
			 * If its a job object, then 'name' below will be refer to the job
			 * attribute name  for example Output_Path, Priority, etc. If it's
			 * a resource object, then 'name'  will actually refer to the jobs
			 * resource list like Resource_List, resources_used, etc...
			 *  Resource_List is fine since  it is listed in the
			 * runjob_modifiable_jobattrs (as ATTR_l). but if its resources_used
			 * for example, which is not in the runjob_modifiable_jobatrs, then if
			 * its not the vnode object, then issue error.
			 * If the parent is of vnode object but if 'name' is not the list of
			 * runjob_modifiable_vnattrs, then issue an error.
			 */
			if (!in_string_list(name, '|', runjob_modifiable_jobattrs) &&
			    (!PyObject_IsInstance(py_owner,
						  pbs_python_types_table[PP_VNODE_IDX].t_class) ||
			     !in_string_list(name, '|', runjob_modifiable_vnattrs) != 0)) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1,
					 FMT_RUNJOB_ERRMSG, runjob_modifiable_jobattrs,
					 runjob_modifiable_vnattrs, name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_AssertionError, log_buffer);
				goto IAVS_ERROR_EXIT;
			}

			break;
		case HOOK_EVENT_PERIODIC:
			if (!PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_VNODE_IDX].t_class) &&
			    !PyObject_IsInstance(py_owner,
						 pbs_python_types_table[PP_RESC_IDX].t_class)) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1,
					 "Can only set node,resource attributes under %s event.", HOOKSTR_PERIODIC);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_AssertionError, log_buffer);
				goto IAVS_ERROR_EXIT;
			}
			break;
		default:
			PyErr_SetString(PyExc_AssertionError, "Unexpected event");
			goto IAVS_ERROR_EXIT;
	}

	/* Now check validity of the value */

	if (PyObject_HasAttrString(py_self, PY_DESCRIPTOR_VALUE_TYPE)) {
		py_value_type = PyObject_GetAttrString(py_self,
						       PY_DESCRIPTOR_VALUE_TYPE); /* NEW */
		if (!PyTuple_Check(py_value_type)) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "For name=%s res=%s, value type is not a tuple",
				 name, resource);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			PyErr_SetString(PyExc_AssertionError, log_buffer);
			goto IAVS_ERROR_EXIT;
		}
	}

	if (py_value_type) {
		py_value_type_0 = PyTuple_GetItem(py_value_type, 0);
	}

	if (py_value_type_0) {
		if (PyObject_HasAttrString(py_value_type_0, PY_CLASS_DERIVED_TYPES)) {
			py_value_type_0_derived =
				PyObject_GetAttrString(py_value_type_0,
						       PY_CLASS_DERIVED_TYPES); /* NEW */
		}
	}

	/* Ok if py_value is None, or py_value is of py_value_type, or */
	/* py_value is of py_value_type_o_derived */
	if ((py_value != Py_None) &&
	    (py_value_type && !PyObject_IsInstance(py_value, py_value_type)) &&
	    (!py_value_type_0_derived || !PyObject_IsInstance(py_value,
							      py_value_type_0_derived))) {
		char cls[STRBUF];
		char att[STRBUF];
		char vtype[STRBUF];
		char dtype[STRBUF];
		int dlen = 0;
		char *the_dtype = NULL;
		char *msgbuf;

		memset(cls, '\0', STRBUF);
		memset(att, '\0', STRBUF);
		memset(vtype, '\0', STRBUF);
		memset(dtype, '\0', STRBUF);

		pstr = pbs_python_object_get_attr_string_value(py_self,
							       PY_DESCRIPTOR_CLASS_NAME);
		if (pstr)
			strncpy(cls, pstr, STRBUF - 1);

		pstr = pbs_python_object_get_attr_string_value(py_self,
							       PY_DESCRIPTOR_NAME);

		if (pstr)
			strncpy(att, pstr, STRBUF - 1);

		if (py_value_type) {
			strncpy(vtype,
				pbs_python_object_str(py_value_type), STRBUF - 1);
		}
		if (py_value_type_0_derived) {
			strncpy(dtype,
				pbs_python_object_str(py_value_type_0_derived), STRBUF - 1);
			the_dtype = dtype;
			dlen = strlen(dtype);
			/* clear extra leading '(' and trailing ',)' in */
			/* <derived_type> value if both appear. */
			if ((dtype[0] == '(') && (dlen >= 2) &&
			    (dtype[dlen - 2] == ',') && (dtype[dlen - 1] == ')')) {
				dtype[dlen - 2] = '\0';
				the_dtype = dtype + 1; /* move past leading '(' */
			}
		}

		pbs_asprintf(&msgbuf,
			     "value for class <%s> attribute <%s> must be 'None' or '%s%s%s'",
			     cls, att, vtype,
			     the_dtype ? "," : "",
			     the_dtype ? the_dtype : "");

		if (is_resource == 1)
			PyErr_SetString(
				pbs_python_types_table[PP_BAD_RESC_VTYPE_ERR_IDX].t_class,
				msgbuf);
		else
			PyErr_SetString(
				pbs_python_types_table[PP_BADATTR_VTYPE_ERR_IDX].t_class,
				msgbuf);
		free(msgbuf);
		goto IAVS_ERROR_EXIT;
	}

	if (strcmp(name, ATTR_a) == 0) {
		long exec_time = 0;
		int ret;

		/* parse floats nicely since time.time() returns floats */
		if (py_value != Py_None) {
			if (PyFloat_Check(py_value)) {
				double ftime;
				ret = PyArg_Parse(py_value, "d", &ftime);
				if (ret != 0)
					exec_time = (long) ftime;
			} else {
				ret = PyArg_Parse(py_value, "l", &exec_time);
			}

			/* if the parse worked but the time is in the past */
			if (ret == 0) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1,
					 "exec_time could not be parsed");
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(
					pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
					log_buffer);
				rc = 1;
				goto IAVS_ERROR_EXIT;
			} else if (exec_time < time(0)) {
				char *str_time = NULL;

				str_time = ctime(&exec_time);
				if (str_time != NULL)
					str_time[strlen(str_time) - 1] = '\0';

				snprintf(log_buffer, LOG_BUF_SIZE - 1,
					 "exec_time '%s' not in the future",
					 (str_time ? str_time : ""));
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(
					pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
					log_buffer);
				rc = 1;
				goto IAVS_ERROR_EXIT;
			}
		}
	} else if (strcmp(name, ATTR_runcount) == 0) {
		long runcount;

		if ((PyArg_Parse(py_value, "l", &runcount) == 0) ||
		    (runcount < 0)) {

			PyErr_SetString(
				pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
				"run_count value must be >= 0");
			rc = 1;
			goto IAVS_ERROR_EXIT;
		}
	}

	rc = 0;

IAVS_OK_EXIT:
	Py_CLEAR(py_value_type);
	Py_CLEAR(py_value_type_0_derived);
	free(name);
	free(resource);
	ret = (rc == 0) ? Py_True : Py_False;
	Py_INCREF(ret);
	return (ret);

IAVS_ERROR_EXIT:
	Py_CLEAR(py_value_type);
	Py_CLEAR(py_value_type_0_derived);
	free(name);
	free(resource);
	return NULL;
}

/*
 * Methods related to  hook_pbsevent_accept.
 */
/* pbs_v1_module method event */

const char pbsv1mod_meth_event_accept_doc[] =
	"event_accept()\n\
\n\
         make the current event to accept the corresponding request.\n\
";

/**
 * @brief
 *	make the current event to accept the corresponding request.
 *
 */
PyObject *
pbsv1mod_meth_event_accept(void)
{
	_pbs_python_event_accept();
	Py_RETURN_NONE;
}

const char pbsv1mod_meth_event_reject_doc[] =
	"event_reject()\n\
\n\
         make the current event to reject a corresponding request.\n\
";

/**
 * @brief
 *	make the current event to reject a corresponding request.
 */
PyObject *
pbsv1mod_meth_event_reject(PyObject *self, PyObject *args, PyObject *kwds)
{

	static char *kwlist[] = {"message", NULL};
	char *emsg = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "|s:_event_reject",
					 kwlist,
					 &emsg)) {
		return NULL;
	}
	_pbs_python_event_reject(emsg);

	Py_RETURN_NONE;
}

/*
 * Methods related to  hook_pbsevent_stop_processing flag.
 */
/* pbs_v1_module method event */

const char pbsv1mod_meth_event_param_mod_allow_doc[] =
	"event_accept()\n\
\n\
         Allow changes to the event object's param.\n\
";

/**
 * @brief
 *	Allow changes to the event object's param.
 */
PyObject *
pbsv1mod_meth_event_param_mod_allow(void)
{
	_pbs_python_event_param_mod_allow();
	Py_RETURN_NONE;
}

const char pbsv1mod_meth_event_param_mod_disallow_doc[] =
	"event_reject()\n\
\n\
         Disallow changes to the event object's param.\n\
";

/**
 * @brief
 *	Disallow changes to the event object's param.
 */
PyObject *
pbsv1mod_meth_event_param_mod_disallow(PyObject *self)
{
	_pbs_python_event_param_mod_disallow();
	Py_RETURN_NONE;
}

/*
 * Create an event object and stuff the fixed attributes
 */
/* pbs_v1_module method event */

const char pbsv1mod_meth_event_doc[] =
	"event()\n\
\n\
     returns:\n\
         instance of _event type corresponding to the event that the\n\
	 current hook is responding to.\n\
\n\
    Event attributes:\n\
      e.type = {pbs.QUEUEJOB, pbs.MODIFYJOB, pbs.RESVSUB, pbs.MOVEJOB}\n\
      e. requestor - who made the request.\n\
   		   Special values: PBS_Server, Scheduler, pbs_mom\n\
\n\
      e.requestor_host  where the request came from.\n\
      e.hook_name  name of the hook being executed.\n\
\n\
    Event methods:\n\
      e.accept()       : accepts the current event request and raises SystemExit\n\
      e.reject([<msg>]): rejects the current event request and raises SystemExit\n\
		         Where <msg> shows up in STDERR of the originating\n\
			 command, and the PBS daemon log.\n\
\n\
    Event parameters:\n\
\n\
      If e.type is pbs.QUEUEJOB:   e.job\n\
	 e.job.<attribute_name> = <attribute_value>\n\
	 e.job.Priority = 7\n\
	 e.job.Resource_List[walltime] = pbs.duration(00:30:00)\n\
	 e.job.Resource_List[mem] = None\n\
\n\
      If e.type is pbs.MODIFYJOB:   e.job, e.job_o\n\
	 e.job.<attribute_name> = <attribute_value>  (where <attribute_name> != queue)\n\
\n\
      If e.type is pbs.RESVSU:   e.resv\n\
	 e.resv.<attribute_name> = <attribute_value>\n\
	 e.resv.Reserve_Name = Altair##\n\
	 e.resv.Resource_List[select] = pbs.select(5:ncpus=1:mem=2gb)\n\
\n\
      If e.type is pbs.MOVEJOB:   e.job, e.src_queue\n\
	 e.job.queue = pbs.server().queue(<destination_queue>)\n\
";

/**
 * @brief
 *      Returns the current Python event object (i.e. pbs.event()).
 *
 * @return the Python event object.
 * @retval	the current event object
 * @retval	None if no event found
 *
 */

PyObject *
pbsv1mod_meth_event(void)
{
	/* This function gets invoked in Python realm (i.e. hook script),     */
	/* which causes Python to think py_hook_pbsevent was created in that  */
	/* realm. Then under Python world, the returned py_hook_pbsevent      */
	/* gets its reference count decremented by 1 after every access.      */
	/* Continually decrementing the reference count would cause us to     */
	/* crash! So we need to bump up by 1 the count under the C realm, to  */
	/* match th decrements under the Python realm.			      */

	if (py_hook_pbsevent == NULL) {
		Py_RETURN_NONE;
	}
	Py_INCREF(py_hook_pbsevent);
	return (py_hook_pbsevent);
}

/**
 * @brief
 *	check whether job input is valid
 *
 * @return	int
 * @retval	0 	if 'value' is a valid value for job attribute/resource 'name';
 * @retval	1 	if not a valid value;
 * @retval	2 	if did not find a criteria for determining validity of value against 'name'..
 *
 * @par Note:
 *	THis code is taken from the qsub/qalter parsing of input.
 *
 */
static int
is_job_input_valid(char *name, char *value)
{
	/* create a attribute structure to pass to verify functionality */
	struct attropl pattr;
	int verified;
	int err_code;
	char *err_msg = NULL;

	/* create a copy of the attribute value pointers
	 * because the verify_an_attribute function could change it
	 */
	memset(&pattr, 0, sizeof(struct attropl));
	pattr.name = name;
	pattr.value = strdup(value);
	if (pattr.value == NULL) {
		pbs_errno = PBSE_SYSTEM;
		return (1);
	}
	err_code = verify_an_attribute(PBS_BATCH_QueueJob, MGR_OBJ_JOB,
				       MGR_CMD_NONE, &pattr, &verified, &err_msg);
	if (err_msg)
		free(err_msg);
	if (pattr.value)
		free(pattr.value);

	if (!verified)
		return (2);
	else if (err_code)
		return (1);

	return (0);
}

/**
 * @brief
 *	validate the input for reservation
 *
 * @return	int
 * @retval	0 	if 'value' is a valid value for reservation attribute/resource 'name';
 * @retval	1 	if not a valid value;
 * @retval	2 	if did not find a criteria for determining validity of value against 'name'..
 *
 * @par	NOTE:
 *	This code is taken from the pbs_rsub parsing of input.
 *
 */
static int
is_resv_input_valid(char *name, char *value)
{
	/* create a attribute structure to pass to verify functionality */
	struct attropl pattr;
	int verified;
	int err_code;
	char *err_msg = NULL;

	/* create a copy of the attribute name and value pointers
	 * because the verify_an_attribute function could change it
	 */
	memset(&pattr, 0, sizeof(struct attropl));
	pattr.name = name;
	pattr.value = strdup(value);
	if (pattr.value == NULL) {
		pbs_errno = PBSE_SYSTEM;
		return (1);
	}
	err_code = verify_an_attribute(PBS_BATCH_SubmitResv, MGR_OBJ_RESV,
				       MGR_CMD_NONE, &pattr, &verified, &err_msg);
	if (err_msg)
		free(err_msg);
	if (pattr.value)
		free(pattr.value);

	if (!verified)
		return (2);
	else if (err_code)
		return (1);

	return (0);
}

const char pbsv1mod_meth_validate_input_doc[] =
	"validate_input(table_descr, strName, strValue)\n\
\n\
   table_descr  : pbs table to consult: resc, job, queue, server, resv, float\n\
   strName      : an attribute name\n\
   strValue     : the value as a string\n\
\n\
   raises an exception if (strName, strValue) is not valid in 'table_descr'\n\
";

/**
 * @brief
 *	This is callable in a Python script, for validating  the validity
 *	of a PBS tuple: <attrbute_name>, <attribute_value>
 *
 * @param[in]	self - the calling parent object
 * @param[in]	args - the passed arguments:
 *
 *		args[1]	- the validation table("job", "resv", "server", "queue")
 * 		args[2] - the attribute name.
 * 		args[3] - the attribute value.
 * @param[in]	kwds  - Python variable arguments
 *
 * @return	PyObject *
 * @retval	NULL	- failed validation of input
 * @retval	Py_None - successful validation of input
 *
 */
PyObject *
pbsv1mod_meth_validate_input(PyObject *self, PyObject *args, PyObject *kwds)
{

	static char *kwlist[] = {"table_descr", "attribute", "value", NULL};
	char *table = NULL;
	char *name = NULL;
	char *value = NULL;
	char *value_tmp = NULL;
	int attr_idx = -1;
	attribute attr;
	int rc;
	int is_v;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "sss:validate_input",
					 kwlist,
					 &table,
					 &name,
					 &value)) {
		return NULL;
	}

	if (hook_set_mode == C_MODE) {
		/* No need to validate input if not called inside a hook script. */
		Py_RETURN_NONE;
	}

	/*  The *decode* functions below "munges" the value argument, so will use */
	/*  a copy */
	value_tmp = strdup(value);
	if (value_tmp == NULL) {
		PyErr_SetString(PyExc_AssertionError, "strdup of value failed");
		goto validate_input_error_exit;
	}
	if (strcmp(table, PY_TYPE_JOB) == 0) {

		is_v = is_job_input_valid(name, value_tmp);

		if (is_v == 1) {

			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "input value %s not of the right format for '%s'",
				 value, name);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			PyErr_SetString(
				pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
				log_buffer);
			goto validate_input_error_exit;

		} else if (is_v == 2) { /* go to job table to validate */

			attr_idx = find_attr(job_attr_idx, job_attr_def, name);
			if (attr_idx == -1) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1, "job attribute %s not found", name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_LookupError, log_buffer);
				goto validate_input_error_exit;
			}

			clear_attr(&attr, job_attr_def);
			rc = set_attr_generic(&attr, &job_attr_def[attr_idx], value_tmp, NULL, INTERNAL);
			free_attr(job_attr_def, &attr, attr_idx);

			if (rc != 0) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1,
					 "input value %s not of the right format for '%s'",
					 value, name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(
					pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
					log_buffer);
				goto validate_input_error_exit;
			}
		}

	} else if (strcmp(table, PY_RESOURCE) == 0) {
		resource_def *rescdef;

		rescdef = find_resc_def(svr_resc_def, name);
		if (!rescdef) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1, "resource attribute %s not found", name);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			PyErr_SetString(PyExc_LookupError, log_buffer);
			goto validate_input_error_exit;
		}

		if (rescdef->rs_decode) {
			memset((void *) &attr, 0, sizeof(attribute));
			rc = rescdef->rs_decode(&attr, name, NULL, value_tmp);

			if (rescdef->rs_free) {
				rescdef->rs_free(&attr);
			}
			if (rc != 0) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1,
					 "input value %s not of the right format for '%s'",
					 value, name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(
					pbs_python_types_table[PP_BAD_RESC_VALUE_ERR_IDX].t_class,
					log_buffer);
				goto validate_input_error_exit;
			}
		}
	} else if (strcmp(table, PY_TYPE_RESV) == 0) {

		is_v = is_resv_input_valid(name, value_tmp);

		if (is_v == 1) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "input value %s not of the right format for '%s'",
				 value, name);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			PyErr_SetString(
				pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
				log_buffer);
			goto validate_input_error_exit;
		} else if (is_v == 2) { /* go to resv table to validate */

			attr_idx = find_attr(resv_attr_idx, resv_attr_def, name);
			if (attr_idx == -1) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1, "reservation attribute %s not found", name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(PyExc_LookupError, log_buffer);
				goto validate_input_error_exit;
			}

			clear_attr(&attr, resv_attr_def);
			rc = set_attr_generic(&attr, &resv_attr_def[attr_idx], value_tmp, NULL, INTERNAL);
			free_attr(resv_attr_def, &attr, attr_idx);
			if (rc != 0) {
				snprintf(log_buffer, LOG_BUF_SIZE - 1,
					 "input value %s not of the right format for '%s'",
					 value, name);
				log_buffer[LOG_BUF_SIZE - 1] = '\0';
				PyErr_SetString(
					pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
					log_buffer);
				goto validate_input_error_exit;
			}
		}
	} else if (strcmp(table, PY_TYPE_SERVER) == 0) {
		attr_idx = find_attr(svr_attr_idx, svr_attr_def, name);
		if (attr_idx == -1) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1, "server attribute %s not found", name);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			PyErr_SetString(PyExc_LookupError, log_buffer);
			goto validate_input_error_exit;
		}

		clear_attr(&attr, svr_attr_def);
		rc = set_attr_generic(&attr, &svr_attr_def[attr_idx], value_tmp, NULL, INTERNAL);
		free_attr(svr_attr_def, &attr, attr_idx);
		if (rc != 0) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "input value %s not of the right format for '%s'",
				 value, name);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			PyErr_SetString(
				pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
				log_buffer);
			goto validate_input_error_exit;
		}
	} else if (strcmp(table, PY_TYPE_QUEUE) == 0) {
		attr_idx = find_attr(que_attr_idx, que_attr_def, name);
		if (attr_idx == -1) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1, "queue attribute %s not found", name);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			PyErr_SetString(PyExc_LookupError, log_buffer);
			goto validate_input_error_exit;
		}

		clear_attr(&attr, que_attr_def);
		rc = set_attr_generic(&attr, &que_attr_def[attr_idx], value_tmp, NULL, INTERNAL);
		free_attr(que_attr_def, &attr, attr_idx);
		if (rc != 0) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "input value %s not of the right format for '%s'",
				 value, name);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			PyErr_SetString(
				pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
				log_buffer);
			goto validate_input_error_exit;
		}
	} else if (strcmp(table, PY_TYPE_FLOAT2) == 0) {
		clear_attr(&attr, que_attr_def);
		rc = decode_f(&attr, name, NULL, value_tmp);
		if (rc != 0) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "input value %s not of the right format for '%s'",
				 value, name);
			log_buffer[LOG_BUF_SIZE - 1] = '\0';
			PyErr_SetString(
				pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
				log_buffer);
			goto validate_input_error_exit;
		}
	} else {
		snprintf(log_buffer, LOG_BUF_SIZE - 1,
			 "could not find an attributes table called '%s'", table);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		PyErr_SetString(PyExc_LookupError, log_buffer);
		goto validate_input_error_exit;
	}

	if (value_tmp) {
		free(value_tmp);
	}
	Py_RETURN_NONE;

validate_input_error_exit:
	if (value_tmp) {
		free(value_tmp);
	}
	return NULL;
}

const char pbsv1mod_meth_duration_to_secs_doc[] =
	"duration_to_secs(strTime)\n\
\n\
   strTime:  a time string ([HH:MM:]SS[.ms]) to be converted into # of seconds \n\
\n\
         returns an int. If time_str is \"\", then returns 0\n\
";

/**
 * @brief
 *	convert the time in ([HH:MM:]SS[.ms]) format to secs.
 *
 * @return	PyObject*
 * @retval	secs		success
 * @retval	NULL		error
 *
 */
PyObject *
pbsv1mod_meth_duration_to_secs(PyObject *self, PyObject *args, PyObject *kwds)
{

	static char *kwlist[] = {"time_str", NULL};
	char *time_str = NULL;
	long num_secs;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "s:duration_to_secs",
					 kwlist,
					 &time_str)) {
		return NULL;
	}

	num_secs = duration_to_secs(time_str);
	if (num_secs == -1) {
		PyErr_SetString(PyExc_AssertionError, "strdup of value failed");
		goto duration_error_exit;
	}

	if (num_secs == -2) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1, "input value '%s' not of the right format",
			 time_str);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		PyErr_SetString(
			pbs_python_types_table[PP_BADATTR_VALUE_ERR_IDX].t_class,
			log_buffer);
		goto duration_error_exit;
	}

	return (PyLong_FromLong(num_secs));

duration_error_exit:

	return NULL;
}

/* pbs_v1_module method event */

const char pbsv1mod_meth_wordsize_doc[] =
	"wordsize()\n\
\n\
  returns:\n\
         size of a word in bytes(an int).\n\
";

/**
 * @brief
 *	return the size of a word in bytes.
 */
PyObject *
pbsv1mod_meth_wordsize(void)
{
	return (PyLong_FromSsize_t((ssize_t) sizeof(int)));
}

/**
 * @brief
 * 	_pbs_python_event_job_getval_hookset:
 * 	This is a new, general purpose function that looks into the current hook
 * 	event object's job parameter (i.e. pbs.event().job), and see if the job
 * 	attribute attrib_name was set inside a hook script. If so, return the
 * 	attributes value as a string; NULL otherwise. On some attributes like
 * 	ATTR_h, optional value strings opval and delval are returned. opval tells
 * 	how the attribute value was obtained: via __init__, __add__, or __sub__
 * 	methods; delval tells which hold values (e.g. "us") were actually removed
 * 	by a __sub__ (i.e. unset) action. opval_len and delval_len are the actual
 * 	number of bytes pre-allocated for the string arrays 'opval' and 'delval'.
 * 	The caller is responsible for allocating enough space for these parameters.
 *
 * @par	NOTE:
 *	This returns the string value returned by pbs_python_object_str(),
 * 	which returns a fixed memory area that gets overwritten by subsequent
 * 	calls to this function. So The return value of this function must be
 * 	immediately used.
 *
 */
char *
_pbs_python_event_job_getval_hookset(char *attrib_name, char *opval,
				     int opval_len, char *delval, int delval_len)
{
	PyObject *py_job = NULL;
	PyObject *py_attr_hookset_dict = NULL;
	char *strval = NULL;

	if (py_hook_pbsevent == NULL) {
		log_err(PBSE_INTERNAL, __func__, "No hook event found!");
		return NULL;
	}

	if (!PyObject_HasAttrString(py_hook_pbsevent, PY_EVENT_PARAM_JOB)) {
		LOG_ERROR_ARG2("%s: does not have attribute <%s>",
			       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
		return NULL;
	}

	py_job = PyObject_GetAttrString(py_hook_pbsevent, PY_EVENT_PARAM_JOB);
	/* NEW */

	if (py_job == NULL || (py_job == Py_None)) {
		LOG_ERROR_ARG2("%s: does not have a value for <%s>",
			       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
		return NULL;
	}

	/*
	 * Get the attributes that have been set in the hook script via the
	 * _attributes_hook_set dictionary.
	 */
	py_attr_hookset_dict = PyObject_GetAttrString(
		py_job, PY_ATTRIBUTES_HOOK_SET); /* NEW */
	if (py_attr_hookset_dict == NULL) {
		LOG_ERROR_ARG2("%s: does not have a value for <%s>",
			       PY_TYPE_JOB, PY_ATTRIBUTES_HOOK_SET);
		goto getval_exit;
	}
	if (!PyDict_Check(py_attr_hookset_dict)) {
		LOG_ERROR_ARG2("%s: <%s> is not a dict",
			       PY_TYPE_JOB, PY_ATTRIBUTES_HOOK_SET);
		goto getval_exit;
	}

	if (PyDict_GetItemString(py_attr_hookset_dict, attrib_name) != NULL) {
		if (PyObject_HasAttrString(py_job, attrib_name)) {
			PyObject *py_attrval = NULL;

			py_attrval = PyObject_GetAttrString(py_job,
							    attrib_name); /* NEW */

			if ((py_attrval != NULL) && (py_attrval != Py_None)) {

				if ((opval != NULL) && (opval_len > 1)) {
					strval = pbs_python_object_get_attr_string_value(
						py_attrval, PY_OPVAL);
					strncpy(opval, (strval ? strval : ""), opval_len - 1);
				}
				if ((delval != NULL) && (delval_len > 1)) {
					strval = pbs_python_object_get_attr_string_value(
						py_attrval, PY_DELVAL);
					strncpy(delval, (strval ? strval : ""), delval_len - 1);
				}
				strval = pbs_python_object_str(py_attrval);
				Py_DECREF(py_attrval);
			}
		}
	}

getval_exit:

	Py_CLEAR(py_job);
	Py_CLEAR(py_attr_hookset_dict);

	return (strval);
}

const char pbsv1mod_meth_iter_nextfunc_doc[] =
	"iter_nextfunc(meth_mode, obj_name, filter1, filter2)\n\
\n\
   meth_mode:	can be 1 if called from __init__() or 0 if from next()\n\
		method of a pbs_iter type.\n\
   obj_name:	what type of object being iterated on: \"queues\", \"jobs\",\n\
		\"resvs\", \"vnodes\".\n\
   filter1:	is usually the <server_name> where the queues,\n\
		jobs, resvs, vnodes reside. A <server_name> of \"\"\n\
		means the local server host.\n\
   filter2:	can be any string that can further restrict the list\n\
		being referenced. For example, this can be set to\n\
		some <queue_name>, to have the iterator represents\n\
		a list of jobs on <queue_name>@<server_name>\n\
\n\
   Returns the next PBS object in Python form to evaluate within a looping\n\
   construct. The idea is on a iterator instantiation, the following gets\n\
   called:\n\
   	_pbs_v1.iter_nextfunc(self, 1, obj_name, filter1, filter2)\n\
   This causes an iterator reference (i.e. self) to be internally stored by\n\
   PBS in the internal pbs_iter_list, setting pbs_iter_items data field to\n\
   the first element of the obj_name (e.g. queues) list, filtered\n\
   according to filter1 (e.g. fest) and/or filter2 (e.g. workq).\n\
\n\
   Then on an interator next call, the following gets invoked:\n\
	_pbs_v1.iter_nextfunc(self, 0, obj_name, filter1, filter2)\n\
   This returns the next Python object to process among the list of PBS\n\
   objects represented by iterator self.\n\
";

PyObject *
pbsv1mod_meth_iter_nextfunc(PyObject *self, PyObject *args, PyObject *kwds)
{
#ifdef NAS /* localmod 014 */
	static char *kwlist[] = {"iter_obj", "meth_mode", "obj_name", "filter1", "filter2", "ignore_fin", "filter_user",
				 NULL};
#else
	static char *kwlist[] = {"iter_obj", "meth_mode", "obj_name", "filter1", "filter2",
				 NULL};
#endif /* localmod 014 */
	int meth_mode;
	char *obj_name = NULL;
	char *filter1 = NULL;
	char *filter2 = NULL;
#ifdef NAS /* localmod 014 */
	int ignore_fin;
	char *filter_user = NULL;
#endif /* localmod 014 */
	pbs_iter_item *iter_entry = NULL;
	pbs_queue *pque = NULL;
	PyObject *py_object = NULL;
	PyObject *py_self = NULL;
	int vi;

#ifdef NAS /* localmod 014 */
	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "Oisssis:iter_nextfunc",
					 kwlist,
					 &py_self,
					 &meth_mode,
					 &obj_name,
					 &filter1,
					 &filter2,
					 &ignore_fin,
					 &filter_user)) {
#else
	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "Oisss:iter_nextfunc",
					 kwlist,
					 &py_self,
					 &meth_mode,
					 &obj_name,
					 &filter1,
					 &filter2)) {
#endif /* localmod 014 */
		return NULL;
	}

	iter_entry = (pbs_iter_item *) GET_NEXT(pbs_iter_list);

	while (iter_entry) {
		if (iter_entry->py_iter == py_self)
			break;

		iter_entry = (pbs_iter_item *) GET_NEXT(iter_entry->all_iters);
	}

	switch (meth_mode) {

		case 1: /* in __init__ method */

			if (iter_entry != NULL) { /* must be NULL */
				PyErr_SetString(PyExc_AssertionError,
						"attempted to initialize an already initialized iterator!");
				return NULL;
			}

			iter_entry = (pbs_iter_item *) malloc(sizeof(pbs_iter_item));
			if (iter_entry == NULL) {
				log_err(errno, __func__, "no memory");
				PyErr_SetString(PyExc_AssertionError,
						"failed to malloc memory");
				return NULL;
			}
			(void) memset((char *) iter_entry, (int) 0,
				      (size_t) sizeof(pbs_iter_item));
			CLEAR_LINK(iter_entry->all_iters);

			iter_entry->py_iter = py_self;
			Py_INCREF(py_self);

			if (strcmp(obj_name, ITER_QUEUES) == 0) {
				if ((filter1 != NULL) && (filter1[0] != '\0') &&
				    (strcmp(filter1, server_name) != 0)) {
					PyErr_SetString(PyExc_StopIteration, "");
					return NULL;
				}
				iter_entry->data = (pbs_queue *) GET_NEXT(svr_queues);
			} else if (strcmp(obj_name, ITER_JOBS) == 0) {

				if ((filter1 != NULL) && (filter1[0] != '\0') &&
				    (strcmp(filter1, server_name) != 0)) {
					PyErr_SetString(PyExc_StopIteration, "");
					return NULL;
				}
#ifdef NAS /* localmod 014 */
				if (!ignore_fin &&
				    (filter_user == NULL || filter_user[0] == '\0') &&
				    (filter2 != NULL) && (filter2[0] != '\0')) {
#else
				if ((filter2 != NULL) && (filter2[0] != '\0')) {
#endif /* localmod 014 */
					/* refers to the queue name */
					pque = find_queuebyname(filter2);
					if (pque == NULL) {
						sprintf(log_buffer, "queue %s not found",
							filter2);
						PyErr_SetString(PyExc_ValueError,
								log_buffer);
						return NULL;
					}
					iter_entry->data = (job *) GET_NEXT(pque->qu_jobs);

				} else { /* get jobs from server */
					iter_entry->data = (job *) GET_NEXT(svr_alljobs);

#ifdef NAS /* localmod 014 */
					/* skip jobs according to filters requested for the iterator */
					job *njob = (job *) iter_entry->data;
					while (njob != NULL &&
					       ((ignore_fin && check_job_state(njob, JOB_STATE_LTR_FINISHED)) ||
						(filter2 != NULL && filter2[0] != '\0' && strcmp(filter2, njob->ji_qs.ji_queue)) ||
						(filter_user != NULL && filter_user[0] != '\0' && is_jattr_set(njob, JOB_ATR_euser) && get_jattr_str(njob, JOB_ATR_euser) != NULL && strcmp(filter_user, get_jattr_str(njob, JOB_ATR_euser))))) {
						njob = (job *) GET_NEXT(njob->ji_alljobs);
					}

					iter_entry->data = njob;
#endif /* localmod 014 */
				}
			} else if (strcmp(obj_name, ITER_RESERVATIONS) == 0) {
				if ((filter1 != NULL) && (filter1[0] != '\0') &&
				    (strcmp(filter1, server_name) != 0)) {
					PyErr_SetString(PyExc_StopIteration, "");
					return NULL;
				}
				iter_entry->data = (resc_resv *) GET_NEXT(svr_allresvs);
			} else if (strcmp(obj_name, ITER_VNODES) == 0) {
				if ((filter1 != NULL) && (filter1[0] != '\0') &&
				    (strcmp(filter1, server_name) != 0)) {
					PyErr_SetString(PyExc_StopIteration, "");
					return NULL;
				}

				if ((pbsndlist == NULL) || (svr_totnodes <= 0)) {
					PyErr_SetString(PyExc_StopIteration, "");
					return NULL;
				}
				iter_entry->data = NULL;

				for (vi = 0; vi < svr_totnodes; vi++) {

					if ((pbsndlist[vi]->nd_state & INUSE_DELETED) == 0) {
						iter_entry->data =
							(struct pbsnode *) pbsndlist[vi];
						iter_entry->data_index = vi;
						break;
					}
				}
			} else {
				sprintf(log_buffer,
					"invalid parameter %s to iter_nextfunc()",
					obj_name);
				PyErr_SetString(PyExc_AssertionError, log_buffer);
				return NULL;
			}

			append_link(&pbs_iter_list, &iter_entry->all_iters,
				    (pbs_iter_item *) iter_entry);
			Py_RETURN_NONE; /* nothing to return since this is __init__ */

		case 0:				  /* in next() */
			if (iter_entry == NULL) { /* must be stored internally */
				PyErr_SetString(PyExc_AssertionError,
						"internal iterator should exist during next() call");
				return NULL;
			}

			if (iter_entry->data == NULL) {
				PyErr_SetString(PyExc_StopIteration, "");
				return NULL;
			}

			hook_set_mode = C_MODE;
			if (strcmp(obj_name, ITER_RESERVATIONS) == 0) {
				py_object = _pps_helper_get_resv((resc_resv *) iter_entry->data, NULL, HOOK_PERF_FUNC);
				iter_entry->data = (resc_resv *) GET_NEXT(
					((resc_resv *) iter_entry->data)->ri_allresvs);
			} else if (strcmp(obj_name, ITER_QUEUES) == 0) {
				py_object = _pps_helper_get_queue((pbs_queue *) iter_entry->data, NULL, HOOK_PERF_FUNC);
				iter_entry->data = (pbs_queue *) GET_NEXT(
					((pbs_queue *) iter_entry->data)->qu_link);
			} else if (strcmp(obj_name, ITER_JOBS) == 0) {
				py_object = _pps_helper_get_job((job *) iter_entry->data, NULL, NULL, HOOK_PERF_FUNC);

#ifdef NAS /* localmod 014 */
				if (!ignore_fin &&
				    (filter_user == NULL || filter_user[0] == '\0') &&
				    (filter2 != NULL) && (filter2[0] != '\0')) {
#else
				if ((filter2 != NULL) && (filter2[0] != '\0')) {
#endif /* localmod 014 */

					/* list of jobs filtered by queue   */
					/* 'filter2', as setup in meth_mode */
					/* of 1 above. So we need to return */
					/* the jobs on the same queue       */
					/* (i.e. use ji_jobque here)        */
					iter_entry->data = (job *) GET_NEXT(
						((job *) iter_entry->data)->ji_jobque);
				} else {
					iter_entry->data = (job *) GET_NEXT(
						((job *) iter_entry->data)->ji_alljobs);
#ifdef NAS /* localmod 014 */
					/* skip jobs according to filters requested for the iterator */
					job *njob = (job *) iter_entry->data;
					while (njob != NULL &&
					       ((ignore_fin && check_job_state(njob, JOB_STATE_LTR_FINISHED)) ||
						(filter2 != NULL && filter2[0] != '\0' && strcmp(filter2, njob->ji_qs.ji_queue)) ||
						(filter_user != NULL && filter_user[0] != '\0' && get_jattr_str(njob, JOB_ATR_euser) != NULL && strcmp(filter_user, get_jattr_str(njob, JOB_ATR_euser))))) {
						njob = (job *) GET_NEXT(njob->ji_alljobs);
					}

					iter_entry->data = njob;
#endif /* localmod 014 */
				}
			} else if (strcmp(obj_name, ITER_VNODES) == 0) {

				py_object = _pps_helper_get_vnode((struct pbsnode *) iter_entry->data, NULL, HOOK_PERF_FUNC);

				iter_entry->data = NULL;
				vi = iter_entry->data_index + 1;
				while (vi < svr_totnodes) {

					if ((pbsndlist[vi]->nd_state & INUSE_DELETED) == 0) {
						iter_entry->data =
							(struct pbsnode *) pbsndlist[vi];
						iter_entry->data_index = vi;
						break;
					}
					vi++;
				}

			} else {
				sprintf(log_buffer,
					"invalid parameter %s to iter_nextfunc()",
					obj_name);
				PyErr_SetString(PyExc_AssertionError, log_buffer);
				hook_set_mode = PY_MODE;
				return NULL;
			}
			hook_set_mode = PY_MODE;
			return py_object;

		default:
			sprintf(log_buffer,
				"invalid method mode %d to iter_nextfunc()",
				meth_mode);
			PyErr_SetString(PyExc_AssertionError, log_buffer);
	}
	return NULL;
}

const char pbsv1mod_meth_mark_vnode_set_doc[] =
	"mark_vnode_set(vnode_name, attr_name, attr_value)\n\
\n\
   vnode_name:  name of vnode to set\n\
   attr_name:   the attribute name\n\
   attr_value:  the attribute value\n\
\n\
   Adds to some internal, pending operations table,\n\
   an 'attr_name=attr_value' set operation for the given 'vnode_name'.\n\
";

/**
 * @brief
 *	This is callable in a Python script, for populating the internal
 *	list 'pbs_vnode_set_list' with
 *		(<vnode_name>, <attribute_name>) representing a pending "set"
 *	operation.
 *
 * @param[in]	args[1]	- the vnode name.
 * @param[in]	args[2] - the attribute name.
 * @param[in]	args[3] - the attribute value.
 *
 * @return	PyObject *
 * @retval	NULL	- with an accompanying AssertionError Python exception.
 * @retval	Py_None - successful execution.
 *
 */
PyObject *
pbsv1mod_meth_mark_vnode_set(PyObject *self, PyObject *args, PyObject *kwds)
{

	static char *kwlist[] = {"vnode_name", "attr_name", "attr_value",
				 NULL};
	char *vnode_name = NULL;
	char *attr_name = NULL;
	char *attr_value = NULL;
	vnode_set_req *vn_set_req = NULL;
	svrattrl *plist = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "sss:mark_vnode_set",
					 kwlist,
					 &vnode_name,
					 &attr_name,
					 &attr_value)) {
		return NULL;
	}
	if ((attr_name == NULL) || (attr_name[0] == '\0') ||
	    (attr_value == NULL) || (attr_value[0] == '\0')) {
		PyErr_SetString(PyExc_AssertionError,
				"mark_vnode_set: bad parameter!");
		return NULL;
	}

	vn_set_req = (vnode_set_req *) GET_NEXT(pbs_vnode_set_list);

	while (vn_set_req) {
		if (strcmp(vn_set_req->vnode_name, vnode_name) == 0)
			break;

		vn_set_req = (vnode_set_req *) GET_NEXT(vn_set_req->all_reqs);
	}

	if (vn_set_req == NULL) {
		vn_set_req = (vnode_set_req *) malloc(sizeof(vnode_set_req));
		if (vn_set_req == NULL) {
			log_err(errno, __func__, "no memory");
			PyErr_SetString(PyExc_AssertionError,
					"failed to malloc memory");
			return NULL;
		}
		(void) memset((char *) vn_set_req, (int) 0,
			      (size_t) sizeof(vnode_set_req));
		CLEAR_LINK(vn_set_req->all_reqs);
		CLEAR_HEAD(vn_set_req->rq_attr);
		strncpy(vn_set_req->vnode_name, vnode_name, PBS_MAXNODENAME);
		append_link(&pbs_vnode_set_list, &vn_set_req->all_reqs,
			    (vnode_set_req *) vn_set_req);
	}

	if ((plist = find_svrattrl_list_entry(&vn_set_req->rq_attr, attr_name,
					      NULL)) != NULL) {

		/* let's remove the entry and just recreate. */
		/* it's too messy and buggy, error prone to have */
		/* to "extend" the svratrl entry!                */

		delete_link(&plist->al_link);
		free(plist);
	}

	if (add_to_svrattrl_list(&vn_set_req->rq_attr, attr_name, NULL,
				 ((strcmp(attr_name, ATTR_NODE_state) == 0) ? vnode_state_to_str(atoi(attr_value)) : attr_value),
				 ATR_VFLAG_HOOK, NULL) == -1) {
		snprintf(log_buffer, LOG_BUF_SIZE - 1,
			 "failed to add_to_svrattrl_list(%s, 0, %s, ATR_VFLAG_HOOK)",
			 attr_name, attr_value);
		log_buffer[LOG_BUF_SIZE - 1] = '\0';
		log_err(errno, __func__, log_buffer);
		PyErr_SetString(PyExc_AssertionError, "");
		return NULL;
	}

	Py_RETURN_NONE; /* nothing to return since this is __init__ */
}

const char pbsv1mod_meth_vnode_state_to_str_doc[] =
	"vnode_state_to_str(state_bit)\n\
\n\
   state_bit:	vnode state in bit flag.\n\
\n\
   Returns the human readable form of 'state_bit'\n\
   Ex: vnode_state_to_str(3) -> \"offline,down\"\n\
";

/**
 * @brief
 *	This is the C->Python wrapper program to vnode_state_to_str() function,
 *	that is callable in Python script.
 *
 * @param[in]	self	- owning object
 * @param[in]	args[1]	- the  state bit value.
 * @param[in]	kwds	- keywords to objects mappings
 *
 * @return	PyObject *
 * @retval	A Python string corresponding to args[1].
 *
 */
PyObject *
pbsv1mod_meth_vnode_state_to_str(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {"state_bit", NULL};
	int state_bit;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "i:vnode_state_to_str",
					 kwlist,
					 &state_bit)) {
		return NULL;
	}

	return (PyUnicode_FromString(vnode_state_to_str(state_bit)));
}

const char pbsv1mod_meth_vnode_sharing_to_str_doc[] =
	"vnode_sharing_to_str(share_val)\n\
\n\
   share_val:	vnode sharing value in int.\n\
\n\
   Returns the human readable form of 'state_bit'\n\
   Ex: vnode_sharing_to_str(5) -> \"force_excl\"\n\
";

/**
 * @brief
 *	This is the C->Python wrapper program to vnode_sharing_to_str() function,
 that is callable in Python script.
 *
 * @param[in]	self	- owning object
 * @param[in]	args[1]	- the  vnode sharing value.
 * @param[in]	kwds	- keywords to objects mappings
 *
 * @return	PyObject *
 * @retval	A Python string corresponding to args[1].
 *
 */
PyObject *
pbsv1mod_meth_vnode_sharing_to_str(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {"share_val", NULL};
	int share_val;
	char *vns;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "i:vnode_sharing_to_str",
					 kwlist,
					 &share_val)) {
		return NULL;
	}

	vns = vnode_sharing_to_str(share_val);
	return (PyUnicode_FromString(vns ? vns : ""));
}

const char pbsv1mod_meth_vnode_ntype_to_str_doc[] =
	"vnode_ntype_to_str(ntype)\n\
\n\
   share_val:	ntype vnode type.\n\
\n\
   Returns the node type\n\
   Ex: vnode_ntype_to_str(1) -> \"PBS\"\n\
";

/**
 * @brief
 *	This is the C->Python wrapper program to vnode_ntype_to_str() function,
 *	that is callable in Python script.
 *
 * @param[in]	self	- owning object
 * @param[in]	args[1]	- the  vnode sharing value.
 * @param[in]	kwds	- keywords to objects mappings
 *
 * @return	PyObject *
 * @retval	A Python string corresponding to args[1].
 *
 */
PyObject *
pbsv1mod_meth_vnode_ntype_to_str(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {"node_type", NULL};
	int node_type;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "i:vnode_ntype_to_str",
					 kwlist,
					 &node_type)) {
		return NULL;
	}

	return (PyUnicode_FromString(vnode_ntype_to_str(node_type)));
}

/**
 *
 * @brief
 *	Checks if there's at least one pending "set"  vnode operation in
 *	the internal list 'pbs_vnode_set_list'.
 *
 * @return int
 * @retval 1	- if a "set" operation was found.
 * @retval 0   - if not found.
 *
 */
int
_pbs_python_has_vnode_set(void)
{
	if (GET_NEXT(pbs_vnode_set_list) != NULL)
		return (1);
	return (0);
}

/**
 * @brief
 *	Perform all the "set" operations found in 'pbs_vnode_set_list'.
 * @par
 *	This goes through each entry in the internal 'pbs_vnode_set_list',
 *	and for every vnode_name, obtain the struct pbsnode * entry in the
 *	system pbsdnlist[] table, and perform the set operation for the
 *	corresponding rq_attr entry.
 *
 * @note
 *	Failures in set operations will be reflected in the daemons logs
 *	output.
 *
 */
void
_pbs_python_do_vnode_set(void)
{

	vnode_set_req *vn_set_req = NULL;
	struct pbsnode *pnode;
	int bad = 0;
	int rc;
	char *hook_name = NULL;
	svrattrl *plist;
	svrattrl *pal;

	hook_name = _pbs_python_event_get_attrval(PY_EVENT_HOOK_NAME);
	if (hook_name == NULL) {
		log_err(PBSE_INTERNAL, __func__,
			"No hook name associated with set vnode operation!");
		return;
	}

	vn_set_req = (vnode_set_req *) GET_NEXT(pbs_vnode_set_list);
	while (vn_set_req != NULL) {

		pnode = find_nodebyname(vn_set_req->vnode_name);

		if ((pnode == NULL) ||
		    (pnode->nd_state & INUSE_DELETED)) {
			vn_set_req = (vnode_set_req *) GET_NEXT(vn_set_req->all_reqs);
			continue;
		}

		plist = (svrattrl *) GET_NEXT(vn_set_req->rq_attr);

		rc = mgr_set_attr(pnode->nd_attr, node_attr_idx, node_attr_def, ND_ATR_LAST,
				  plist, ATR_PERM_ALLOW_INDIRECT, &bad, (void *) pnode, ATR_ACTION_ALTER);

		if (rc != 0) {
			char *pbse_err;
			char raw_err[10];
			svrattrl *pal;
			int i;

			pbse_err = pbse_to_txt(rc);
			snprintf(raw_err, sizeof(raw_err) - 1, "%d", rc);

			i = 0;
			pal = plist;
			bad--; /* mgr_set_attr returns +1 of actual 'bad' index */
			while (pal) {
				if (i == bad) {
					sprintf(log_buffer,
						"vnode %s: failed to set %s to %s: %s",
						vn_set_req->vnode_name,
						pal->al_name, pal->al_value ? pal->al_value : "",
						pbse_err ? pbse_err : raw_err);
					log_err(PBSE_SYSTEM, __func__, log_buffer);
					break;
				}
				if (hook_debug.output_fp != NULL) {
					fprintf(hook_debug.output_fp,
						"%s(%s).%s=%s\n",
						SERVER_VNODE_OBJECT,
						pnode->nd_name,
						pal->al_name,
						pal->al_value);
				}
				i++;
				pal = (svrattrl *) GET_NEXT(pal->al_link);
			}
			return;
		} else {

			mgr_log_attr(msg_man_set, plist,
				     PBS_EVENTCLASS_NODE, pnode->nd_name, hook_name);

			pal = plist;
			while (pal) {
				if (hook_debug.output_fp != NULL) {
					fprintf(hook_debug.output_fp,
						"%s(%s).%s=%s\n",
						SERVER_VNODE_OBJECT,
						pnode->nd_name,
						pal->al_name,
						pal->al_value);
				}
				pal = (svrattrl *) GET_NEXT(pal->al_link);
			}
		}

		vn_set_req = (vnode_set_req *) GET_NEXT(vn_set_req->all_reqs);
	}

	save_nodes_db(0, NULL);
}

const char pbsv1mod_meth_set_python_mode_doc[] =
	"set_python_mode()\n\
\n\
  returns:\n\
         Sets the internal variable 'hook_set_mode' to PY_MODE.\n\
  	 This is an internal function.\n\
";

/**
 *;p
 * @brief
 *	This is the C->Python wrapper program to
 *	set the internal variable 'hook_set_mode' to PY_MODE.
 *
 * @return Python object
 * @retval PY_TRUE
 *
 */
PyObject *
pbsv1mod_meth_set_python_mode(void)
{
	PyObject *ret;
	hook_set_mode = PY_MODE;
	ret = Py_True;
	Py_INCREF(ret);
	return (ret);
}

const char pbsv1mod_meth_set_c_mode_doc[] =
	"set_c_mode()\n\
\n\
  returns:\n\
         Sets the internal variable 'hook_set_mode' to C_MODE.\n\
  	 This is an internal function.\n\
";

/**
 *
 * @brief
 *	This is the C->Python wrapper program to
 *	set the internal variable 'hook_set_mode' to C_MODE.
 *
 * @return Python object
 * @retval PY_TRUE
 *
 */
PyObject *
pbsv1mod_meth_set_c_mode(void)
{
	PyObject *ret;
	hook_set_mode = C_MODE;
	ret = Py_True;
	Py_INCREF(ret);
	return (ret);
}

const char pbsv1mod_meth_get_python_daemon_name_doc[] =
	"get_python_daemon_name()\n\
\n\
  returns:\n\
         Returns the svr_interp_data.daemon_name value.\n\
  	 This is an internal function.\n\
";

/**
 * @brief
 *	This is the C->Python wrapper program for
 *	returning the svr_interp_data.daemon_name value.
 *	This is callable within a Python script.
 *
 * @return Python str
 * @retval name of "daemon" invoking Python interpreter.
 *
 */
PyObject *
pbsv1mod_meth_get_python_daemon_name(void)
{
	if (svr_interp_data.daemon_name == NULL)
		Py_RETURN_NONE;

	return (PyUnicode_FromString(svr_interp_data.daemon_name));
}

const char pbsv1mod_meth_str_to_vnode_state_doc[] =
	"str_to_vnode_state(state_str)\n\
\n\
   state_str:	vnode state as a  string.\n\
\n\
   Returns the human readable form of 'state_str'\n\
   Ex: str_to_vnode_state(\"offline,down\") -> \"3\"\n\
";

/**
 * @brief
 *	This is the C->Python wrapper program to str_to_vnode_state()
 *	function, that is callable in Python script.
 *
 * @param[in]	self	- owning object
 * @param[in]	args[1]	- the  state str value.
 * @param[in]	kwds	- keywords to objects mappings
 *
 * @return	PyObject *
 * @retval	A Python int corresponding to args[1].
 *
 */
PyObject *
pbsv1mod_meth_str_to_vnode_state(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {"state_str", NULL};
	char *state_str = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "s:str_to_vnode_state",
					 kwlist,
					 &state_str)) {
		return NULL;
	}

	return (PyUnicode_FromFormat("%d", str_to_vnode_state(state_str)));
}

const char pbsv1mod_meth_str_to_vnode_ntype_doc[] =
	"str_to_vnode_ntype(type_str)\n\
\n\
   type_str:	vnode type as a string.\n\
\n\
   Returns the human readable form of 'type_str'\n\
   Ex: str_to_vnode_ntype(\"pbs\") -> \"0\"\n\
";

/**
 * @brief
 *	This is the C->Python wrapper program to str_to_vnode_ntype()
 *	function, that is callable in Python script.
 *
 * @param[in]	self	- owning object
 * @param[in]	args[1]	- the  type str value.
 * @param[in]	kwds	- keywords to objects mappings
 *
 * @return	PyObject *
 * @retval	A Python string corresponding to args[1].
 *
 */
PyObject *
pbsv1mod_meth_str_to_vnode_ntype(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {"type_str", NULL};
	char *type_str = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "s:str_to_vnode_ntype",
					 kwlist,
					 &type_str)) {
		return NULL;
	}

	return (PyUnicode_FromFormat("%d", str_to_vnode_ntype(type_str)));
}

const char pbsv1mod_meth_str_to_vnode_sharing_doc[] =
	"str_to_vnode_sharing(share_str)\n\
\n\
   share_str:	vnode share as a  string.\n\
\n\
   Returns the human readable form of 'share_str'\n\
   Ex: str_to_vnode_sharing(\"default_shared\") -> \"1\"\n\
";

/**
 * @brief
 *	This is the C->Python wrapper program to str_to_vnode_sharing()
 *	function that is callable in Python script.
 *
 * @param[in]	self	- owning object
 * @param[in]	args[1]	- the  share str value.
 * @param[in]	kwds	- keywords to objects mappings
 *
 * @return	PyObject *
 * @retval	A Python string corresponding to args[1].
 *
 */
PyObject *
pbsv1mod_meth_str_to_vnode_sharing(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {"share_str", NULL};
	char *share_str = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "s:str_to_vnode_sharing",
					 kwlist,
					 &share_str)) {
		return NULL;
	}
	return (PyUnicode_FromFormat("%d", str_to_vnode_sharing(share_str)));
}

const char pbsv1mod_meth_get_pbs_server_name_doc[] =
	"get_pbs_server_name()\n\
\n\
  returns:\n\
         Returns the configured server host name.\n\
  	 This is an internal function.\n\
";

/**
 *
 * @brief
 *	This is the C->Python wrapper program for
 *	returning the PBS_SERVER value in pbs.conf file.
 *	This is callable within a Python script.
 *
 * @return Python str
 * @retval name of "daemon" invoking Python interpreter.
 *
 */
PyObject *
pbsv1mod_meth_get_pbs_server_name(void)
{
	if (server_host[0] == '\0')
		Py_RETURN_NONE;

	return (PyUnicode_FromString(server_host));
}

const char pbsv1mod_meth_get_local_host_name_doc[] =
	"get_local_host_name()\n\
\n\
  returns:\n\
         Returns the local host name associated with currently instantiated.\n\
	 Python interpreter.\n\
  	 This is an internal function.\n\
";

/**
 *
 * @brief
 *	This is the C->Python wrapper program for
 *	returning the local host name associated with an instantiated
 *	Python interpreter.
 *	This is callable within a Python script.
 *
 * @return Python str
 * @retval local hostname associated with the current Python interpreter.
 *
 */
PyObject *
pbsv1mod_meth_get_local_host_name(void)
{
	return (PyUnicode_FromString((char *) svr_interp_data.local_host_name));
}

/**
 * @brief
 * 	Returns the command line string that was set in  a hook
 *	as an alternate to the normal reboot() call.
 */
char *
pbs_python_get_reboot_host_cmd(void)
{
	if (hook_reboot_host_cmd[0] != '\0')
		return ((char *) hook_reboot_host_cmd);

	return NULL;
}

/**
 * @brief
 *	Returns the value of the flag that tells pbs to reboot host or not.
 *
 * @return	int
 * @retval	TRUE 	- 	means yes, reboot host
 * @retval	FALSE 	- 	means no.
 */
int
pbs_python_get_reboot_host_flag(void)
{
	return (hook_reboot_host);
}

/**
 * @brief
 *	Sets the flag that tells pbs to reboot current host.
 *	Also, if 'cmd' is not NULL, sets the reboot command to use as an
 *	alternate to the default reboot() call.
 *
 * @param[in]	cmd - the command line (including arguments) that should
 *			be used when the time comes to reboot host.
 */
void
pbs_python_reboot_host(char *cmd)
{

	hook_reboot_host = TRUE;
	hook_reboot_host_cmd[0] = '\0';
	if (cmd != NULL) {
		snprintf((char *) hook_reboot_host_cmd, sizeof(hook_reboot_host_cmd),
			 "%s", cmd);
	}
}

const char pbsv1mod_meth_reboot_doc[] =
	"reboot([cmd])\n\
\n\
         Flags pbs that current host should be rebooted.\n\
  where:\n\
\n\
   cmd:  optional command line to use by pbs to reboot host, as an\n\
   alternate to the default reboot()  call.\n\
";

/**
 *
 * @brief
 *	This is the C->Python wrapper program to the
 *	pbs_python_reboot_host() call.
 *
 *	This is callable within a Python script.
 *
 * @param[in]	args - arguments to this Python function
 * @param[in]	kwds - keywords mappings  to the alternate 'cmd' used for
 *			rebooting host.
 *
 * @return PyObject *
 * @retval Python None
 */
PyObject *
pbsv1mod_meth_reboot(PyObject *self, PyObject *args, PyObject *kwds)
{

	static char *kwlist[] = {"cmd", NULL};
	char *cmd = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "|s:reboot",
					 kwlist,
					 &cmd)) {
		return NULL;
	}
	pbs_python_reboot_host(cmd);

	Py_RETURN_NONE;
}

/**
 * @brief
 *	Returns the value of the flag that tells pbs to scheduler_restart_cycle host or not.
 * @return 	int
 * @retval	TRUE	 - 	means yes, scheduler_restart_cycle host
 * @retval	FALSE 	- 	means no.
 */
int
pbs_python_get_scheduler_restart_cycle_flag(void)
{
	return (hook_scheduler_restart_cycle);
}

/**
 * @brief
 *	Sets the flag that tells pbs server to NOT tell scheduler to restart its
 *	scheduling cycle.
 */
void
pbs_python_no_scheduler_restart_cycle(void)
{

	hook_scheduler_restart_cycle = FALSE;
}

/**
 * @brief
 *	Sets the flag that tells pbs server to tell scheduler to restart its
 *	scheduling cycle.
 */
void
pbs_python_scheduler_restart_cycle(void)
{

	hook_scheduler_restart_cycle = TRUE;
}

const char pbsv1mod_meth_scheduler_restart_cycle_doc[] =
	"scheduler_restart_cycle(<server_host>)\n\
\n\
         Flags pbs server at 'server_host' to tell scheduler to restart its scheduling cycle.\n\
  where:\n\
\n\
   server_host:  the name of the server to send the request to.\n\
   NOTE: Currently, only supports on the same host.\n\
";

/**
 *
 * @brief
 *	This is the C->Python wrapper program to the
 *	pbs_python_scheduler_restart_cycle() call.
 *
 *	This is callable within a Python script.
 *
 * @param[in]	args - arguments to this Python function
 * @param[in]	kwds - keywords mappings  to the 'server_host' needing a
 *			scheduler restart cycle.
 *
 * @return PyObject *
 * @retval Python None
 */
PyObject *
pbsv1mod_meth_scheduler_restart_cycle(PyObject *self, PyObject *args, PyObject *kwds)
{

	static char *kwlist[] = {"server_host", NULL};
	char *shost = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "|s:scheduler_restart_cycle",
					 kwlist,
					 &shost)) {
		return NULL;
	}

	if ((strcmp(shost, LOCALHOST_SHORTNAME) != 0) &&
	    (strcmp(shost, LOCALHOST_FULLNAME) != 0) &&
	    (strcmp(shost, pbs_conf.pbs_server_name) != 0)) {
		PyErr_SetString(PyExc_NotImplementedError,
				"Allowed only to owning pbs server host");
		return NULL;
	}

	pbs_python_scheduler_restart_cycle();

	Py_RETURN_NONE;
}

const char pbsv1mod_meth_set_pbs_statobj_doc[] =
	"set_pbs_statobj(function_name)\n\
\n\
   function_name:  name of function that creates/populates a PBS object \n\
\n\
";

/**
 * @brief
 *	set the status of job.
 */
PyObject *
pbsv1mod_meth_set_pbs_statobj(PyObject *self, PyObject *args,
			      PyObject *kwds)
{
	static char *kwlist[] = {"func", NULL};
	PyObject *f = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "O:set_pbs_statobj", kwlist, &f)) {
		PyErr_SetString(PyExc_AssertionError,
				"set_pbs_statobj: Failed to parse arguments");
		return NULL;
	}
	if (!PyCallable_Check(f)) {
		PyErr_SetString(PyExc_AssertionError,
				"Failed to get pbs_statobj function");
		return NULL;
	}
	Py_XINCREF(f);
	Py_XDECREF(py_pbs_statobj); /* release any previously set value    */
	/* NOTE: *XDECREF() is safe where      */
	/* if py_pbs_statobj is NULL, then     */
	/* nothing is released.                */
	/* Current value of py_pbs_statobj is  */
	/* released when Python interpreter is */
	/* restarted, during call to           */
	/* pbs_python_unload_python_types().   */
	py_pbs_statobj = f;
	Py_RETURN_NONE;
}

/**
 *
 * @brief
 * 	Looks into the current hook event object's job's 'attr_name'
 *	attribute, and returns the attribute value.
 *
 * @param[in]	attr_name - the name of an attribute.
 *
 * @return char *
 * @retval 	string - the string value returned by pbs_python_object_str(),
 * 			which returns a fixed memory area that gets
 *			overwritten by subsequent calls to this function.
 *			So the return value of this function must be
 *			immediately used.
 * @retval	NULL - if no proper value could be returned.
 *
 */
char *
_pbs_python_event_job_getval(char *attr_name)
{
	PyObject *py_job = NULL;
	PyObject *py_val = NULL;
	char *strval = NULL;

	if (py_hook_pbsevent == NULL) {
		log_err(PBSE_INTERNAL, __func__, "No hook event found!");
		return NULL;
	}

	if (!PyObject_HasAttrString(py_hook_pbsevent, PY_EVENT_PARAM_JOB)) {
		LOG_ERROR_ARG2("%s: does not have attribute <%s>",
			       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
		return NULL;
	}

	py_job = PyObject_GetAttrString(py_hook_pbsevent, PY_EVENT_PARAM_JOB); /* NEW */
	if (py_job == NULL || (py_job == Py_None)) {
		LOG_ERROR_ARG2("%s: does not have a value for <%s>",
			       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
		return NULL;
	}

	if (PyObject_HasAttrString(py_job, attr_name)) {

		py_val = PyObject_GetAttrString(py_job,
						attr_name); /* NEW */

		if ((py_val != NULL) && (py_val != Py_None)) {

			strval = pbs_python_object_str(py_val);
		}
	}

	Py_CLEAR(py_job);
	Py_CLEAR(py_val);

	return (strval);
}

/**
 *
 * @brief
 * 	Looks into the current hook event object's job's 'attr_name'
 *	parameter of type resource (e.g. pbs.event().job.Resource_List),
 *	and returns the resource 'resc_name' value, if it was set inside
 *	a hook script.
 *
 * @param[in]	attr_name - the name of an attribute of type resource.
 * @param[in]	resc_name - the name of the resource whose value is being
 *				requested.
 * @return char *
 * @retval 	string - the string value returned by pbs_python_object_str(),
 * 			which returns a fixed memory area that gets
 *			overwritten by subsequent calls to this function.
 *			So the return value of this function must be
 *			immediately used.
 * @retval	NULL - if no proper value could be returned.
 *
 */
char *
_pbs_python_event_jobresc_getval_hookset(char *attr_name, char *resc_name)
{
	PyObject *py_job = NULL;
	PyObject *py_jobresc = NULL;
	PyObject *py_attr_hookset_dict = NULL;
	PyObject *py_rescval = NULL;

	char *strval = NULL;

	if (py_hook_pbsevent == NULL) {
		log_err(PBSE_INTERNAL, __func__, "No hook event found!");
		return NULL;
	}

	if (!PyObject_HasAttrString(py_hook_pbsevent, PY_EVENT_PARAM_JOB)) {
		LOG_ERROR_ARG2("%s: does not have attribute <%s>",
			       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
		return NULL;
	}

	py_job = PyObject_GetAttrString(py_hook_pbsevent, PY_EVENT_PARAM_JOB); /* NEW */

	if (py_job == NULL || (py_job == Py_None)) {
		LOG_ERROR_ARG2("%s: does not have a value for <%s>",
			       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
		return NULL;
	}

	/* Get:pbs.event().job.<attr_name>[]
	 Ex. pbs.event().job.Resource_List[]
	 */
	py_jobresc = PyObject_GetAttrString(py_job, attr_name); /* NEW */

	if (py_jobresc == NULL || (py_jobresc == Py_None)) {
		LOG_ERROR_ARG2("%s: does not have a value for <%s>",
			       PY_TYPE_JOB, attr_name);
		goto jobresc_getval_hookset_exit;
	}

	/*
	 * Get the attributes that have been set in the hook script via the
	 * _attributes_hook_set dictionary.
	 */
	py_attr_hookset_dict = PyObject_GetAttrString(
		py_jobresc, PY_ATTRIBUTES_HOOK_SET); /* NEW */
	if (py_attr_hookset_dict == NULL) {
		LOG_ERROR_ARG2("%s: does not have a value for <%s>",
			       attr_name, PY_ATTRIBUTES_HOOK_SET);
		goto jobresc_getval_hookset_exit;
	}
	if (!PyDict_Check(py_attr_hookset_dict)) {
		LOG_ERROR_ARG2("%s: <%s> is not a dict",
			       attr_name, PY_ATTRIBUTES_HOOK_SET);
		goto jobresc_getval_hookset_exit;
	}

	/* And this all boils down to (whew!):
	 Ex. pbs.event().job.Resource_List[]._attributes_hook_set[<pbs.event().job.Resource_List[]'s instance>][<resc_name>] = <resc_val>
	 */

	if (PyDict_GetItemString(py_attr_hookset_dict, resc_name) != NULL) {
		if (PyObject_HasAttrString(py_jobresc, resc_name)) {
			py_rescval = PyObject_GetAttrString(py_jobresc,
							    resc_name); /* NEW */

			if ((py_rescval != NULL) && (py_rescval != Py_None)) {

				strval = pbs_python_object_str(py_rescval);
			}
		}
	}

jobresc_getval_hookset_exit:

	Py_CLEAR(py_job);
	Py_CLEAR(py_jobresc);
	Py_CLEAR(py_attr_hookset_dict);
	Py_CLEAR(py_rescval);

	return (strval);
}
/**
 *
 * @brief
 * 	Looks into the current hook event object's job's 'attr_name'
 *	parameter of type resource (e.g. pbs.event().job.Resource_List),
 *	and returns the resource 'resc_name' value.
 *
 * @param[in]	attr_name - the name of an attribute of type resource.
 * @param[in]	resc_name - the name of the resource whose value is being
 *				requested.
 * @return char *
 * @retval 	string - the string value returned by pbs_python_object_str(),
 * 			which returns a fixed memory area that gets
 *			overwritten by subsequent calls to this function.
 *			So the return value of this function must be
 *			immediately used.
 * @retval	NULL - if no proper value could be returned.
 *
 */
char *
_pbs_python_event_jobresc_getval(char *attr_name, char *resc_name)
{
	PyObject *py_job = NULL;
	PyObject *py_jobresc = NULL;
	PyObject *py_rescval = NULL;
	char *strval = NULL;

	if (py_hook_pbsevent == NULL) {
		log_err(PBSE_INTERNAL, __func__, "No hook event found!");
		return NULL;
	}

	if (!PyObject_HasAttrString(py_hook_pbsevent, PY_EVENT_PARAM_JOB)) {
		LOG_ERROR_ARG2("%s: does not have attribute <%s>",
			       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
		return NULL;
	}

	py_job = PyObject_GetAttrString(py_hook_pbsevent, PY_EVENT_PARAM_JOB); /* NEW */

	if (py_job == NULL || (py_job == Py_None)) {
		LOG_ERROR_ARG2("%s: does not have a value for <%s>",
			       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
		return NULL;
	}
	py_jobresc = PyObject_GetAttrString(py_job, attr_name); /* NEW */

	if (py_jobresc == NULL || (py_jobresc == Py_None)) {
		LOG_ERROR_ARG2("%s: does not have a value for <%s>",
			       PY_TYPE_JOB, attr_name);
		goto jobresc_getval_exit;
	}

	if (PyObject_HasAttrString(py_jobresc, resc_name)) {

		py_rescval = PyObject_GetAttrString(py_jobresc,
						    resc_name); /* NEW */

		if ((py_rescval != NULL) && (py_rescval != Py_None)) {

			strval = pbs_python_object_str(py_rescval);
		}
	}

jobresc_getval_exit:
	Py_CLEAR(py_jobresc);
	Py_CLEAR(py_job);
	Py_CLEAR(py_rescval);
	return (strval);
}

/**
 *
 * @brief
 * 	Clears/Empties the current hook event object's job's
 *	PY_ATTRIBUTES_HOOK_SET dictionary. This is the set of resources that
 *      have been flagged as set in a hook script.
 *
 * @param[in]	attr_name - the name of an attribute of type resource.
 * @return int
 * @retval 	0 - success
 * @retval	1 - if unsuccessful clearing the dictionary.
 *
 */
int
_pbs_python_event_jobresc_clear_hookset(char *attr_name)
{
	PyObject *py_job = NULL;
	PyObject *py_jobresc = NULL;
	PyObject *py_attr_hookset_dict = NULL;
	int rc = 1;

	if (py_hook_pbsevent == NULL) {
		log_err(PBSE_INTERNAL, __func__, "No hook event found!");
		return (1);
	}

	if (!PyObject_HasAttrString(py_hook_pbsevent, PY_EVENT_PARAM_JOB)) {
		LOG_ERROR_ARG2("%s: does not have attribute <%s>",
			       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
		return (1);
	}

	py_job = PyObject_GetAttrString(py_hook_pbsevent, PY_EVENT_PARAM_JOB); /* NEW */

	if (py_job == NULL || (py_job == Py_None)) {
		LOG_ERROR_ARG2("%s: does not have a value for <%s>",
			       PY_TYPE_EVENT, PY_EVENT_PARAM_JOB);
		return (1);
	}

	/* Get:pbs.event().job.<attr_name>[]
	 Ex. pbs.event().job.Resource_List[]
	 */
	py_jobresc = PyObject_GetAttrString(py_job, attr_name); /* NEW */

	if (py_jobresc == NULL || (py_jobresc == Py_None)) {
		LOG_ERROR_ARG2("%s: does not have a value for <%s>",
			       PY_TYPE_JOB, attr_name);
		goto jobresc_clear_hookset_exit;
	}

	/*
	 * Get the attributes that have been set in the hook script via the
	 * _attributes_hook_set dictionary.
	 */
	py_attr_hookset_dict = PyObject_GetAttrString(
		py_jobresc, PY_ATTRIBUTES_HOOK_SET); /* NEW */
	if (py_attr_hookset_dict == NULL) {
		LOG_ERROR_ARG2("%s: does not have a value for <%s>",
			       attr_name, PY_ATTRIBUTES_HOOK_SET);
		goto jobresc_clear_hookset_exit;
	}
	if (!PyDict_Check(py_attr_hookset_dict)) {
		LOG_ERROR_ARG2("%s: <%s> is not a dict",
			       attr_name, PY_ATTRIBUTES_HOOK_SET);
		goto jobresc_clear_hookset_exit;
	}

	PyDict_Clear(py_attr_hookset_dict);
	rc = 0;

jobresc_clear_hookset_exit:

	Py_CLEAR(py_job);
	Py_CLEAR(py_jobresc);
	Py_CLEAR(py_attr_hookset_dict);

	return (rc);
}

const char pbsv1mod_meth_size_to_kbytes_doc[] =
	"size_to_kbytes(py_size)\n\
\n\
   py_size: Python size object\n\
\n\
";

/**
 * @brief
 *	convert and return the size to kilobytes.
 *
 */
PyObject *
pbsv1mod_meth_size_to_kbytes(PyObject *self, PyObject *args,
			     PyObject *kwds)
{
	static char *kwlist[] = {"py_size", NULL};
	PyObject *l = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "O:size_to_kbytes", kwlist, &l)) {
		PyErr_SetString(PyExc_AssertionError,
				"size_to_kbytes: Failed to parse arguments");
		return NULL;
	}

	return (PyLong_FromUnsignedLongLong(pps_size_to_kbytes(l)));
}

/**
 * @brief
 *	set the hook debug input file name.
 */
void
pbs_python_set_hook_debug_input_fp(FILE *fp)
{
	hook_debug.input_fp = fp;
}

/**
 * @brief
 *	get the reference to hook debug input file
 *
 */
FILE *
pbs_python_get_hook_debug_input_fp(void)
{
	return (hook_debug.input_fp);
}

/**
 * @brief
 *	set the hook debug input file name.
 *
 */
void
pbs_python_set_hook_debug_input_file(char *filename)
{
	if (filename != NULL)
		strncpy(hook_debug.input_file, filename, MAXPATHLEN);
}

/**
 * @brief
 *      get the reference to hook debug input file
 *
 */
char *
pbs_python_get_hook_debug_input_file(void)
{
	if (hook_debug.input_file == NULL)
		return NULL;
	return (hook_debug.input_file);
}

/**
 * @brief
 *      get the reference to hook debug output file
 *
 */
void
pbs_python_set_hook_debug_output_fp(FILE *fp)
{
	hook_debug.output_fp = fp;
}

/**
 * @brief
 *	get reference of hook debug output file
 *
 */
FILE *
pbs_python_get_hook_debug_output_fp(void)
{
	return (hook_debug.output_fp);
}

/**
 * @brief
 *      set hook debug output file
 *
 */

void
pbs_python_set_hook_debug_output_file(char *filename)
{
	if (filename != NULL)
		strncpy(hook_debug.output_file, filename, MAXPATHLEN);
}

/**
 * @brief
 *      get the reference to hook debug output file
 *
 */

char *
pbs_python_get_hook_debug_output_file(void)
{
	if (hook_debug.output_file == NULL)
		return NULL;
	return (hook_debug.output_file);
}

/**
 * @brief
 *      set hook debug input file
 *
 */

void
pbs_python_set_hook_debug_data_fp(FILE *fp)
{
	hook_debug.data_fp = fp;
}

/**
 * @brief
 *	get reference of hook debug data file.
 *
 */
FILE *
pbs_python_get_hook_debug_data_fp(void)
{
	return (hook_debug.data_fp);
}

/**
 * @brief
 *	set hook debug data file.
 *
 */
void
pbs_python_set_hook_debug_data_file(char *filename)
{
	if (filename != NULL)
		strncpy(hook_debug.data_file, filename, MAXPATHLEN);
}

/**
 * @brief
 *	get hook debug data file name.
 */
char *
pbs_python_get_hook_debug_data_file(void)
{
	if (hook_debug.data_file == NULL)
		return NULL;
	return (hook_debug.data_file);
}

/**
 * @brief
 *	set the hook debug object name
 */
void
pbs_python_set_hook_debug_objname(char *objname)
{
	if (objname != NULL)
		strncpy(hook_debug.objname, objname, HOOK_BUF_SIZE);
}

/**
 * @brief
 *	get the hook debug object name
 */
char *
pbs_python_get_hook_debug_objname(void)
{
	if (hook_debug.objname == NULL)
		return NULL;
	return (hook_debug.objname);
}
/**
 * @brief
 *	set servr information in server data.
 *
 */
void
pbs_python_set_server_info(pbs_list_head *server_input)
{
	server_data = server_input;
}

/**
 * @brief
 *	set the job information into server_job data.
 */
void
pbs_python_set_server_jobs_info(pbs_list_head *server_jobs_input,
				pbs_list_head *ids)
{
	server_jobs.data = server_jobs_input;
	server_jobs.ids = ids;
}

/**
 * @brief
 *      set the queue information into server_queue data.
 */

void
pbs_python_set_server_queues_info(pbs_list_head *server_queues_input,
				  pbs_list_head *names)
{
	server_queues.data = server_queues_input;
	server_queues.names = names;
}

/**
 * @brief
 *      set the reservation information into server's reservation data.
 */

void
pbs_python_set_server_resvs_info(pbs_list_head *server_resvs_input,
				 pbs_list_head *resvids)
{
	server_resvs.data = server_resvs_input;
	server_resvs.resvids = resvids;
}

/**
 * @brief
 *      set the vnode information into server vnode data.
 */

void
pbs_python_set_server_vnodes_info(pbs_list_head *server_vnodes_input,
				  pbs_list_head *names)
{
	server_vnodes.data = server_vnodes_input;
	server_vnodes.names = names;
}

/**
 * @brief
 *      unset the server information.
 *
 */

void
pbs_python_unset_server_info(void)
{
	server_data = NULL;
}

/**
 * @brief
 *	unset job info from server_job data
 */
void
pbs_python_unset_server_jobs_info(void)
{
	server_jobs.data = NULL;
	server_jobs.ids = NULL;
}

/**
 * @brief
 *      unset queue info from server_queue data
 */

void
pbs_python_unset_server_queues_info(void)
{
	server_queues.data = NULL;
	server_queues.names = NULL;
}

/**
 * @brief
 *      unset reservation info from server_resv data
 */

void
pbs_python_unset_server_resvs_info(void)
{
	server_resvs.data = NULL;
	server_resvs.resvids = NULL;
}

/**
 * @brief
 *      unset vnode info from server_vnode data
 */

void
pbs_python_unset_server_vnodes_info(void)
{
	server_vnodes.data = NULL;
	server_vnodes.names = NULL;
}

/**
 *
 * @brief
 * 	Helper method returning a server Python Object representing the local
 *	(current) server, with data taken from some a static source.
 *  @note
 *	This marks the server object "read-only" in Python mode.
 *
 * @return	PyObject *	pointer to a Python server object to map the
 *				local server values.
 */
static PyObject *
py_get_server_static(void)
{
	PyObject *py_svr_class = NULL;
	PyObject *py_svr = NULL;
	PyObject *py_sargs = NULL;
	int tmp_rc = -1;
	char perf_label[MAXBUFLEN];

	if (!use_static_data || (server_data == NULL))
		Py_RETURN_NONE;

	py_svr_class = pbs_python_types_table[PP_SVR_IDX].t_class;

	py_sargs = Py_BuildValue("(s)", server_name); /* NEW ref */
	if (!py_sargs) {
		log_err(-1, pbs_python_daemon_name, "could not build args list for server");
		goto server_static_error_exit;
	}

	py_svr = PyObject_Call(py_svr_class, py_sargs, NULL);
	if (!py_svr) {
		log_err(-1, pbs_python_daemon_name, "failed to create a python server object");
		goto server_static_error_exit;
	}
	Py_CLEAR(py_sargs);

	snprintf(perf_label, sizeof(perf_label), "hook_func:%s(%s)", SERVER_OBJECT, server_name);
	tmp_rc = pbs_python_populate_python_class_from_svrattrl(py_svr,
								server_data, perf_label, HOOK_PERF_POPULATE);

	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__,
			"partially populated python server object");
	}

	tmp_rc = pbs_python_mark_object_readonly(py_svr);

	if (tmp_rc == -1) {
		log_err(PBSE_INTERNAL, __func__, "Failed to mark server readonly!");
		goto server_static_error_exit;
	}

	object_counter++;
	return py_svr;
server_static_error_exit:
	if (PyErr_Occurred())
		pbs_python_write_error_to_log(__func__);
	if (py_sargs)
		Py_CLEAR(py_sargs);
	if (py_svr)
		Py_CLEAR(py_svr);

	PyErr_SetString(PyExc_AssertionError, "Failed to create server object");

	return NULL;
}

const char pbsv1mod_meth_get_server_static_doc[] =
	"get_server_static()\n\
\n\
  returns:\n\
         a Python server object representing the current instance of the.\n\
         PBS server from a static source.\n\
         or None of static data source\n\
";

/**
 * @brief
 *	return server object representing the current instance of the PBS server.
 *
 * @return	PyObject *
 * @retval	PBS server	success
 * @retval	NULL		error
 */
PyObject *
pbsv1mod_meth_get_server_static(void)
{
	PyObject *py_obj = NULL;

	hook_set_mode = C_MODE;
	py_obj = py_get_server_static();
	hook_set_mode = PY_MODE;
	return (py_obj);
}

/**
 * @brief
 *	Returns a Python object that maps to a struct queue * taken directly
 *	from static server_queues data,, or a list of queue names if
 *	'qname' is the empty string ("")..
 * @param[in]	qname		- name of a queue to obtain "struct queue *"
 *				  content to populate a Python queue object,
 *				  or  the empty string ("") to obtain the
 *				  list of queue names.
 * @return      PyObject *	- the Python queue object corresponding to
 *				  'qname', or to a list queue names.
 */
static PyObject *
py_get_queue_static(char *qname, char *svr_name)
{
	PyObject *py_queue_class = NULL;
	PyObject *py_queue = NULL;
	PyObject *py_qargs = NULL;
	svrattrl *plist, *plist_next;
	char *p = NULL;
	char *pn = NULL;
	char *p1 = NULL;
	char *attr_name = NULL;
	pbs_list_head queuel;
	int rc;
	char perf_label[MAXBUFLEN];

	if (!use_static_data || (server_queues.data == NULL)) {
		Py_RETURN_NONE;
	}

	if (qname == NULL) {
		log_err(PBSE_INTERNAL, __func__,
			"Unable to populate python queue object");
		return NULL;
	}

	if (qname[0] == '\0') {
		return (create_py_strlist_from_svrattrl_names(server_queues.names));
	}

	CLEAR_HEAD(queuel);

	plist = (svrattrl *) GET_NEXT(*server_queues.data);
	do {
		if (plist == NULL)
			break;

		plist_next = (svrattrl *) GET_NEXT(plist->al_link);

		/* look for last dot as the name could be dotted like a queue name */
		p = strrchr(plist->al_name, '.');
		if (p == NULL) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "warning: encountered an attribute %s without a queue name...ignoring", plist->al_name);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			plist = plist_next;
			continue;
		}
		*p = '\0'; /* now plist->al_name would be the queue name */
		if (strcmp(plist->al_name, qname) != 0) {
			*p = '.'; /* restore */
			plist = plist_next;
			continue;
		}
		attr_name = p + 1; /* p will be the actual attribute name */

		p1 = NULL;
		if (plist->al_resc != NULL) {
			p1 = strchr(plist->al_resc, ',');
			if (p1 != NULL) {
				*p1 = '\0'; /* now plist->al_resc is just */
					    /* the resource */
			}
		}

		if ((strcmp(attr_name, ATTR_server) == 0) &&
		    (svr_name != NULL) && (svr_name[0] != '\0') &&
		    (strcmp(svr_name, "localhost") != 0) &&
		    (strcmp(plist->al_value, svr_name) != 0)) {
			if (p != NULL)
				*p = '.'; /* restore orig plist->al_name */
			if (p1 != NULL)
				*p1 = ','; /* restore orig plist->al_resc */
			free_attrlist(&queuel);
			Py_RETURN_NONE;
		}

		if (add_to_svrattrl_list(&queuel, attr_name,
					 plist->al_resc, plist->al_value, 0, NULL) != 0) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "warning: failed to add_to_svrattrl_list(%s,%s,%s)",
				 plist->al_name,
				 plist->al_resc ? plist->al_resc : "", plist->al_value);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			if (p != NULL)
				*p = '.'; /* restore orig plist->al_name */
			if (p1 != NULL)
				*p1 = ','; /* restore the orig plist->al_resc */
			goto get_queue_error_exit;
		}
		if (p1 != NULL)
			*p1 = ',';

		/* Check if we're done processing the attributes/resources */
		/* of the current queue. 				    */
		pn = NULL;
		if (plist_next != NULL) {
			/* look at last dot for "dotted" queue names */
			pn = strrchr(plist_next->al_name, '.');
			if (pn == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "warning: encountered the next attribute %s without a queue name...ignoring", plist_next->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				/* skip the next one */
				plist = (svrattrl *) GET_NEXT(plist_next->al_link);
				if (p != NULL)
					*p = '.'; /* restore orig plist->al_name */
				continue;
			}
			*pn = '\0'; /* now plist_next->al_name would be the */
			/* queue name */
			/* The next queuelist entry is for a different queue name */
			/* since list is sorted according to queue name */
			if ((strcmp(plist->al_name, plist_next->al_name) != 0)) {
				if (p != NULL)
					*p = '.'; /* restore orig plist->al_name */
				*pn = '.';	  /* restore orig plist_next->al_name */
				break;
			}
			*pn = '.'; /* restore orig plist_next->al_name */
		}

		plist = plist_next;
		if (p != NULL)
			*p = '.'; /* restore orig plist->al_name */

	} while (plist);
	if (GET_NEXT(queuel) == NULL)
		Py_RETURN_NONE;

	py_queue_class = pbs_python_types_table[PP_QUE_IDX].t_class;
	py_qargs = Py_BuildValue("(s)", qname); /* NEW ref */
	if (py_qargs == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "could not build args list for queue %s",
			 plist->al_name);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto get_queue_error_exit;
	}

	py_queue = PyObject_Call(py_queue_class, py_qargs,
				 NULL); /* NEW ref */
	if (py_queue == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "failed to create a python queue %s object",
			 plist->al_name);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto get_queue_error_exit;
	}

	snprintf(perf_label, sizeof(perf_label), "hook_func:%s(%s)", SERVER_QUEUE_OBJECT, qname);
	rc = pbs_python_populate_python_class_from_svrattrl(py_queue, &queuel, perf_label, HOOK_PERF_POPULATE);

	if (rc == -1) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "failed to fully populate Python"
			 " queue %s object",
			 plist->al_name);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto get_queue_error_exit;
	}

	free_attrlist(&queuel);
	CLEAR_HEAD(queuel);
	Py_CLEAR(py_qargs);

	return (py_queue);

get_queue_error_exit:
	if (PyErr_Occurred())
		pbs_python_write_error_to_log(__func__);
	Py_CLEAR(py_qargs);
	Py_CLEAR(py_queue);
	PyErr_SetString(PyExc_AssertionError, "Failed to create queue object");

	return NULL;
}

const char pbsv1mod_meth_get_queue_static_doc[] =
	"get_queue_static(qnamne)\n\
\n\
  'qnamne' is the name of queue whose info is beting returned.\n\
  returns:\n\
         a Python queue object representing the current instance of the.\n\
         PBS queue 'qnamne', from a static source.\n\
         or None of static data source is not available.\n\
";

/**
 * @brief
 *	return a Python queue object representing the current instance
 *	of the PBS queue 'qnamne' from a static source.
 *
 * @return	PyObject *
 * @retval	reference to queue	success
 * @retval	NULL			error
 *
 * @par	Note:
 *	'qnamne' is the name of queue whose info is beting returned.
 *
 */
PyObject *
pbsv1mod_meth_get_queue_static(PyObject *self, PyObject *args, PyObject *kwds)
{

	static char *kwlist[] = {"queue", "server_name", NULL};
	char *qname = NULL;
	char *svr_name = NULL;
	PyObject *py_obj = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "ss:get_queue_static",
					 kwlist,
					 &qname,
					 &svr_name)) {
		return NULL;
	}

	hook_set_mode = C_MODE;
	py_obj = py_get_queue_static(qname, svr_name);
	hook_set_mode = PY_MODE;
	return (py_obj);
}

/**
 * @brief
 *	Returns a Python object that maps to a struct pbsnodes * taken directly
 *	from static server_vnodes, or a list of vnode names if 'vname'
 *	given is "".
 * @param[in]	vname		- name of a vnode to obtain "struct pbsnode *"
 *				  content to populate a Python vnode object,
 *				  or the empty string ("") to match a list of
 *				  vnode names.
 * @return      PyObject *	- the Python vnode object corresponding to
 *				  'vname', or a list of vnode names.
 * @retval	<actual_vnode_object>
 * @retval	None		- if vnode object was not found.
 */
static PyObject *
py_get_vnode_static(char *vname, char *svr_name)
{
	PyObject *py_vnode_class = NULL;
	PyObject *py_vnode = NULL;
	PyObject *py_vargs = NULL;
	svrattrl *plist, *plist_next;
	char *p = NULL;
	char *pn = NULL;
	char *p1 = NULL;
	char *attr_name = NULL;
	pbs_list_head vnodel;
	int rc;
	char perf_label[MAXBUFLEN];

	if (!use_static_data || (server_vnodes.data == NULL)) {
		Py_RETURN_NONE;
	}

	if (vname == NULL) {
		log_err(PBSE_INTERNAL, __func__,
			"Unable to populate python vnode object");
		return NULL;
	}

	if (vname[0] == '\0') {
		return (create_py_strlist_from_svrattrl_names(server_vnodes.names));
	}

	CLEAR_HEAD(vnodel);

	/* NOTE: The list is sorted according to vnode name */
	plist = (svrattrl *) GET_NEXT(*server_vnodes.data);
	do {
		if (plist == NULL)
			break;

		plist_next = (svrattrl *) GET_NEXT(plist->al_link);

		/* look for last dot as the name could be dotted like a node name */
		p = strrchr(plist->al_name, '.');
		if (p == NULL) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "warning: encountered an attribute %s without a node name...ignoring", plist->al_name);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			plist = plist_next;
			continue;
		}
		*p = '\0'; /* now plist->al_name would be the node name */
		if (strcmp(plist->al_name, vname) != 0) {
			plist = plist_next;
			*p = '.'; /* restore orig plist->al_name */
			continue;
		}
		attr_name = p + 1; /* p will be the actual attribute name */
		p1 = NULL;
		if (plist->al_resc != NULL) {
			p1 = strchr(plist->al_resc, ',');
			if (p1 != NULL) {
				*p1 = '\0';
			}
		}

		if ((strcmp(attr_name, ATTR_server) == 0) &&
		    (svr_name != NULL) && (svr_name[0] != '\0') &&
		    (strcmp(svr_name, "localhost") != 0) &&
		    (strcmp(plist->al_value, svr_name) != 0)) {
			if (p != NULL)
				*p = '.'; /* restore orig plist->al_name */
			if (p1 != NULL)
				*p1 = ','; /* restore orig plist->al_resc */
			free_attrlist(&vnodel);
			Py_RETURN_NONE;
		}

		if (add_to_svrattrl_list(&vnodel, attr_name,
					 plist->al_resc, plist->al_value, 0, NULL) != 0) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "warning: failed to add_to_svrattrl_list(%s,%s,%s)",
				 plist->al_name,
				 plist->al_resc ? plist->al_resc : "", plist->al_value);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			if (p != NULL)
				*p = '.'; /* restore orig plist->al_name */
			if (p1 != NULL)
				*p1 = ','; /* restore orig plist->al_resc */
			goto get_vnode_error_exit;
		}

		if (p1 != NULL)
			*p1 = ','; /* restore the orig plist->al_resc */

		/* Check if we're done processing the attributes/resources */
		/* of the current node. 				    */
		pn = NULL;
		if (plist_next != NULL) {

			/* look at last dot for "dotted" node names */
			pn = strrchr(plist_next->al_name, '.');
			if (pn == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "warning: encountered the next attribute %s without a node name...ignoring", plist_next->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				/* skip the next one */
				plist = (svrattrl *) GET_NEXT(plist_next->al_link);
				if (p != NULL)
					*p = '.'; /* restore orig plist->al_name */
				continue;
			}
			*pn = '\0'; /* now plist_next->al_name would be the */
			/* node name */

			if ((strcmp(plist->al_name, plist_next->al_name) != 0)) {
				*pn = '.';
				if (p != NULL)
					*p = '.'; /* restore orig plist->al_name */
				break;
			}
			*pn = '.'; /* restore orig plist_next->al_name */
		}

		plist = plist_next;
		if (p != NULL)
			*p = '.'; /* restore orig plist->al_name */

	} while (plist);

	if (GET_NEXT(vnodel) == NULL)
		Py_RETURN_NONE;

	py_vnode_class = pbs_python_types_table[PP_VNODE_IDX].t_class;
	py_vargs = Py_BuildValue("(s)", vname); /* NEW ref */
	if (py_vargs == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "could not build args list for vnode %s",
			 plist->al_name);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto get_vnode_error_exit;
	}

	py_vnode = PyObject_Call(py_vnode_class, py_vargs,
				 NULL); /* NEW ref */
	if (py_vnode == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "failed to create a python vnode %s object",
			 plist->al_name);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto get_vnode_error_exit;
	}

	snprintf(perf_label, sizeof(perf_label), "hook_func:%s(%s)", SERVER_VNODE_OBJECT, vname);
	rc = pbs_python_populate_python_class_from_svrattrl(py_vnode, &vnodel, perf_label, HOOK_PERF_POPULATE);
	if (rc == -1) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "failed to fully populate Python"
			 " vnode %s object",
			 plist->al_name);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto get_vnode_error_exit;
	}

	free_attrlist(&vnodel);
	CLEAR_HEAD(vnodel);
	Py_CLEAR(py_vargs);

	return (py_vnode);

get_vnode_error_exit:
	if (PyErr_Occurred())
		pbs_python_write_error_to_log(__func__);
	Py_CLEAR(py_vargs);
	Py_CLEAR(py_vnode);
	PyErr_SetString(PyExc_AssertionError, "Failed to create vnode object");

	return NULL;
}

const char pbsv1mod_meth_get_vnode_static_doc[] =
	"get_vnode_static(vname)\n\
\n\
  'vname' is the name of vnode whose info is beting returned.\n\
  returns:\n\
         a Python vnode object representing the current instance of the.\n\
         PBS vnode 'vname', from a static source.\n\
         or None of static data source is not available.\n\
";
/**
 * @brief
 *	get vnode info
 *
 * @par Note:
 *	'vname' is the name of vnode whose info is beting returned.
 *
 * @return	PyObject*
 * @retval	a Python vnode object representing the current instance of the.\n
 *		PBS vnode 'vname', from a static source.				success
 * @retval	None of static data source is not available.				error
 *
 */
PyObject *
pbsv1mod_meth_get_vnode_static(PyObject *self, PyObject *args, PyObject *kwds)
{

	static char *kwlist[] = {"vnode", "server_name", NULL};
	char *vname = NULL;
	char *svr_name = NULL;
	PyObject *py_obj = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "ss:get_vnode_static",
					 kwlist,
					 &vname,
					 &svr_name)) {
		return NULL;
	}

	hook_set_mode = C_MODE;
	py_obj = py_get_vnode_static(vname, svr_name);
	hook_set_mode = PY_MODE;
	return (py_obj);
}

/**
 * @brief
 *	If 'jid' is not the empty string (""), then this returns a Python
 *	job object that maps to a struct job * taken directly from static
 *	server_jobs data.
 *	If 'jid' is the empty string (""), this returns a Python list
 *	string object enumerating the list of jobs found.
 *
 * @param[in]	jid		- name of a job to obtain "struct job *"
 *				  content to populate a Python job object,
 *				  or the empty string "", to enumerate
 *				  the list of jobs.
 *
 * @param[in]	svr_name	- return job @ server_name only
 * @param[in]	queue_name	- return job @ queue_name only
 *
 * @return      PyObject *	- the Python job object corresponding to
 *				  'jid'.
 * @retval	<job_object> or <list of string objects>
 * @retval	None		- if no matching job.
 */
static PyObject *
py_get_job_static(char *jid, char *svr_name, char *queue_name)
{
	PyObject *py_job_class = NULL;
	PyObject *py_job = NULL;
	PyObject *py_jargs = NULL;
	svrattrl *plist, *plist_next;
	char *p = NULL;
	char *pn = NULL;
	char *p1 = NULL;
	char *attr_name = NULL;
	pbs_list_head jobl;
	/* set job.queue to actual queue object */
	char *qname;
	PyObject *py_server = NULL;
	PyObject *py_que = NULL;
	int rc;
	char perf_label[MAXBUFLEN];

	if (!use_static_data || (server_jobs.data == NULL)) {
		Py_RETURN_NONE;
	}

	if (jid == NULL) {
		log_err(PBSE_INTERNAL, __func__,
			"Unable to populate python job object");
		return NULL;
	}

	if (jid[0] == '\0') {
		return (create_py_strlist_from_svrattrl_names(server_jobs.ids));
	}

	CLEAR_HEAD(jobl);

	/* NOTE: The list is sorted according to job name */
	plist = (svrattrl *) GET_NEXT(*server_jobs.data);
	do {
		if (plist == NULL)
			break;

		plist_next = (svrattrl *) GET_NEXT(plist->al_link);

		/* look for last dot as the name could be dotted like a job name */
		p = strrchr(plist->al_name, '.');
		if (p == NULL) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "warning: encountered an attribute %s without a job name...ignoring", plist->al_name);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			plist = plist_next;
			continue;
		}
		*p = '\0'; /* now plist->al_name would be the job name */
		if (strcmp(plist->al_name, jid) != 0) {
			*p = '.'; /* restore orig plist->al_name */
			plist = plist_next;
			continue;
		}
		attr_name = p + 1; /* p will be the actual attribute name */
		p1 = NULL;
		if (plist->al_resc != NULL) {
			p1 = strchr(plist->al_resc, ',');
			if (p1 != NULL) {
				*p1 = '\0';
			}
		}

		if ((strcmp(attr_name, ATTR_server) == 0) &&
		    (svr_name != NULL) && (svr_name[0] != '\0') &&
		    (strcmp(svr_name, "localhost") != 0) &&
		    (strcmp(plist->al_value, svr_name) != 0)) {
			if (p != NULL)
				*p = '.'; /* restore orig plist->al_name */
			if (p1 != NULL)
				*p1 = ','; /* restore orig plist->al_resc */
			free_attrlist(&jobl);
			Py_RETURN_NONE;
		}

		if ((strcmp(attr_name, ATTR_queue) == 0) &&
		    (queue_name != NULL) && (queue_name[0] != '\0') &&
		    (strcmp(plist->al_value, queue_name) != 0)) {
			if (p != NULL)
				*p = '.'; /* restore orig plist->al_name */
			if (p1 != NULL)
				*p1 = ','; /* restore orig plist->al_resc */
			free_attrlist(&jobl);
			Py_RETURN_NONE;
		}

		if (add_to_svrattrl_list(&jobl, attr_name,
					 plist->al_resc, plist->al_value, 0, NULL) != 0) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "warning: failed to add_to_svrattrl_list(%s,%s,%s)",
				 plist->al_name,
				 plist->al_resc ? plist->al_resc : "", plist->al_value);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			if (p != NULL)
				*p = '.'; /* restore orig plist->al_name */
			if (p1 != NULL)
				*p1 = ','; /* restore orig plist->al_resc */

			goto get_job_error_exit;
		}
		if (p1 != NULL)
			*p1 = ','; /* restore the orig plist->al_resc */

		/* Check if we're done processing the attributes/resources */
		/* of the current job. 				    */
		pn = NULL;
		if (plist_next != NULL) {

			/* look at last dot for "dotted" job names */
			pn = strrchr(plist_next->al_name, '.');
			if (pn == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "warning: encountered the next attribute %s without a job name...ignoring", plist_next->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				/* skip the next one */
				plist = (svrattrl *) GET_NEXT(plist_next->al_link);
				if (p != NULL)
					*p = '.'; /* restore orig plist->al_name */
				continue;
			}
			*pn = '\0'; /* now plist_next->al_name would be the */
			/* job name */

			if ((strcmp(plist->al_name, plist_next->al_name) != 0)) {
				*pn = '.'; /* restore orig plist_next->al_name */
				if (p != NULL)
					*p = '.'; /* restore orig plist->al_name */
				break;
			}
			*pn = '.'; /* restore orig plist_next->al_name */
		}

		plist = plist_next;
		if (p != NULL)
			*p = '.'; /* restore orig plist->al_name */

	} while (plist);

	if (GET_NEXT(jobl) == NULL) {
		free_attrlist(&jobl);
		Py_RETURN_NONE;
	}

	py_job_class = pbs_python_types_table[PP_JOB_IDX].t_class;
	py_jargs = Py_BuildValue("(s)", jid); /* NEW ref */
	if (py_jargs == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "could not build args list for job %s",
			 plist ? plist->al_name : "");
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto get_job_error_exit;
	}

	py_job = PyObject_Call(py_job_class, py_jargs,
			       NULL); /* NEW ref */
	if (py_job == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "failed to create a python job %s object",
			 plist ? plist->al_name : "");
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto get_job_error_exit;
	}

	snprintf(perf_label, sizeof(perf_label), "hook_func:%s(%s)", SERVER_JOB_OBJECT, jid);
	rc = pbs_python_populate_python_class_from_svrattrl(py_job, &jobl, perf_label, HOOK_PERF_POPULATE);

	if (rc == -1) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "failed to fully populate Python"
			 " job %s object",
			 plist ? plist->al_name : "");
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto get_job_error_exit;
	}

	free_attrlist(&jobl);
	CLEAR_HEAD(jobl);
	Py_CLEAR(py_jargs);

	if (PyObject_HasAttrString(py_job, ATTR_queue)) {
		qname = pbs_python_object_get_attr_string_value(py_job, ATTR_queue);
		if (qname != NULL) {
			py_que = py_get_queue_static(qname, svr_name); /* NEW ref */
			if (py_que != NULL) {
				/* py_server queue ref ct incremented as part of py_job */
				(void) PyObject_SetAttrString(py_job, ATTR_queue, py_que);
			}
			Py_DECREF(py_que); /* we no longer need to reference */
		}
	}

	/* set job.server to actual server object */
	py_server = py_get_server_static(); /* NEW Ref */

	if (py_server != NULL) {
		if (PyObject_HasAttrString(py_job, ATTR_server)) {
			/* py_server ref ct incremented as part of py_job */
			(void) PyObject_SetAttrString(py_job, ATTR_server, py_server);
		}
		Py_DECREF(py_server);
	}

	return (py_job);

get_job_error_exit:
	if (PyErr_Occurred())
		pbs_python_write_error_to_log(__func__);
	free_attrlist(&jobl);
	Py_CLEAR(py_jargs);
	Py_CLEAR(py_job);
	PyErr_SetString(PyExc_AssertionError, "Failed to create job object");

	return NULL;
}

const char pbsv1mod_meth_get_job_static_doc[] =
	"get_job_static(jobid, queue_name, server_name)\n\
\n\
  'jobid' is the name of job whose info s getting returned.\n\
  'queue_name' is the name of the queue where 'jobid' reside.\n\
  'server_name' is the name of the server owning 'jobid'.\n\
  returns:\n\
         a Python job object representing the current instance of the.\n\
         PBS job 'jobid', from a static source.\n\
         or None of static data source is not available.\n\
";

/**
 * @brief
 *	a Python job object representing the current instance of the.
 *	PBS job 'jobid', from a static source.
 *
 * @return	PyObject *
 * @retval	reference to jobid	success
 * @retval	NULL			error
 *
 */

PyObject *
pbsv1mod_meth_get_job_static(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {"job", "server_name", "queue_name", NULL};
	char *jobid = NULL;
	PyObject *py_obj = NULL;
	char *qname = NULL;
	char *sname = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "sss:get_job_static",
					 kwlist,
					 &jobid,
					 &sname,
					 &qname)) {
		return NULL;
	}
	hook_set_mode = C_MODE;
	py_obj = py_get_job_static(jobid, sname, qname);
	hook_set_mode = PY_MODE;
	return (py_obj);
}

/**
 * @brief
 *	Returns a Python object that maps to a struct resv * taken directly
 *	from static server_resvs, or a list of reservation ids if 'resvid'
 *	matches empty string ("").
 * @param[in]	resvid		- name of a resv to obtain "struct resv *"
 *				  content to populate a Python resv object,
 *				  or the empty string ("") to return the
 *				  list of reservation names.
 * @param[in]	svr_name	- return resv @ server_name only
 * @param[in]	queue_name	- return resv @ queue_name only
 * @return      PyObject *	- the Python resv object corresponding to
 *				  'resvid'.
 */
static PyObject *
py_get_resv_static(char *resvid, char *svr_name)
{
	PyObject *py_resv_class = NULL;
	PyObject *py_resv = NULL;
	PyObject *py_rargs = NULL;
	svrattrl *plist, *plist_next;
	char *p = NULL;
	char *pn = NULL;
	char *p1 = NULL;
	char *attr_name = NULL;
	pbs_list_head resvl;
	PyObject *py_server = NULL;
	PyObject *py_que = NULL;
	int rc;
	char perf_label[MAXBUFLEN];

	if (!use_static_data || (server_resvs.data == NULL)) {
		Py_RETURN_NONE;
	}

	if (resvid == NULL) {
		log_err(PBSE_INTERNAL, __func__,
			"Unable to populate python resv object");
		return NULL;
	}

	if (resvid[0] == '\0') {
		return (create_py_strlist_from_svrattrl_names(server_resvs.resvids));
	}

	CLEAR_HEAD(resvl);
	/* NOTE: The list is sorted according to resv name */
	plist = (svrattrl *) GET_NEXT(*server_resvs.data);
	do {
		if (plist == NULL)
			break;

		plist_next = (svrattrl *) GET_NEXT(plist->al_link);

		/* look for last dot as the name could be dotted like a resv name */
		p = strrchr(plist->al_name, '.');
		if (p == NULL) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "warning: encountered an attribute %s without a resv name...ignoring", plist->al_name);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			plist = plist_next;
			continue;
		}
		*p = '\0'; /* now plist->al_name would be the resv name */

		if (strcmp(plist->al_name, resvid) != 0) {
			*p = '.'; /* restore orig plist->al_name */
			plist = plist_next;
			continue;
		}
		attr_name = p + 1; /* p will be the actual attribute name */

		p1 = NULL;
		if (plist->al_resc != NULL) {
			p1 = strchr(plist->al_resc, ',');
			if (p1 != NULL) {
				*p1 = '\0';
			}
		}

		if ((strcmp(attr_name, ATTR_server) == 0) &&
		    (svr_name != NULL) && (svr_name[0] != '\0') &&
		    (strcmp(svr_name, "localhost") != 0) &&
		    (strcmp(plist->al_value, svr_name) != 0)) {
			if (p != NULL)
				*p = '.'; /* restore orig plist->al_name */
			if (p1 != NULL)
				*p1 = ','; /* restore orig plist->al_resc */
			free_attrlist(&resvl);
			Py_RETURN_NONE;
		}

		if (add_to_svrattrl_list(&resvl, attr_name,
					 plist->al_resc, plist->al_value, 0, NULL) != 0) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "warning: failed to add_to_svrattrl_list(%s,%s,%s)",
				 plist->al_name,
				 plist->al_resc ? plist->al_resc : "", plist->al_value);
			log_err(PBSE_INTERNAL, __func__, log_buffer);
			if (p != NULL)
				*p = '.'; /* restore orig plist->al_name */
			if (p1 != NULL)
				*p1 = ','; /* restore orig plist->al_resc */
			goto get_resv_error_exit;
		}
		if (p1 != NULL) {
			*p1 = ','; /* restore the orig plist->al_resc */
		}

		/* Check if we're done processing the attributes/resources */
		/* of the current resv. 				    */
		pn = NULL;
		if (plist_next != NULL) {

			/* look at last dot for "dotted" resv names */
			pn = strrchr(plist_next->al_name, '.');
			if (pn == NULL) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "warning: encountered the next attribute %s without a resv name...ignoring", plist_next->al_name);
				log_err(PBSE_INTERNAL, __func__, log_buffer);
				/* skip the next one */
				plist = (svrattrl *) GET_NEXT(plist_next->al_link);
				if (p != NULL)
					*p = '.'; /* restore orig plist->al_name */
				continue;
			}
			*pn = '\0'; /* now plist_next->al_name would be the */
			/* resv name */

			if ((strcmp(plist->al_name, plist_next->al_name) != 0)) {
				*pn = '.'; /* restore orig plist_next->al_name */
				if (p != NULL)
					*p = '.'; /* restore orig plist->al_name */
				break;
			}
		}

		plist = plist_next;
		if (p != NULL)
			*p = '.'; /* restore orig plist->al_name */

	} while (plist);

	if (GET_NEXT(resvl) == NULL) {
		free_attrlist(&resvl);
		Py_RETURN_NONE;
	}

	py_resv_class = pbs_python_types_table[PP_RESV_IDX].t_class;
	py_rargs = Py_BuildValue("(s)", resvid); /* NEW ref */
	if (py_rargs == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "could not build args list for resv %s",
			 plist->al_name);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto get_resv_error_exit;
	}

	py_resv = PyObject_Call(py_resv_class, py_rargs,
				NULL); /* NEW ref */
	if (py_resv == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "failed to create a python resv %s object",
			 plist->al_name);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto get_resv_error_exit;
	}

	snprintf(perf_label, sizeof(perf_label), "hook_func:%s(%s)", SERVER_RESV_OBJECT, resvid);
	rc = pbs_python_populate_python_class_from_svrattrl(py_resv, &resvl, perf_label, HOOK_PERF_POPULATE);

	if (rc == -1) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "failed to fully populate Python"
			 " resv %s object",
			 plist->al_name);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto get_resv_error_exit;
	}

	free_attrlist(&resvl);
	CLEAR_HEAD(resvl);
	Py_CLEAR(py_rargs);

	if (PyObject_HasAttrString(py_resv, ATTR_queue)) {
		char *qname;

		qname = pbs_python_object_get_attr_string_value(py_resv, ATTR_queue);
		if (qname != NULL) {
			py_que = py_get_queue_static(qname, svr_name); /* NEW ref */
			if (py_que != NULL) {
				/* py_server queue ref ct incremented as part of py_resv */
				(void) PyObject_SetAttrString(py_resv, ATTR_queue, py_que);
				Py_DECREF(py_que); /* we no longer need to reference */
			}
		}
	}

	/* set resv.server to actual server object */
	py_server = py_get_server_static(); /* NEW Ref */

	if (py_server != NULL) {
		if (PyObject_HasAttrString(py_resv, ATTR_server)) {
			/* py_server ref ct incremented as part of py_resv */
			(void) PyObject_SetAttrString(py_resv, ATTR_server, py_server);
		}
		Py_DECREF(py_server);
	}

	return (py_resv);

get_resv_error_exit:
	if (PyErr_Occurred())
		pbs_python_write_error_to_log(__func__);
	free_attrlist(&resvl);
	Py_CLEAR(py_rargs);
	Py_CLEAR(py_resv);
	PyErr_SetString(PyExc_AssertionError, "Failed to create resv object");

	return NULL;
}

const char pbsv1mod_meth_get_resv_static_doc[] =
	"get_resv_static(resvid, server)\n\
\n\
  'resvid' is the name of resv whose info is beting returned.\n\
  'server' is the location of 'resvid'. Empty string ("
	") means local server\n\
  returns:\n\
         a Python resv object representing the current instance of the.\n\
         PBS resv 'resvid', from a static source.\n\
         or None of static data source is not available.\n\
";

/**
 * @brief
 *	'resvid' is the name of resv whose info is beting returned.
 *	server' is the location of 'resvid'. Empty string ("") means local server.
 *
 * @return	PyObject *
 * @retval	a Python resv object representing the current instance of the
 *		PBS resv 'resvid', from a static source.				success
 * @retval	None of static data source is not available				error
 */
PyObject *
pbsv1mod_meth_get_resv_static(PyObject *self, PyObject *args, PyObject *kwds)
{

	static char *kwlist[] = {"resv", "server_name", NULL};
	char *resvid = NULL;
	char *svr_name = NULL;
	PyObject *py_obj = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "ss:get_resv_static",
					 kwlist,
					 &resvid,
					 &svr_name)) {
		return NULL;
	}

	hook_set_mode = C_MODE;
	py_obj = py_get_resv_static(resvid, svr_name);
	hook_set_mode = PY_MODE;
	return (py_obj);
}

const char pbsv1mod_meth_get_server_data_fp_doc[] =
	"server_data_fp()\n\
\n\
   Returns the Python file object representing the already opened file 'fp.'\n\
\n\
";

/**
 * @brief
 *	Returns the Python file object representing the already opened file 'fp.
 */
PyObject *
pbsv1mod_meth_get_server_data_fp(void)
{
	PyObject *fp_obj = NULL;
	int data_fd;

	if (hook_debug.data_fp == NULL)
		Py_RETURN_NONE;

	data_fd = fileno(hook_debug.data_fp);

	fp_obj = PyFile_FromFd(data_fd, hook_debug.data_file, "w", -1,
			       NULL, NULL, NULL, 1);
	if (fp_obj == NULL)
		Py_RETURN_NONE;

	return (fp_obj);
}

const char pbsv1mod_meth_get_server_data_file_doc[] =
	"server_data_fp()\n\
\n\
   Returns the Python string representing the pathname to the hook data file.\n\
\n\
";

/**
 * @brief
 *	Returns the Python string representing the pathname to the hook data file.
 */
PyObject *
pbsv1mod_meth_get_server_data_file(void)
{
	if ((hook_debug.data_file == NULL) || (hook_debug.data_file[0] == '\0')) {
		Py_RETURN_NONE;
	}
	return (PyUnicode_FromString(hook_debug.data_file));
}

const char pbsv1mod_meth_use_static_data_doc[] =
	"use_static_data()\n\
\n\
  returns:\n\
         True if PBS is using static data for pbs.server()* calls.\n\
  	 This is an internal function.\n\
";

/**
 * @brief
 *	check for static data
 *
 * @return	PyObject*
 * @retval	Py_True	if yes
 * @retval	Py_False if not
 */
PyObject *
pbsv1mod_meth_use_static_data(void)
{
	PyObject *ret;
	ret = (use_static_data) ? Py_True : Py_False;
	Py_INCREF(ret);
	return (ret);
}

/**
 * @brief
 *	assign value to use_static_data
 */
void
pbs_python_set_use_static_data_value(int value)
{
	use_static_data = value;
}

/**
 * @brief
 * 	Set the environment variable 'env_var' to 'env_val'.
 *
 * @return int
 * @retval 0 	for success
 * @retval !=0	for error
 */
int
pbs_python_set_os_environ(char *env_var, char *env_val)
{
	PyObject *pystr_env_val = NULL;
	PyObject *pystr_env_var = NULL;
	PyObject *temp_item = NULL;
	PyObject *os_mod_obj = NULL; /* 'sys' module  */
	PyObject *os_mod_env = NULL; /* os.environ */

	if (env_var == NULL) {
		log_err(PBSE_INTERNAL, __func__, "passed NULL env_var!");
		return -1;
	}

	PyErr_Clear(); /* clear any exceptions */

	/* if sucess we get a NEW ref */
	if (!(os_mod_obj = PyImport_ImportModule("os"))) { /* failed */
		snprintf(log_buffer, sizeof(log_buffer), "%s:import os module",
			 __func__);
		pbs_python_write_error_to_log(log_buffer);
		return (-1);
	}

	/* if sucess we get a NEW ref */
	if ((os_mod_env =
		     PyObject_GetAttrString(os_mod_obj, "environ")) == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "%s:could not retrieve os environment",
			 __func__);
		pbs_python_write_error_to_log(log_buffer);
		Py_CLEAR(os_mod_obj);
		return (-1);
	}

	if ((pystr_env_var = PyUnicode_FromString(env_var)) == NULL) {
		snprintf(log_buffer, sizeof(log_buffer),
			 "%s:creating pystr_env_var <%s>",
			 __func__, env_var);
		pbs_python_write_error_to_log(log_buffer);
		Py_CLEAR(os_mod_obj);
		Py_CLEAR(os_mod_env);
		return (-1);
	}

	if (env_val == NULL) {

		if ((temp_item = PyObject_GetItem(os_mod_env, pystr_env_var)) != NULL) {
			if (PyObject_DelItem(os_mod_env,
					     pystr_env_var) == -1) {
				snprintf(log_buffer, sizeof(log_buffer),
					 "%s: error unsetting environment <%s>",
					 __func__, env_var);
				pbs_python_write_error_to_log(log_buffer);
				Py_CLEAR(os_mod_obj);
				Py_CLEAR(os_mod_env);
				Py_CLEAR(pystr_env_var);
				return (-1);
			}
			Py_CLEAR(temp_item);
		}
	} else {
		/* if sucess we get a NEW ref */
		if ((pystr_env_val = PyUnicode_FromString(env_val)) == NULL) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "%s:creating pystr_env_val <%s>",
				 __func__, env_val);
			pbs_python_write_error_to_log(log_buffer);
			Py_CLEAR(os_mod_obj);
			Py_CLEAR(os_mod_env);
			Py_CLEAR(pystr_env_var);
			return (-1);
		}

		if (PyObject_SetItem(os_mod_env, pystr_env_var,
				     pystr_env_val) == -1) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "%s: error setting os.environ[%s]=%s",
				 __func__, env_var, env_val);
			pbs_python_write_error_to_log(log_buffer);
			Py_CLEAR(os_mod_obj);
			Py_CLEAR(os_mod_env);
			Py_CLEAR(pystr_env_val);
			Py_CLEAR(pystr_env_var);
			return (-1);
		}
	}
	Py_CLEAR(os_mod_obj);
	Py_CLEAR(os_mod_env);
	Py_CLEAR(pystr_env_val);
	Py_CLEAR(pystr_env_var);

	return (0);
}

/**
 * @brief
 * 	Set the pbs.hook_config_filename value to 'conf_file'.
 *
 * @param[in]	conf_file - path to the pbs hook config file.
 *
 * @return int
 * @retval 0 	for success
 * @retval !=0	for error
 */
int
pbs_python_set_pbs_hook_config_filename(char *conf_file)
{
	PyObject *pbs_mod_obj = NULL; /* 'pbs' module  */
	char *configfile_attrname = "hook_config_filename";

	PyErr_Clear(); /* clear any exceptions */

	/* if success we get a NEW ref */
	if (!(pbs_mod_obj = PyImport_ImportModule(PBS_OBJ))) { /* failed */
		snprintf(log_buffer, sizeof(log_buffer), "%s:import pbs module",
			 __func__);
		pbs_python_write_error_to_log(log_buffer);
		return (-1);
	}

	if (conf_file != NULL) {
		if (pbs_python_object_set_attr_string_value(pbs_mod_obj,
							    configfile_attrname, conf_file) == -1) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "%s: error setting pbs.hook_config_filename = %s",
				 __func__, conf_file);
			pbs_python_write_error_to_log(log_buffer);
			Py_CLEAR(pbs_mod_obj);
			return (-1);
		}
	} else {
		if (PyObject_SetAttrString(pbs_mod_obj,
					   configfile_attrname, Py_None) == -1) {
			snprintf(log_buffer, sizeof(log_buffer),
				 "%s: error setting pbs.hook_config_filename = None",
				 __func__);
			pbs_python_write_error_to_log(log_buffer);
			Py_CLEAR(pbs_mod_obj);
			return (-1);
		}
	}
	Py_CLEAR(pbs_mod_obj);

	return (0);
}

const char pbsv1mod_meth_get_pbs_conf_doc[] =
	"get_pbs_conf()\n\
\n\
  returns:\n\
         Returns a dictionary containing the entries to pbs conf file, which is by default\n\
         /etc/pbs.conf in Linux/Unix or 'C:\\Program Files (x86)\\PBS\\pbs.conf'.\n\
";

/**
 * @brief
 *	This is the C->Python wrapper program for
 *	returning some key values in pbs.conf  file as loaded by the calling program.
 *	This is callable within a Python script.
 *
 * @return Python dict
 * @retval dictionary items are of the form: <pbs_conf_name>:<pbs_conf_value>
 *
 */
PyObject *
pbsv1mod_meth_get_pbs_conf(void)
{
	return (Py_BuildValue("{s:s,s:s,s:s,s:s,s:s,s:s,s:s,s:s,s:s,s:s,s:s}",
			      "PBS_HOME", pbs_conf.pbs_home_path ? pbs_conf.pbs_home_path : "",
			      "PBS_EXEC", pbs_conf.pbs_exec_path ? pbs_conf.pbs_exec_path : "",
			      "PBS_ENVIRONMENT", pbs_conf.pbs_environment ? pbs_conf.pbs_environment : "",
			      "PBS_RCP", pbs_conf.rcp_path ? pbs_conf.rcp_path : "",
			      "PBS_CP", pbs_conf.cp_path ? pbs_conf.cp_path : "",
			      "PBS_SCP", pbs_conf.scp_path ? pbs_conf.scp_path : "",
			      "PBS_MOM_HOME", pbs_conf.pbs_mom_home ? pbs_conf.pbs_mom_home : "",
			      "PBS_TMPDIR", pbs_conf.pbs_tmpdir ? pbs_conf.pbs_tmpdir : "",
			      "PBS_SERVER", pbs_conf.pbs_server_name ? pbs_conf.pbs_server_name : "",
			      "PBS_SERVER_HOST_NAME", pbs_conf.pbs_server_host_name ? pbs_conf.pbs_server_host_name : "",
			      "PBS_PRIMARY", pbs_conf.pbs_primary ? pbs_conf.pbs_primary : "",
			      "PBS_SECONDARY", pbs_conf.pbs_secondary ? pbs_conf.pbs_secondary : ""));
}

const char pbsv1mod_meth_load_resource_value_doc[] =
	"load_resource_value(resc_object)\n\
\n\
   resc_object:  resource object whose values are to be set\n\
\n\
   Load the values internally cached for 'resc_object'.\n\
";

/**
 * @brief
 *	This is callable in a Python script, for populating 'resc_object' of
 *	type 'pbs_resource' with values cached in the internal
 *	list 'pbs_resource_value_list'.
 *
 * @param[in]	args[1]	- the pbs_resource Python object.
 *
 * @return	PyObject *
 * @retval	NULL	- with an accompanying AssertionError Python exception.
 * @retval	Py_None - successful execution.
 *
 */
PyObject *
pbsv1mod_meth_load_resource_value(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {"resc_object", NULL};
	PyObject *py_resource_match = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "O:load_resource_value",
					 kwlist,
					 &py_resource_match)) {
		return NULL;
	}

	if (load_cached_resource_value(py_resource_match) != 0) {
		PyErr_SetString(PyExc_AssertionError,
				"Failed to load cached value for resoure list");
		return NULL;
	}

	Py_RETURN_NONE;
}

const char pbsv1mod_meth_resource_str_value_doc[] =
	"str_resource_value(resc_object)\n\
\n\
   resc_object: resource object whose string value is to be returned\n\
";

/**
 * @brief
 *	This is callable in a Python script, for returning the
 *	string value of a type 'pbs_resource' resc_object. The string value
 *	is the comma-separated list of resource values for 'resc_object'.
 *
 * @param[in]	args[1]	- the pbs_resource Python object.
 *
 * @return	PyObject *
 * @retval	NULL	- with an accompanying AssertionError Python exception.
 * @retval	<python string value> - a Python object representing the
 *					string value.
 *
 */
PyObject *
pbsv1mod_meth_resource_str_value(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {"resc_object", NULL};
	pbs_resource_value *resc_val;
	PyObject *py_resource_match = NULL;

	if (!PyArg_ParseTupleAndKeywords(args, kwds,
					 "O:str_resource_value",
					 kwlist,
					 &py_resource_match)) {
		return NULL;
	}

	resc_val = (pbs_resource_value *) GET_NEXT(pbs_resource_value_list);
	while (resc_val != NULL) {

		if ((resc_val->py_resource != NULL) &&
		    (py_resource_match == resc_val->py_resource))
			break;

		resc_val = (pbs_resource_value *) GET_NEXT(resc_val->all_rescs);
	}

	if (resc_val == NULL) {
		/* no match */
		Py_RETURN_NONE;
	}

	if (resc_val->py_resource_str_value == NULL) {
		Py_RETURN_NONE;
	}

	Py_INCREF(resc_val->py_resource_str_value);
	return (resc_val->py_resource_str_value);
}

const char pbsv1mod_meth_release_nodes_doc[] =
	"release_nodes(job,node_list,keep_select)\n\
  where:\n\
\n\
   job		:  job whose nodes are being released\n\
   node_list	:  dictionary of pbs.vnode objects mapping to nodes being released\n\
   keep_select	:  string value mapping to a new job's select value\n\
\n\
  returns:\n\
	job object with new values given to the following attributes:\n\
	exec_vnode\n\
	exec_host\n\
	exec_host2\n\
       as a result of keeping nodes in 'node_list' or nodes that satisfy the\n\
	job's keep_select value.\n\
";

/**
 *
 * @brief
 *	This is callable in a Python hook script for releasing assigned nodes
  *	that don't appear in 'node_list' or are not needed
 *	to satisfy the input 'keep_select' value.
 * @param[in]	job		- Python job object in question
 * @param[in]	node_list	- Python dictionary of pbs.vnode objects mapping to
 *				nodes being released.
 * @param[in]	keep_select	- Python string representing the new value to the
 *				job's select value.
 *
 * @return	PyObject *
 * @retval	job	- the passed 'job' parameter but with new values to
 *			'exec_vnode', 'exec_host, and 'exec_host2' items  to
 *			satisfy request.
 *		None	- if release of nodes is not possible due to some error or
 *			not enough resources are available to satisfy request.
 */
PyObject *
pbsv1mod_meth_release_nodes(PyObject *self, PyObject *args, PyObject *kwds)
{
	static char *kwlist[] = {"job", "node_list", "keep_select", NULL};

	PyObject *py_job = (PyObject *) NULL;
	PyObject *py_node_list = (PyObject *) NULL;
	PyObject *py_keep_select = (PyObject *) NULL;
	PyObject *py_nodes = (PyObject *) NULL;
	PyObject *py_attr_hookset_dict = (PyObject *) NULL;
	PyObject *py_attr_keys = (PyObject *) NULL;
	char *vnodelist = NULL;
	int vnodelist_sz = 0;

	int rc = 0;
	int entry = 0;
	relnodes_input_t r_input;
	relnodes_input_vnodelist_t r_input_vnlist;
	relnodes_input_select_t r_input_select;

	char *keep_select = NULL;
	char *execvnode = NULL;
	char *exechost = NULL;
	char *exechost2 = NULL;
	char *schedselect = NULL;
	pbs_list_head exec_vnode_list;
	pbs_list_head failed_mom_list;
	pbs_list_head succeeded_mom_list;
	vnl_t *failed_vnodes = NULL;
	vnl_t *good_vnodes = NULL;
	char *new_exec_vnode = NULL;
	char *new_exec_host = NULL;
	char *new_exec_host2 = NULL;
	char *new_schedselect = NULL;
	char *tmpstr = NULL;
	PyObject *py_return = Py_None;
	int hook_set_mode_orig;
	char *jobid = NULL;
	char err_msg[LOG_BUF_SIZE] = {'\0'};

	hook_set_mode_orig = hook_set_mode;

	CLEAR_HEAD(exec_vnode_list);
	CLEAR_HEAD(succeeded_mom_list);
	CLEAR_HEAD(failed_mom_list);
	memset(log_buffer, '\0', LOG_BUF_SIZE);

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOO:release_nodes",
					 kwlist, &py_job, &py_node_list, &py_keep_select)) {
		log_err(-1, __func__, "PyArg_ParseTupleAndKeywords failed!");
		goto release_nodes_exit;
	}

	if (py_node_list != Py_None) {
		int num_attrs;
		int i;
		char *vn_name;

		if (!PyDict_Check(py_node_list)) {
			log_err(PBSE_INTERNAL, __func__, "node_list is not a dictionary");
			goto release_nodes_exit;
		}

		py_attr_keys = PyDict_Keys(py_node_list); /* NEW ref */

		if (py_attr_keys == NULL) {
			log_err(PBSE_INTERNAL, __func__, "Failed to obtain node_list's keys");
			goto release_nodes_exit;
		}

		if (!PyList_Check(py_attr_keys)) {
			log_err(PBSE_INTERNAL, __func__, "node_list key is not a list");
			Py_CLEAR(py_attr_keys);
			goto release_nodes_exit;
		}

		vnodelist_sz = HOOK_BUF_SIZE;
		vnodelist = (char *) malloc(vnodelist_sz);
		if (vnodelist == NULL) {
			log_err(errno, __func__, "malloc failure");
			goto release_nodes_exit;
		}

		vnodelist[0] = '\0';

		num_attrs = PyList_Size(py_attr_keys);
		for (i = 0; i < num_attrs; i++) {

			vn_name = strdup(pbs_python_list_get_item_string_value(py_attr_keys, i));
			if ((vn_name == NULL) || (vn_name[0] == '\0')) {
				free(vn_name);
				continue;
			}

			if (vnodelist[0] != '\0') {
				if (pbs_strcat(&vnodelist, &vnodelist_sz, "+") == NULL) {
					log_err(errno, __func__, "pbs_strcat failure");
					free(vn_name);
					goto release_nodes_exit;
				}
			}

			if (pbs_strcat(&vnodelist, &vnodelist_sz, vn_name) == NULL) {
				log_err(errno, __func__, "pbs_strcat failure");
				free(vn_name);
				goto release_nodes_exit;
			}

			free(vn_name);
		}
	} else {
		char *str = pbs_python_object_str(py_keep_select);

		if ((str == NULL) || (str[0] == '\0')) {
			log_err(-1, __func__, "empty keep_select value");
			goto release_nodes_exit;
		}
		/* release nodes in such a way that 'keep_select' request is satisfied */
		keep_select = strdup(str);
		if (keep_select == NULL) {
			log_err(-1, __func__, "strdup keep_select failed");
			goto release_nodes_exit;
		}

		/* populate failed_mom_list used to decide which nodes to release */
		if (PyObject_HasAttrString(py_job, PY_JOB_FAILED_MOM_LIST)) {
			py_nodes = PyObject_GetAttrString(py_job, PY_JOB_FAILED_MOM_LIST);
			if (py_nodes != NULL) {
				if (PyList_Check(py_nodes)) {
					if (py_strlist_to_reliable_job_node_list(py_nodes, &failed_mom_list) == -1) {
						snprintf(log_buffer, LOG_BUF_SIZE - 1, "%s: Failed to dump Python string list values into a svrattrl list!", PY_JOB_FAILED_MOM_LIST);
						log_err(PBSE_INTERNAL, __func__, log_buffer);
						goto release_nodes_exit;
					}
				}
				Py_CLEAR(py_nodes);
			}
		}

		/* populate succeeded_mom_list used to decide which nodes to keep */
		if (PyObject_HasAttrString(py_job, PY_JOB_SUCCEEDED_MOM_LIST)) {
			py_nodes = PyObject_GetAttrString(py_job, PY_JOB_SUCCEEDED_MOM_LIST); /* NEW */

			if (py_nodes != NULL) {
				if (PyList_Check(py_nodes)) {
					if (py_strlist_to_reliable_job_node_list(py_nodes, &succeeded_mom_list) == -1) {
						snprintf(log_buffer, sizeof(log_buffer), "%s: Failed to dump Python string list values into a svrattrl list!", PY_JOB_SUCCEEDED_MOM_LIST);
						log_err(PBSE_INTERNAL, __func__, log_buffer);
						goto release_nodes_exit;
					}
				}
				Py_CLEAR(py_nodes);
			}
		}
	}

	/* jobid */
	tmpstr = pbs_python_object_get_attr_string_value(py_job, "id");
	if ((tmpstr == NULL) || (tmpstr[0] == '\0')) {
		log_err(-1, __func__, "did not find a value to jobid");
		goto release_nodes_exit;
	}
	jobid = strdup(tmpstr);
	if (jobid == NULL) {
		log_err(-1, __func__, "strdup jobid failed");
		goto release_nodes_exit;
	}

	/* exec_vnode */
	tmpstr = pbs_python_object_get_attr_string_value(py_job, ATTR_execvnode);
	if ((tmpstr == NULL) || (tmpstr[0] == '\0')) {
		log_err(-1, __func__, "did not find a value to exec_vnode");
		goto release_nodes_exit;
	}
	execvnode = strdup(tmpstr);
	if (execvnode == NULL) {
		log_err(-1, __func__, "strdup exec_vnode failed");
		goto release_nodes_exit;
	}

	/* exec_host or exec_host2 */
	tmpstr = pbs_python_object_get_attr_string_value(py_job, ATTR_exechost);
	if ((tmpstr != NULL) && (tmpstr[0] != '\0')) {
		exechost = strdup(tmpstr);
		if (exechost == NULL) {
			log_err(-1, __func__, "strdup exec_host failed");
			goto release_nodes_exit;
		}
	}

	tmpstr = pbs_python_object_get_attr_string_value(py_job, ATTR_exechost2);
	if ((tmpstr != NULL) && (tmpstr[0] != '\0')) {
		exechost2 = strdup(tmpstr);
		if (exechost2 == NULL) {
			log_err(-1, __func__, "strdup exec_host2 failed");
			goto release_nodes_exit;
		}
	}

	if ((exechost == NULL) && (exechost2 == NULL)) {
		log_err(-1, __func__, "no value found for exec_host/exec_host2 ");
		goto release_nodes_exit;
	}

	if (exechost == NULL) {
		exechost = strdup(exechost2);
		if (exechost == NULL) {
			log_err(-1, __func__, "strdup exec_host failed");
			goto release_nodes_exit;
		}
	}

	if (exechost2 == NULL) {
		exechost2 = strdup(exechost);
		if (exechost2 == NULL) {
			log_err(-1, __func__, "strdup exec_host2 failed");
			goto release_nodes_exit;
		}
	}

	/* schedselect */
	tmpstr = pbs_python_object_get_attr_string_value(py_job, ATTR_SchedSelect);
	if ((tmpstr == NULL) || (tmpstr[0] == '\0')) {
		log_err(-1, __func__, "did not find a value to exec_vnode");
		goto release_nodes_exit;
	}
	schedselect = strdup(tmpstr);
	if (schedselect == NULL) {
		log_err(-1, __func__, "strdup schedselect failed");
		goto release_nodes_exit;
	}

	/* populate exec_vnode_list */
	if (populate_svrattrl_from_vnodelist_param(PY_EVENT_PARAM_VNODELIST, &exec_vnode_list) != 0) {
		snprintf(log_buffer, sizeof(log_buffer), "%s: Failed to dump Python string list values into a svrattrl list", PY_EVENT_PARAM_VNODELIST);
		log_err(PBSE_INTERNAL, __func__, log_buffer);
		goto release_nodes_exit;
	}

	err_msg[0] = '\0';
	relnodes_input_init(&r_input);
	r_input.jobid = jobid;
	r_input.vnodes_data = &exec_vnode_list;
	r_input.execvnode = execvnode;
	r_input.exechost = exechost;
	r_input.exechost2 = exechost2;
	r_input.schedselect = schedselect;
	r_input.p_new_exec_vnode = &new_exec_vnode;
	r_input.p_new_exec_host[0] = &new_exec_host;
	r_input.p_new_exec_host[1] = &new_exec_host2;
	r_input.p_new_schedselect = &new_schedselect;

	if (vnodelist != NULL) {
		relnodes_input_vnodelist_init(&r_input_vnlist);
		r_input_vnlist.vnodelist = vnodelist;
		rc = pbs_release_nodes_given_nodelist(&r_input, &r_input_vnlist, err_msg, LOG_BUF_SIZE);
		snprintf(log_buffer, sizeof(log_buffer), "release_nodes_given_nodelist: AFT rc=%d jobid=%s vnodelist=%s execvnode=%s exechost=%s exechost2=%s schedselect=%s new_exec_vnode=%s new_exec_host=%s new_exec_host2=%s new_schedselect=%s", rc, jobid, vnodelist, execvnode, exechost ? exechost : "null", exechost2 ? exechost2 : "null", schedselect, new_exec_vnode, new_exec_host, new_exec_host2, new_schedselect);
		log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_SERVER, LOG_ERR, __func__, log_buffer);
	} else if (keep_select != NULL) {
		relnodes_input_select_init(&r_input_select);
		r_input_select.select_str = keep_select;
		r_input_select.failed_mom_list = &failed_mom_list;
		r_input_select.succeeded_mom_list = &succeeded_mom_list;
		r_input_select.failed_vnodes = &failed_vnodes;
		r_input_select.good_vnodes = &good_vnodes;
		rc = pbs_release_nodes_given_select(&r_input, &r_input_select, err_msg, LOG_BUF_SIZE);
		snprintf(log_buffer, sizeof(log_buffer), "release_nodes_given_select: AFT rc=%d keep_select=%s execvnode=%s exechost=%s exechost2=%s new_exec_vnode=%s new_exec_host=%s new_exec_host2=%s new_schedselect=%s", rc, keep_select, execvnode, exechost ? exechost : "null", exechost2 ? exechost2 : "null", new_exec_vnode, new_exec_host, new_exec_host2, new_schedselect);
		log_event(PBSEVENT_DEBUG4, PBS_EVENTCLASS_SERVER, LOG_ERR, __func__, log_buffer);
	} else {
		log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER, LOG_ERR, __func__, "nothing to release: No values to both node_list and keep_select");
		goto release_nodes_exit;
	}

	if (rc != 0) {
		if (err_msg[0] != '\0')
			log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER, LOG_ERR, __func__, err_msg);
		goto release_nodes_exit;
	}

	hook_set_mode = C_MODE;

	/* Get the attributes that have been set in the hook script */
	py_attr_hookset_dict = PyObject_GetAttrString(py_job, PY_ATTRIBUTES_HOOK_SET); /* new ref */
	if (py_attr_hookset_dict == NULL) {
		py_attr_hookset_dict = PyDict_New();
		PyObject_SetAttrString(py_job, PY_ATTRIBUTES_HOOK_SET, py_attr_hookset_dict);
	}

	if ((new_exec_vnode != NULL) && (new_exec_vnode[0] != '\0')) {
		entry = strlen(new_exec_vnode) - 1;
		if (new_exec_vnode[entry] == '+')
			new_exec_vnode[entry] = '\0';

		rc = pbs_python_object_set_attr_string_value(py_job, ATTR_execvnode, new_exec_vnode);
		if (rc == -1) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1, "failed to set job attribute %s = %s", ATTR_execvnode, new_exec_vnode);
			log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER, LOG_ERR, __func__, log_buffer);
			goto release_nodes_exit;
		}
		if (py_attr_hookset_dict != NULL) {
			/* mark that exec_vnode of the job has been set in a hook */
			PyDict_SetItemString(py_attr_hookset_dict, ATTR_execvnode, Py_None);
		}
	}

	if ((new_exec_host != NULL) && (new_exec_host[0] != '\0')) {
		entry = strlen(new_exec_host) - 1;
		if (new_exec_host[entry] == '+')
			new_exec_host[entry] = '\0';
		rc = pbs_python_object_set_attr_string_value(py_job, ATTR_exechost, new_exec_host);
		if (rc == -1) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "failed to set job attribute %s = %s", ATTR_exechost, new_exec_host);
			log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER,
				  LOG_ERR, __func__, log_buffer);
			goto release_nodes_exit;
		}
		if (py_attr_hookset_dict != NULL) {
			/* mark that exec_host of the job has been set in a hook */
			PyDict_SetItemString(py_attr_hookset_dict, ATTR_exechost, Py_None);
		}
	}

	if ((new_exec_host2 != NULL) && (new_exec_host2[0] != '\0')) {
		entry = strlen(new_exec_host2) - 1;
		if (new_exec_host2[entry] == '+')
			new_exec_host2[entry] = '\0';
		rc = pbs_python_object_set_attr_string_value(py_job, ATTR_exechost2, new_exec_host2);
		if (rc == -1) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1,
				 "failed to set job attribute %s = %s", ATTR_exechost2, new_exec_host2);
			log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER, LOG_ERR, __func__, log_buffer);
			goto release_nodes_exit;
		}
		if (py_attr_hookset_dict != NULL) {
			/* mark that exec_host2 of the job has been set in a hook */
			PyDict_SetItemString(py_attr_hookset_dict, ATTR_exechost2, Py_None);
		}
	}

	if ((new_schedselect != NULL) && (new_schedselect[0] != '\0')) {
		rc = pbs_python_object_set_attr_string_value(py_job, ATTR_SchedSelect, new_schedselect);
		if (rc == -1) {
			snprintf(log_buffer, LOG_BUF_SIZE - 1, "failed to set event attribute %s = %s", ATTR_SchedSelect, new_schedselect);
			log_event(PBSEVENT_SYSTEM | PBSEVENT_FORCE, PBS_EVENTCLASS_SERVER, LOG_ERR, __func__, log_buffer);
			goto release_nodes_exit;
		}
		if (py_attr_hookset_dict != NULL) {
			/* mark that exec_host2 of the job has been set in a hook */
			PyDict_SetItemString(py_attr_hookset_dict, ATTR_SchedSelect, Py_None);
		}
	}

	py_return = py_job;

release_nodes_exit:
	free(vnodelist);
	Py_CLEAR(py_attr_keys);
	free(keep_select);
	free(execvnode);
	free(exechost);
	free(exechost2);
	free(schedselect);
	free_attrlist(&exec_vnode_list);
	Py_CLEAR(py_nodes);
	reliable_job_node_free(&failed_mom_list);
	reliable_job_node_free(&succeeded_mom_list);
	free(new_exec_vnode);
	free(new_exec_host);
	free(new_exec_host2);
	free(new_schedselect);
	vnl_free(failed_vnodes);
	vnl_free(good_vnodes);
	Py_CLEAR(py_attr_hookset_dict);
	free(jobid);

	hook_set_mode = hook_set_mode_orig;
	return (py_return);
}

/**
 *
 * @brief
 *	Returns a Python List of _server_attribute objects or  or NULL on error.
 * @param[in]	phead	- pointer to the head of the list containing data.
 *
 * @return 	PyObject *
 * @retval	<object>	- the Python list object holding
 * @retval				the _server_attribute objects.
 * @retval	NULL		- if an error occurred.
 * @note
 * 		the returned PyObject must be cleared(Py_CLEAR) as it's a new
 * 		reference.
 */
PyObject *
svrattrl_list_to_pyobject(int rq_cmd, pbs_list_head *phead)
{
	svrattrl *plist = NULL;
	PyObject *py_list = PyList_New(0);

	if (phead == NULL) {
		log_err(errno, __func__, "NULL input parameters!");
		Py_CLEAR(py_list);
		return NULL;
	}

	for (plist = (svrattrl *) GET_NEXT(*phead); plist != NULL;
	     plist = (svrattrl *) GET_NEXT(plist->al_link)) {
		PyObject *py_server_attribute = svrattrl_to_server_attribute(rq_cmd, plist);
		if (py_server_attribute) {
			svrattrl *slist = NULL;
			PyObject *py_slist = PyObject_GetAttrString(py_server_attribute, "sisters");
			if (py_slist) {
				for (slist = plist->al_sister; slist != NULL; slist = slist->al_sister) {
					PyObject *py_server_attribute_sister = svrattrl_to_server_attribute(rq_cmd, slist);
					if (py_server_attribute_sister) {
						PyList_Append(py_slist, py_server_attribute_sister);
						Py_CLEAR(py_server_attribute_sister);
					} else {
						snprintf(log_buffer, LOG_BUF_SIZE - 1,
							 "could not translate the sister for attribute <%s>", plist->al_name);
						log_buffer[LOG_BUF_SIZE - 1] = '\0';
						log_err(PBSE_INTERNAL, __func__, log_buffer);
						break;
					}
				}
			} /* else {
				log_err(PBSE_INTERNAL, __func__,
					"failed to acquire sisters in server_attribute object");
			} */
			PyList_Append(py_list, py_server_attribute);
			Py_CLEAR(py_server_attribute);
		}
	}
	return py_list;
}

/**
 *
 * @brief
 *	Returns a Python _server_attribute object or NULL on error.
 * @param[in]	attribute	- pointer to the head of the list containing data.
 *
 * @return 	PyObject *
 * @retval	<object>	- the Python _server_attribute object.
 * @retval	NULL		- if an error occurred.
 * @note
 * 		the returned PyObject must be cleared(Py_CLEAR) as it's a new
 * 		reference.
 */
PyObject *
svrattrl_to_server_attribute(int rq_cmd, svrattrl *attribute)
{
	PyObject *py_server_attribute = NULL;
	PyObject *py_server_attribute_class = NULL;
	PyObject *py_server_attribute_args = NULL;

	if (attribute == NULL) {
		goto server_attribute_exit;
	}

	py_server_attribute_class = pbs_python_types_table[PP_SERVER_ATTRIBUTE_IDX].t_class;
	if (!py_server_attribute_class) {
		log_err(PBSE_INTERNAL, __func__, "failed to acquire server_attribute class");
		goto server_attribute_exit;
	}

	py_server_attribute_args = Py_BuildValue("(sssii)",
						 attribute->al_name,
						 attribute->al_resc,
						 attribute->al_value,
						 (rq_cmd != MGR_CMD_UNSET ? attribute->al_op : UNSET),
						 attribute->al_flags); /* NEW ref */

	if (!py_server_attribute_args) {
		log_err(PBSE_INTERNAL, __func__, "could not build args list for server_attribute");
		goto server_attribute_exit;
	}
	py_server_attribute = PyObject_CallObject(py_server_attribute_class, py_server_attribute_args);

	if (!py_server_attribute) {
		pbs_python_write_error_to_log(__func__);
		log_err(PBSE_INTERNAL, __func__, "failed to create a python server_attribute object");
		goto server_attribute_exit;
	}
server_attribute_exit:
	Py_CLEAR(py_server_attribute_args);
	return py_server_attribute;
}
