diff --git a/src/cmds/pbsnodes.c b/src/cmds/pbsnodes.c index ab0bbf0..61658c7 100644 --- a/src/cmds/pbsnodes.c +++ b/src/cmds/pbsnodes.c @@ -207,10 +207,11 @@ static void prt_node_attr( continue; } - printf(" %s = %s\n", + if (pat->resource != NULL && (strcmp(pat->resource,"") != 0)) + printf("\t%s.%s = %s\n", pat->name, pat->resource, pat->value); + else + printf("\t%s = %s\n", pat->name, pat->value); - pat->name, - pat->value); } /* END for (pat) */ return; @@ -406,7 +407,17 @@ void addxmlnode( if (pat->value == NULL) continue; - MXMLCreateE(&AE, pat->name); + if (pat->resource != NULL && (strcmp(pat->resource,"") != 0)) + { + char *buffer = malloc(strlen(pat->name)+strlen(pat->resource)+2); + strcpy(buffer,pat->name); + strcat(buffer,"."); + strcat(buffer,pat->resource); + MXMLCreateE(&AE, buffer); + free(buffer); + } + else + MXMLCreateE(&AE, pat->name); MXMLSetVal(AE, pat->value, mdfString); diff --git a/src/include/attribute.h b/src/include/attribute.h index 069fa87..c4a6688 100644 --- a/src/include/attribute.h +++ b/src/include/attribute.h @@ -106,7 +106,7 @@ /* define the size of fields in the structures */ -#define ATRFLAG 16 +#define ATRFLAG 18 #define ATRTYPE 6 /* sync w/ATR_TYPE_* (see #defines below) */ #define ATRPART 3 @@ -262,6 +262,9 @@ typedef struct attribute_def attribute_def; #define ATR_DFLAG_RASSN 0x4000 /* resc to be summed in resources_used */ #define ATR_DFLAG_RMOMIG 0x8000 /* resource to be ignored by mom */ +#define ATR_DFLAG_SELECT_PROC 0x10000 /* per-proc resource in the select and nodespec statements */ +#define ATR_DFLAG_SELECT_MOM 0x20000 /* per-node resource in the select and nodespec statements */ + /* combination defines for permission field */ #define READ_ONLY ATR_DFLAG_USRD | ATR_DFLAG_OPRD | ATR_DFLAG_MGRD @@ -275,6 +278,7 @@ typedef struct attribute_def attribute_def; #define ATR_VFLAG_MODIFY 0x02 /* value has been modified */ #define ATR_VFLAG_DEFLT 0x04 /* value is default value */ #define ATR_VFLAG_SEND 0x08 /* value to be sent to server */ +#define ATR_VFLAG_FORCED 0x10 /* this value is forced by config */ #define ATR_TRUE "True" #define ATR_FALSE "False" @@ -451,6 +455,7 @@ extern int set_node_ntype A_((attribute*, attribute*, enum batch_op)); extern int set_node_props A_((attribute*, attribute*, enum batch_op)); extern int set_null A_((attribute*, attribute*, enum batch_op)); extern int node_state A_((attribute*, void*, int)); +extern int node_resources_action(attribute*, void*, int); extern int node_np_action A_((attribute*, void*, int)); extern int node_ntype A_((attribute*, void*, int)); extern int node_prop_list A_((attribute*, void*, int)); @@ -458,6 +463,7 @@ extern int node_status_list A_((attribute*, void*, int)); extern int node_note A_((attribute*, void*, int)); extern int set_note_str A_((attribute *attr, attribute *new, enum batch_op)); extern void replace_attr_string A_((attribute*, char*)); +extern int count_resc(attribute*); /* Token manipulation functions */ diff --git a/src/include/pbs_error_db.h b/src/include/pbs_error_db.h index 81c8c7c..7f249f5 100644 --- a/src/include/pbs_error_db.h +++ b/src/include/pbs_error_db.h @@ -193,6 +193,12 @@ PbsErrClient(PBSE_NOFAULTTOLERANT, "Queue does not allow fault tolerant jobs") /* only fault tolerant jobs allowed in queue */ PbsErrClient(PBSE_NOFAULTINTOLERANT, "Queue does not allow fault intolerant jobs") PbsErrClient(PBSE_NOJOBARRAYS, "Queue does not allow job arrays") +/* Routine preconditions test failed */ +PbsErrClient(PBSE_PRECONDITION, "Routine precondition failed.") +/* Routine postconditions test failed */ +PbsErrClient(PBSE_POSTCONDITION, "Routine postcondition failed.") +/* Routine consistency test failed */ +PbsErrClient(PBSE_CONSISTENCY, "Routine consistency check failed.") /* pbs client errors ceiling (max_client_err + 1) */ PbsErrClient(PBSE_CEILING, (char*)0) #endif diff --git a/src/include/pbs_ifl.h b/src/include/pbs_ifl.h index cd6d9d7..1620ad0 100644 --- a/src/include/pbs_ifl.h +++ b/src/include/pbs_ifl.h @@ -180,6 +180,8 @@ #define ATTR_reported "reported" #define ATTR_intcmd "inter_cmd" #define ATTR_P "proxy_user" +#define ATTR_schedspec "sched_nodespec" +#define ATTR_total_resources "total_resources" #ifdef USEJOBCREATE #define ATTR_pagg "pagg_id" @@ -291,6 +293,8 @@ #define ATTR_npdefault "np_default" #define ATTR_jobstarttimeout "job_start_timeout" #define ATTR_jobforcecanceltime "job_force_cancel_time" +#define ATTR_ResourcesToStore "node_resources_to_store" +#define ATTR_ResourcesMappings "node_resources_mappings" /* additional node "attributes" names */ @@ -301,6 +305,8 @@ #define ATTR_NODE_jobs "jobs" #define ATTR_NODE_status "status" #define ATTR_NODE_note "note" +#define ATTR_NODE_resources_total "resources_total" +#define ATTR_NODE_resources_used "resources_used" /* notification email formating */ #define ATTR_mailsubjectfmt "mail_subject_fmt" diff --git a/src/include/pbs_job.h b/src/include/pbs_job.h index e8bcc9c..5e3232c 100644 --- a/src/include/pbs_job.h +++ b/src/include/pbs_job.h @@ -296,6 +296,8 @@ enum job_atr JOB_ATR_jobtype, /* opaque job type string */ JOB_ATR_inter_cmd, /* command for interactive job */ JOB_ATR_proxy_user, + JOB_ATR_sched_spec, /* schedulers nodespec sent during job run request */ + JOB_ATR_total_resources, /* total resources (read-only) */ #ifdef USEJOBCREATE JOB_ATR_pagg_id, #endif /* USEJOBCREATE */ @@ -485,6 +487,8 @@ struct job int ji_isparent; /* set to TRUE if this is a "parent job"*/ #endif/* PBS_MOM */ /* END SERVER ONLY */ + char *ji_expanded_spec; /* pre-parsed nodespec */ + /* * fixed size internal data - maintained via "quick save" * some of the items are copies of attributes, if so this diff --git a/src/include/pbs_nodes.h b/src/include/pbs_nodes.h index 3723eb5..d45c665 100644 --- a/src/include/pbs_nodes.h +++ b/src/include/pbs_nodes.h @@ -85,6 +85,9 @@ /* NOTE: requires server_limits.h */ +#include "attribute.h" + + #define BM_ERROR -20 enum psit @@ -98,6 +101,7 @@ enum psit struct prop { char *name; + char *value; /* for resources */ short mark; struct prop *next; @@ -107,23 +111,21 @@ struct jobinfo { struct job *job; + int order; struct jobinfo *next; }; struct pbssubn { - struct pbsnode *host; - struct pbssubn *next; - - struct jobinfo *jobs; /* list of jobs allocating resources within subnode */ - /* does this include suspended jobs? */ + struct jobinfo *jobs; /* list of jobs allocating resources within subnode */ + /* does this include suspended jobs? */ resource_t allocto; - enum psit flag; /* XXX */ + enum psit flag; /* XXX */ unsigned short inuse; - short index; /* subnode index */ + short index; /* subnode index */ }; struct pbsnode @@ -158,6 +160,8 @@ struct pbsnode short nd_order; /* order of user's request */ time_t nd_warnbad; time_t nd_lastupdate; /* time of last update. */ + + struct attribute attributes[2]; /* resources_total, resources_used */ }; struct howl @@ -255,6 +259,8 @@ enum nodeattr ND_ATR_jobs, ND_ATR_status, ND_ATR_note, + ND_ATR_resources_total, + ND_ATR_resources_used, ND_ATR_LAST }; /* WARNING: Must be the highest valued enum */ @@ -287,7 +293,8 @@ extern void free_prop_list A_((struct prop*)); extern void free_prop_attr A_((attribute*)); extern void recompute_ntype_cnts A_(()); extern int create_pbs_node A_((char *, svrattrl *, int, int *)); -extern int mgr_set_node_attr A_((struct pbsnode *, attribute_def *, int, svrattrl *, int, int *, void *, int)); +extern int mgr_set_node_attr(struct pbsnode *, attribute_def *, int, svrattrl *, int, int *, void *, int); +extern int mgr_set_attr(attribute *pattr, attribute_def*, int, svrattrl*, int, int*, void*, int, int); struct prop *init_prop A_((char *pname)); #endif /* BATCH_REQUEST_H */ diff --git a/src/include/qmgr_node_public.h b/src/include/qmgr_node_public.h index 81a2abe..4086e8a 100644 --- a/src/include/qmgr_node_public.h +++ b/src/include/qmgr_node_public.h @@ -96,3 +96,5 @@ ATTR_NODE_np, ATTR_NODE_ntype, ATTR_NODE_status, ATTR_NODE_note, +ATTR_NODE_resources_total, + diff --git a/src/include/qmgr_node_readonly.h b/src/include/qmgr_node_readonly.h index 9ba0a0f..e32772b 100644 --- a/src/include/qmgr_node_readonly.h +++ b/src/include/qmgr_node_readonly.h @@ -94,3 +94,4 @@ */ ATTR_NODE_jobs, +ATTR_NODE_resources_used, diff --git a/src/include/qmgr_svr_public.h b/src/include/qmgr_svr_public.h index e8e596c..b6c30a1 100644 --- a/src/include/qmgr_svr_public.h +++ b/src/include/qmgr_svr_public.h @@ -157,3 +157,5 @@ ATTR_mailbodyfmt, ATTR_npdefault, ATTR_jobstarttimeout, ATTR_jobforcecanceltime, +ATTR_ResourcesToStore, +ATTR_ResourcesMappings, diff --git a/src/include/server.h b/src/include/server.h index 8c57615..6b01342 100644 --- a/src/include/server.h +++ b/src/include/server.h @@ -173,6 +173,8 @@ enum srv_atr SRV_ATR_NPDefault, SRV_ATR_JobStartTimeout, SRV_ATR_JobForceCancelTime, + SRV_ATR_ResourcesToStore, + SRV_ATR_ResourcesMappings, #include "site_svr_attr_enum.h" /* This must be last */ SRV_ATR_LAST diff --git a/src/lib/Libattr/attr_atomic.c b/src/lib/Libattr/attr_atomic.c index 49197ce..c665d44 100644 --- a/src/lib/Libattr/attr_atomic.c +++ b/src/lib/Libattr/attr_atomic.c @@ -152,7 +152,13 @@ int attr_atomic_set( } else { - index = unkn; /* if unknown attr are allowed */ + if (unkn != 0) + index = unkn; /* if unknown attr are allowed */ + else /* simply jump over unknown */ + { + plist = (struct svrattrl *)GET_NEXT(plist->al_link); + continue; + } } } @@ -291,6 +297,7 @@ attr_atomic_node_set( attribute temp; listidx = 0; + resc_access_perm = privil; /* set privilege for decode_resc() */ while (plist) { @@ -306,7 +313,12 @@ attr_atomic_node_set( break; } else - index = unkn; /*if unknown attr are allowed*/ + { + /*index = unkn;*/ /*if unknown attr are allowed*/ + /* if unknown enabled, then simply ignore it */ + plist = (struct svrattrl *)GET_NEXT(plist->al_link); + continue; + } } diff --git a/src/lib/Libattr/attr_fn_resc.c b/src/lib/Libattr/attr_fn_resc.c index 68bec25..5885823 100644 --- a/src/lib/Libattr/attr_fn_resc.c +++ b/src/lib/Libattr/attr_fn_resc.c @@ -199,6 +199,7 @@ int decode_resc( patr->at_flags |= ATR_VFLAG_SET | ATR_VFLAG_MODIFY; rv = prdef->rs_decode(&prsc->rs_value, name, rescn, val); + prsc->rs_value.at_flags |= ATR_VFLAG_FORCED; if (rv == 0) { @@ -363,7 +364,7 @@ int set_resc( { /* call resource type dependent set routine */ - + oldresc->rs_value.at_flags |= (newresc->rs_value.at_flags & ATR_VFLAG_FORCED); if ((rc = oldresc->rs_defin->rs_set(&oldresc->rs_value, &newresc->rs_value, local_op)) != 0) return (rc); @@ -383,6 +384,36 @@ int set_resc( +/* count resources in list */ +int count_resc(struct attribute *attr) + { + resource *wiresc; + int count = 0; + + if (attr == NULL) + { + /* FAILURE */ + + return(-1); + } + + wiresc = (resource *)GET_NEXT(attr->at_val.at_list); + + while (wiresc != NULL) + { + if ((wiresc->rs_value.at_flags & ATR_VFLAG_SET) && + ((wiresc->rs_value.at_flags & ATR_VFLAG_DEFLT) == 0)) + { + count++; + } + wiresc = (resource *)GET_NEXT(wiresc->rs_link); + } /* END while() */ + + return(count); + } /* END comp_resc() */ + + + /* * comp_resc - compare two attributes of type ATR_TYPE_RESR diff --git a/src/server/job_attr_def.c b/src/server/job_attr_def.c index adff3f0..28d789d 100644 --- a/src/server/job_attr_def.c +++ b/src/server/job_attr_def.c @@ -933,6 +933,33 @@ attribute_def job_attr_def[] = PARENT_TYPE_JOB }, + /* JOB_ATR_sched_spec */ + { ATTR_schedspec, /* sched_nodespec */ + decode_str, + encode_str, + set_str, + comp_str, + free_str, + NULL_FUNC, + READ_ONLY | ATR_DFLAG_MOM, + ATR_TYPE_STR, + PARENT_TYPE_JOB + }, + + /* JOB_ATR_total_resources */ + { ATTR_total_resources, /* "total_resources" */ + decode_resc, + encode_resc, + set_resc, + comp_resc, + free_resc, + action_resc, + READ_ONLY, + ATR_TYPE_RESC, + PARENT_TYPE_JOB + }, + + #ifdef USEJOBCREATE /* JOB_ATR_pagg_id */ { ATTR_pagg, /* "pagg_id" */ diff --git a/src/server/job_func.c b/src/server/job_func.c index 8eb0c12..4045805 100644 --- a/src/server/job_func.c +++ b/src/server/job_func.c @@ -661,6 +661,8 @@ void job_free( bp = (badplace *)GET_NEXT(pj->ji_rejectdest); } + free(pj->ji_expanded_spec); + /* now free the main structure */ free((char *)pj); diff --git a/src/server/node_attr_def.c b/src/server/node_attr_def.c index 9af5008..262c7f9 100644 --- a/src/server/node_attr_def.c +++ b/src/server/node_attr_def.c @@ -211,4 +211,30 @@ attribute_def node_attr_def[] = PARENT_TYPE_NODE, }, + /* ND_ATR_resources_total */ + { ATTR_NODE_resources_total, + decode_resc, + encode_resc, + set_resc, + comp_resc, + free_resc, + NULL_FUNC, + MGR_ONLY_SET, + ATR_TYPE_RESC, + PARENT_TYPE_NODE, + }, + + /* ND_ATR_resources_used */ + { ATTR_NODE_resources_used, + decode_resc, + encode_resc, + set_resc, + comp_resc, + free_resc, + NULL_FUNC, + READ_ONLY, + ATR_TYPE_RESC, + PARENT_TYPE_NODE, + }, + }; diff --git a/src/server/node_func.c b/src/server/node_func.c index b11bd1f..c685e5e 100644 --- a/src/server/node_func.c +++ b/src/server/node_func.c @@ -431,6 +431,7 @@ static short old_ntype = (short)0xdead; /*node's ntype */ static int old_nprops = 0xdead; /*node's nprops */ static int old_nstatus = 0xdead; /*node's nstatus */ static char *old_note = NULL; /*node's note */ +static struct attribute *old_resources = (struct attribute*)0; @@ -472,6 +473,15 @@ void save_characteristic( old_note = strdup(pnode->nd_note); } + if (old_resources == NULL) + old_resources = malloc(sizeof(struct attribute)); + + if (old_resources == NULL) + return; /* XXX silent death */ + + clear_attr(old_resources,&node_attr_def[ND_ATR_resources_total]); + node_attr_def[ND_ATR_resources_total].at_set(old_resources,&pnode->attributes[0],SET); + return; } /* END save_characteristic() */ @@ -479,6 +489,8 @@ void save_characteristic( + + /* * chk_characteristic() - check the value of the characteristics against * that which was saved earlier. @@ -488,8 +500,6 @@ void save_characteristic( * bit(s) set depending on the results of the check. * The "returned" bits get used by the caller. */ - - int chk_characteristic( struct pbsnode *pnode, /* I */ @@ -498,6 +508,10 @@ int chk_characteristic( { short tmp; char tmpLine[1024]; +/* extern int comp_resc_eq;*/ + extern int comp_resc_gt; + extern int comp_resc_lt; + extern int comp_resc_nc; if ((pnode != old_address) || (pnode == NULL)) { @@ -575,6 +589,16 @@ int chk_characteristic( old_address = NULL; + + if (node_attr_def[ND_ATR_resources_total].at_comp(&pnode->attributes[0],old_resources) == 0) + { + if (count_resc(&pnode->attributes[0]) != count_resc(old_resources)) + *pneed_todo |= WRITE_NEW_NODESFILE; + + if (comp_resc_lt != 0 || comp_resc_gt != 0 || comp_resc_nc != 0) + *pneed_todo |= WRITE_NEW_NODESFILE; + } + return(0); } /* END chk_characteristic() */ @@ -631,7 +655,17 @@ int status_nodeattrib( else if (!strcmp((padef + i)->at_name, ATTR_NODE_np)) atemp[i].at_val.at_long = pnode->nd_nsn; else if (!strcmp((padef + i)->at_name, ATTR_NODE_note)) - atemp[i].at_val.at_str = pnode->nd_note; + atemp[i].at_val.at_str = pnode->nd_note; + else if (!strcmp((padef + i)->at_name, ATTR_NODE_resources_total)) + { + clear_attr(&atemp[i],(padef+i)); + (padef+i)->at_set(&atemp[i],&pnode->attributes[0],SET); + } + else if (!strcmp((padef + i)->at_name, ATTR_NODE_resources_used)) + { + clear_attr(&atemp[i],(padef+i)); + (padef+i)->at_set(&atemp[i],&pnode->attributes[1],SET); + } else { /*we don't ever expect this*/ @@ -788,6 +822,10 @@ static void initialize_pbsnode( tinsert(pul[i], pnode, &ipaddrs); } /* END for (i) */ + /* clear the resource atributes */ + clear_attr(&pnode->attributes[0], &node_attr_def[ND_ATR_resources_total]); + clear_attr(&pnode->attributes[1], &node_attr_def[ND_ATR_resources_used]); + return; } /* END initialize_pbsnode() */ @@ -886,13 +924,15 @@ void effective_node_delete( pnode->nd_nsn = 0; pnode->nd_nsnfree = 0; + /* free resource attributes */ + node_attr_def[ND_ATR_resources_total].at_free(&pnode->attributes[0]); + node_attr_def[ND_ATR_resources_used].at_free(&pnode->attributes[1]); + return; } /* END effective_node_delete() */ - - /** * NOTE: pul can return NULL even on SUCCESS of routine * @@ -1164,6 +1204,8 @@ update_nodes_file(void) #endif struct pbsnode *np; + tlist_head head; + resource *res; int i, j; FILE *nin; @@ -1222,6 +1264,25 @@ update_nodes_file(void) ATTR_NODE_np, np->nd_nsn); + /* write out resources */ + head = np->attributes[0].at_val.at_list; + while ((res = (resource*)GET_NEXT(head))) + { + if (res != 0 && (res->rs_value.at_flags & ATR_VFLAG_FORCED)) + { + tlist_head head; + svrattrl *patlist; + + CLEAR_HEAD(head); + res->rs_defin->rs_encode(&res->rs_value,&head,"resources_total", + res->rs_defin->rs_name,ATR_ENCODE_CLIENT); + patlist = (svrattrl *)GET_NEXT(head); + fprintf(nin," resources_total.%s=%s", patlist->al_atopl.resource, + patlist->al_atopl.value); + } + head = *head.ll_next; + } + /* write out properties */ for (j = 0;j < np->nd_nprops - 1;++j) @@ -1326,6 +1387,7 @@ struct prop *init_prop( if ((pp = (struct prop *)malloc(sizeof(struct prop))) != NULL) { pp->name = pname; + pp->value = 0; pp->mark = 0; pp->next = 0; } @@ -1415,8 +1477,10 @@ int create_pbs_node( int ntype; /* node type; time-shared, not */ char *pname; /* node name w/o any :ts */ u_long *pul; /* 0 terminated host adrs array*/ - int rc; + int rc, rc2; int iht; + tlist_head head; + resource *res; if ((rc = process_host_name_part( objname, /* I */ @@ -1535,16 +1599,29 @@ int create_pbs_node( } rc = mgr_set_node_attr( - pnode, node_attr_def, - ND_ATR_LAST, + ND_ATR_resources_total, plist, perms, bad, (void *)pnode, ATR_ACTION_ALTER); + if (rc == 0) + rc2 = mgr_set_attr( + pnode->attributes, + &node_attr_def[ND_ATR_LAST-2], + 2, + plist, + perms, + bad, + (void *)pnode, + ATR_ACTION_ALTER, + 1); + + if (rc == 0 && rc2 != 0) rc = rc2; + if (rc != 0) { effective_node_delete(pnode); @@ -1552,6 +1629,15 @@ int create_pbs_node( return(rc); } + /* for all set resources, mark the force flag ATR_VFLAG_FORCED */ + head = pnode->attributes[0].at_val.at_list; + while ((res = (resource*)GET_NEXT(head))) + { + if (res != 0) + res->rs_value.at_flags |= ATR_VFLAG_FORCED; + head = *head.ll_next; + } + recompute_ntype_cnts(); return(PBSE_NONE); /*create completely successful*/ @@ -1652,9 +1738,9 @@ int setup_nodes(void) char note[MAX_NOTE+1]; char *nodename; char propstr[256]; - char *token; + char *token, *token2; int bad, i, num, linenum; - int err; + int err, is_resource; struct pbsnode *np; char *val; @@ -1738,6 +1824,7 @@ int setup_nodes(void) /* now process remaining tokens (if any), they may be either */ /* attributes (keyword=value) or old style properties */ + /* or total resources (resources_total.res_name=value) */ while (1) { @@ -1758,7 +1845,25 @@ int setup_nodes(void) if ((val == NULL) || (err != 0) || (xchar == '=')) goto errtoken1; - pal = attrlist_create(token, 0, strlen(val) + 1); + is_resource = strncmp(token,ATTR_NODE_resources_total, + strlen(ATTR_NODE_resources_total)); + + if (is_resource == 0 || is_resource == 15) + is_resource = (strchr(token,'.')!=NULL)?1:0; + else + is_resource = 0; + + if (is_resource) + { + token[15] = '\0'; + token2 = token+strlen(ATTR_NODE_resources_total)+1; + } + else + { + token2 = 0; + } + + pal = attrlist_create(token, token2, strlen(val) + 1); if (pal == NULL) { @@ -2011,10 +2116,6 @@ static void delete_a_subnode( } /* END delete_a_subnode() */ - - - - /* * node_np_action - action routine for node's np attribute */ diff --git a/src/server/node_manager.c b/src/server/node_manager.c index bb42a1c..7dcb38a 100644 --- a/src/server/node_manager.c +++ b/src/server/node_manager.c @@ -117,6 +117,8 @@ #include "mcom.h" #include "utils.h" +#include "assertions.h" + #define IS_VALID_STR(STR) (((STR) != NULL) && ((STR)[0] != '\0')) extern void DIS_rpp_reset A_((void)); @@ -172,14 +174,16 @@ extern int SvrNodeCt; #define MAX_BM 64 #endif -int hasprop(struct pbsnode *, struct prop *); +int hasprop(struct pbsnode *, struct prop *, int proc_count); void send_cluster_addrs(struct work_task *); int add_cluster_addrs(int); int is_compose(int, int); -int add_job_to_node(struct pbsnode *,struct pbssubn *,short,job *,int); +int add_job_to_node(struct pbsnode *,struct pbssubn *,short,job *,int,int); int node_satisfies_request(struct pbsnode *,char *); int reserve_node(struct pbsnode *,short,job *,char *,struct howl **); int build_host_list(struct howl **,struct pbssubn *,struct pbsnode *); +void adjust_resources_use(struct pbsnode *pnode, struct jobinfo *jp, int, + enum batch_op op); /* @@ -1263,6 +1267,73 @@ int is_stat_get( } } + else + { + resource_def *def; + resource *res; + int store = 0; + char *target_name = ret_info, *c = strchr(ret_info,'='); + if (c != NULL) + *c = '\0'; + + /* check if the resource is in the list specified in ATTR_ResourcesToStore */ + if (server.sv_attr[SRV_ATR_ResourcesToStore].at_flags & ATR_VFLAG_SET) + { + int i; + struct array_strings *tmp = + server.sv_attr[SRV_ATR_ResourcesToStore].at_val.at_arst; + + for (i = 0; i < tmp->as_usedptr; i++) + { + if (strcmp(tmp->as_string[i],ret_info) == 0) + { + store = 1; + break; + } + } + } + + /* check if the resource is mapped to some other name */ + if (store && (server.sv_attr[SRV_ATR_ResourcesMappings].at_flags & ATR_VFLAG_SET)) + { + int i; + struct array_strings *tmp = + server.sv_attr[SRV_ATR_ResourcesMappings].at_val.at_arst; + + for (i = 0; i < tmp->as_usedptr; i++) + { + if (strncmp(tmp->as_string[i],ret_info,strlen(ret_info)) == 0) + { + char *new_name = strchr(tmp->as_string[i],'='); + + if (new_name == NULL) /* mallformed value */ + continue; + + target_name = ++new_name; + } + } + } + + if (store) + { + def = find_resc_def(svr_resc_def,target_name,svr_resc_size); + if (def != 0) + { + res = find_resc_entry(&np->attributes[0],def); + if (res != 0) + { + if ((res->rs_value.at_flags & ATR_VFLAG_FORCED) == 0) + def->rs_decode(&res->rs_value,0,0,c+1); + } + else + { + res = add_resource_entry(&np->attributes[0],def); + def->rs_decode(&res->rs_value,0,0,c+1); + res->rs_value.at_flags &= ~ATR_VFLAG_FORCED; + } + } + } + } free(ret_info); } /* END while (rc != DIS_EOD) */ @@ -2314,6 +2385,10 @@ static void free_prop( prop = pp->next; free(pp->name); + + if (pp->value) + free(pp->value); + free(pp); } /* END for (pp) */ @@ -2371,6 +2446,70 @@ void node_unreserve( +int hasres(struct pbsnode *pnode, char *name, char *value, int proc_count) + { + resource_def *rd; + resource *total, *used, req, tmp; + int rc; + + rd = find_resc_def(svr_resc_def,name,svr_resc_size); + + if (rd == NULL) /* unknown resource */ + return 0; + + if ((rd->rs_flags & (ATR_DFLAG_SELECT_PROC | ATR_DFLAG_SELECT_MOM)) == 0) + return 1; /* this resource is not per-proc or per-node and therefore not checked */ + + total = find_resc_entry(&pnode->attributes[0],rd); + + if (total == NULL) /* resource not present on node */ + return 0; + + used = find_resc_entry(&pnode->attributes[1],rd); + + rd->rs_decode(&req.rs_value,0,name,value); /* encode the request into resource */ + + /* now we need to determine if this resource is per-proc or per-node */ + /* if it is per-proc, we need to multiply the value accordingly by the number of procs */ + /* because there is no universal logic for this, we simply have to manually multiply the value */ + if ((rd->rs_flags & ATR_DFLAG_SELECT_PROC) != 0) + { + switch (rd->rs_type) + { + case ATR_TYPE_LONG: + req.rs_value.at_val.at_long *= proc_count; + break; + case ATR_TYPE_LL: + req.rs_value.at_val.at_ll *= proc_count; + break; + case ATR_TYPE_SHORT: + req.rs_value.at_val.at_short *= proc_count; + break; + case ATR_TYPE_SIZE: + /* keep it in the same unit */ + req.rs_value.at_val.at_size.atsv_num *= proc_count; + break; + default: /* we can't multiply non-numeric values */ + break; /* ATR_TYPE_JINFOP, ATR_TYPE_ACL, ATR_TYPE_RESC, ATR_TYPE_CHAR, ATR_TYPE_STR, ATR_TYPE_ARST, ATR_TYPE_LIST */ + } + } + + rd->rs_set(&tmp.rs_value,&total->rs_value,SET); /* set the total */ + + if (used != NULL) + rd->rs_set(&tmp.rs_value,&used->rs_value,DECR); /* decrement the used amount */ + + rc = rd->rs_comp(&tmp.rs_value,&req.rs_value); /* finally compare the result */ + + rd->rs_free(&tmp.rs_value); + rd->rs_free(&req.rs_value); + + if (rc >= 0) + return 1; + else + return 0; + } + /* ** Look through the property list and make sure that all @@ -2378,9 +2517,9 @@ void node_unreserve( */ int hasprop( - struct pbsnode *pnode, - struct prop *props) + struct prop *props, + int proc_count ) { @@ -2391,18 +2530,26 @@ int hasprop( struct prop *pp; - if (need->mark == 0) /* not marked, skip */ - continue; + if (need->mark == 0 && (props->value == NULL)) /* not marked, skip */ + continue; /* do not skip resources */ - for (pp = pnode->nd_first;pp != NULL;pp = pp->next) + if (props->value == NULL) { - if (strcmp(pp->name, need->name) == 0) - break; /* found it */ - } + for (pp = pnode->nd_first;pp != NULL;pp = pp->next) + { + if (strcmp(pp->name, need->name) == 0) + break; /* found it */ + } - if (pp == NULL) + if (pp == NULL) + { + return(0); + } + } + else { - return(0); + if (!hasres(pnode,props->name,props->value,proc_count)) + return 0; } } @@ -2539,7 +2686,7 @@ static int search( continue; */ - if (!hasprop(pnode, glorf)) + if (!hasprop(pnode, glorf, vpreq)) continue; if ((skip == SKIP_NONE) || (skip == SKIP_NONE_REUSE)) @@ -2610,7 +2757,7 @@ static int search( (vpreq < (pnode->nd_nsnfree + pnode->nd_nsnshared))) continue; - if (!hasprop(pnode, glorf)) + if (!hasprop(pnode, glorf, vpreq)) continue; pnode->nd_flag = conflict; @@ -2793,6 +2940,18 @@ static int proplist( return(1); } } + /* check if it is a known resource */ + else if (find_resc_def(svr_resc_def,pname,svr_resc_size) != NULL) + { + pequal++; + + pp = (struct prop *)malloc(sizeof(struct prop)); + pp->mark = 0; /* TODO for now resources are marked not to be checked */ + pp->name = strdup(pname); + pp->value = strdup(pequal); + pp->next = *plist; + *plist = pp; + } else { return(1); /* not recognized - error */ @@ -2804,6 +2963,7 @@ static int proplist( pp->mark = 1; pp->name = strdup(pname); + pp->value = NULL; pp->next = *plist; *plist = pp; @@ -2818,7 +2978,194 @@ static int proplist( return 0; } /* END proplist() */ +static int nodespec_to_proplist(char **str, int *cnodes, int *cprocs, struct prop **list) + { + int i = 0; + /* determine number of nodes */ + if ((i = number(str, cnodes)) == -1) + { + return -1; + } + + if (i == 0) /* number exists */ + { + if (**str == ':') + { + /* there are properties */ + (*str)++; + + if (proplist(str, list, cprocs)) + { + return -1; + } + } + } + else /* no number */ + { + if (proplist(str, list, cprocs)) + { + /* must be a prop list with no number in front */ + + return -1; + } + } + + return 0; + } + +void regenerate_total_resources(job * pjob) + { + int cnodes = 0; + int cprocs = 0; + + /* cleanup any previous values */ + job_attr_def[(int)JOB_ATR_total_resources]. + at_free(&pjob->ji_wattr[(int)JOB_ATR_total_resources]); + + /* pass 1. + * - find nodespec + * - add each part that represents a counted resource into total resources + * - calculate total amount of procs and nodes + */ + if ((pjob->ji_wattr[(int)JOB_ATR_resource].at_flags & ATR_VFLAG_SET) != 0) + { + resource_def *rd; + resource *rs; + + rd = find_resc_def(svr_resc_def,"nodes",svr_resc_size); + if (rd != NULL) + rs = find_resc_entry(&pjob->ji_wattr[(int)JOB_ATR_resource],rd); + + if (rd != NULL && rs != NULL) + { + char *buf, *part, *next; + + next = buf = part = strdup(rs->rs_value.at_val.at_str); + if (buf == NULL) + return; + + /* process each nodespec part (between + signs) */ + do + { + int procs = 1, nodes = 1; + struct prop *prop = NULL, *iter; + + part = next; + + next = strchr(part,'+'); + if (next != NULL) + { + *next = '\0'; + next++; + } + + /* parse this nodespec part */ + if (nodespec_to_proplist(&part,&nodes,&procs,&prop) == -1) + { + free(buf); + return; + } + + /* for each property determine if it is a counted resource */ + for (iter = prop;iter;iter = iter->next) + { + int total_count = 0; + int i, ret; + resource *value; + resource decoded; + resource_def *defin = find_resc_def(svr_resc_def,iter->name,svr_resc_size); + + if (defin != NULL) + { + /* only count per proc and per node resources in the nodespec */ + if ((defin->rs_flags & ATR_DFLAG_SELECT_MOM) != 0) + total_count = nodes; + if ((defin->rs_flags & ATR_DFLAG_SELECT_PROC) != 0) + total_count = nodes * procs; + + ret = defin->rs_decode(&decoded.rs_value,0,iter->name,iter->value); + if (ret != 0) + return; + + value = find_resc_entry(&pjob->ji_wattr[(int)JOB_ATR_total_resources],defin); + + for (i = 0;i < total_count;i++) + { + if (value == NULL) + { + value = add_resource_entry(&pjob->ji_wattr[(int)JOB_ATR_total_resources],defin); + ret = defin->rs_set(&value->rs_value,&decoded.rs_value,SET); + if (ret != 0) + return; + continue; + } + + ret = defin->rs_set(&value->rs_value,&decoded.rs_value,INCR); + if (ret != 0) + return; + } + } + } + + cnodes += nodes; + cprocs += nodes*procs; + } + while (next != NULL); + + free(buf); + } + else + { + /* if there is no nodes request, assume 1 proc and 1 node */ + cnodes = 1; + cprocs = 1; + } + } + + /* pass 2. + * - process all counted resources + */ + if ((pjob->ji_wattr[(int)JOB_ATR_resource].at_flags & ATR_VFLAG_SET) != 0) + { + resource *jbrc = (resource *)GET_NEXT(pjob->ji_wattr[(int)JOB_ATR_resource].at_val.at_list); + + while (jbrc != NULL) + { + int total_count = 1; + int i, ret; + resource *value; + + if ((jbrc->rs_defin->rs_flags & ATR_DFLAG_SELECT_MOM) != 0) + total_count = cnodes; + if ((jbrc->rs_defin->rs_flags & ATR_DFLAG_SELECT_PROC) != 0) + total_count = cprocs; + + value = find_resc_entry(&pjob->ji_wattr[(int)JOB_ATR_total_resources],jbrc->rs_defin); + + for (i = 0;i < total_count;i++) + { + if (value == NULL) + { + value = add_resource_entry(&pjob->ji_wattr[(int)JOB_ATR_total_resources],jbrc->rs_defin); + ret = jbrc->rs_defin->rs_set(&value->rs_value,&jbrc->rs_value,SET); + if (ret != 0) + return; + continue; + } + + ret = jbrc->rs_defin->rs_set(&value->rs_value,&jbrc->rs_value,INCR); + if (ret != 0) + return; + } + + + jbrc = (resource*)GET_NEXT(jbrc->rs_link); + } + } + + return; + } /* @@ -2892,7 +3239,7 @@ static int listelem( if (pnode->nd_ntype == NTYPE_CLUSTER) { - if (hasprop(pnode, prop) && hasppn(pnode, node_req, SKIP_NONE)) + if (hasprop(pnode, prop, node_req) && hasppn(pnode, node_req, SKIP_NONE)) hit++; if (hit == num) @@ -2951,72 +3298,6 @@ done: } /* END listelem() */ - - - -/* -** Add the "global" spec to every sub-spec in "spec". -** RETURNS: allocated string buffer (must be freed externally) -*/ - -static char *mod_spec( - - char *spec, /* I */ - char *global) /* I */ - - { - char *line; - char *cp; - int len; - int nsubspec; - - nsubspec = 1; - - for (cp = spec;*cp != '\0';cp++) - { - if (*cp == '+') - { - nsubspec++; - } - } - - len = strlen(global); - - line = malloc(nsubspec * (len + 1) + strlen(spec) + 1); - - if (line == NULL) - { - /* FAILURE */ - - return(NULL); - } - - cp = line; - - while (*spec) - { - if (*spec == '+') - { - *cp++ = ':'; - - strcpy(cp, global); - - cp += len; - } - - *cp++ = *spec++; - } - - *cp++ = ':'; - - strcpy(cp, global); - - return(line); - } /* END mod_spec() */ - - - - /* cntjons - count jobs on (shared) nodes */ static int cntjons( @@ -3176,7 +3457,211 @@ int MSNPrintF( } /* END MSNPrintF() */ +/** Count the parts in a nodespec + * + * (Only works for local specs) + * + * @param spec Nodespec to parse + * @return Count of parts + */ +static int nodespec_part_count(const char *spec) + { + int result = 1; + + dbg_precondition(spec != NULL, "This function does not accept NULL"); + + while (*spec != '\0') + { + if (*spec == '+') + result++; + spec++; + } + + return result; + } + +/** Append requirements to each part of a spec + * + * @param spec the spec to be modified + * @param app requirements to be appended + * @return Modified nodespec + */ +static char *nodespec_app(const char *spec, const char *app) + { + char *cp; + char *result; + + result = malloc(nodespec_part_count(spec) * strlen(app+1) + strlen(spec) + 1); + if (result == NULL) /* alloc fail */ + return NULL; + + cp = result; + + while (*spec) + { + if (*spec == '+') /* add the requirements before each '+' */ + { + *cp++ = ':'; + + strcpy(cp, app); + + cp += strlen(app); + } + + *cp++ = *spec++; + } + *cp++ = ':'; /* and also after the last part of the spec */ + + strcpy(cp, app); + + return(result); + } /* END nodespec_app() */ + +/** Expand nodespec + * + * Add the global nodespec part to local parts of nodespec and determine exclusivity. + * + * @param spec The spec to be parsed + * @param exclusive 0 if shared, 1 if exclusive, 2 if node exclusive + * @return NULL on failure or allocated modified spec + */ +static char *nodespec_expand(job *pjob, const char *spec, int *exclusive) + { + char *result, *globs, *cp, *tmp; + static char shared[] = "shared"; /* shared */ + static char excl[] = "excl"; /* node exclusive */ + + result = strdup(spec); + if (result == NULL) /* alloc failure */ + return NULL; + + if ((globs = strchr(result, '#')) != NULL) + /*find the first #, everything behind is global nodespec */ + { + *globs++ = '\0'; + + globs = strdup(globs); + if (globs == NULL) /* alloc failure */ + goto fail; + + /* glob now stores the global part of the nodespec + * - go thru each part of the global spec and append + */ + while ((cp = strrchr(globs, '#')) != NULL) + { + *cp++ = '\0'; + + if (!strcmp(cp, shared)) /* #shared */ + { + *exclusive = 0; + continue; + } + + if (!strcmp(cp, excl)) /* #excl */ + { + *exclusive = 1; + continue; + } + + tmp = nodespec_app(result, cp); + if (tmp == NULL) /* alloc failure */ + { + free(globs); + goto fail; + } + + free(result); + result = tmp; + } + + /* now parse the first part of the global nodespec */ + if (!strcmp(globs, shared)) /* #shared */ + { + *exclusive = 0; + free(globs); + goto done_stage1; + } + + if (!strcmp(globs, excl)) /* #excl */ + { + *exclusive = 1; + free(globs); + goto done_stage1; + } + + tmp = nodespec_app(result, globs); + if (tmp == NULL) /* alloc failure */ + { + free(globs); + goto fail; + } + + free(result); + result = tmp; + + free(globs); + } /* END if ((globs = strchr(spec,'#')) != NULL) */ + +done_stage1: + + /* if job is provided, also add each of the requested resources in the job resource list */ + if (pjob != NULL && (pjob->ji_wattr[(int)JOB_ATR_resource].at_flags & ATR_VFLAG_SET) != 0) + { + resource *jbrc = (resource *)GET_NEXT(pjob->ji_wattr[(int)JOB_ATR_resource].at_val.at_list); + + while (jbrc != NULL) + { + tlist_head head; + svrattrl *patlist; + char *buff, *tmp; + int len; + + /* only add resources that are per-proc or per-node */ + if ((jbrc->rs_defin->rs_flags & (ATR_DFLAG_SELECT_MOM | ATR_DFLAG_SELECT_PROC)) == 0) + { + jbrc = (resource *)GET_NEXT(jbrc->rs_link); + continue; + } + + /* convert resource into char* */ + CLEAR_HEAD(head); + jbrc->rs_defin->rs_encode(&jbrc->rs_value,&head,"ignored", + jbrc->rs_defin->rs_name,ATR_ENCODE_CLIENT); + patlist = (svrattrl *)GET_NEXT(head); + + if (patlist == NULL) + goto fail; + + len = strlen(patlist->al_atopl.resource); + len += strlen(patlist->al_atopl.value); + len += 2; /* '=' and '\0' */ + buff = malloc(len); + + if (buff == NULL) + goto fail; + + sprintf(buff,"%s=%s",patlist->al_atopl.resource,patlist->al_atopl.value); + + /* append to each part of the nodespec */ + tmp = nodespec_app(result, buff); + if (tmp == NULL) + goto fail; + + free(buff); + free(result); + result = tmp; + + jbrc = (resource *)GET_NEXT(jbrc->rs_link); + } + } + + return result; + +fail: + free(result); + return NULL; + } /* @@ -3195,6 +3680,7 @@ static int node_spec( int early, /* I (boolean) */ int exactmatch, /* I (boolean) - NOT USED */ char *ProcBMStr, /* I */ + job *pjob, /* O (optional) */ char *FailNode, /* O (optional,minsize=1024) */ char *EMsg) /* O (optional,minsize=1024) */ @@ -3204,10 +3690,9 @@ static int node_spec( struct pbsnode *pnode; struct pbssubn *snp; - char *str, *globs, *cp, *hold; + char *str; int i, num; int rv; - static char shared[] = "shared"; extern int PNodeStateToString(int, char *, int); @@ -3219,132 +3704,62 @@ static int node_spec( if (LOGLEVEL >= 6) { - sprintf(log_buffer, "entered spec=%.4000s", - spec); - - log_record( - PBSEVENT_SCHED, - PBS_EVENTCLASS_REQUEST, - id, - log_buffer); - - DBPRT(("%s\n", - log_buffer)); + sprintf(log_buffer, "entered spec=%.4000s", spec); + log_record(PBSEVENT_SCHED, PBS_EVENTCLASS_REQUEST, id, log_buffer); + DBPRT(("%s\n", log_buffer)); } exclusive = 1; /* by default, nodes (VPs) are requested exclusively */ + /* expand the global reqs into the local parts */ + spec = nodespec_expand(pjob, spec,&exclusive); - spec = strdup(spec); - - if (spec == NULL) + if (pjob != NULL) /* store the expanded nodespec */ { - /* FAILURE */ + if (pjob->ji_expanded_spec != NULL) + free(pjob->ji_expanded_spec); + pjob->ji_expanded_spec = strdup(spec); + } + if (spec == NULL) /* memory alloc fail */ + { sprintf(log_buffer,"cannot alloc memory"); - if (LOGLEVEL >= 1) - { - log_record( - PBSEVENT_SCHED, - PBS_EVENTCLASS_REQUEST, - id, - log_buffer); - } + log_record(PBSEVENT_SCHED, PBS_EVENTCLASS_REQUEST, id, log_buffer); if (EMsg != NULL) - { strncpy(EMsg,log_buffer,1024); - } return(-1); } - if ((globs = strchr(spec, '#')) != NULL) - { - *globs++ = '\0'; - - globs = strdup(globs); - - while ((cp = strrchr(globs, '#')) != NULL) - { - *cp++ = '\0'; - - if (strcmp(cp, shared) != 0) - { - hold = mod_spec(spec, cp); - - free(spec); - - spec = hold; - } - else - { - exclusive = 0; - } - } - - if (strcmp(globs, shared) != 0) - { - hold = mod_spec(spec, globs); - - free(spec); - - spec = hold; - } - else - { - exclusive = 0; - } - - free(globs); - } /* END if ((globs = strchr(spec,'#')) != NULL) */ - str = spec; - + /* count total nodes in the request */ num = ctnodes(str); - if (num > svr_clnodes) + if (num > svr_clnodes) /* the request is for more nodes then the server has */ { - /* FAILURE */ - free(spec); - sprintf(log_buffer, "job allocation request exceeds available cluster nodes, %d requested, %d available", - num, - svr_clnodes); + sprintf(log_buffer, "job allocation request exceeds available cluster" + " nodes, %d requested, %d available", num, svr_clnodes); if (LOGLEVEL >= 6) - { - log_record( - PBSEVENT_SCHED, - PBS_EVENTCLASS_REQUEST, - id, - log_buffer); - } + log_record(PBSEVENT_SCHED, PBS_EVENTCLASS_REQUEST, id, log_buffer); if (EMsg != NULL) - { strncpy(EMsg, log_buffer, 1024); - } return(-1); } if (LOGLEVEL >= 6) { - sprintf(log_buffer, "job allocation debug: %d requested, %d svr_clnodes, %d svr_totnodes", - num, - svr_clnodes, - svr_totnodes); + sprintf(log_buffer, "job allocation debug: %d requested, %d svr_clnodes," + " %d svr_totnodes", num, svr_clnodes, svr_totnodes); - log_record( - PBSEVENT_SCHED, - PBS_EVENTCLASS_REQUEST, - id, - log_buffer); + log_record(PBSEVENT_SCHED, PBS_EVENTCLASS_REQUEST, id, log_buffer); - DBPRT(("%s\n", - log_buffer)); + DBPRT(("%s\n", log_buffer)); } /* @@ -3882,8 +4297,6 @@ int reserve_node( #endif /* GEOMETRY_REQUESTS */ - - /** * adds this job to the node's list of jobs * checks to be sure not to add duplicates @@ -3903,7 +4316,8 @@ int add_job_to_node( struct pbssubn *snp, /* I/O */ short newstate, /* I */ job *pjob, /* I */ - int exclusive) /* I */ + int exclusive, /* I */ + int first) { char *id = "add_job_to_node"; @@ -3940,10 +4354,13 @@ int add_job_to_node( jp->next = snp->jobs; snp->jobs = jp; jp->job = pjob; + jp->order = pnode->nd_order; + pnode->nd_nsnfree--; /* reduce free count */ + adjust_resources_use(pnode,jp,first,INCR); /* if no free VPs, set node state */ - if (pnode->nd_nsnfree <= 0) + if (pnode->nd_nsnfree <= 0) pnode->nd_state = newstate; if (snp->inuse == INUSE_FREE) @@ -4041,6 +4458,8 @@ int set_nodes( char ProcBMStr[MAX_BM]; + int first; + if (FailHost != NULL) FailHost[0] = '\0'; @@ -4067,7 +4486,7 @@ int set_nodes( /* allocate nodes */ - if ((i = node_spec(spec, 1, 1, ProcBMStr, FailHost, EMsg)) == 0) /* check spec */ + if ((i = node_spec(spec, 1, 1, ProcBMStr, pjob, FailHost, EMsg)) == 0) /* check spec */ { /* no resources located, request failed */ @@ -4134,6 +4553,7 @@ int set_nodes( } #endif /* GEOMETRY_REQUESTS */ + first = 1; for (snp = pnode->nd_psn;snp && pnode->nd_needed;snp = snp->next) { if (exclusive) @@ -4149,7 +4569,8 @@ int set_nodes( /* Mark subnode as being IN USE */ - add_job_to_node(pnode,snp,newstate,pjob,exclusive); + add_job_to_node(pnode,snp,newstate,pjob,exclusive,first); + first = 0; build_host_list(&hlist,snp,pnode); } /* END for (snp) */ @@ -4275,7 +4696,7 @@ int node_avail_complex( holdnum = svr_numnodes; - ret = node_spec(spec, 1, 0, NULL, NULL, NULL); + ret = node_spec(spec, 1, 0, NULL, NULL, NULL, NULL); svr_numnodes = holdnum; @@ -4366,7 +4787,7 @@ int node_avail( if (pn->nd_state & INUSE_DELETED) continue; - if ((pn->nd_ntype == NTYPE_CLUSTER) && hasprop(pn, prop)) + if ((pn->nd_ntype == NTYPE_CLUSTER) && hasprop(pn, prop, node_req)) { if (pn->nd_state & (INUSE_OFFLINE | INUSE_DOWN)) ++xdown; @@ -4463,7 +4884,7 @@ int node_reserve( return(-1); } - if ((ret_val = node_spec(nspec, 0, 0, NULL, NULL, NULL)) >= 0) + if ((ret_val = node_spec(nspec, 0, 0, NULL, NULL, NULL, NULL)) >= 0) { /* ** Zero or more of the needed Nodes are available to be @@ -4591,8 +5012,140 @@ find_ts_node(void) return(NULL); } /* END find_ts_node() */ +/** Adjust a node resource value + * + * @param pattr Attribute holding the resources + * @param name Name of the resource + * @param value Value by which to adjust + * @param first 1 if this the first adjustment on node, 0 if not + * @param op Operation, INCR or DECR + */ +static void adjust_resource_value(attribute *pattr, char *name, char *value, int first, enum batch_op op) + { + resource_def *defin; + resource *val; + resource decoded; + int ret; + + if (value == NULL) + return; + + defin = find_resc_def(svr_resc_def,name,svr_resc_size); + + if (defin == NULL) /* not a known resource */ + return; + + /* if this is not a per-proc or per-node resource, ignore */ + if ((defin->rs_flags & (ATR_DFLAG_SELECT_MOM | ATR_DFLAG_SELECT_PROC)) == 0) + return; + + /* if this is not the first adjustment on node and the resources is not per-proc, ignore */ + if ((defin->rs_flags & ATR_DFLAG_SELECT_PROC) == 0 && first == 0) + return; + + ret = defin->rs_decode(&decoded.rs_value,0,name,value); + + if (ret != 0) /* could not decode value */ + return; + + val = find_resc_entry(pattr,defin); + + if (val == NULL && op == INCR) + { /* if increasing the value and the resource is not yet present, add new entry */ + val = add_resource_entry(pattr,defin); + + if (val == NULL) + return; + + ret = defin->rs_set(&val->rs_value,&decoded.rs_value,SET); + } + else if (val != NULL) + { + ret = defin->rs_set(&val->rs_value,&decoded.rs_value,op); + } + } + + + + +/** Adjust the resources use on a node + * + * @param pnode Node with the resources + * @param jp Job info + * @param first Determines if this is the first adjustment for this node + * @param op Operation to do (increment, decrement) + */ +void adjust_resources_use(struct pbsnode *pnode, struct jobinfo *jp, int first, + enum batch_op op) + { + struct prop *prop = NULL, *iter = NULL; + char *str, *spec, *token; + int i,count,num; + + if (jp->job->ji_expanded_spec == NULL) + return; + + spec = strdup(jp->job->ji_expanded_spec); + token = spec; + + /* determine the count of nodespec parts */ + for (i = 1; token != NULL; i++) + { + token = strchr(token,'+'); + if (token != NULL) + token++; + } + + count = i; + + token = spec; + + /* record the starts of the node specs */ + for (i = 1; token != NULL; i++) + { + if (i == jp->order) + { + str = token; + } + + token = strchr(token,'+'); + if (token != NULL) + { + *token = '\0'; + token++; + } + } + if ((i = number(&str, &num)) == -1) /* get number */ + return; + if (i == 0) + { + /* number exists */ + if (*str == ':') + { + /* there are properties */ + (str)++; + + if (proplist(&str, &prop, &num)) + return; + } + } + else + { + /* no number */ + if (proplist(&str, &prop, &num)) + return; + } + + iter = prop; /* iterating through resources in nodespec */ + while (iter != NULL) + { + /* adjust the value */ + adjust_resource_value(&pnode->attributes[1],iter->name,iter->value,first,op); + iter = iter->next; + } + } /* @@ -4612,6 +5165,7 @@ void free_nodes( struct jobinfo *jp, *prev; int i; + int first; if (LOGLEVEL >= 3) { @@ -4636,6 +5190,7 @@ void free_nodes( /* examine all subnodes in node */ + first = 1; for (np = pnode->nd_psn;np != NULL;np = np->next) { /* examine all jobs allocated to subnode */ @@ -4665,6 +5220,9 @@ void free_nodes( else prev->next = jp->next; + adjust_resources_use(pnode,jp,first,DECR); + first = 0; + free(jp); pnode->nd_nsnfree++; /* up count of free */ @@ -4718,7 +5276,9 @@ static void set_one_old( char *name, job *pjob, - int shared) /* how used flag, either INUSE_JOB or INUSE_JOBSHARE */ + int shared, /* how used flag, either INUSE_JOB or INUSE_JOBSHARE */ + int order, + int first) { int i; @@ -4769,8 +5329,11 @@ static void set_one_old( snp->jobs = jp; jp->job = pjob; + jp->order = order; } + adjust_resources_use(pnode,jp,first,INCR); + if (--pnode->nd_nsnfree <= 0) pnode->nd_state |= shared; @@ -4798,10 +5361,13 @@ void set_old_nodes( job *pjob) /* I (modified) */ { - char *old; - char *po; + char *old = NULL, *fold = NULL; + char *po, *ps; + char *spec = NULL, *fspec = NULL; resource *presc; int shared = INUSE_JOB; + int order = 1, remaining = 0; + int excl = 0; if ((pbsndmast != NULL) && (pjob->ji_wattr[(int)JOB_ATR_exec_host].at_flags & ATR_VFLAG_SET)) @@ -4821,25 +5387,88 @@ void set_old_nodes( } } + /* reset the jobs expanded nodespec from job attribute */ + if ((pjob->ji_wattr[(int)JOB_ATR_sched_spec].at_flags & ATR_VFLAG_SET) != 0) + { + free(pjob->ji_expanded_spec); + pjob->ji_expanded_spec = nodespec_expand(pjob, pjob->ji_wattr[(int)JOB_ATR_sched_spec].at_val.at_str,&excl); + } + + /* duplicate the expanded nodespec, so we can work with it */ + if (pjob->ji_expanded_spec != NULL) + { + spec = strdup(pjob->ji_expanded_spec); + fspec = spec; + ps = spec; + } + old = strdup(pjob->ji_wattr[(int)JOB_ATR_exec_host].at_val.at_str); + fold = old; + po = old; - if (old == NULL) + if (old == NULL || spec == NULL) { /* FAILURE - cannot alloc memory */ return; } - while ((po = strrchr(old, (int)'+')) != NULL) + /* nodes in exec host are in the right order, but we don't know + * how many belong to each of the nodespec parts + * we have to cut out the ppn=value and also the number of nodes */ + + do /* for each part of the nodespec */ { - *po++ = '\0'; + char *ppn = NULL; + int ppn_count = 0; + + spec = ps; /* swap for the next part */ + + remaining = atoi(spec); /* determine number of nodes requested */ + if (remaining == 0) + remaining = 1; + + ps = strchr(ps,'+'); /* find next part */ + if (ps != NULL) + { + *ps = '\0'; ps++; + } + + /* determine ppn */ + ppn = strstr(spec,"ppn="); + if (ppn != NULL) /* there is ppn */ + { + ppn+=4; /* move beyond ppn= */ + + ppn_count = atoi(ppn); + if (ppn_count == 0) /* should not happen */ + ppn_count = 1; + + remaining *= ppn_count; /* vps are requested for each node */ + } + + do /* for each remaining, eat part from exec host */ + { + old = po; + + po = strchr(po,'+'); + if (po != NULL) + { + *po = '\0'; po++; + } + + set_one_old(old, pjob, shared, order, (remaining % ppn_count == 0) ? 1 : 0); + remaining--; + } + while (remaining > 0 && po != NULL); - set_one_old(po, pjob, shared); + order++; } + while (ps != NULL); - set_one_old(old, pjob, shared); - free(old); + free(fold); + free(fspec); } /* END if ((pbsndmast != NULL) && ...) */ return; diff --git a/src/server/req_manager.c b/src/server/req_manager.c index 5f276a1..9cb4b73 100644 --- a/src/server/req_manager.c +++ b/src/server/req_manager.c @@ -150,7 +150,7 @@ extern char *msg_man_uns; extern int que_purge(pbs_queue *); extern void save_characteristic(struct pbsnode *); extern int chk_characteristic(struct pbsnode *, int *); -extern int hasprop(struct pbsnode *, struct prop *); +extern int hasprop(struct pbsnode *, struct prop *, int proc_count); extern int PNodeStateToString(int, char *, int); @@ -362,7 +362,7 @@ mgr_log_attr( * attributes must be successfully set, or none are modified. */ -static int mgr_set_attr( +int mgr_set_attr( attribute *pattr, /* current attributes */ attribute_def *pdef, @@ -371,7 +371,8 @@ static int mgr_set_attr( int privil, int *bad, void *parent, - int mode) + int mode, + int skip_unknown) { int index; @@ -398,7 +399,7 @@ static int mgr_set_attr( * and update it with the newly decoded value */ - if ((rc = attr_atomic_set(plist, pattr, new, pdef, limit, -1, privil, bad)) != 0) + if ((rc = attr_atomic_set(plist, pattr, new, pdef, limit, skip_unknown?0:-1, privil, bad)) != 0) { attr_atomic_kill(new, pdef, limit); @@ -733,7 +734,7 @@ int mgr_set_node_attr( * return code (rc) shapes caller's reply */ - if ((rc = attr_atomic_node_set(plist, unused, new, pdef, limit, -1, privil, bad)) != 0) + if ((rc = attr_atomic_node_set(plist, unused, new, pdef, limit, 0, privil, bad)) != 0) { attr_atomic_kill(new, pdef, limit); @@ -968,7 +969,8 @@ void mgr_queue_create( preq->rq_perm, &bad, (void *)pque, - ATR_ACTION_NEW); + ATR_ACTION_NEW, + 0); if (rc != 0) { @@ -1189,7 +1191,8 @@ void mgr_server_set( preq->rq_perm, &bad_attr, (void *) & server, - ATR_ACTION_ALTER); + ATR_ACTION_ALTER, + 0); /* PBSE_BADACLHOST - lets show the user the first bad host in the ACL */ @@ -1446,7 +1449,8 @@ void mgr_queue_set( preq->rq_perm, &bad, (void *)pque, - ATR_ACTION_ALTER); + ATR_ACTION_ALTER, + 0); if (rc != 0) @@ -1590,7 +1594,96 @@ void mgr_queue_unset( } +/* + * mgr_node_unset - Unset (clear) Node Attribute Values + * + * Finds the node, clears the requested attributes and returns a reply + */ + +void mgr_node_unset( + + struct batch_request *preq) + + { + int allnodes; + int bad_attr = 0; + svrattrl *plist; + struct pbsnode **pnode; + char *nname; + int rc; + int total_count; + int position; + + if ((*preq->rq_ind.rq_manager.rq_objname == '\0') || + (*preq->rq_ind.rq_manager.rq_objname == '@')) + { + allnodes = TRUE; + nname = "all"; + + pnode = pbsndlist; + total_count = svr_totnodes; + } + else + { + allnodes = FALSE; + nname = preq->rq_ind.rq_manager.rq_objname; + + pnode = malloc(sizeof(struct pbsnode*)*1); + pnode[0] = find_nodebyname(nname); + total_count = 1; + } + + if (pnode == NULL) + { + req_reject(PBSE_UNKQUE, 0, preq, NULL, NULL); + + return; + } + + sprintf(log_buffer, msg_manager, + + msg_man_uns, + preq->rq_user, + preq->rq_host); + + log_event( + PBSEVENT_ADMIN, + PBS_EVENTCLASS_QUEUE, + nname, + log_buffer); + + plist = (svrattrl *)GET_NEXT(preq->rq_ind.rq_manager.rq_attr); + + for (position = 0; position < total_count; position++) + { + rc = mgr_unset_attr( + pnode[0]->attributes, + &node_attr_def[ND_ATR_LAST-2], + 2, + plist, + preq->rq_perm, + &bad_attr); + + if (rc != 0) + { + reply_badattr(rc, bad_attr, plist, preq); + + return; + } + + update_nodes_file(); + + mgr_log_attr(msg_man_uns, plist, PBS_EVENTCLASS_NODE, pnode[0]->nd_name); + } + + if (total_count == 1) + free(pnode); + + reply_ack(preq); + + return; + } /* @@ -1614,7 +1707,7 @@ void mgr_node_set( struct pbsnode *pnode; char *nodename = NULL; - int rc; + int rc, rc2; int i, len; int problem_cnt = 0; @@ -1642,6 +1735,7 @@ void mgr_node_set( propnodes = 1; nodename = preq->rq_ind.rq_manager.rq_objname; props.name = nodename + 1; + props.value = 0; props.mark = 1; props.next = NULL; } @@ -1693,7 +1787,7 @@ void mgr_node_set( for (i = 0;i < svr_totnodes;i++, pnode = pbsndlist[i]) { - if (propnodes && !hasprop(pnode, &props)) + if (propnodes && !hasprop(pnode, &props, 1)) continue; save_characteristic(pnode); @@ -1701,13 +1795,28 @@ void mgr_node_set( rc = mgr_set_node_attr( pnode, node_attr_def, - ND_ATR_LAST, + ND_ATR_resources_total, plist, preq->rq_perm, &bad, (void *)pnode, ATR_ACTION_ALTER); + if (rc == 0) + rc2 = mgr_set_attr( + pnode->attributes, + &node_attr_def[ND_ATR_LAST-2], + 2, + plist, + preq->rq_perm, + &bad, + (void*)pnode, + ATR_ACTION_ALTER, + 1); + + if (rc == 0 && rc2 != 0) + rc = rc2; + if (rc != 0) { if (allnodes || propnodes) @@ -2285,18 +2394,12 @@ void req_manager( break; -#if 0 - - /* "unsetting" nodes isn't currently supported */ - case MGR_OBJ_NODE: mgr_node_unset(preq); break; -#endif /* 0 */ - default: req_reject(PBSE_IVALREQ, 0, preq, NULL, NULL); diff --git a/src/server/req_quejob.c b/src/server/req_quejob.c index daada74..13098ad 100644 --- a/src/server/req_quejob.c +++ b/src/server/req_quejob.c @@ -131,6 +131,7 @@ extern int svr_authorize_jobreq A_((struct batch_request *, job *)); extern int svr_chkque A_((job *, pbs_queue *, char *, int, char *)); extern int job_route A_((job *)); extern int node_avail_complex(char *, int *, int *, int *, int*); +void regenerate_total_resources(job *); /* Global Data Items: */ @@ -865,6 +866,13 @@ void req_quejob( NULL, server_name); + /* Generate the resources total job attribute + * + * This attribute contains all counted resources + * from both the nodespec request and resource requests. + */ + regenerate_total_resources(pj); + /* * See if the job is qualified to go into the requested queue. * Note, if an execution queue, then ji_qs.ji_un.ji_exect is set up @@ -908,6 +916,8 @@ void req_quejob( pj->ji_qs.ji_un.ji_newt.ji_scriptsz = 0; + pj->ji_expanded_spec = NULL; + /* acknowledge the request with the job id */ if (reply_jobid(preq, pj->ji_qs.ji_jobid, BATCH_REPLY_CHOICE_Queue) != 0) @@ -1682,8 +1692,6 @@ void req_commit( } /* END req_commit() */ - - /* * locate_new_job - locate a "new" job which has been set up req_quejob on * the servers new job list. diff --git a/src/server/req_runjob.c b/src/server/req_runjob.c index d2fc93e..4b441c9 100644 --- a/src/server/req_runjob.c +++ b/src/server/req_runjob.c @@ -1641,6 +1641,13 @@ static int assign_hosts( hosttoalloc = PBS_DEFAULT_NODE; } + /* now we have a nodespec */ + job_attr_def[(int)JOB_ATR_sched_spec].at_decode( + &pjob->ji_wattr[(int)JOB_ATR_sched_spec], + NULL, + NULL, + hosttoalloc); /* we don't really care if this fails */ + /* do we need to allocate the (cluster) node(s)? */ if (svr_totnodes != 0) diff --git a/src/server/req_stat.c b/src/server/req_stat.c index e70f6f7..4853bf3 100644 --- a/src/server/req_stat.c +++ b/src/server/req_stat.c @@ -129,7 +129,7 @@ int status_job A_((job *, struct batch_request *, svrattrl *, tlist_head *, int int status_attrib A_((svrattrl *, attribute_def *, attribute *, int, int, tlist_head *, int *, int)); extern int svr_connect A_((pbs_net_t, unsigned int, void (*)(int), enum conn_type)); extern int status_nodeattrib(svrattrl *, attribute_def *, struct pbsnode *, int, int, tlist_head *, int*); -extern int hasprop(struct pbsnode *, struct prop *); +extern int hasprop(struct pbsnode *, struct prop *, int proc_count); extern void rel_resc(job*); /* Private Data Definitions */ @@ -1195,6 +1195,7 @@ void req_stat_node( { type = 2; props.name = name + 1; + props.value = NULL; props.mark = 1; props.next = NULL; } @@ -1231,7 +1232,7 @@ void req_stat_node( { pnode = pbsndmast[i]; - if ((type == 2) && !hasprop(pnode, &props)) + if ((type == 2) && !hasprop(pnode, &props, 1)) continue; if ((rc = status_node(pnode, preq, &preply->brp_un.brp_status)) != 0) diff --git a/src/server/resc_def_all.c b/src/server/resc_def_all.c index 2076aea..3af1f7a 100644 --- a/src/server/resc_def_all.c +++ b/src/server/resc_def_all.c @@ -170,7 +170,7 @@ resource_def svr_resc_def_const[] = comp_size, free_null, NULL_FUNC, - READ_WRITE | ATR_DFLAG_MOM | ATR_DFLAG_ALTRUN | ATR_DFLAG_RASSN, + READ_WRITE | ATR_DFLAG_MOM | ATR_DFLAG_ALTRUN | ATR_DFLAG_RASSN | ATR_DFLAG_SELECT_MOM, ATR_TYPE_SIZE }, { "pmem", @@ -190,7 +190,7 @@ resource_def svr_resc_def_const[] = comp_l, free_null, NULL_FUNC, - READ_WRITE | ATR_DFLAG_MOM | ATR_DFLAG_RMOMIG | ATR_DFLAG_RASSN, + READ_WRITE | ATR_DFLAG_MOM | ATR_DFLAG_RMOMIG | ATR_DFLAG_RASSN | ATR_DFLAG_SELECT_PROC, ATR_TYPE_LONG }, { "vmem", diff --git a/src/server/svr_attr_def.c b/src/server/svr_attr_def.c index 2092c15..f20e924 100644 --- a/src/server/svr_attr_def.c +++ b/src/server/svr_attr_def.c @@ -1101,6 +1101,30 @@ attribute_def svr_attr_def[] = PARENT_TYPE_SERVER }, + /* SRV_ATR_ResourcesToStore */ + { ATTR_ResourcesToStore, /* "resources_to_store" */ + decode_arst, + encode_arst, + set_arst, + comp_arst, + free_arst, + NULL_FUNC, + MGR_ONLY_SET, + ATR_TYPE_ARST, + PARENT_TYPE_SERVER + }, + /* SRV_ATR_ResourcesMappings */ + { ATTR_ResourcesMappings, /* "resources_mappings" */ + decode_arst, + encode_arst, + set_arst, + comp_arst, + free_arst, + NULL_FUNC, + MGR_ONLY_SET, + ATR_TYPE_ARST, + PARENT_TYPE_SERVER + }, /* site supplied server attribute definitions if any, see site_svr_attr_*.h */ #include "site_svr_attr_def.h" diff --git a/src/server/svr_jobfunc.c b/src/server/svr_jobfunc.c index 4a2cd32..3e87ae7 100644 --- a/src/server/svr_jobfunc.c +++ b/src/server/svr_jobfunc.c @@ -1721,7 +1721,7 @@ int svr_chkque( /* 6. resources of the job must be in the limits of the queue */ - if ((i = chk_resc_limits(&pjob->ji_wattr[(int)JOB_ATR_resource], pque, EMsg)) != 0) + if ((i = chk_resc_limits(&pjob->ji_wattr[(int)JOB_ATR_total_resources], pque, EMsg)) != 0) { /* FAILURE */