/*___INFO__MARK_BEGIN__*/ /************************************************************************* * * The Contents of this file are made available subject to the terms of * the Sun Industry Standards Source License Version 1.2 * * Sun Microsystems Inc., March, 2001 * * * Sun Industry Standards Source License Version 1.2 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.2 (the "License"); You may not use this file * except in compliance with the License. You may obtain a copy of the * License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc. * * Copyright: 2001 by Sun Microsystems, Inc. * * All Rights Reserved. * ************************************************************************/ /*___INFO__MARK_END__*/ #include #include #include "rmon/sgermon.h" #include "uti/sge_log.h" #include "uti/sge_parse_num_par.h" #include "uti/sge_string.h" #include "sched/sge_resource_utilization.h" #include "sge.h" #include "sge_resource_quota.h" #include "sge_str.h" #include "sge_answer.h" #include "sge_object.h" #include "sge_centry.h" #include "sge_qref.h" #include "sge_job.h" #include "sge_ja_task.h" #include "sge_pe.h" #include "sge_hgroup.h" #include "sge_userset.h" #include "sge_href.h" #include "sge_host.h" #include "sge_cqueue.h" #include "sge_resource_utilization_RUE_L.h" #include "msg_common.h" #include "msg_sgeobjlib.h" static bool rqs_match_user_host_scope(lList *scope, int filter_type, const char *value, lList *master_userset_list, lList *master_hgroup_list, const char *group, bool is_xscope); /****** sge_resource_quota/rqs_parse_filter_from_string() ************************* * NAME * rqs_parse_filter_from_string() -- parse a RQRF Object from string * * SYNOPSIS * bool rqs_parse_filter_from_string(lListElem **filter, const char* buffer, * lList **alp) * * FUNCTION * Converts a spooled RQRF Object to a CULL Element * * INPUTS * lListElem **filter - resulting RQRF object * const char* buffer - string to be converted * lList **alp - answer_list * * RESULT * bool - true on success * false on error * * NOTES * MT-NOTE: rqs_parse_filter_from_string() is MT safe * *******************************************************************************/ bool rqs_parse_filter_from_string(lListElem **filter, const char* buffer, lList **alp) { lListElem *tmp_filter = NULL; lListElem *scope = NULL; lList *lp = NULL; lList *scope_list = NULL; lList *xscope_list = NULL; char delims[] = "\t \v\r,{}"; DENTER(TOP_LAYER, "rqs_parse_filter_from_string"); if (buffer == NULL) { DRETURN(false); } tmp_filter = lCreateElem(RQRF_Type); if ( buffer[0] == '{' ) { /* We have a expanded list */ lSetBool(tmp_filter, RQRF_expand, true); if (buffer[strlen(buffer)-1] != '}') { ERROR((SGE_EVENT, MSG_RESOURCEQUOTA_NOVALIDEXPANDEDLIST)); answer_list_add(alp, SGE_EVENT, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR); DRETURN(false); } } else { lSetBool(tmp_filter, RQRF_expand, false); } lString2List(buffer, &lp, ST_Type, ST_name, delims); for_each(scope, lp) { const char *name = lGetString(scope, ST_name); if ( name[0] == '!' ) { lAddElemStr(&xscope_list, ST_name, name+1, ST_Type); } else { lAddElemStr(&scope_list, ST_name, name, ST_Type); } } lFreeList(&lp); lSetList(tmp_filter, RQRF_scope, scope_list); lSetList(tmp_filter, RQRF_xscope, xscope_list); *filter = tmp_filter; DRETURN(true); } /****** sge_resource_quota/rqs_append_filter_to_dstring() ************************* * NAME * rqs_append_filter_to_dstringRQRF_object_append_to_dstring() -- RQRF Element to string * * SYNOPSIS * bool rqs_append_filter_to_dstring(lListElem *filter, dstring *buffer, * lList **alp) * * FUNCTION * Converts a RQRF element to string for spooling * * INPUTS * lListElem *filter - Element to be converted * dstring *buffer - buffer for the element string * lList **alp - answer_list * * RESULT * bool - true on success * false on error * * NOTES * MT-NOTE: rqs_append_filter_to_dstring() is MT safe * *******************************************************************************/ bool rqs_append_filter_to_dstring(const lListElem *filter, dstring *buffer, lList **alp){ lList *tlp = NULL; lListElem *scope = NULL; bool first = true; bool expand = false; bool ret = false; if (filter == NULL) { return ret; } expand = lGetBool(filter, RQRF_expand) ? true : false; if (expand) { sge_dstring_append_char(buffer, '{'); } tlp = lGetList(filter, RQRF_scope); for_each(scope, tlp) { ret = true; if (!first) { sge_dstring_append_char(buffer, ','); } else { first = false; } sge_dstring_append(buffer, lGetString(scope, ST_name)); } tlp = lGetList(filter, RQRF_xscope); for_each(scope, tlp) { ret = true; if (!first) { sge_dstring_append_char(buffer, ','); } else { first = false; } sge_dstring_append_char(buffer, '!'); sge_dstring_append(buffer, lGetString(scope, ST_name)); } if (expand) { sge_dstring_append_char(buffer, '}'); } return ret; } /****** sge_resource_quota/rqs_set_defaults() ******************************* * NAME * rqs_set_defaults() -- set default values to given rqs * * SYNOPSIS * lListElem* rqs_set_defaults(lListElem* rqs) * * FUNCTION * This function sets the default values to a resource quota set. * The default rule set is: * { * name = * enabled = true * limit to slots=0 * } * * INPUTS * lListElem* rqs - Already created object to be modified. * * RESULT * lListElem* - modified Object * * NOTES * MT-NOTE: rqs_set_defaults() is MT safe *******************************************************************************/ lListElem* rqs_set_defaults(lListElem* rqs) { DENTER(TOP_LAYER, "rqs_set_defaults"); if (rqs != NULL) { lList *limit_list = NULL; lList *rule_list = NULL; lListElem *rule = NULL; lListElem *limit = NULL; /* Free RQS_rule */ rule_list = lGetList(rqs, RQS_rule); lFreeList(&rule_list); /* Create Rule List */ rule_list = lCreateList("Rule_List", RQR_Type); rule = lCreateElem(RQR_Type); limit_list = lCreateList("Limit_List", RQRL_Type); limit = lCreateElem(RQRL_Type); lSetString(limit, RQRL_name, "slots"); lSetString(limit, RQRL_value, "0"); lAppendElem(limit_list, limit); lSetList(rule, RQR_limit, limit_list); lAppendElem(rule_list, rule); /* Set RQS_enabled */ lSetBool(rqs, RQS_enabled, false); /* Set RQS_rule */ lSetList(rqs, RQS_rule, rule_list); } DRETURN(rqs); } /****** sge_resource_quota/rqs_verify_filter() ********************************* * NAME * rqs_verify_filter() -- verify a filter in rqs rule * * SYNOPSIS * static bool * rqs_verify_filter(const lListElem *rule, lList **answer_list, * int nm, const char *message) * * FUNCTION * Checks validity of a filter rule. * The object names referenced in the scope and xscope parts of the rule * are verified. * * INPUTS * const lListElem *rule - the rule to check * lList **answer_list - to pass back error messages * int nm - nm of the filter to check, e.g. RQR_filter_users * const char *message - error message to add to anwer_list in case of error * * RESULT * static bool - true if everything is OK, else false * * NOTES * MT-NOTE: rqs_verify_filter() is MT safe * * SEE ALSO * sge_resource_quota/rqs_verify_attributes() *******************************************************************************/ static bool rqs_verify_filter(const lListElem *rule, lList **answer_list, int nm, const char *message) { bool ret = true; if (rule == NULL) { ret = false; } else { const lListElem *filter = lGetObject(rule, nm); if (filter != NULL) { const lListElem *ep; for_each(ep, lGetList(filter, RQRF_scope)) { if (lGetString(ep, ST_name) == NULL) { answer_list_add(answer_list, message, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR); ret = false; break; } } for_each(ep, lGetList(filter, RQRF_xscope)) { if (lGetString(ep, ST_name) == NULL) { answer_list_add(answer_list, message, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR); ret = false; break; } } } } return ret; } /****** sge_resource_quota/rqs_verify_attributes() ********************** * NAME * rqs_verify_attributes() -- verify the attributes of a rqs Object * * SYNOPSIS * bool rqs_verify_attributes(lListElem *rqs, lList * **answer_list, bool in_master) * * FUNCTION * This function verifies the attributes of a given rqs object. A valid rqs * object has a name and at least one rule set. After verification it sets the * double limit value. * Addition checks are done if in_master is true. * * INPUTS * lListElem *rqs - Object that should be verified * lList **answer_list - answer list in case of errors * bool in_master - flag if called by qmaster or by qconf * * RESULT * bool - true on success * false on error * * NOTES * MT-NOTE: rqs_verify_attributes() is MT safe *******************************************************************************/ bool rqs_verify_attributes(lListElem *rqs, lList **answer_list, bool in_master) { bool ret = true; lList *rules = NULL; DENTER(TOP_LAYER, "rqs_verify_attributes"); /* every rule set needs a RQS_name */ if (lGetString(rqs, RQS_name) == NULL) { answer_list_add_sprintf(answer_list, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR, MSG_RESOURCEQUOTA_NONAME); ret = false; } /* every rule set needs at least one rule */ rules = lGetList(rqs, RQS_rule); if (ret && (rules == NULL || lGetNumberOfElem(rules) < 1)) { answer_list_add_sprintf(answer_list, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR, MSG_RESOURCEQUOTA_NORULES); ret = false; } if (ret && in_master) { lList *master_centry_list = (*centry_list_get_master_list()); lListElem *rule = NULL; for_each(rule, rules) { bool host_expand = false; bool queue_expand = false; lListElem *limit = NULL; lListElem *filter = NULL; lList *limit_list = lGetList(rule, RQR_limit); /* check user, project and pe filters */ if (!rqs_verify_filter(rule, answer_list, RQR_filter_users, MSG_RESOURCEQUOTA_INVALIDUSERFILTER)) { ret = false; break; } if (!rqs_verify_filter(rule, answer_list, RQR_filter_projects, MSG_RESOURCEQUOTA_INVALIDPROJECTFILTER)) { ret = false; break; } if (!rqs_verify_filter(rule, answer_list, RQR_filter_pes, MSG_RESOURCEQUOTA_INVALIDPEFILTER)) { ret = false; break; } /* check host and queue filters, set rule level. Needed by schedd */ if ((filter = lGetObject(rule, RQR_filter_hosts))) { lListElem *host = NULL; host_expand = lGetBool(filter, RQRF_expand) ? true : false; for_each(host, lGetList(filter, RQRF_xscope)) { sge_resolve_host(host, ST_name); } for_each(host, lGetList(filter, RQRF_scope)) { sge_resolve_host(host, ST_name); } } if ((filter = lGetObject(rule, RQR_filter_queues))) { queue_expand = lGetBool(filter, RQRF_expand) ? true : false; } if (host_expand == false && queue_expand == false) { lSetUlong(rule, RQR_level, RQR_GLOBAL); } else if (host_expand == true && queue_expand == false) { /* per host */ lSetUlong(rule, RQR_level, RQR_HOST); } else if (host_expand == false && queue_expand == true) { /* per queue */ lSetUlong(rule, RQR_level, RQR_CQUEUE); } else { lSetUlong(rule, RQR_level, RQR_QUEUEI); } for_each(limit, limit_list) { const char *name = lGetString(limit, RQRL_name); const char *strval = lGetString(limit, RQRL_value); lListElem *centry = centry_list_locate(master_centry_list, name); if (centry == NULL) { sprintf(SGE_EVENT, MSG_NOTEXISTING_ATTRIBUTE_SS, SGE_RQS_NAME, name); answer_list_add(answer_list, SGE_EVENT, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR); ret = false; break; } lSetString(limit, RQRL_name, lGetString(centry, CE_name)); if (strval == NULL) { answer_list_add_sprintf(answer_list, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR, MSG_RESOURCEQUOTA_NORULES); ret = false; break; } if (strchr(strval, '$') != NULL) { if (lGetUlong(rule, RQR_level) == RQR_HOST || lGetUlong(rule, RQR_level) == RQR_QUEUEI) { /* the value is a dynamical limit */ if (!validate_load_formula(lGetString(limit, RQRL_value), answer_list, master_centry_list, SGE_ATTR_DYNAMICAL_LIMIT)) { ret = false; break; } lSetUlong(limit, RQRL_type, lGetUlong(centry, CE_valtype)); lSetBool(limit, RQRL_dynamic, true); } else { answer_list_add_sprintf(answer_list, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR, MSG_RESOURCEQUOTA_DYNAMICLIMITNOTSUPPORTED); ret = false; break; } /* The evaluation of the value needs to be done at scheduling time. Per default it's zero */ } else { lListElem *tmp_ce = lCopyElem(centry); /* fix limit, fill up missing attributes */ lSetBool(limit, RQRL_dynamic, false); lSetString(tmp_ce, CE_stringval, strval); if (centry_fill_and_check(tmp_ce, answer_list, false, false)) { ret = false; lFreeElem(&tmp_ce); break; } lSetString(limit, RQRL_value, lGetString(tmp_ce, CE_stringval)); lSetDouble(limit, RQRL_dvalue, lGetDouble(tmp_ce, CE_doubleval)); lSetUlong(limit, RQRL_type, lGetUlong(tmp_ce, CE_valtype)); lFreeElem(&tmp_ce); } } if (ret == false) { break; } } } DRETURN(ret); } /****** sge_resource_quota/rqs_list_verify_attributes() ********************* * NAME * rqs_list_verify_attributes() -- verifies the attributes of a rqs list * * SYNOPSIS * bool rqs_list_verify_attributes(lList *rqs_list, lList * **answer_list, bool in_master) * * FUNCTION * This function iterates over a rqs list and checks for every rqs the attributes * * INPUTS * lList *rqs_list - List that should be verified * lList **answer_list - answer list * bool in_master - flag if called by qmaster or qconf * * RESULT * bool - true on success * false on error * NOTES * MT-NOTE: rqs_list_verify_attributes() is MT safe * * SEE ALSO * sge_resource_quota/rqs_verify_attributes() *******************************************************************************/ bool rqs_list_verify_attributes(lList *rqs_list, lList **answer_list, bool in_master) { bool ret = true; DENTER(TOP_LAYER, "rqs_list_verify_attributes"); if (rqs_list != NULL) { lListElem *rqs = NULL; for_each(rqs, rqs_list) { ret = rqs_verify_attributes(rqs, answer_list, in_master); if (!ret) { break; } } } DRETURN(ret); } /****** sge_resource_quota/rqs_list_locate() **************************** * NAME * rqs_list_locate() -- locate a specific resource quota set by name * * SYNOPSIS * lListElem* rqs_list_locate(lList *lp, const char *name) * * FUNCTION * This function searches the rule set list for a specific resource quota * set. The search criteria is the name of the resource quota set. * * INPUTS * lList *lp - rule set list to be searched in * const char *name - rule set name of interest * * RESULT * lListElem* - if found the reference to the resource quota set, else NULL * * NOTES * MT-NOTE: rqs_list_locate() is MT safe *******************************************************************************/ lListElem *rqs_list_locate(lList *lp, const char *name) { lListElem *ep = NULL; DENTER(TOP_LAYER, "rqs_list_locate"); ep = lGetElemStr(lp, RQS_name, name); DRETURN(ep); } /****** sge_resource_quota/rqs_rule_locate() ************************************* * NAME * rqs_rule_locate() -- locate a specific rule by name * * SYNOPSIS * lListElem* rqs_rule_locate(lList *lp, const char *name) * * FUNCTION * This function searches a rule list for a specific rule. The search criteria * is the name or the index of the rule. * The index is used if the name was successfully converted to an integer by * the atoi() function. If atoi() was not able to convert the name the rule is * searched by a sting compare. * * INPUTS * lList *lp - list to be searched in * const char *name - rule name of interest * * RESULT * lListElem* - reference to found rule * NULL if rule was not found * * NOTES * MT-NOTE: rqs_rule_locate() is MT safe *******************************************************************************/ lListElem* rqs_rule_locate(lList *lp, const char *name) { lListElem *ep = NULL; int get_pos = 0; int act_pos = 1; DENTER(TOP_LAYER, "rqs_rule_locate"); if (name == NULL) { DRETURN(NULL); } get_pos = atoi(name); for_each(ep, lp) { const char *rule_name = lGetString(ep, RQR_name); if (get_pos != -1 && act_pos == get_pos) { break; } else if (rule_name != NULL && !strcasecmp(name, lGetString(ep, RQR_name))) { break; } act_pos++; } DRETURN(ep); } /****** sge_resource_quota/rqs_xattr_pre_gdi() ************************************* * NAME * rqs_xattr_pre_gdi() -- qconf xattr list preformat * * SYNOPSIS * bool rqs_xattr_pre_gdi(lList *this_list, lList **answer_list) * * FUNCTION * This function preformates the given list created by xattr. The xattr switch * allows to address single rules by using the special rule set name "rule_set_name/rule_name". * The rule name can be the name for named rules or the index of the rule. * This function splits such a name into the single rule set name and set the correct * rule name. * * INPUTS * lList *this_list - list to be modified * lList **answer_list - answer list * * RESULT * bool - true on success * false on error * * NOTES * MT-NOTE: rqs_xattr_pre_gdi() is MT safe *******************************************************************************/ bool rqs_xattr_pre_gdi(lList *this_list, lList **answer_list) { bool ret = true; char delim[] = "/"; DENTER(TOP_LAYER, "rqs_xattr_pre_gdi"); if (this_list != NULL) { lListElem *rqs = NULL; for_each(rqs, this_list) { lList *lp = NULL; const char *name = lGetString(rqs, RQS_name); lString2List(name, &lp, ST_Type, ST_name, delim); if (lGetNumberOfElem(lp) == 2) { lListElem *ep = NULL; lListElem *rule = NULL; lList *rules = lGetList(rqs, RQS_rule); ep = lFirst(lp); lSetString(rqs, RQS_name, lGetString(ep, ST_name)); ep = lNext(ep); for_each(rule, rules) { lSetString(rule, RQR_name, lGetString(ep, ST_name)); } } lFreeList(&lp); } } DRETURN(ret); } /****** sge_resource_quota/rqs_get_rue_string() ***************************** * NAME * rqs_get_rue_string() -- creates a rue name * * SYNOPSIS * bool rqs_get_rue_string(dstring *name, lListElem *rule, const char * *user, const char *project, const char *host, const char *queue, const * char* pe) * * FUNCTION * Creates the rue name used for debiting a specific job request. The name * consists of the five touples devided by a /. The order is user, project, * pe, queue, host. * Filters that count for a sum * of hosts are saved as a empty string because they don't need to be matched. * * For example the rule * limit users `*` queues `all.q,my.q` to slots=10 * may result in the rue name * user1///all.q// * * INPUTS * dstring *name - out: rue_name * lListElem *rule - resource quota rule (RQR_Type) * const char *user - user name * const char *project - project name * const char *host - host name * const char *queue - queue name * const char* pe - pe name * * RESULT * bool - always true * * NOTES * MT-NOTE: rqs_get_rue_string() is MT safe * *******************************************************************************/ bool rqs_get_rue_string(dstring *name, const lListElem *rule, const char *user, const char *project, const char *host, const char *queue, const char* pe) { lListElem *filter = NULL; DENTER(BASIS_LAYER, "rqs_get_rue_string"); if (rule == NULL) { DRETURN(false); } sge_dstring_clear(name); if ((filter = lGetObject(rule, RQR_filter_users)) != NULL) { if (filter != NULL && user != NULL && lGetBool(filter, RQRF_expand) == true) { sge_dstring_append(name, user); } } sge_dstring_append(name, "/"); if ((filter = lGetObject(rule, RQR_filter_projects)) != NULL) { if (filter != NULL && project != NULL && lGetBool(filter, RQRF_expand) == true) { sge_dstring_append(name, project); } } sge_dstring_append(name, "/"); if ((filter = lGetObject(rule, RQR_filter_pes)) != NULL) { if (filter != NULL && pe != NULL && lGetBool(filter, RQRF_expand) == true) { sge_dstring_append(name, pe); } } sge_dstring_append(name, "/"); if ((filter = lGetObject(rule, RQR_filter_queues)) != NULL) { if (filter != NULL && queue != NULL && lGetBool(filter, RQRF_expand) == true) { sge_dstring_append(name, queue); } } sge_dstring_append(name, "/"); if ((filter = lGetObject(rule, RQR_filter_hosts)) != NULL) { if (filter != NULL && host != NULL && lGetBool(filter, RQRF_expand) == true) { char buffer[10240]; sge_hostcpy(buffer, host); sge_dstring_append(name, buffer); } } sge_dstring_append(name, "/"); DPRINTF(("rue_name: %s\n", sge_dstring_get_string(name))); DRETURN(true); } /****** sge_resource_quota/rqs_debit_consumable() ********************************* * NAME * rqs_debit_consumable() -- debit slots in all relevant rule sets * * SYNOPSIS * int rqs_debit_consumable(lListElem *rqs, lListElem *job, lListElem * *granted, lListElem *pe, lList *centry_list, int slots) * * FUNCTION * iterater over all rules in the given rule set and debit the amount of slots * in the relevant rule * * INPUTS * lListElem *rqs - resource quota set (RQS_Type) * lListElem *job - job request (JB_Type) * lListElem *granted - granted destination identifier (JG_Type) * lListElem *pe - granted pe (PE_Type) * lList *centry_list - consumable resouces list (CE_Type) * int slots - slot amount * * RESULT * int - amount of modified rule * * NOTES * MT-NOTE: rqs_debit_consumable() is not MT safe * *******************************************************************************/ int rqs_debit_consumable(lListElem *rqs, lListElem *job, lListElem *granted, const char *pename, lList *centry_list, lList *acl_list, lList *hgrp_list, int slots, bool is_master_task) { lListElem *rule = NULL; int mods = 0; const char* hostname = lGetHost(granted, JG_qhostname); const char* username = lGetString(job, JB_owner); const char* groupname = lGetString(job, JB_group); char *qname = NULL; const char *queue_instance = lGetString(granted, JG_qname); const char* project = lGetString(job, JB_project); DENTER(TOP_LAYER, "rqs_debit_consumable"); if (!lGetBool(rqs, RQS_enabled)) { DRETURN(0); } /* remove the host name part of the queue instance name */ qname = cqueue_get_name_from_qinstance(queue_instance); rule = rqs_get_matching_rule(rqs, username, groupname, project, pename, hostname, qname, acl_list, hgrp_list, NULL); if (rule != NULL) { /* debit usage */ dstring rue_name = DSTRING_INIT; rqs_get_rue_string(&rue_name, rule, username, project, hostname, qname, pename); mods = rqs_debit_rule_usage(job, rule, &rue_name, slots, centry_list, lGetString(rqs, RQS_name), is_master_task); sge_dstring_free(&rue_name); } FREE(qname); DRETURN(mods); } /****** sge_resource_quota/rqs_get_matching_rule() ******************************** * NAME * rqs_get_matching_rule() -- found relevant rule for job request * * SYNOPSIS * lListElem* rqs_get_matching_rule(lListElem *rqs, const char *user, * const char *project, const char* pe, const char *host, const char *queue, * lList *userset_list, lList* hgroup_list, dstring *rule_name) * * FUNCTION * This function searches in a resource quota set the relevant rule. * * INPUTS * lListElem *rqs - rule set (RQS_Type) * const char *user - user name * const char *project - project name * const char* pe - pe name * const char *host - host name * const char *queue - queue name * lList *userset_list - master user set list (US_Type) * lList* hgroup_list - master host group list (HG_Type); * dstring *rule_name - out: name or matching rule * * RESULT * lListElem* - pointer to matching rule * * NOTES * MT-NOTE: rqs_get_matching_rule() is MT safe * *******************************************************************************/ lListElem * rqs_get_matching_rule(const lListElem *rqs, const char *user, const char *group, const char *project, const char* pe, const char *host, const char *queue, lList *userset_list, lList* hgroup_list, dstring *rule_name) { lListElem *rule = NULL; lList *rule_list = lGetList(rqs, RQS_rule); int i = 0; DENTER(BASIS_LAYER, "rqs_get_matching_rule"); for_each (rule, rule_list) { i++; if (!rqs_is_matching_rule(rule, user, group, project, pe, host, queue, userset_list, hgroup_list)) { continue; } if (lGetString(rule, RQR_name)) { DPRINTF(("Using resource quota %s\n", lGetString(rule, RQR_name))); sge_dstring_sprintf(rule_name, "%s/%s", lGetString(rqs, RQS_name), lGetString(rule, RQR_name)); } else { DPRINTF(("Using resource quota %d\n", i)); sge_dstring_sprintf(rule_name, "%s/%d", lGetString(rqs, RQS_name), i); } /* if all filter object matches this is our rule */ break; } DRETURN(rule); } /****** sge_resource_quota/rqs_debit_rule_usage() ********************************* * NAME * rqs_debit_rule_usage() -- debit usage in a resource quota rule * * SYNOPSIS * int rqs_debit_rule_usage(lListElem *job, lListElem *rule, dstring * *rue_name, int slots, lList *centry_list, const char *obj_name) * * FUNCTION * Debit an amount of slots in all limits of one resource quota rule * * INPUTS * lListElem *job - job request (JG_Type) * lListElem *rule - resource quota rule (RQR_Type) * dstring *rue_name - rue name that counts * int slots - amount of slots to debit * lList *centry_list - consumable resource list (CE_Type) * const char *obj_name - name of the limit * * RESULT * int - amount of debited limits * * NOTES * MT-NOTE: rqs_debit_rule_usage() is MT safe *******************************************************************************/ int rqs_debit_rule_usage(lListElem *job, lListElem *rule, dstring *rue_name, int slots, lList *centry_list, const char *obj_name, bool is_master_task) { lList *limit_list; lListElem *limit; const char *centry_name; int mods = 0; DENTER(TOP_LAYER, "rqs_debit_rule_usage"); limit_list = lGetList(rule, RQR_limit); for_each(limit, limit_list) { u_long32 consumable; lListElem *raw_centry; lListElem *rue_elem; double dval; int debit_slots = slots; centry_name = lGetString(limit, RQRL_name); if (!(raw_centry = centry_list_locate(centry_list, centry_name))) { /* ignoring not defined centry */ continue; } consumable = lGetUlong(raw_centry, CE_consumable); if (consumable == CONSUMABLE_NO) { continue; } if (consumable == CONSUMABLE_JOB) { if (!is_master_task) { /* no error, only master_task is debited */ continue; } /* it's a job consumable, we don't multiply with slots */ if (slots > 0) { debit_slots = 1; } else if (slots < 0) { debit_slots = -1; } } rue_elem = lGetSubStr(limit, RUE_name, sge_dstring_get_string(rue_name), RQRL_usage); if (rue_elem == NULL) { rue_elem = lAddSubStr(limit, RUE_name, sge_dstring_get_string(rue_name), RQRL_usage, RUE_Type); /* RUE_utilized_now is implicitly set to zero */ } if (job) { bool tmp_ret = job_get_contribution(job, NULL, centry_name, &dval, raw_centry); if (tmp_ret && dval != 0.0) { DPRINTF(("debiting %f of %s on rqs %s for %s %d slots\n", dval, centry_name, obj_name, sge_dstring_get_string(rue_name), debit_slots)); lAddDouble(rue_elem, RUE_utilized_now, debit_slots * dval); mods++; } else if (lGetUlong(raw_centry, CE_relop) == CMPLXEXCL_OP) { dval = 1.0; DPRINTF(("debiting (non-exclusive) %f of %s on rqs %s for %s %d slots\n", dval, centry_name, obj_name, sge_dstring_get_string(rue_name), debit_slots)); lAddDouble(rue_elem, RUE_utilized_now_nonexclusive, debit_slots * dval); mods++; } if (lGetDouble(rue_elem, RUE_utilized_now) == 0 && lGetDouble(rue_elem, RUE_utilized_now_nonexclusive) == 0 && !lGetList(rue_elem, RUE_utilized) && !lGetList(rue_elem, RUE_utilized_nonexclusive)) { rue_elem = lDechainElem(lGetList(limit, RQRL_usage), rue_elem); lFreeElem(&rue_elem); } } } DRETURN(mods); } /****** sge_resource_quota/rqs_match_user_host_scope() **************************** * NAME * rqs_match_user_host_scope() -- match user or host scope * * SYNOPSIS * static bool rqs_match_user_host_scope(lList *scope, int filter_type, * const char *value, lList *master_userset_list, lList *master_hgroup_list) * * FUNCTION * This function verifies a user or host scope. The function allows for every * scope entry and for the value a wildcard definition. Hostgroups and Usergroups * are resolved and matched against the value * * * INPUTS * lList *scope - Scope to match (ST_Type) * int filter_type - filter type (FILTER_USERS or FILTER_HOSTS) * const char *value - value to match * lList *master_userset_list - master userset list (US_Type) * lList *master_hgroup_list - master hostgroup list (HG_Type) * * RESULT * static bool - true, if value was found in scope * false, if value was not found in scope * * NOTES * MT-NOTE: rqs_match_user_host_scope() is MT safe * * SEE ALSO * sge_resource_quota/rqs_match_user_host_scope() *******************************************************************************/ static bool rqs_match_user_host_scope(lList *scope, int filter_type, const char *value, lList *master_userset_list, lList *master_hgroup_list, const char *group, bool is_xscope) { bool found = false; lListElem *ep; DENTER(TOP_LAYER, "rqs_match_user_host_scope"); if (!sge_is_pattern(value)) { /* used in scheduler/qmaster and qquota */ if (lGetElemStr(scope, ST_name, value) != NULL) { found = true; } else { for_each(ep, scope) { lListElem *group_ep; const char *cp = lGetString(ep, ST_name); const char *group_name = NULL; const char *query = NULL; if (fnmatch(cp, value, 0) == 0) { found = true; break; } if (!is_hgroup_name(value) && is_hgroup_name(cp)) { group_name = cp; query = value; } else if (is_hgroup_name(value) && !is_hgroup_name(cp)) { group_name = value; query = cp; } if (group_name != NULL && query != NULL) { DPRINTF(("group_name=%s, query=%s\n", group_name, query)); if (filter_type == FILTER_USERS) { /* the userset name does not contain the preattached \@ sign */ group_name++; if (!sge_is_pattern(group_name)) { if ((group_ep = userset_list_locate(master_userset_list, group_name)) != NULL) { if (sge_contained_in_access_list(query, group, group_ep, NULL) == 1) { found = true; break; } } } else { for_each(group_ep, master_userset_list) { if (fnmatch(group_name, lGetString(group_ep, US_name), 0) == 0) { if (sge_contained_in_access_list(query, group, group_ep, NULL) == 1) { found = true; break; } } } if (found == true) { break; } } } else { /* FILTER_HOSTS */ lListElem *hgroup = NULL; lList *host_list = NULL; if (!sge_is_pattern(group_name)) { if ((hgroup = hgroup_list_locate(master_hgroup_list, group_name))) { hgroup_find_all_references(hgroup, NULL, master_hgroup_list, &host_list, NULL); if (host_list != NULL && lGetElemHost(host_list, HR_name, query) != NULL) { lFreeList(&host_list); found = true; break; } else if (sge_is_pattern(query)) { lListElem *host_ep; for_each(host_ep, host_list) { if (fnmatch(query, lGetHost(host_ep, HR_name), 0) == 0) { lFreeList(&host_list); found = true; break; } } if (found == true) { break; } } lFreeList(&host_list); } } else { for_each(group_ep, master_hgroup_list) { if (fnmatch(group_name, lGetHost(group_ep, HGRP_name), 0) == 0) { hgroup_find_all_references(group_ep, NULL, master_hgroup_list, &host_list, NULL); if (host_list != NULL && lGetElemHost(host_list, HR_name, query) != NULL) { lFreeList(&host_list); found = true; break; } else if (sge_is_pattern(query)) { lListElem *host_ep; for_each(host_ep, host_list) { if (fnmatch(query, lGetHost(host_ep, HR_name), 0) == 0) { lFreeList(&host_list); found = true; break; } } if (found == true) { break; } } lFreeList(&host_list); } } } } } } } } else { /* only used in qquota */ for_each(ep, scope) { const char *cp = lGetString(ep, ST_name); const char *group_name = NULL; const char *query = NULL; if (fnmatch(value, cp, 0) == 0) { if (!is_xscope) { found = true; break; } else { if (strcmp(value, cp) == 0) { /* amount of sets is equal */ found = true; break; } else { /* amount of sets does not overlap. We can not reject in xscope context and have to wave through */ found = false; break; } } } if (sge_is_pattern(cp) && (fnmatch(cp, value, 0) == 0)) { found = true; break; } if (!is_hgroup_name(value) && is_hgroup_name(cp)) { group_name = cp; query = value; } else if (is_hgroup_name(value) && !is_hgroup_name(cp)) { group_name = value; query = cp; } if (group_name != NULL && query != NULL) { lListElem *group_ep; DPRINTF(("group_name=%s, query=%s\n", group_name, query)); if (filter_type == FILTER_USERS) { /* the userset name does not contain the preattached \@ sign */ group_name++; for_each(group_ep, master_userset_list) { if (fnmatch(group_name, lGetString(group_ep, US_name), 0) == 0) { if (sge_contained_in_access_list(query, group, group_ep, NULL) == 1) { found = true; break; } } } if (found == true) { break; } } else { lList *host_list = NULL; for_each(group_ep, master_hgroup_list) { if (fnmatch(group_name, lGetHost(group_ep, HGRP_name), 0) == 0) { lListElem *host_ep; hgroup_find_all_references(group_ep, NULL, master_hgroup_list, &host_list, NULL); for_each(host_ep, host_list) { if (fnmatch(query, lGetHost(host_ep, HR_name), 0) == 0) { found = true; break; } } lFreeList(&host_list); if (found == true) { break; } } } if (found == true) { break; } } } } } DRETURN(found); } /****** sge_resource_quota/rqs_is_matching_rule() ********************************* * NAME * rqs_is_matching_rule() -- matches a rule with the filter touples * * SYNOPSIS * bool rqs_is_matching_rule(lListElem *rule, const char *user, const char * *project, const char *pe, const char *host, const char *queue, lList * *master_userset_list, lList *master_hgroup_list) * * FUNCTION * The function verifies for every filter touple if the request matches * the configured resource quota rule. If only one touple does not match * the whole rule will not match * * INPUTS * lListElem *rule - resource quota rule (RQR_Type) * const char *user - user name * const char *project - project name * const char *pe - pe name * const char *host - host name * const char *queue - queue name * lList *master_userset_list - master user set list (US_Type) * lList *master_hgroup_list - master hostgroup list (HG_Type) * * RESULT * bool - true if the rule does match * false if the rule does not match * * NOTES * MT-NOTE: rqs_is_matching_rule() is MT safe * *******************************************************************************/ bool rqs_is_matching_rule(lListElem *rule, const char *user, const char *group, const char *project, const char *pe, const char *host, const char *queue, lList *master_userset_list, lList *master_hgroup_list) { DENTER(TOP_LAYER, "rqs_is_matching_rule"); if (!rqs_filter_match(lGetObject(rule, RQR_filter_users), FILTER_USERS, user, master_userset_list, NULL, group)) { DPRINTF(("user doesn't match\n")); DRETURN(false); } if (!rqs_filter_match(lGetObject(rule, RQR_filter_projects), FILTER_PROJECTS, project, NULL, NULL, NULL)) { DPRINTF(("project doesn't match\n")); DRETURN(false); } if (!rqs_filter_match(lGetObject(rule, RQR_filter_pes), FILTER_PES, pe, NULL, NULL, NULL)) { DPRINTF(("pe doesn't match\n")); DRETURN(false); } if (!rqs_filter_match(lGetObject(rule, RQR_filter_queues), FILTER_QUEUES, queue, NULL, NULL, NULL)) { DPRINTF(("queue doesn't match\n")); DRETURN(false); } if (!rqs_filter_match(lGetObject(rule, RQR_filter_hosts), FILTER_HOSTS, host, NULL, master_hgroup_list, NULL)) { DPRINTF(("host doesn't match\n")); DRETURN(false); } DRETURN(true); } /****** sge_resource_quota/rqs_match_host_scope() ****************************** * NAME * rqs_match_host_scope() -- Match name with host scope * * SYNOPSIS * static bool rqs_match_host_scope(lList *scope, const char *name, lList * *master_hgroup_list) * * FUNCTION * The function matches the passed name with the host scope. Name * may not only be a hostname, but also host group name or a wildcard * expression. For performance reasons qref_list_host_rejected() is * used for matching, if we got no pattern and no hostgroup. * * INPUTS * lList *scope - A scope list (ST_Type) * const char *name - hostname/hostgroup name/wildcard expression * lList *master_hgroup_list - the host group list (HGRP_Type) * * RESULT * bool - Returns true if 'name' matches * * NOTES * MT-NOTE: rqs_match_host_scope() is MT safe *******************************************************************************/ static bool rqs_match_host_scope(lList *scope, const char *name, lList *master_hgroup_list, bool is_xscope) { lListElem *ep; DENTER(TOP_LAYER, "rqs_match_host_scope"); if (lGetElemStr(scope, ST_name, "*")) { DRETURN(true); } if (sge_is_pattern(name) || is_hgroup_name(name)) { DRETURN(rqs_match_user_host_scope(scope, FILTER_HOSTS, name, NULL, master_hgroup_list, NULL, is_xscope)); } /* at this stage we know 'name' is a simple hostname */ for_each(ep, scope) { if (!qref_list_host_rejected(lGetString(ep, ST_name), name, master_hgroup_list)) { DRETURN(true); } } DRETURN(false); } /****** sge_resource_quota/rqs_filter_match() ******************************* * NAME * rqs_filter_match() -- compares value with configured filter * * SYNOPSIS * bool rqs_filter_match(lListElem *filter, int filter_type, const * char *value, lList *master_userset_list, lList *master_hgroup_list) * * FUNCTION * This function compares for the given filter if the value does match * the configured one. Wildcards are allowed for the filter as well as for * the value. * * INPUTS * lListElem *filter - filter element (RQRF_Type) * int filter_type - filter type * const char *value - value to match * lList *master_userset_list - master userset list (US_Type) * lList *master_hgroup_list - master hostgroup list (HG_Type) * * RESULT * bool - true if the value does match * false if the value does not match * * NOTES * MT-NOTE: rqs_filter_match() is MT safe * *******************************************************************************/ bool rqs_filter_match(lListElem *filter, int filter_type, const char *value, lList *master_userset_list, lList *master_hgroup_list, const char *group) { bool ret = true; lListElem* ep; DENTER(BASIS_LAYER, "rqs_filter_match"); if (filter != NULL) { lList* scope = lGetList(filter, RQRF_scope); lList* xscope = lGetList(filter, RQRF_xscope); switch (filter_type) { case FILTER_HOSTS: DPRINTF(("matching hosts with %s\n", value)); /* inverse logic because of xscope */ ret = rqs_match_host_scope(xscope, value, master_hgroup_list, true) ? false: true; if (ret == true && scope != NULL) { if (!rqs_match_host_scope(scope, value, master_hgroup_list, false)) { ret = false; } } break; case FILTER_USERS: { DPRINTF(("matching users or hosts with %s\n", value)); /* inverse logic because of xscope */ ret = rqs_match_user_host_scope(xscope, filter_type, value, master_userset_list, NULL, group, true) ? false: true; if (ret == true && scope != NULL) { if (!rqs_match_user_host_scope(scope, filter_type, value, master_userset_list, NULL, group, false)) { ret = false; } } break; } case FILTER_PROJECTS: case FILTER_PES: case FILTER_QUEUES: DPRINTF(("matching projects, pes or queues with %s\n", value? value: "NULL")); if (lGetElemStr(xscope, ST_name, value) != NULL) { ret = false; } else { for_each(ep, xscope) { const char *cp = lGetString(ep, ST_name); if (value == NULL || (strcmp(value, "*") == 0)) { break; } DPRINTF(("xscope: strcmp(%s,%s)\n", cp, value)); if ((strcmp(cp, "*") == 0) || (fnmatch(cp, value, 0) == 0) || (fnmatch(value,cp, 0) == 0)) { DPRINTF(("match\n")); ret = false; break; } DPRINTF(("no match\n")); } } if (ret != false) { bool found = false; if (lGetElemStr(scope, ST_name, value) != NULL) { found = true; } else { for_each(ep, scope) { const char *cp = lGetString(ep, ST_name); if (value == NULL) { break; } DPRINTF(("scope: strcmp(%s,%s)\n", cp, value)); if ((strcmp(cp, "*") == 0) || (fnmatch(cp, value, 0) == 0) || (fnmatch(value,cp, 0) == 0)) { found = true; break; } } } if (scope != NULL && found == false) { ret = false; } } break; } } DRETURN(ret); } /****** sge_resource_quota/sge_centry_referenced_in_rqs() ************************* * NAME * sge_centry_referenced_in_rqs() -- search for a centry reference in * a resource quota set * * SYNOPSIS * bool sge_centry_referenced_in_rqs(const lListElem *rqs, const lListElem * *centry) * * FUNCTION * This function search a centry reference in a resource quota set * * INPUTS * const lListElem *rqs - resource quota set * const lListElem *centry - complex entry * * RESULT * bool - true if found * false if not found * * NOTES * MT-NOTE: sge_centry_referenced_in_rqs() is MT safe * *******************************************************************************/ bool sge_centry_referenced_in_rqs(const lListElem *rqs, const lListElem *centry) { bool ret = false; const char *centry_name = lGetString(centry, CE_name); lListElem *rule; DENTER(TOP_LAYER, "sge_centry_referenced_in_rqs"); for_each(rule, lGetList(rqs, RQS_rule)) { lListElem *limit; for_each(limit, lGetList(rule, RQR_limit)) { const char *limit_name = lGetString(limit, RQRL_name); DPRINTF(("limit name %s\n", limit_name)); if (strchr(limit_name, '$') != NULL) { /* dynamical limit */ if (load_formula_is_centry_referenced(limit_name, centry)) { ret = true; break; } } else { /* static limit */ if (strcmp(limit_name, centry_name) == 0) { ret = true; break; } } } if (ret) { break; } } DRETURN(ret); } /****** sge_resource_quota/rqs_replace_request_verify() ************************ * NAME * rqs_replace_request_verify() -- verify a rqs replace request * * SYNOPSIS * bool rqs_replace_request_verify(lList **answer_list, const lList * *request) * * FUNCTION * Verify a rqs replace request (e.g. coming from a qconf -mrqs). * We make sure, that no duplicate names appear in the request. * * INPUTS * lList **answer_list - answer list to report errors * const lList *request - the request to check * * RESULT * bool - true, if it is ok, false on error * * NOTES * MT-NOTE: rqs_replace_request_verify() is MT safe *******************************************************************************/ bool rqs_replace_request_verify(lList **answer_list, const lList *request) { lListElem *ep; DENTER(TOP_LAYER, "rqs_replace_request_verify"); /* search for duplicate rqs names in the request */ for_each(ep, request) { const char *name = lGetString(ep, RQS_name); lListElem *second = lNext(ep); while (second != NULL) { const char *second_name = lGetString(second, RQS_name); if (strcmp(name, second_name) == 0) { answer_list_add_sprintf(answer_list, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR, MSG_RQS_REQUEST_DUPLICATE_NAME_S, name); DRETURN(false); } second = lNext(second); } } DRETURN(true); }