diff --git a/doc/markdown/man/man5/sge_conf.md b/doc/markdown/man/man5/sge_conf.md index a7fbc4dca..90e830a01 100644 --- a/doc/markdown/man/man5/sge_conf.md +++ b/doc/markdown/man/man5/sge_conf.md @@ -1307,6 +1307,40 @@ Examples: usage_patterns=gpu:nvidia.*|amd.* usage_patterns=gpu:nvidia.*;power:power-* +***online_usage*** + +When set to a non-empty value, xxQS_NAMExx writes a continuous stream of *online_usage* records to the +JSONL reporting file. One record is generated for every job report received by xxqs_name_sxx_qmaster(8) +from xxqs_name_sxx_execd(8) for a running job. The value of this parameter selects which usage variables +shall be included in each record. The format is: + + [|[|...]] + +Variables are separated by `|`. There is no closed list of accepted names: any token that is a valid +xxQS_NAMExx *complex_name* (see xxqs_name_sxx_types(1)) is accepted by the configuration parser. +A record will carry only those configured variables that the execution daemon actually reports for +the job — names the execd does not report are silently skipped. The set of variables reported by +xxqs_name_sxx_execd(8) today includes *cpu*, *mem*, *io*, *iow*, *ioops*, *vmem*, *maxvmem*, *rss*, +*maxrss*, *pss*, *maxpss*, *smem*, *pmem*, *wallclock*. + +Examples: + + online_usage=cpu|mem|maxvmem|wallclock + online_usage=cpu|mem|io|iow|ioops|vmem|rss|maxrss + +If the parameter is absent or set to an empty value, no *online_usage* records will be written. This +is the default — the feature must be opted into explicitly. + +For tightly-integrated parallel jobs the granularity of *online_usage* records follows the same +convention as the accounting record: if the parallel environment has *accounting_summary* set to +*TRUE*, one record per ja_task is written and pe_task usage is aggregated into it; otherwise one +record is written per pe_task (and one for the master task). + +*online_usage* records are written only to the new JSONL reporting file. The deprecated colon-separated +reporting file (see *old_reporting*) does not carry *online_usage* records. + +See xxqs_name_sxx_reporting(5) for the *online_usage* record schema. + ## finished_jobs Note: Deprecated, may be removed in future release. xxQS_NAMExx stores a certain number of *just finished* jobs to diff --git a/doc/markdown/man/man5/sge_reporting.md b/doc/markdown/man/man5/sge_reporting.md index 765835386..842cf8488 100644 --- a/doc/markdown/man/man5/sge_reporting.md +++ b/doc/markdown/man/man5/sge_reporting.md @@ -173,6 +173,44 @@ fine-grained resource usage monitoring over time. For the contents and structure of the accounting records see xxqs_name_sxx_accounting(5). +## online_usage + +Records of type online_usage are written for every job report that xxqs_name_sxx_qmaster(8) receives +from xxqs_name_sxx_execd(8) for a running job, when *online_usage* in *reporting_params* has been +configured with a non-empty list of usage variables (see xxqs_name_sxx_sge_conf(5)). They provide a +continuous time series of scaled usage values per running job and are intended for live monitoring +and for being consumed by a downstream database writer. + +For tightly-integrated parallel jobs, the granularity is driven by the parallel environment's +*accounting_summary* attribute (see xxqs_name_sxx_sge_pe(5)): + +* `accounting_summary=TRUE` — one record per ja_task. Usage values are aggregated across all pe_tasks + of the ja_task — the same convention the *acct* record follows. +* `accounting_summary=FALSE` — one record per pe_task and one for the master task. + +online_usage records have the following fields: + +* job_number + The job number. + +* task_number + The array task id. 0 for non-array jobs, otherwise the index of the array task. + +* pe_taskid + The task id of a parallel sub-task. Present only when the record describes the usage of a single + pe_task. Absent when the record represents the master task or an aggregated ja_task-level record + for a PE with *accounting_summary=TRUE*. + +* usage + A JSON object containing the usage variables configured in + `reporting_params=...online_usage=[|...]` that are present in the job report. Each entry + is the scaled usage value of one variable. Variables that are configured but not yet reported by + the execd are silently skipped (no `null` value is emitted). For the list of valid variable names + see the description of *online_usage* in xxqs_name_sxx_sge_conf(5). + +The online_usage record type is emitted only to the JSONL reporting file. The deprecated colon-separated +reporting format does not carry online_usage records. + ## queue Records of type queue contain state information for queues (queue instances). A queue record has the following fields: diff --git a/source/daemons/qmaster/configuration_qmaster.cc b/source/daemons/qmaster/configuration_qmaster.cc index 7688333ca..6e9019c4b 100644 --- a/source/daemons/qmaster/configuration_qmaster.cc +++ b/source/daemons/qmaster/configuration_qmaster.cc @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include @@ -480,6 +482,27 @@ check_config(lList **alpp, lListElem *conf) { answer_list_add(alpp, SGE_EVENT, STATUS_EEXIST, ANSWER_QUALITY_ERROR); DRETURN(STATUS_EEXIST); } + } else if (!strcmp(name, "reporting_params")) { + // Validate parameters whose grammar we can fail-fast on. Today this + // covers only online_usage; other reporting_params tokens are + // sanity-checked at merge time in sge_conf.cc::merge_configuration(). + struct saved_vars_s *context = nullptr; + bool rp_valid = true; + for (const char *s = sge_strtok_r(value, PARAMS_DELIMITER, &context); + rp_valid && s != nullptr; + s = sge_strtok_r(nullptr, PARAMS_DELIMITER, &context)) { + std::string online_usage_str; + if (parse_string_param(s, "online_usage", online_usage_str)) { + std::vector discard; + if (!parse_online_usage_value(alpp, online_usage_str.c_str(), discard)) { + rp_valid = false; + } + } + } + sge_free_saved_vars(context); + if (!rp_valid) { + DRETURN(STATUS_EEXIST); + } } else if (!strcmp(name, "admin_user")) { struct passwd pw_struct; char *buffer; diff --git a/source/daemons/qmaster/job_report_qmaster.cc b/source/daemons/qmaster/job_report_qmaster.cc index 6ffe9cfb6..0e6203dbb 100644 --- a/source/daemons/qmaster/job_report_qmaster.cc +++ b/source/daemons/qmaster/job_report_qmaster.cc @@ -27,7 +27,7 @@ * * All Rights Reserved. * - * Portions of this software are Copyright (c) 2023-2025 HPC-Gridware GmbH + * Portions of this software are Copyright (c) 2023-2026 HPC-Gridware GmbH * ************************************************************************/ /*___INFO__MARK_END__*/ @@ -43,6 +43,7 @@ #include "sgeobj/ocs_DataStore.h" #include "sgeobj/sge_answer.h" #include "sgeobj/sge_ja_task.h" +#include "sgeobj/sge_pe.h" #include "sgeobj/sge_pe_task.h" #include "sgeobj/sge_usage.h" #include "sgeobj/sge_host.h" @@ -383,6 +384,32 @@ void process_job_report(lListElem *report, lListElem *hep, char *rhost, char *co } } + /* + * online_usage record: emit a JSONL snapshot of the scaled + * usage on every job report from execd, gated by the + * reporting_params=online_usage list. Honour the PE's + * accounting_summary flag with the same convention as + * sge_write_rusage / create_acct_records: + * - pe_task JR + accounting_summary TRUE -> skip + * (aggregated record will be written on the master JR) + * - pe_task JR + accounting_summary FALSE -> per-pe_task + * - master JR -> ja_task, + * summing pe_task usage when accounting_summary TRUE + */ + if (ocs::ReportingFileWriter::is_online_usage_required()) { + const bool do_accounting_summary = + pe_do_accounting_summary(lGetObject(jatep, JAT_pe_object)); + if (pe_task_id_str != nullptr) { + if (petask != nullptr && !do_accounting_summary) { + ocs::ReportingFileWriter::create_online_usage_records(nullptr, jr, jep, jatep, + petask, false); + } + } else { + ocs::ReportingFileWriter::create_online_usage_records(nullptr, jr, jep, jatep, + nullptr, do_accounting_summary); + } + } + /* * once a day write an intermediate usage record to the * reporting file to have correct daily usage reporting with diff --git a/source/daemons/qmaster/ocs_JsonReportingFileWriter.cc b/source/daemons/qmaster/ocs_JsonReportingFileWriter.cc index dd9276b86..698edbdef 100644 --- a/source/daemons/qmaster/ocs_JsonReportingFileWriter.cc +++ b/source/daemons/qmaster/ocs_JsonReportingFileWriter.cc @@ -32,6 +32,7 @@ #include "sgeobj/sge_qinstance.h" #include "sgeobj/sge_qinstance_state.h" #include "sgeobj/sge_str.h" +#include "sgeobj/sge_usage.h" #include "uti/sge_lock.h" #include "uti/sge_log.h" @@ -76,6 +77,128 @@ namespace ocs { DRETURN(ret); } + /** + * Emit a single JSONL online_usage record for the running job. + * + * When pe_task is non-null the record carries that pe_task's scaled + * usage and a pe_taskid field. When pe_task is null the record carries + * the ja_task's scaled usage; if aggregate_pe_tasks is set, scaled + * usage of every pe_task in `ja_task->JAT_task_list` is summed into the + * ja_task value (matching the convention used by sge_write_rusage when + * the PE has `accounting_summary` set). Only the variables listed in + * `reporting_params=online_usage=...` are emitted under `usage`; + * variables missing from the scaled usage list are silently skipped + * (no `null` field). + * + * If none of the configured variables have any data yet (e.g. right + * after job start, before the execd has produced its first usage + * report), no record is emitted at all — empty-`usage` records would + * clutter the reporting file without adding information. + * + * @see ReportingFileWriter::create_online_usage_records() + */ + bool + JsonReportingFileWriter::create_online_usage_record(lList **answer_list, lListElem *job_report, + lListElem *job, lListElem *ja_task, lListElem *pe_task, + bool aggregate_pe_tasks) { + DENTER(TOP_LAYER); + + if (job == nullptr || ja_task == nullptr) { + DRETURN(true); + } + + // take a snapshot of the configured variable names + std::vector vars; + sge_mutex_lock(config_mutex_name.c_str(), __func__, __LINE__, &config_mutex); + vars = online_usage_vars; + sge_mutex_unlock(config_mutex_name.c_str(), __func__, __LINE__, &config_mutex); + + if (vars.empty()) { + DRETURN(true); + } + + // pe_task != nullptr: report scaled usage of the pe_task, + // otherwise the (possibly aggregated) scaled usage on the ja_task + const lList *primary_usage = (pe_task != nullptr) + ? lGetList(pe_task, PET_scaled_usage) + : lGetList(ja_task, JAT_scaled_usage_list); + + // aggregation only applies to ja_task-level records + const bool aggregate = (pe_task == nullptr) && aggregate_pe_tasks; + + // Suppress empty-usage records: if none of the configured variables + // have data yet, do not emit anything. + bool any_data = false; + for (const auto &name : vars) { + if (lGetElemStr(primary_usage, UA_name, name.c_str()) != nullptr) { + any_data = true; + break; + } + if (aggregate) { + const lListElem *pe_task_iter; + for_each_ep(pe_task_iter, lGetList(ja_task, JAT_task_list)) { + if (lGetSubStr(pe_task_iter, UA_name, name.c_str(), PET_scaled_usage) != nullptr) { + any_data = true; + break; + } + } + if (any_data) { + break; + } + } + } + if (!any_data) { + DRETURN(true); + } + + rapidjson::StringBuffer stringBuffer; + rapidjson::Writer writer(stringBuffer); + + writer.StartObject(); + write_json(writer, "time", sge_get_gmt64()); + write_json(writer, "type", "online_usage"); + write_json(writer, "job_number", lGetUlong(job, JB_job_number)); + int task_number = job_is_array(job) ? (int) lGetUlong(ja_task, JAT_task_number) : 0; + write_json(writer, "task_number", task_number); + if (pe_task != nullptr) { + write_json(writer, "pe_taskid", lGetString(pe_task, PET_id)); + } + + writer.Key("usage"); + writer.StartObject(); + for (const auto &name : vars) { + double total = 0.0; + bool found_any = false; + + const lListElem *u = lGetElemStr(primary_usage, UA_name, name.c_str()); + if (u != nullptr) { + total = lGetDouble(u, UA_value); + found_any = true; + } + + if (aggregate) { + const lListElem *pe_task_iter; + for_each_ep(pe_task_iter, lGetList(ja_task, JAT_task_list)) { + const lListElem *pu = lGetSubStr(pe_task_iter, UA_name, name.c_str(), PET_scaled_usage); + if (pu != nullptr) { + total += lGetDouble(pu, UA_value); + found_any = true; + } + } + } + + if (found_any) { + write_json(writer, name.c_str(), total); + } + } + writer.EndObject(); + + writer.EndObject(); + create_record(stringBuffer); + + DRETURN(true); + } + void JsonReportingFileWriter::create_record(rapidjson::StringBuffer &stringBuffer) { stringBuffer.Put('\n'); diff --git a/source/daemons/qmaster/ocs_JsonReportingFileWriter.h b/source/daemons/qmaster/ocs_JsonReportingFileWriter.h index f8cbd3cfa..23b0502ff 100644 --- a/source/daemons/qmaster/ocs_JsonReportingFileWriter.h +++ b/source/daemons/qmaster/ocs_JsonReportingFileWriter.h @@ -2,7 +2,7 @@ /*___INFO__MARK_BEGIN_NEW__*/ /*************************************************************************** * - * Copyright 2024 HPC-Gridware GmbH + * Copyright 2024,2026 HPC-Gridware GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,6 +48,10 @@ namespace ocs { create_acct_record(lList **answer_list, lListElem *job_report, lListElem *job, lListElem *ja_task, bool intermediate) override; + bool + create_online_usage_record(lList **answer_list, lListElem *job_report, lListElem *job, + lListElem *ja_task, lListElem *pe_task, bool aggregate_pe_tasks) override; + bool create_host_record(lList **answer_list, const lListElem *host, u_long64 report_time) override; diff --git a/source/daemons/qmaster/ocs_ReportingFileWriter.cc b/source/daemons/qmaster/ocs_ReportingFileWriter.cc index c10896b56..e7450fe6f 100644 --- a/source/daemons/qmaster/ocs_ReportingFileWriter.cc +++ b/source/daemons/qmaster/ocs_ReportingFileWriter.cc @@ -54,6 +54,7 @@ namespace ocs { std::string ReportingFileWriter::reporting_params; std::string ReportingFileWriter::usage_patterns; std::vector> ReportingFileWriter::usage_pattern_list; + std::vector ReportingFileWriter::online_usage_vars; // We have two mutexes to protec access to the writers and the configuration parameters. // Make sure to use the mutexes in the right order, i.e., always lock the writer_mutex before @@ -273,6 +274,12 @@ namespace ocs { } sge_mutex_unlock(config_mutex_name.c_str(), __func__, __LINE__, &config_mutex); + // online_usage variables + std::vector new_online_usage_vars = mconf_get_online_usage_vars(); + sge_mutex_lock(config_mutex_name.c_str(), __func__, __LINE__, &config_mutex); + online_usage_vars = std::move(new_online_usage_vars); + sge_mutex_unlock(config_mutex_name.c_str(), __func__, __LINE__, &config_mutex); + // now configure all active Writers for (auto w: writers) { if (w != nullptr) { @@ -335,6 +342,53 @@ namespace ocs { return ret; } + /** + * Static wrapper: forward an online_usage event to every active writer. + * + * Each writer decides whether to emit a record. Only the JSON reporting + * writer overrides this virtual; the classic reporting writer and the + * accounting writers keep the no-op default. + * + * `aggregate_pe_tasks` is honoured only when pe_task is null and signals + * that the writer should sum scaled usage across the ja_task's pe_tasks + * (the same convention `sge_write_rusage` follows for `accounting_summary`). + * + * @see create_online_usage_record() + * @see is_online_usage_required() + */ + bool ReportingFileWriter::create_online_usage_records(lList **answer_list, lListElem *job_report, lListElem *job, + lListElem *ja_task, lListElem *pe_task, bool aggregate_pe_tasks) { + bool ret = true; + + sge_mutex_lock(writer_mutex_name.c_str(), __func__, __LINE__, &writer_mutex); + for (auto w: writers) { + if (w != nullptr && !w->create_online_usage_record(answer_list, job_report, job, ja_task, pe_task, aggregate_pe_tasks)) { + ret = false; + } + } + sge_mutex_unlock(writer_mutex_name.c_str(), __func__, __LINE__, &writer_mutex); + + return ret; + } + + /** + * True iff `reporting_params=online_usage=...` is configured with at + * least one variable. Callers use this as a fast gate before invoking + * the dispatcher on every job report from sge_execd, so feature-off + * configurations pay only a single mutex'd vector-empty check per JR. + * + * @see create_online_usage_records() + */ + bool ReportingFileWriter::is_online_usage_required() { + bool required; + + sge_mutex_lock(config_mutex_name.c_str(), __func__, __LINE__, &config_mutex); + required = !online_usage_vars.empty(); + sge_mutex_unlock(config_mutex_name.c_str(), __func__, __LINE__, &config_mutex); + + return required; + } + bool ReportingFileWriter::create_host_records(lList **answer_list, const lListElem *host, u_long64 report_time) { bool ret = true; diff --git a/source/daemons/qmaster/ocs_ReportingFileWriter.h b/source/daemons/qmaster/ocs_ReportingFileWriter.h index bd5de3642..9b8fd9af0 100644 --- a/source/daemons/qmaster/ocs_ReportingFileWriter.h +++ b/source/daemons/qmaster/ocs_ReportingFileWriter.h @@ -2,7 +2,7 @@ /*___INFO__MARK_BEGIN_NEW__*/ /*************************************************************************** * - * Copyright 2024-2025 HPC-Gridware GmbH + * Copyright 2024-2026 HPC-Gridware GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,6 +60,7 @@ namespace ocs { u_long64 config_flush_time; u_long64 next_flush_time; static std::vector> usage_pattern_list; + static std::vector online_usage_vars; static pthread_mutex_t config_mutex; static std::string config_mutex_name; @@ -92,6 +93,13 @@ namespace ocs { create_acct_records(lList **answer_list, lListElem *job_report, lListElem *job, lListElem *ja_task, bool intermediate); + static bool + create_online_usage_records(lList **answer_list, lListElem *job_report, lListElem *job, + lListElem *ja_task, lListElem *pe_task, bool aggregate_pe_tasks); + + static bool + is_online_usage_required(); + static bool create_host_records(lList **answer_list, const lListElem *host, u_long64 report_time); @@ -144,6 +152,10 @@ namespace ocs { create_acct_record(lList **answer_list, lListElem *job_report, lListElem *job, lListElem *ja_task, bool intermediate) = 0; + virtual bool + create_online_usage_record(lList **answer_list, lListElem *job_report, lListElem *job, + lListElem *ja_task, lListElem *pe_task, bool aggregate_pe_tasks) { return true; } + virtual bool create_host_record(lList **answer_list, const lListElem *host, u_long64 report_time) { return true; } diff --git a/source/libs/sgeobj/msg_sgeobjlib.h b/source/libs/sgeobj/msg_sgeobjlib.h index f32fbb04b..7f85e26f8 100644 --- a/source/libs/sgeobj/msg_sgeobjlib.h +++ b/source/libs/sgeobj/msg_sgeobjlib.h @@ -30,7 +30,7 @@ * * Portions of this software are Copyright (c) 2011 Univa Corporation * - * Portions of this software are Copyright (c) 2023-2025 HPC-Gridware GmbH + * Portions of this software are Copyright (c) 2023-2026 HPC-Gridware GmbH * ************************************************************************/ /*___INFO__MARK_END__*/ @@ -367,4 +367,6 @@ #define MSG_OBJCONT_DUP_S _MESSAGE(64557, _("Duplicate entry: " SFQ)) #define MSG_OBJCONT_DUP_SS _MESSAGE(64558, _("Duplicate entry " SFQ " in " SFQ)) +#define MSG_CONF_INVALIDPARAM_EMPTYTOKEN_SS _MESSAGE(64559, _("invalid setting for " SFQ ", attribute " SFQ " - empty token between '|' separators (keeping previous value)")) + // clang-format on diff --git a/source/libs/sgeobj/sge_conf.cc b/source/libs/sgeobj/sge_conf.cc index ff2330e4e..f306cbe06 100644 --- a/source/libs/sgeobj/sge_conf.cc +++ b/source/libs/sgeobj/sge_conf.cc @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef LINUX #include @@ -63,6 +64,7 @@ #include "sgeobj/sge_answer.h" #include "sgeobj/sge_userprj.h" #include "sgeobj/sge_userset.h" +#include "sgeobj/sge_utility.h" #include "basis_types.h" @@ -218,6 +220,7 @@ static bool old_reporting = false; static int sharelog_time = 0; static bool log_consumables = false; static std::string usage_patterns; +static std::vector online_usage_vars; // Binding specific parameters static bool is_binding_enabled = true; @@ -951,6 +954,7 @@ int merge_configuration(lList **answer_list, u_long32 progid, const char *cell_r sharelog_time = 0; log_consumables = false; usage_patterns.clear(); + online_usage_vars.clear(); enable_addgrp_kill = false; strcpy(s_descriptors, "UNDEFINED"); strcpy(h_descriptors, "UNDEFINED"); @@ -1194,6 +1198,19 @@ int merge_configuration(lList **answer_list, u_long32 progid, const char *cell_r if (parse_string_param(s, "usage_patterns", usage_patterns)) { continue; } + std::string online_usage_str; + if (parse_string_param(s, "online_usage", online_usage_str)) { + // The pre-commit validator in qmaster's check_config rejects bad + // values before they reach us. Re-validate here as defence in + // depth (e.g. for spool data written before validation existed). + // On rejection, leave online_usage_vars unchanged. + std::vector new_online_usage_vars; + if (parse_online_usage_value(answer_list, online_usage_str.c_str(), + new_online_usage_vars)) { + online_usage_vars = std::move(new_online_usage_vars); + } + continue; + } } SGE_UNLOCK(LOCK_MASTER_CONF, LOCK_WRITE); sge_free_saved_vars(conf_context); @@ -2800,6 +2817,76 @@ std::string mconf_get_usage_patterns() { DRETURN(ret); } +/** + * Validate an online_usage value (the text after `online_usage=`). + * + * The value is a `|`-separated list of tokens. Each token must be a + * valid complex_name (= object_name) per sge_types(1); empty tokens are + * rejected. An empty whole value is the disabled case and is valid; it + * produces an empty `out_vars`. + * + * On success: returns true; `out_vars` holds the parsed tokens. + * On failure: returns false; explanatory entries are appended to + * `answer_list`; `out_vars` content is unspecified. + * + * Used both by check_config() in qmaster (pre-commit rejection of bad + * configurations from qconf -Mconf) and by merge_configuration() + * (parse-time defence in depth). + */ +bool +parse_online_usage_value(lList **answer_list, const char *value, + std::vector &out_vars) { + out_vars.clear(); + if (value == nullptr || *value == '\0') { + return true; + } + const char *start = value; + for (const char *p = value;; ++p) { + if (*p == '|' || *p == '\0') { + const size_t len = static_cast(p - start); + if (len == 0) { + answer_list_add_sprintf(answer_list, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR, + MSG_CONF_INVALIDPARAM_EMPTYTOKEN_SS, + "reporting_params", "online_usage"); + return false; + } + std::string token(start, len); + if (verify_str_key(answer_list, token.c_str(), MAX_VERIFY_STRING, + "complex_name", KEY_TABLE) != STATUS_OK) { + // verify_str_key has already added a descriptive message. + return false; + } + out_vars.emplace_back(std::move(token)); + if (*p == '\0') { + break; + } + start = p + 1; + } + } + return true; +} + +/** + * Return the parsed online_usage variable names from the reporting_params. + * + * Set via `reporting_params=... online_usage=[|...]`. + * An empty vector means the feature is disabled and no online_usage + * records shall be written to the JSONL reporting file. + * + * @see merge_configuration() + */ +std::vector mconf_get_online_usage_vars() { + std::vector ret; + + DENTER(BASIS_LAYER); + SGE_LOCK(LOCK_MASTER_CONF, LOCK_READ); + + ret = online_usage_vars; + + SGE_UNLOCK(LOCK_MASTER_CONF, LOCK_READ); + DRETURN(ret); +} + bool mconf_get_enable_forced_qdel() { bool ret; diff --git a/source/libs/sgeobj/sge_conf.h b/source/libs/sgeobj/sge_conf.h index 4fa1b85a5..8667fa9e7 100644 --- a/source/libs/sgeobj/sge_conf.h +++ b/source/libs/sgeobj/sge_conf.h @@ -37,6 +37,7 @@ #include #include +#include #include "sgeobj/cull/sge_conf_CONF_L.h" #include "sgeobj/cull/sge_conf_CF_L.h" @@ -186,6 +187,8 @@ bool mconf_get_old_reporting(); int mconf_get_sharelog_time(); int mconf_get_log_consumables(); std::string mconf_get_usage_patterns(); +std::vector mconf_get_online_usage_vars(); +bool parse_online_usage_value(lList **answer_list, const char *value, std::vector &out_vars); bool mconf_get_enable_forced_qdel(); bool mconf_get_enable_sup_grp_eval(); bool mconf_get_enable_forced_qdel_if_unknown();