/** * @brief * queuestart_action - when queue is stopped or started, * for all jobs in queue and determine their accrue type * action function for QA_ATR_started. * * @param[in] pattr - pointer to special attributes of an Array Job * @param[in] pobject - queue which is stopped or started * @param[in] actmode - not used. * * @return int * @retval 0 - success */ int queuestart_action(attribute *pattr, void *pobject, int actmode) { job *pj; /* pointer to job */ long oldtype; long newaccruetype = -1; /* if determining accrue type */ pbs_queue *pque = (pbs_queue *) pobject; if ((pque != NULL) && (server.sv_attr[SRV_ATR_EligibleTimeEnable].at_val.at_long == 1)) { if (pattr->at_val.at_long == 0) { /* started = OFF */ /* queue stopped, start accruing eligible time */ /* running jobs and jobs accruing ineligible time are exempted */ /* jobs accruing eligible time are also exempted */ pj = (job*)GET_NEXT(pque->qu_jobs); while (pj != (job*)0) { oldtype = pj->ji_wattr[(int)JOB_ATR_accrue_type].at_val.at_long; if (oldtype != JOB_RUNNING && oldtype != JOB_INELIGIBLE && oldtype != JOB_ELIGIBLE) { /* determination of accruetype not required here */ (void)update_eligible_time(JOB_ELIGIBLE, pj); } pj = (job*)GET_NEXT(pj->ji_jobque); } } else { /* started = ON */ /* determine accrue type and accrue time */ pj = (job*)GET_NEXT(pque->qu_jobs); while (pj != (job*)0) { oldtype = pj->ji_wattr[(int)JOB_ATR_accrue_type].at_val.at_long; if (oldtype != JOB_RUNNING && oldtype != JOB_INELIGIBLE && oldtype != JOB_ELIGIBLE) { newaccruetype = determine_accruetype(pj); (void)update_eligible_time(newaccruetype, pj); } pj = (job*)GET_NEXT(pj->ji_jobque); } /* if scheduling = True, notify scheduler to start */ if (server.sv_attr[SRV_ATR_scheduling].at_val.at_long) set_scheduler_flag(SCH_SCHEDULE_STARTQ); } } return 0; }
/** * @brief * modify_job_attr - modify the attributes of a job automatically * Used by req_modifyjob() to alter the job attributes and by * stat_update() [see req_stat.c] to update with latest from MOM * * @param[in,out] pjob - job structure * @param[in,out] plist - Pointer to list of attributes * @param[in] perm - Permissions of the caller requesting the operation * @param[out] bad - Pointer to the attribute index in case of a failed */ int modify_job_attr(job *pjob, svrattrl *plist, int perm, int *bad) { int changed_resc; int allow_unkn; long i; int modified = 0; attribute *newattr; attribute *pre_copy; attribute *attr_save; attribute *pattr; resource *prc; int rc; int newstate = -1; int newsubstate = -1; long newaccruetype = -1; if (pjob->ji_qhdr->qu_qs.qu_type == QTYPE_Execution) allow_unkn = -1; else allow_unkn = (int)JOB_ATR_UNKN; pattr = pjob->ji_wattr; /* call attr_atomic_set to decode and set a copy of the attributes. * We need 2 copies: 1 for copying to pattr and 1 for calling the action functions * We can't use the same copy for the action functions because copying to pattr * is a shallow copy and array pointers will be cleared during the copy. */ newattr = calloc(JOB_ATR_LAST, sizeof(attribute)); if (newattr == NULL) return PBSE_SYSTEM; rc = attr_atomic_set(plist, pattr, newattr, job_attr_def, JOB_ATR_LAST, allow_unkn, perm, bad); if (rc) { attr_atomic_kill(newattr, job_attr_def, JOB_ATR_LAST); return rc; } pre_copy = calloc(JOB_ATR_LAST, sizeof(attribute)); if(pre_copy == NULL) { attr_atomic_kill(newattr, job_attr_def, JOB_ATR_LAST); return PBSE_SYSTEM; } attr_atomic_copy(pre_copy, newattr, job_attr_def, JOB_ATR_LAST); attr_save = calloc(JOB_ATR_LAST, sizeof(attribute)); if (attr_save == NULL) { attr_atomic_kill(newattr, job_attr_def, JOB_ATR_LAST); attr_atomic_kill(pre_copy, job_attr_def, JOB_ATR_LAST); return PBSE_SYSTEM; } attr_atomic_copy(attr_save, pattr, job_attr_def, JOB_ATR_LAST); /* If resource limits are being changed ... */ changed_resc = newattr[(int)JOB_ATR_resource].at_flags & ATR_VFLAG_SET; if ((rc == 0) && (changed_resc != 0)) { /* first, remove ATR_VFLAG_DEFLT from any value which was set */ /* it can no longer be a "default" as it explicitly changed */ prc = (resource *)GET_NEXT(newattr[(int)JOB_ATR_resource].at_val.at_list); while (prc) { if ((prc->rs_value.at_flags & (ATR_VFLAG_MODIFY|ATR_VFLAG_DEFLT)) == (ATR_VFLAG_MODIFY|ATR_VFLAG_DEFLT)) prc->rs_value.at_flags &= ~ATR_VFLAG_DEFLT; if ((prc->rs_value.at_flags & (ATR_VFLAG_MODIFY|ATR_VFLAG_SET)) == (ATR_VFLAG_MODIFY|ATR_VFLAG_SET)) { /* if being changed at all, see if "select" */ if (prc->rs_defin == pseldef) { /* select is modified, recalc chunk sums */ rc = set_chunk_sum(&prc->rs_value, &newattr[(int)JOB_ATR_resource]); if (rc) break; } } prc = (resource *)GET_NEXT(prc->rs_link); } /* Manager/Operator can modify job just about any old way */ /* So, the following checks are made only if not the Op/Admin */ if ((perm & (ATR_DFLAG_MGWR | ATR_DFLAG_OPWR)) == 0) { if (pjob->ji_qs.ji_state == JOB_STATE_RUNNING) { /* regular user cannot raise the limits of a running job */ if ((comp_resc(&pjob->ji_wattr[(int)JOB_ATR_resource], &newattr[(int)JOB_ATR_resource]) == -1) || comp_resc_lt) rc = PBSE_PERM; } /* Also check against queue, system and entity limits */ if (rc == 0) { rc = chk_resc_limits(&newattr[(int)JOB_ATR_resource], pjob->ji_qhdr); } if (rc == 0) { rc = check_entity_resc_limit_max(pjob, pjob->ji_qhdr, &newattr[(int)JOB_ATR_resource]); if (rc == 0) { rc = check_entity_resc_limit_queued(pjob, pjob->ji_qhdr, &newattr[(int)JOB_ATR_resource]); if (rc == 0) { rc = check_entity_resc_limit_max(pjob, NULL, &newattr[(int)JOB_ATR_resource]); if (rc == 0) rc = check_entity_resc_limit_queued(pjob, NULL, &newattr[(int)JOB_ATR_resource]); } } } } } /* special check on permissions for hold */ if ((rc == 0) && (newattr[(int)JOB_ATR_hold].at_flags & ATR_VFLAG_MODIFY)) { svrattrl *hold_e = find_name_in_svrattrl(plist, ATTR_h); /* don't perform permission check if Hold_Types attribute */ /* was set in a hook script (special privilege) */ if ((hold_e == NULL) || ((hold_e->al_flags & ATR_VFLAG_HOOK) == 0)) { i = newattr[(int)JOB_ATR_hold].at_val.at_long ^ (pattr+(int)JOB_ATR_hold)->at_val.at_long; rc = chk_hold_priv(i, perm); } } if ((rc == 0) && ((newattr[(int)JOB_ATR_userlst].at_flags & ATR_VFLAG_MODIFY) || (newattr[(int)JOB_ATR_grouplst].at_flags & ATR_VFLAG_MODIFY))) { /* Need to reset execution uid and gid */ rc = set_objexid((void *)pjob, JOB_OBJECT, newattr); } if (rc) { attr_atomic_kill(newattr, job_attr_def, JOB_ATR_LAST); attr_atomic_kill(attr_save, job_attr_def, JOB_ATR_LAST); attr_atomic_kill(pre_copy, job_attr_def, JOB_ATR_LAST); return (rc); } /* OK, if resources changed, reset entity sums */ if (changed_resc) { account_entity_limit_usages(pjob, NULL, &newattr[(int)JOB_ATR_resource], INCR, ETLIM_ACC_ALL_RES); account_entity_limit_usages(pjob, pjob->ji_qhdr, &newattr[(int)JOB_ATR_resource], INCR, ETLIM_ACC_ALL_RES); } /* Now copy the new values into the job attribute array for the purposes of running the action functions */ for (i = 0; i < JOB_ATR_LAST; i++) { if (newattr[i].at_flags & ATR_VFLAG_MODIFY) { /* * The function update_eligible_time() expects it is the only one setting accrue_type. * If we set it here, it will get confused. There is no action function for accrue_type, * so pre-setting it for the action function calls isn't required. */ if (i == JOB_ATR_accrue_type) continue; job_attr_def[i].at_free(&pattr[i]); if ((pre_copy[i].at_type == ATR_TYPE_LIST) || (pre_copy[i].at_type == ATR_TYPE_RESC)) { list_move(&pre_copy[i].at_val.at_list, &pattr[i].at_val.at_list); } else { pattr[i] = pre_copy[i]; } /* ATR_VFLAG_MODCACHE will be included if set */ pattr[i].at_flags = pre_copy[i].at_flags; } } for (i = 0; i < JOB_ATR_LAST; i++) { /* Check newattr instead of pattr for modify. It is possible that * the attribute already has the modify flag before we added the new attributes to it. * We only want to call the action functions for attributes which are being modified by this function. */ if (newattr[i].at_flags & ATR_VFLAG_MODIFY) { if ((job_attr_def[i].at_flags & ATR_DFLAG_NOSAVM) == 0) modified = 1; /* full save to disk for job */ if (job_attr_def[i].at_action) { rc = job_attr_def[i].at_action(&newattr[i], pjob, ATR_ACTION_ALTER); if (rc) { *bad = i; break; } } } } if (rc) { attr_atomic_copy(pjob->ji_wattr, attr_save, job_attr_def, JOB_ATR_LAST); free(pre_copy); attr_atomic_kill(newattr, job_attr_def, JOB_ATR_LAST); attr_atomic_kill(attr_save, job_attr_def, JOB_ATR_LAST); return (rc); } /* The action functions may have modified the attributes, need to set them to newattr2 */ for (i = 0; i < JOB_ATR_LAST; i++) { if (newattr[i].at_flags & ATR_VFLAG_MODIFY) { job_attr_def[i].at_free(&pattr[i]); switch (i) { case JOB_ATR_state: newstate = state_char2int(newattr[i].at_val.at_char); break; case JOB_ATR_substate: newsubstate = newattr[i].at_val.at_long; break; case JOB_ATR_accrue_type: newaccruetype = newattr[i].at_val.at_long; break; default: if ((newattr[i].at_type == ATR_TYPE_LIST) || (newattr[i].at_type == ATR_TYPE_RESC)) { list_move(&newattr[i].at_val.at_list, &pattr[i].at_val.at_list); } else { pattr[i] = newattr[i]; } } /* ATR_VFLAG_MODCACHE will be included if set */ pattr[i].at_flags = newattr[i].at_flags; } } if (newstate != -1 && newsubstate != -1) { svr_setjobstate(pjob, newstate, newsubstate); } if (newaccruetype != -1) update_eligible_time(newaccruetype, pjob); if (modified) pjob->ji_modified = 1; /* an attr was modified, do full save */ free(newattr); free(pre_copy); attr_atomic_kill(attr_save, job_attr_def, JOB_ATR_LAST); return (0); }
/** * @brief * Move a job to another queue in this Server. * * @par * Check the destination to see if it can accept the job. * If the job can enter the new queue, dequeue from the existing queue and * enqueue into the new queue * * @par * Note - the destination is specified by the queue's name in the * ji_qs.ji_destin element of the job structure. * * param[in] jobp - pointer to job to move * param[in] req - client request from a qmove client, null if a route * * @return int * @retval 0 : success * @retval -1 : permanent failure or rejection, see pbs_errno * @retval 1 : failed but try again later */ int local_move(job *jobp, struct batch_request *req) { pbs_queue *qp; char *destination = jobp->ji_qs.ji_destin; int mtype; attribute *pattr; long newtype = -1; /* search for destination queue */ if ((qp = find_queuebyname(destination)) == NULL) { sprintf(log_buffer, "queue %s does not exist", destination); log_err(-1, __func__, log_buffer); pbs_errno = PBSE_UNKQUE; return -1; } /* * if being moved at specific request of administrator, then * checks on queue availability, etc. are skipped; * otherwise all checks are enforced. */ if (req == NULL) { mtype = MOVE_TYPE_Route; /* route */ } else if (req->rq_perm & (ATR_DFLAG_MGRD | ATR_DFLAG_MGWR)) { mtype = MOVE_TYPE_MgrMv; /* privileged move */ } else { mtype = MOVE_TYPE_Move; /* non-privileged move */ } pbs_errno = svr_chkque(jobp, qp, get_hostPart(jobp->ji_wattr[(int)JOB_ATR_job_owner].at_val.at_str), mtype); if (pbs_errno) { /* should this queue be retried? */ return (should_retry_route(pbs_errno)); } /* dequeue job from present queue, update destination and */ /* queue_rank for new queue and enqueue into destination */ svr_dequejob(jobp); jobp->ji_myResv = NULL; strncpy(jobp->ji_qs.ji_queue, qp->qu_qs.qu_name, PBS_MAXQUEUENAME); jobp->ji_qs.ji_queue[PBS_MAXQUEUENAME] = '\0'; jobp->ji_wattr[(int)JOB_ATR_qrank].at_val.at_long = ++queue_rank; jobp->ji_wattr[(int)JOB_ATR_qrank].at_flags |= ATR_VFLAG_MODCACHE; pattr = &jobp->ji_wattr[(int)JOB_ATR_reserve_ID]; if (qp->qu_resvp) { job_attr_def[(int)JOB_ATR_reserve_ID].at_decode(pattr, (char *)0, (char *)0, qp->qu_resvp->ri_qs.ri_resvID); jobp->ji_myResv = qp->qu_resvp; } else { job_attr_def[(int)JOB_ATR_reserve_ID].at_decode(pattr, (char *)0, (char *)0, (char*)0); } if (server.sv_attr[(int)SRV_ATR_EligibleTimeEnable].at_val.at_long == 1) { newtype = determine_accruetype(jobp); if (newtype == -1) /* unable to determine accruetype, set it to NEW */ (void)update_eligible_time(JOB_INITIAL, jobp); else /* found suiting accruetype, update to this */ (void)update_eligible_time(newtype, jobp); } if ((pbs_errno = svr_enquejob(jobp)) != 0) return -1; /* should never ever get here */ jobp->ji_lastdest = 0; /* reset in case of another route */ (void)job_save(jobp, SAVEJOB_FULL); /* If a scheduling cycle is in progress, then this moved job may have * had changes resulting from the move that would impact scheduling or * placement, add job to list of jobs which cannot be run in this cycle. */ if ((req == NULL || (req->rq_conn != scheduler_sock)) && (scheduler_jobs_stat)) am_jobs_add(jobp); return 0; }