diff --git a/doc/html/priority_multifactor.shtml b/doc/html/priority_multifactor.shtml index dfac60ebd07b13f9aaaacd0d0571bcb982734335..b52a68f282d4b5cfa00b44389907e923e8864db8 100644 --- a/doc/html/priority_multifactor.shtml +++ b/doc/html/priority_multifactor.shtml @@ -146,7 +146,46 @@ computing resources described below.</P> <P> Slurm's fair-share factor is a floating point number between 0.0 and 1.0 that reflects the shares of a computing resource that a user has been allocated and the amount of computing resources the user's jobs have consumed. The higher the value, the higher is the placement in the queue of jobs waiting to be scheduled.</P> -<P> The computing resource is currently defined to be computing cycles delivered by a machine in the units of processor*seconds. Future versions of the fair-share factor may additionally include a memory integral component.</P> +<P> By default, the computing resource is the computing cycles delivered by a +machine in the units of processor*seconds. Other resources can be taken into +account by configuring a partition's TRESBillingWeights option. The +TRESBillingWeights option allows you to account for consumed resources other +than just CPUs by assigning different billing weights to different Trackable +Resources (TRES) such as CPUs, nodes, memory, licenses and generic resources +(GRES). For example, when billing only for CPUs, if a job requests 1CPU and 64GB +of memory on a 16CPU, 64GB node the job will only be billed for 1CPU when it +really used the whole node. +</P> + +<P> By default, when TRESBillingWeights is configured, a job is billed for each +individual TRES used. The billable TRES is calculated as the sum of all TRES +types multiplied by their corresponding billing weight. +</P> + +<P> For example, the following jobs on a partition configured with +TRESBillingWeights=CPU=1.0,MemGB=0.25 and 16CPU, 64GB nodes would be billed as: +<pre> + CPUs Mem GB +Job1: (1 *1.0) + (60*0.25) = (1 + 15) = 16 +Job2: (16*1.0) + (1 *0.25) = (16+.25) = 16.25 +Job3: (16*1.0) + (60*0.25) = (16+ 15) = 31 +</pre> +</P> + +<P> +Another method of calculating the billable TRES is by taking the MAX of the +individual TRES' on a node (e.g. cpus, mem, gres) plus the SUM of the global +TRES' (e.g. licenses). For example the above job's billable TRES would +be calculated as: +<pre> + CPUs Mem GB +Job1: MAX((1 *1.0), (60*0.25)) = 15 +Job2: MAX((15*1.0), (1 *0.25)) = 15 +Job3: MAX((16*1.0), (64*0.25)) = 16 +</pre> +This method is turned on by defining the MAX_TRES priority flags in the +slurm.conf. +</P> <h3> Normalized Shares</h3> diff --git a/doc/man/man5/slurm.conf.5 b/doc/man/man5/slurm.conf.5 index baebc7575208207ee332deaa2dba808ab9ac6c9a..5c251b1f567b6452e9eb1b0322aa4daf3a7afc9c 100644 --- a/doc/man/man5/slurm.conf.5 +++ b/doc/man/man5/slurm.conf.5 @@ -4050,22 +4050,15 @@ Partition name of alternate partition to be used if the state of this partition is "DRAIN" or "INACTIVE." .TP -\fBChargeRate\fR -The ChargeRate is used to define a CPU Equivalent that is multiplied by time -when calculating the usage of a job. Different charge rates may be specified -per resource type; the CPU equivalent is the MAX() of the charges. - -Charge rates are specified as a comma-separated list of -\fIResourceType\fR=\fIResourceChargeRate\fR pairs. The job's quantity of -\fIResourceType\fR is multiplied by the corresponding -\fIResourceChargeRate\fR (floating point). For example, when a job -is allocated 1 CPU core and 8 GB of memory and the partition is configured with -ChargeRate="CPU=1.0,MemGB=0.25,GRES:gpu=2.0", the CPU equivalent is -MAX(1*1.0, 8*0.25, 0*2.0) = 2.0 - -The CPU charge rate defaults to 1.0 and all others to 0.0. Available resource -types include: +\fBTRESBillingWeights\fR +TRESBillingWeights is used to define the billing weights of each TRES type that +will be used in calcuating the usage of a job. +Billing weights are specified as a comma-separated list of +\fI<TRES Type>\fR=\fI<TRES Billing Weight>\fR pairs. + +Available TRES types include: +.RS .RS .TP 10 \fBCPU\fP @@ -4086,6 +4079,23 @@ Charge per allocated GRES of type \fI<type>\fR Charge per allocated license of type \fI<type>\fR .RE +The CPU billing weight defaults to 1.0 and all others to 0.0. + +By default the billing of TRES is calculated as the sum of all TRES types +multiplied by their corresponding billing weight. For example, when a job is +allocated 1 CPU and 8 GB of memory on a partition configured with +TRESBillingWeights="CPU=1.0,MemGB=0.25,GRES:gpu=2.0", the billable TRES will be: +(1*1.0) + (8*0.25) + (0*2.0) = 3.0. + +If PriorityFlags=MAX_TRES is configured, the billable TRES is calculated as the +MAX of individual TRES' on a node (e.g. cpus, mem, gres) plus the sum of all +global TRES' (e.g. licenses). Using the same example above the billable TRES +will be MAX(1*1.0, 8*0.25) + (0*2.0) = 2.0. + +If TRESBillingWeights is not defined then the job is billed against the total +number of allocated CPUs. +.RE + .TP \fBDefault\fR If this keyword is set, jobs submitted without a partition diff --git a/slurm/slurm.h.in b/slurm/slurm.h.in index f234e8240b3d30e75bdfe98f371e5bad55f89106..8c4737740e0f62cc5fb16dc8e2cd61abc6393cc3 100644 --- a/slurm/slurm.h.in +++ b/slurm/slurm.h.in @@ -850,8 +850,12 @@ enum ctx_keys { * value that is higher than slurmd */ #define PRIORITY_FLAGS_ACCRUE_ALWAYS 0x0001 /* Flag to always accrue age - * priority to pending jobs ignoring - * dependencies or holds */ + * priority to pending jobs + * ignoring dependencies or + * holds */ +#define PRIORITY_FLAGS_MAX_TRES 0x0002 /* Calcuate billed_tres as the + * MAX of TRES on a node rather + * than the sum or TRES. */ #define PRIORITY_FLAGS_SIZE_RELATIVE 0x0004 /* Enable job size measurement * relative to its time limit */ #define PRIORITY_FLAGS_DEPTH_OBLIVIOUS 0x0008 /* Flag to use depth oblivious @@ -1403,7 +1407,7 @@ typedef struct job_info { uint16_t contiguous; /* 1 if job requires contiguous nodes */ uint16_t core_spec; /* specialized core count */ uint16_t cores_per_socket; /* cores per socket required by job */ - double cpu_equiv; /* cpu equivalents cache. updated upon resize */ + double billable_tres; /* billable TRES cache. updated upon resize */ uint16_t cpus_per_task; /* number of processors required for * each task */ uint32_t cpu_freq_min; /* Minimum cpu frequency */ @@ -1979,6 +1983,7 @@ typedef struct partition_info { char *allow_qos; /* comma delimited list of qos, * null indicates all */ char *alternate; /* name of alternate partition */ + char *billing_weights; /* per TRES billing weights string */ uint16_t cr_type; /* see CR_* values */ uint32_t def_mem_per_cpu; /* default MB memory per allocated CPU */ uint32_t default_time; /* minutes, NO_VAL or INFINITE */ diff --git a/src/api/partition_info.c b/src/api/partition_info.c index d525cd8773b293e0801cae2773260f9372af3c34..e43a8877f6c268b361682c36ac4dff9b12c33e61 100644 --- a/src/api/partition_info.c +++ b/src/api/partition_info.c @@ -427,6 +427,18 @@ char *slurm_sprint_partition_info ( partition_info_t * part_ptr, xstrcat(out, tmp_line); } + /****** Line 10 ******/ + if (part_ptr->billing_weights) { + if (one_liner) + xstrcat(out, " "); + else + xstrcat(out, "\n "); + + snprintf(tmp_line, sizeof(tmp_line), "TRESBillingWeights=%s", + part_ptr->billing_weights); + xstrcat(out, tmp_line); + } + if (one_liner) xstrcat(out, "\n"); else diff --git a/src/common/read_config.c b/src/common/read_config.c index 5d369875b23eb13bf2c703222902f9cf8bc56494..2d78fc4a768564966127a8423be295722f88423a 100644 --- a/src/common/read_config.c +++ b/src/common/read_config.c @@ -1044,7 +1044,7 @@ static int _parse_partitionname(void **dest, slurm_parser_enum_t type, {"AllowGroups", S_P_STRING}, {"AllowQos", S_P_STRING}, {"Alternate", S_P_STRING}, - {"ChargeRate", S_P_STRING}, + {"TRESBillingWeights", S_P_STRING}, {"DefMemPerCPU", S_P_UINT32}, {"DefMemPerNode", S_P_UINT32}, {"Default", S_P_BOOLEAN}, /* YES or NO */ @@ -1138,9 +1138,11 @@ static int _parse_partitionname(void **dest, slurm_parser_enum_t type, if (!s_p_get_string(&p->alternate, "Alternate", tbl)) s_p_get_string(&p->alternate, "Alternate", dflt); - if (!s_p_get_string(&p->charge_rate, "ChargeRate", tbl) && - !s_p_get_string(&p->charge_rate, "ChargeRate", dflt)) - xfree(p->charge_rate); + if (!s_p_get_string(&p->billing_weights, "TRESBillingWeights", + tbl) && + !s_p_get_string(&p->billing_weights, "TRESBillingWeights", + dflt)) + xfree(p->billing_weights); if (!s_p_get_boolean(&p->default_flag, "Default", tbl) && !s_p_get_boolean(&p->default_flag, "Default", dflt)) @@ -3636,6 +3638,9 @@ _validate_and_set_defaults(slurm_ctl_conf_t *conf, s_p_hashtbl_t *hashtbl) else if (slurm_strcasestr(temp_str, "FAIR_TREE")) conf->priority_flags |= PRIORITY_FLAGS_FAIR_TREE; + if (slurm_strcasestr(temp_str, "MAX_TRES")) + conf->priority_flags |= PRIORITY_FLAGS_MAX_TRES; + xfree(temp_str); } diff --git a/src/common/read_config.h b/src/common/read_config.h index 4ec86c4a2013a7ea21864c75bd8c1cd6cf7afaad..a2ce0572945d58dd6e4f9697db8a6781699ccc6c 100644 --- a/src/common/read_config.h +++ b/src/common/read_config.h @@ -251,7 +251,7 @@ typedef struct slurm_conf_partition { char *alternate; /* name of alternate partition */ uint16_t cr_type; /* Custom CR values for partition (supported * by select/cons_res plugin only) */ - char *charge_rate; /* per resource charge rates */ + char *billing_weights; /* per TRES billing weights */ uint32_t def_mem_per_cpu; /* default MB memory per allocated CPU */ bool default_flag; /* Set if default partition */ uint32_t default_time; /* minutes or INFINITE */ diff --git a/src/common/slurm_protocol_defs.c b/src/common/slurm_protocol_defs.c index af1356deb1ffeda8ca4896e177bb006b71d19461..9ea57605ee1da66c49279af8358dc156130c7c0b 100644 --- a/src/common/slurm_protocol_defs.c +++ b/src/common/slurm_protocol_defs.c @@ -1899,6 +1899,11 @@ extern char *priority_flags_string(uint16_t priority_flags) xstrcat(flag_str, ","); xstrcat(flag_str, "FAIR_TREE"); } + if (priority_flags & PRIORITY_FLAGS_MAX_TRES) { + if (flag_str[0]) + xstrcat(flag_str, ","); + xstrcat(flag_str, "MAX_TRES"); + } return flag_str; } diff --git a/src/common/slurm_protocol_pack.c b/src/common/slurm_protocol_pack.c index d7d0912bdfd1425fb36c5c5f1ff9352215a6cdaa..cf5ecf6a4abce81bb45a6ec0f9d30cd76af397a3 100644 --- a/src/common/slurm_protocol_pack.c +++ b/src/common/slurm_protocol_pack.c @@ -5311,6 +5311,8 @@ _unpack_partition_info_members(partition_info_t * part, Buf buffer, xfree(node_inx_str); node_inx_str = NULL; } + safe_unpackstr_xmalloc(&part->billing_weights, &uint32_tmp, + buffer); } else if (protocol_version >= SLURM_14_03_PROTOCOL_VERSION) { safe_unpackstr_xmalloc(&part->name, &uint32_tmp, buffer); @@ -5905,7 +5907,7 @@ _unpack_job_info_members(job_info_t * job, Buf buffer, safe_unpack_time(&job->resize_time, buffer); safe_unpack_time(&job->preempt_time, buffer); safe_unpack32(&job->priority, buffer); - safe_unpackdouble(&job->cpu_equiv, buffer); + safe_unpackdouble(&job->billable_tres, buffer); safe_unpackstr_xmalloc(&job->nodes, &uint32_tmp, buffer); safe_unpackstr_xmalloc(&job->sched_nodes, &uint32_tmp, buffer); safe_unpackstr_xmalloc(&job->partition, &uint32_tmp, buffer); diff --git a/src/plugins/priority/multifactor/priority_multifactor.c b/src/plugins/priority/multifactor/priority_multifactor.c index 126d4542cff7fabcba1e0a1b41f5035fe7faf020..f8a281d4b41684873d54b5d81bae103fa2db838b 100644 --- a/src/plugins/priority/multifactor/priority_multifactor.c +++ b/src/plugins/priority/multifactor/priority_multifactor.c @@ -675,59 +675,70 @@ static time_t _next_reset(uint16_t reset_period, time_t last_reset) /* - * Calculate CPU equivalents based on partition's defined ChargeRate. If none is - * defined, return total_cpus. This is cached on job_ptr->cpu_equiv and is - * updated if the job was resized since the last iteration. + * Calculate billable TRES based on partition's defined BillingWeights. If none + * is defined, return total_cpus. This is cached on job_ptr->billable_tres and + * is updated if the job was resized since the last iteration. */ -static double _cpu_equivalents(struct job_record *job_ptr, time_t start_time) +static double _calc_billable_tres(struct job_record *job_ptr, time_t start_time) { - double equiv = 0.0; - double charge_mem_mb = 0.0; + double to_bill = 0.0; + double bill_weight_mem_mb = 0.0; + double bill_cpus = 0.0, bill_mem = 0.0, bill_nodes = 0.0; struct part_record *part_ptr = job_ptr->part_ptr; uint32_t total_memory = 0; uint32_t gres_alloc_count = 0; uint32_t lic_alloc_count = 0; - config_key_double_pair_t *charge_pair; + config_key_double_pair_t *bill_weight_pair; ListIterator itr; /* Don't recalculate unless the job is new or resized */ - if ((!fuzzy_equal(job_ptr->cpu_equiv, NO_VAL)) && + if ((!fuzzy_equal(job_ptr->billable_tres, NO_VAL)) && difftime(job_ptr->resize_time, start_time) < 0.0) - return job_ptr->cpu_equiv; + return job_ptr->billable_tres; - debug3("ChargeRate: job %d is either new or it was resized", - job_ptr->job_id); + if (priority_debug) + info("BillingWeight: job %d is either new or it was resized", + job_ptr->job_id); - /* No charge rate defined. Return CPU count */ - if (!part_ptr->charge_rate) { - job_ptr->cpu_equiv = job_ptr->total_cpus; - return job_ptr->cpu_equiv; + /* No billing weights defined. Return CPU count */ + if (!part_ptr->billing_weights) { + job_ptr->billable_tres = job_ptr->total_cpus; + return job_ptr->billable_tres; } - debug3("ChargeRate: job %d using ChargeRate=\"%s\" from partition %s", - job_ptr->job_id, part_ptr->charge_rate, - job_ptr->part_ptr->name); + if (priority_debug) + info("BillingWeight: job %d using \"%s\" from partition %s", + job_ptr->job_id, part_ptr->billing_weights, + job_ptr->part_ptr->name); /* Calculate total memory since it is stored either per node or cpu */ if (job_ptr->details->pn_min_memory & MEM_PER_CPU) - total_memory = (job_ptr->details->pn_min_memory ^ MEM_PER_CPU) * - job_ptr->total_cpus; + total_memory = (job_ptr->details->pn_min_memory & + (~MEM_PER_CPU)) * job_ptr->total_cpus; else total_memory = job_ptr->details->pn_min_memory * job_ptr->total_nodes; - charge_mem_mb = part_ptr->charge_mem_gb / 1024.0l; + bill_weight_mem_mb = part_ptr->bill_weight_mem_gb / 1024.0l; - equiv = (double)job_ptr->total_cpus * part_ptr->charge_cpu; - equiv = MAX(equiv, - (double)job_ptr->total_nodes * part_ptr->charge_node); - equiv = MAX(equiv, (double)total_memory * charge_mem_mb); + bill_mem = (double)total_memory * bill_weight_mem_mb; + bill_cpus = (double)job_ptr->total_cpus * part_ptr->bill_weight_cpu; + bill_nodes = (double)job_ptr->total_nodes * part_ptr->bill_weight_node; + + to_bill = bill_cpus; + if (flags & PRIORITY_FLAGS_MAX_TRES) { + to_bill = MAX(to_bill, bill_nodes); + to_bill = MAX(to_bill, bill_mem); + } else { + to_bill += bill_nodes; + to_bill += bill_mem; + } /* * Potential performance improvement: * It would likely be lower overhead to iterate through allocated - * gres and maybe licenses first then compare to charge rates - * rather than iterate through all defined charge rates then compare to + * gres and maybe licenses first then compare to bill weights + * rather than iterate through all defined bill weights then compare to * gres and licenses. Site-specific differences are likely. * * It may be very uncommon for per partition per license charges to @@ -740,17 +751,25 @@ static double _cpu_equivalents(struct job_record *job_ptr, time_t start_time) /* Calculate for each gres */ if(job_ptr->gres_list && !list_is_empty(job_ptr->gres_list)) { - itr = list_iterator_create(part_ptr->charge_gres); - while ((charge_pair = list_next(itr))) { + itr = list_iterator_create(part_ptr->bill_weight_gres); + while ((bill_weight_pair = list_next(itr))) { gres_alloc_count = gres_get_value_by_type(job_ptr->gres_list, - charge_pair->name); + bill_weight_pair->name); if(gres_alloc_count > 0) { - debug3("ChargeRate: GRES:%s = %d * %f", - charge_pair->name, gres_alloc_count, - charge_pair->value); - equiv = MAX(equiv, gres_alloc_count * - (double)charge_pair->value); + double bill_gres = gres_alloc_count * + (double)bill_weight_pair->value; + + if (priority_debug) + info("BillingWeight: GRES:%s = %d * %f", + bill_weight_pair->name, + gres_alloc_count, + bill_weight_pair->value); + + if (flags & PRIORITY_FLAGS_MAX_TRES) + to_bill = MAX(to_bill, bill_gres); + else + to_bill += bill_gres; } } list_iterator_destroy(itr); @@ -758,31 +777,38 @@ static double _cpu_equivalents(struct job_record *job_ptr, time_t start_time) /* Calculate for each license */ if(job_ptr->license_list && !list_is_empty(job_ptr->license_list)) { - itr = list_iterator_create(part_ptr->charge_lic); - while ((charge_pair = list_next(itr))) { - lic_alloc_count = lic_get_value_by_type( + itr = list_iterator_create(part_ptr->bill_weight_lic); + while ((bill_weight_pair = list_next(itr))) { + lic_alloc_count = license_get_total_cnt_from_list( job_ptr->license_list, - charge_pair->name); + bill_weight_pair->name); if(lic_alloc_count > 0) { - debug3("ChargeRate: License:%s = %d * %f", - charge_pair->name, lic_alloc_count, - charge_pair->value); - equiv = MAX(equiv, lic_alloc_count * - (double)charge_pair->value); + if (priority_debug) + info("BillingWeight: Lic:%s = %d * %f", + bill_weight_pair->name, + lic_alloc_count, + bill_weight_pair->value); + to_bill += lic_alloc_count * + (double)bill_weight_pair->value; } } list_iterator_destroy(itr); } - debug3("ChargeRate: CPU = %d * %f", job_ptr->total_cpus, - part_ptr->charge_cpu); - debug3("ChargeRate: Node = %d * %f", job_ptr->total_nodes, - part_ptr->charge_node); - debug3("ChargeRate: Mem (MB) = %d * (%f/1024)", total_memory, - part_ptr->charge_mem_gb); - debug3("ChargeRate: job %d MAX(...) = %f", job_ptr->job_id, equiv); - job_ptr->cpu_equiv = equiv; - return equiv; + if (priority_debug) { + info("BillingWeight: CPU = %d * %f", job_ptr->total_cpus, + part_ptr->bill_weight_cpu); + info("BillingWeight: Node = %d * %f", job_ptr->total_nodes, + part_ptr->bill_weight_node); + info("BillingWeight: Mem (MB) = %d * (%f/1024)", total_memory, + part_ptr->bill_weight_mem_gb); + info("BillingWeight: Job %d %s = %f", job_ptr->job_id, + (flags & PRIORITY_FLAGS_MAX_TRES) ? + "MAX(node TRES) + SUM(Global TRES)" : "SUM(TRES)", + to_bill); + } + job_ptr->billable_tres = to_bill; + return to_bill; } @@ -975,7 +1001,7 @@ static int _apply_new_usage(struct job_record *job_ptr, /* get the time in decayed fashion */ run_decay = run_delta * pow(decay_factor, run_delta); - real_decay = run_decay * _cpu_equivalents(job_ptr, start_period); + real_decay = run_decay * _calc_billable_tres(job_ptr, start_period); assoc_mgr_lock(&locks); /* Just to make sure we don't make a diff --git a/src/slurmctld/job_mgr.c b/src/slurmctld/job_mgr.c index 52f6027d7270416f0c7943c1e11d5649df15c490..5d25847b182467fce16b841a88fe8b0bff9497e4 100644 --- a/src/slurmctld/job_mgr.c +++ b/src/slurmctld/job_mgr.c @@ -468,7 +468,7 @@ static struct job_record *_create_job_record(int *error_code, uint32_t num_jobs) detail_ptr->submit_time = time(NULL); job_ptr->requid = -1; /* force to -1 for sacct to know this * hasn't been set yet */ - job_ptr->cpu_equiv = (double)NO_VAL; + job_ptr->billable_tres = (double)NO_VAL; (void) list_append(job_list, job_ptr); return job_ptr; @@ -1162,7 +1162,7 @@ static void _dump_job_state(struct job_record *dump_job_ptr, Buf buffer) pack8(dump_job_ptr->power_flags, buffer); pack8(dump_job_ptr->sicp_mode, buffer); pack16(dump_job_ptr->start_protocol_ver, buffer); - packdouble(dump_job_ptr->cpu_equiv, buffer); + packdouble(dump_job_ptr->billable_tres, buffer); if (IS_JOB_COMPLETING(dump_job_ptr)) { if (dump_job_ptr->nodes_completing == NULL) { @@ -1277,7 +1277,7 @@ static int _load_job_state(Buf buffer, uint16_t protocol_version) slurmdb_qos_rec_t qos_rec; bool job_finished = false; char jbuf[JBUFSIZ]; - double cpu_equiv = (double)NO_VAL; + double billable_tres = (double)NO_VAL; char *tres_alloc_str = NULL, *tres_fmt_alloc_str = NULL; if (protocol_version >= SLURM_15_08_PROTOCOL_VERSION) { @@ -1376,7 +1376,7 @@ static int _load_job_state(Buf buffer, uint16_t protocol_version) safe_unpack8(&power_flags, buffer); safe_unpack8(&sicp_mode, buffer); safe_unpack16(&start_protocol_ver, buffer); - safe_unpackdouble(&cpu_equiv, buffer); + safe_unpackdouble(&billable_tres, buffer); if (job_state & JOB_COMPLETING) { safe_unpackstr_xmalloc(&nodes_completing, @@ -1882,7 +1882,7 @@ static int _load_job_state(Buf buffer, uint16_t protocol_version) xfree(job_ptr->comment); job_ptr->comment = comment; comment = NULL; /* reused, nothing left to free */ - job_ptr->cpu_equiv = cpu_equiv; + job_ptr->billable_tres = billable_tres; xfree(job_ptr->gres); job_ptr->gres = gres; gres = NULL; /* reused, nothing left to free */ @@ -7882,7 +7882,7 @@ void pack_job(struct job_record *dump_job_ptr, uint16_t show_flags, Buf buffer, pack_time(dump_job_ptr->resize_time, buffer); pack_time(dump_job_ptr->preempt_time, buffer); pack32(dump_job_ptr->priority, buffer); - packdouble(dump_job_ptr->cpu_equiv, buffer); + packdouble(dump_job_ptr->billable_tres, buffer); /* Only send the allocated nodelist since we are only sending * the number of cpus and nodes that are currently allocated. */ diff --git a/src/slurmctld/licenses.c b/src/slurmctld/licenses.c index 83debdef8a13eedf47bb4210921dd177a085f73b..ada84f965b59b49ec833a94acea6d33ffe7b5c24 100644 --- a/src/slurmctld/licenses.c +++ b/src/slurmctld/licenses.c @@ -232,20 +232,6 @@ static void _add_res_rec_2_lic_list(slurmdb_res_rec_t *rec, bool sync) last_license_update = time(NULL); } -/* Get how many of a given license are in a list */ -extern uint32_t lic_get_value_by_type(List license_list, char *name) -{ - licenses_t *license_entry; - uint32_t used = 0; - - license_entry = list_find_first( - license_list, _license_find_remote_rec, name); - - if(license_entry) - used = license_entry->used; - return used; -} - /* Get string of used license information. Caller must xfree return value */ extern char *get_licenses_used(void) { @@ -857,6 +843,20 @@ extern uint32_t get_total_license_cnt(char *name) return count; } +/* Get how many of a given license are in a list */ +extern uint32_t license_get_total_cnt_from_list(List license_list, char *name) +{ + licenses_t *license_entry; + uint32_t total = 0; + + license_entry = list_find_first( + license_list, _license_find_rec, name); + + if(license_entry) + total = license_entry->total; + return total; +} + /* node_read should be locked before coming in here * returns 1 if change happened. */ diff --git a/src/slurmctld/licenses.h b/src/slurmctld/licenses.h index 0f8da29ade4ab973eeb1e3d1fe7e442ff83d3805..528df94dbba2233d689d3479bf270a504ed3a5b1 100644 --- a/src/slurmctld/licenses.h +++ b/src/slurmctld/licenses.h @@ -74,14 +74,6 @@ extern void license_free(void); /* Free a license_t record (for use by list_destroy) */ extern void license_free_rec(void *x); -/* - * lic_get_value_by_type - Return count of named licenses used by job - * IN licenses - list containing licenses_t records - * IN name - name of the license - * RET number of licenses of the particular type used - */ -extern uint32_t lic_get_value_by_type(List license_list, char *name); - /* * license_job_copy - create a copy of a job's license list * IN license_list_src - job license list to be copied @@ -151,6 +143,14 @@ get_all_license_info(char **buffer_ptr, */ extern uint32_t get_total_license_cnt(char *name); +/* + * lic_get_value_by_type - Return count of named licenses used by job + * IN licenses - list containing licenses_t records + * IN name - name of the license + * RET number of licenses of the particular type used + */ +extern uint32_t license_get_total_cnt_from_list(List license_list, char *name); + /* node_read should be locked before coming in here * returns tres_str of the license_list. */ diff --git a/src/slurmctld/partition_mgr.c b/src/slurmctld/partition_mgr.c index f078b083bfea38b49dd2b9fd5df2de97399ccb99..5a5161b4ad307bad013bcc37d8301407f01a673f 100644 --- a/src/slurmctld/partition_mgr.c +++ b/src/slurmctld/partition_mgr.c @@ -1144,6 +1144,7 @@ void pack_part(struct part_record *part_ptr, Buf buffer, packstr(part_ptr->deny_qos, buffer); packstr(part_ptr->nodes, buffer); pack_bit_fmt(part_ptr->node_bitmap, buffer); + packstr(part_ptr->billing_weights, buffer); } else if (protocol_version >= SLURM_14_03_PROTOCOL_VERSION) { if (default_part_loc == part_ptr) part_ptr->flags |= PART_FLAG_DEFAULT; diff --git a/src/slurmctld/read_config.c b/src/slurmctld/read_config.c index 0dac9cdfb2b438f63d63b8b10bd27cc97400bb82..bfd01f85377f33d61d2e221c1dac1ebf9533bec4 100644 --- a/src/slurmctld/read_config.c +++ b/src/slurmctld/read_config.c @@ -134,77 +134,104 @@ static int _compare_hostnames(struct node_record *old_node_table, struct node_record *node_table, int node_count); +static bool _is_configured_tres(char *type, char *name); + /* Convert the value of a k=v pair to a double. Inputs are the k=v string and an * integer offset representing the start of the value */ -static double _charge_rate_item_to_double(char *item_str, int value_offset) +static double _bill_weight_item_to_double(char *value) { double d; errno = 0; - d = strtod(item_str + value_offset, NULL); + d = strtod(value, NULL); if(errno) - fatal("Unable to convert %s value to double in ChargeRate", - item_str); + fatal("Unable to convert %s value to double in " + "TRESBillingWeights", value); return d; } -static void _charge_rate_item_add_to_list(List l, char *item_str, int offset) +static void _bill_weight_item_add_to_list(List l, char *name, char *value) { - char *tmp_str; - char *kv = item_str+offset; config_key_double_pair_t *pair = xmalloc( sizeof(config_key_double_pair_t)); - if (!(tmp_str = strstr(kv, "="))) - fatal("\"%s\" is an invalid ChargeRate entry", item_str); - - pair->name = xstrndup(kv, tmp_str - kv); - pair->value = _charge_rate_item_to_double(item_str, - tmp_str - item_str + 1); + pair->name = xstrdup(name); + pair->value = _bill_weight_item_to_double(value); list_append(l, pair); } -static void _charge_rate_item(struct part_record *p, char *item_str) +static void _bill_weight_item(struct part_record *p, char *item_str) { + char *type = NULL, *value = NULL, *name = NULL; + if (!item_str) - fatal("ChargeRate item is null"); - - if (!strncasecmp(item_str, "MemGB=", 6)) - p->charge_mem_gb = _charge_rate_item_to_double(item_str, 6); - else if (!strncasecmp(item_str, "CPU=", 4)) - p->charge_cpu = _charge_rate_item_to_double(item_str, 4); - else if (!strncasecmp(item_str, "Node=", 5)) - p->charge_node = _charge_rate_item_to_double(item_str, 5); - else if (!strncasecmp(item_str, "GRES:", 5)) - _charge_rate_item_add_to_list(p->charge_gres, item_str, 5); - else if (!strncasecmp(item_str, "License:", 8)) - _charge_rate_item_add_to_list(p->charge_lic, item_str, 8); + fatal("TRESBillingWeights item is null"); + + type = strtok_r(item_str, "=", &value); + + if (!value || !*value) + fatal("\"%s\" is an invalid TRESBillingWeight entry", item_str); + + if (!strcasecmp(type, "MemGB")) { + if (!_is_configured_tres("mem", NULL)) + goto invalid_tres; + p->bill_weight_mem_gb = _bill_weight_item_to_double(value); + } else if (!strcasecmp(type, "CPU")) { + if (!_is_configured_tres(type, NULL)) + goto invalid_tres; + p->bill_weight_cpu = _bill_weight_item_to_double(value); + } else if (!strcasecmp(type, "Node")) { + if (!_is_configured_tres(type, NULL)) + goto invalid_tres; + p->bill_weight_node = _bill_weight_item_to_double(value); + } else if (!strncasecmp(type, "GRES:", 5)) { + type = strtok_r(type, ":", &name); + if (!_is_configured_tres("gres", name)) + goto invalid_tres; + + _bill_weight_item_add_to_list(p->bill_weight_gres, name, value); + } else if (!strncasecmp(type, "License:", 8)) { + type = strtok_r(type, ":", &name); + if (!_is_configured_tres("license", name)) + goto invalid_tres; + + _bill_weight_item_add_to_list(p->bill_weight_lic, name, value); + } else { + goto invalid_tres; + } + return; + +invalid_tres: + fatal("TRESBillingWeights '%s%s%s' is not a configured TRES type. " + "Please add the TRES Type to " "\"AccountingStorageTRES\" in " + "the slurm.conf", + type, (name) ? ":" : "", (name) ? name : ""); } -static void _charge_rate(struct part_record *p, char *charge_str) +static void _billing_weights(struct part_record *p, char *bill_weight_str) { - char *tmp_str = xstrdup(charge_str); + char *tmp_str = xstrdup(bill_weight_str); char *token, *last = NULL; - p->charge_cpu = 1.0; - p->charge_mem_gb = 0.0; - p->charge_node = 0.0; + p->bill_weight_cpu = 1.0; + p->bill_weight_mem_gb = 0.0; + p->bill_weight_node = 0.0; - if(p->charge_gres) - list_destroy(p->charge_gres); - p->charge_gres = list_create(destroy_config_key_double_pair); + if(p->bill_weight_gres) + list_destroy(p->bill_weight_gres); + p->bill_weight_gres = list_create(destroy_config_key_double_pair); - if(p->charge_lic) - list_destroy(p->charge_lic); - p->charge_lic = list_create(destroy_config_key_double_pair); + if(p->bill_weight_lic) + list_destroy(p->bill_weight_lic); + p->bill_weight_lic = list_create(destroy_config_key_double_pair); token = strtok_r(tmp_str, ",", &last); while (token) { - _charge_rate_item(p, token); + _bill_weight_item(p, token); token = strtok_r(NULL, ",", &last); } xfree(tmp_str); @@ -709,6 +736,28 @@ static int _init_tres(void) return SLURM_SUCCESS; } + +static bool _is_configured_tres(char *type, char *name) +{ + ListIterator itr = NULL; + bool valid = false; + slurmdb_tres_rec_t *tres_rec; + + itr = list_iterator_create(cluster_tres_list); + while ((tres_rec = list_next(itr))) { + if (strcasecmp(type, tres_rec->type)) + continue; + + if ((!tres_rec->name) || /* cpu, mem, etc. */ + (!xstrcmp(name, tres_rec->name))) { /* gres, lic, etc. */ + valid = true; + break; + } + } + + return valid; +} + /* Convert a comma delimited list of account names into a NULL terminated * array of pointers to strings. Call accounts_list_free() to release memory */ extern void accounts_list_build(char *accounts, char ***accounts_array) @@ -884,10 +933,10 @@ static int _build_single_partitionline_info(slurm_conf_partition_t *part) part_ptr->grace_time = part->grace_time; part_ptr->cr_type = part->cr_type; - if (part->charge_rate) { - xfree(part_ptr->charge_rate); - part_ptr->charge_rate = xstrdup(part->charge_rate); - _charge_rate(part_ptr, part_ptr->charge_rate); + if (part->billing_weights) { + xfree(part_ptr->billing_weights); + part_ptr->billing_weights = xstrdup(part->billing_weights); + _billing_weights(part_ptr, part_ptr->billing_weights); } if (part->allow_accounts) { diff --git a/src/slurmctld/slurmctld.h b/src/slurmctld/slurmctld.h index 2b4d17dc87bc51f4c211a126f1a112656d753d82..51a4b2ccff89c7606b5f151f05a8d5f51cf51e14 100644 --- a/src/slurmctld/slurmctld.h +++ b/src/slurmctld/slurmctld.h @@ -317,12 +317,12 @@ struct part_record { bitstr_t *allow_qos_bitstr; /* (DON'T PACK) assocaited with * char *allow_qos but used internally */ char *alternate; /* name of alternate partition */ - double charge_cpu; /* charge per allocated CPU */ - List charge_gres; /* list of per allocated GRES charges */ - List charge_lic; /* list of per allocated license charges */ - double charge_mem_gb; /* charge per allocated memory (GB) */ - double charge_node; /* charge per allocated node */ - char *charge_rate; /* per resource charge rate string */ + double bill_weight_cpu; /* bill weight per allocated CPU */ + List bill_weight_gres; /* list of per allocated GRES bill weights */ + List bill_weight_lic; /* list of per allocated license bill weights */ + double bill_weight_mem_gb;/* bill weight per allocated memory (GB) */ + double bill_weight_node;/* bill weight per allocated node */ + char *billing_weights; /* per TRES billing weight string */ uint32_t def_mem_per_cpu; /* default MB memory per allocated CPU */ uint32_t default_time; /* minutes, NO_VAL or INFINITE */ char *deny_accounts; /* comma delimited list of denied accounts */ @@ -578,11 +578,11 @@ struct job_record { * by the job, decremented while job is * completing (N/A for bluegene * systems) */ - double cpu_equiv; /* CPU equivalents allocated to the job, - * as defined by the partition's charge - * rate. Recalculated upon job resize. - * Cannot be calculated until the job is - * alloocated resources. */ + double billable_tres; /* calculated billable tres for the + * job, as defined by the partition's + * billing weight. Recalculated upon job + * resize. Cannot be calculated until + * the job is alloocated resources. */ uint16_t cr_enabled; /* specify if Consumable Resources * is enabled. Needed since CR deals * with a finer granularity in its