From f5232b18b8e11852e1e0a6a31ea49f5a90b600e2 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 1 Jun 2021 13:14:18 -0700 Subject: [PATCH 1/2] Scheduler: allow negation of regular expressions in plan class XML specs Plan class XML specs allow regular expressions for OS, CPU vendor, CPU model, project prefs, and host summary. If any of these starts with a '!', negate the match. --- sched/plan_class_spec.cpp | 57 +++++++++++++++++++++++---------------- sched/plan_class_spec.h | 33 +++++++++++++++-------- 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/sched/plan_class_spec.cpp b/sched/plan_class_spec.cpp index f29a4682811..9a59ba95d72 100644 --- a/sched/plan_class_spec.cpp +++ b/sched/plan_class_spec.cpp @@ -29,6 +29,29 @@ using std::string; +int REGEX_CLAUSE::init(const char* p) { + present = true; + negate = false; + if (*p == '!') { + p++; + negate = true; + } + if (regcomp(®ex, p, REG_EXTENDED|REG_NOSUB) ) { + return ERR_XML_PARSE; + } + return 0; +} + +// return true if clause is present and string doesn't match +// +bool REGEX_CLAUSE::mismatch(const char* p) { + if (!present) { + return false; + } + bool match = (regexec(®ex, p, 0, NULL, 0) == 0); + return negate?match:!match; +} + // return a numerical OS version for Darwin/OSX and Windows, // letting us define numerical ranges for these OS versions // @@ -302,9 +325,7 @@ bool PLAN_CLASS_SPEC::check( // host summary // - if (have_host_summary_regex - && regexec(&(host_summary_regex), g_reply->host.serialnum, 0, NULL, 0) - ) { + if (host_summary_regex.mismatch(g_reply->host.serialnum)) { if (config.debug_version_select) { log_messages.printf(MSG_NORMAL, "[version] plan_class_spec: host summary '%s' didn't match regexp\n", @@ -316,7 +337,7 @@ bool PLAN_CLASS_SPEC::check( // OS version // - if (have_os_regex && regexec(&(os_regex), sreq.host.os_version, 0, NULL, 0)) { + if (os_regex.mismatch(sreq.host.os_version)) { if (config.debug_version_select) { log_messages.printf(MSG_NORMAL, "[version] plan_class_spec: OS version '%s' didn't match regexp\n", @@ -389,7 +410,7 @@ bool PLAN_CLASS_SPEC::check( // CPU vendor and model // - if (have_cpu_vendor_regex && regexec(&(cpu_vendor_regex), sreq.host.p_vendor, 0, NULL, 0)) { + if (cpu_vendor_regex.mismatch(sreq.host.p_vendor)) { if (config.debug_version_select) { log_messages.printf(MSG_NORMAL, "[version] plan_class_spec: CPU vendor '%s' didn't match regexp\n", @@ -399,7 +420,7 @@ bool PLAN_CLASS_SPEC::check( return false; } - if (have_cpu_model_regex && regexec(&(cpu_model_regex), sreq.host.p_model, 0, NULL, 0)) { + if (cpu_model_regex.mismatch (sreq.host.p_model)) { if (config.debug_version_select) { log_messages.printf(MSG_NORMAL, "[version] plan_class_spec: CPU model '%s' didn't match regexp\n", @@ -521,7 +542,7 @@ bool PLAN_CLASS_SPEC::check( // project-specific preference // - if (have_project_prefs_regex && strlen(project_prefs_tag)) { + if (project_prefs_regex.present && strlen(project_prefs_tag)) { char tag[256], value[256]; char buf[65536]; extract_venue(g_reply->user.project_prefs, g_reply->host.venue, buf, sizeof(buf)); @@ -533,7 +554,7 @@ bool PLAN_CLASS_SPEC::check( project_prefs_tag, p?"true":"false" ); } - if (p ? regexec(&(project_prefs_regex), value, 0, NULL, 0) : !project_prefs_default_true) { + if (p ? project_prefs_regex.mismatch(value) : !project_prefs_default_true) { if (config.debug_version_select) { log_messages.printf(MSG_NORMAL, "[version] plan_class_spec: project prefs setting '%s' value='%s' prevents using plan class.\n", @@ -1066,35 +1087,31 @@ int PLAN_CLASS_SPEC::parse(XML_PARSER& xp) { if (xp.parse_bool("nthreads_cmdline", nthreads_cmdline)) continue; if (xp.parse_double("projected_flops_scale", projected_flops_scale)) continue; if (xp.parse_str("os_regex", buf, sizeof(buf))) { - if (regcomp(&(os_regex), buf, REG_EXTENDED|REG_NOSUB) ) { + if (os_regex.init(buf)) { log_messages.printf(MSG_CRITICAL, "BAD OS REGEXP: %s\n", buf); return ERR_XML_PARSE; } - have_os_regex = true; continue; } if (xp.parse_str("cpu_vendor_regex", buf, sizeof(buf))) { - if (regcomp(&(cpu_vendor_regex), buf, REG_EXTENDED|REG_NOSUB) ) { + if (cpu_vendor_regex.init(buf)) { log_messages.printf(MSG_CRITICAL, "BAD CPU VENDOR REGEXP: %s\n", buf); return ERR_XML_PARSE; } - have_cpu_vendor_regex = true; continue; } if (xp.parse_str("cpu_model_regex", buf, sizeof(buf))) { - if (regcomp(&(cpu_model_regex), buf, REG_EXTENDED|REG_NOSUB) ) { + if (cpu_model_regex.init(buf)) { log_messages.printf(MSG_CRITICAL, "BAD CPU MODEL REGEXP: %s\n", buf); return ERR_XML_PARSE; } - have_cpu_model_regex = true; continue; } if (xp.parse_str("host_summary_regex", buf, sizeof(buf))) { - if (regcomp(&(host_summary_regex), buf, REG_EXTENDED|REG_NOSUB) ) { + if (host_summary_regex.init(buf)) { log_messages.printf(MSG_CRITICAL, "BAD HOST SUMMARY REGEXP: %s\n", buf); return ERR_XML_PARSE; } - have_host_summary_regex = true; continue; } if (xp.parse_int("user_id", user_id)) continue; @@ -1105,11 +1122,10 @@ int PLAN_CLASS_SPEC::parse(XML_PARSER& xp) { if (xp.parse_int("max_android_version", max_android_version)) continue; if (xp.parse_str("project_prefs_tag", project_prefs_tag, sizeof(project_prefs_tag))) continue; if (xp.parse_str("project_prefs_regex", buf, sizeof(buf))) { - if (regcomp(&(project_prefs_regex), buf, REG_EXTENDED|REG_NOSUB) ) { + if (project_prefs_regex.init(buf)) { log_messages.printf(MSG_CRITICAL, "BAD PROJECT PREFS REGEXP: %s\n", buf); return ERR_XML_PARSE; } - have_project_prefs_regex = true; continue; } if (xp.parse_bool("project_prefs_default_true", project_prefs_default_true)) continue; @@ -1198,20 +1214,15 @@ PLAN_CLASS_SPEC::PLAN_CLASS_SPEC() { mem_usage_per_cpu = 0; nthreads_cmdline = false; projected_flops_scale = 1; - have_os_regex = false; - have_cpu_vendor_regex = false; - have_cpu_model_regex = false; min_os_version = 0; max_os_version = 0; min_android_version = 0; max_android_version = 0; strcpy(project_prefs_tag, ""); - have_project_prefs_regex = false; project_prefs_default_true = false; avg_ncpus = 0; min_core_client_version = 0; max_core_client_version = 0; - have_host_summary_regex = false; user_id = 0; infeasible_random = 0; min_wu_id=0; diff --git a/sched/plan_class_spec.h b/sched/plan_class_spec.h index 67ac85132d8..b7998d8da22 100644 --- a/sched/plan_class_spec.h +++ b/sched/plan_class_spec.h @@ -23,7 +23,23 @@ #include #include -// if you add anything here, initialize if in the constructor +// represents a plan class clause with a regular expression +// +struct REGEX_CLAUSE { + bool present; // clause is present + bool negate; // regex is negated (starts with !) + regex_t regex; // compiled regex + + REGEX_CLAUSE() { + present = 0; + } + int init(const char* p); + // p is the regex, possibly preceded by ! + bool mismatch(const char*); + // clause is present, and the string doesn't match it +}; + +// if you add anything here, initialize it in the constructor // struct PLAN_CLASS_SPEC { char name[256]; @@ -40,27 +56,22 @@ struct PLAN_CLASS_SPEC { double mem_usage_per_cpu; bool nthreads_cmdline; double projected_flops_scale; - bool have_os_regex; - regex_t os_regex; - bool have_cpu_vendor_regex; - regex_t cpu_vendor_regex; - bool have_cpu_model_regex; - regex_t cpu_model_regex; + REGEX_CLAUSE os_regex; + REGEX_CLAUSE cpu_vendor_regex; + REGEX_CLAUSE cpu_model_regex; double min_os_version; // Win versions can be 9 digits; may as well be safe double max_os_version; int min_android_version; int max_android_version; char project_prefs_tag[256]; - bool have_project_prefs_regex; - regex_t project_prefs_regex; + REGEX_CLAUSE project_prefs_regex; bool project_prefs_default_true; double avg_ncpus; int min_core_client_version; int max_core_client_version; // for non-compute-intensive, or override for GPU apps - bool have_host_summary_regex; - regex_t host_summary_regex; + REGEX_CLAUSE host_summary_regex; int user_id; double infeasible_random; long min_wu_id; From 6c0652490088553234ba628e0b760851aba8dcb3 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 2 Jun 2021 11:31:10 -0700 Subject: [PATCH 2/2] scheduler: store plan class reg exprs so we can including them in messages --- sched/plan_class_spec.cpp | 21 +++++++++++++-------- sched/plan_class_spec.h | 13 ++++++++----- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/sched/plan_class_spec.cpp b/sched/plan_class_spec.cpp index 9a59ba95d72..1a7ac9c7828 100644 --- a/sched/plan_class_spec.cpp +++ b/sched/plan_class_spec.cpp @@ -32,6 +32,7 @@ using std::string; int REGEX_CLAUSE::init(const char* p) { present = true; negate = false; + expr = p; if (*p == '!') { p++; negate = true; @@ -328,8 +329,9 @@ bool PLAN_CLASS_SPEC::check( if (host_summary_regex.mismatch(g_reply->host.serialnum)) { if (config.debug_version_select) { log_messages.printf(MSG_NORMAL, - "[version] plan_class_spec: host summary '%s' didn't match regexp\n", - g_reply->host.serialnum + "[version] plan_class_spec: host summary '%s' didn't match regexp %s\n", + g_reply->host.serialnum, + host_summary_regex.expr.c_str() ); } return false; @@ -340,8 +342,9 @@ bool PLAN_CLASS_SPEC::check( if (os_regex.mismatch(sreq.host.os_version)) { if (config.debug_version_select) { log_messages.printf(MSG_NORMAL, - "[version] plan_class_spec: OS version '%s' didn't match regexp\n", - sreq.host.os_version + "[version] plan_class_spec: OS version '%s' didn't match regexp %s\n", + sreq.host.os_version, + os_regex.expr.c_str() ); } return false; @@ -413,8 +416,9 @@ bool PLAN_CLASS_SPEC::check( if (cpu_vendor_regex.mismatch(sreq.host.p_vendor)) { if (config.debug_version_select) { log_messages.printf(MSG_NORMAL, - "[version] plan_class_spec: CPU vendor '%s' didn't match regexp\n", - sreq.host.p_vendor + "[version] plan_class_spec: CPU vendor '%s' didn't match regexp %s\n", + sreq.host.p_vendor, + cpu_vendor_regex.expr.c_str() ); } return false; @@ -423,8 +427,9 @@ bool PLAN_CLASS_SPEC::check( if (cpu_model_regex.mismatch (sreq.host.p_model)) { if (config.debug_version_select) { log_messages.printf(MSG_NORMAL, - "[version] plan_class_spec: CPU model '%s' didn't match regexp\n", - sreq.host.p_model + "[version] plan_class_spec: CPU model '%s' didn't match regexp %s\n", + sreq.host.p_model, + cpu_model_regex.expr.c_str() ); } return false; diff --git a/sched/plan_class_spec.h b/sched/plan_class_spec.h index b7998d8da22..bc6dc56a6a8 100644 --- a/sched/plan_class_spec.h +++ b/sched/plan_class_spec.h @@ -23,12 +23,15 @@ #include #include -// represents a plan class clause with a regular expression +// Represents a plan class clause with a regular expression +// (os, cpu_vendor, etc.). +// Has the compiled regex, and whether it's negated // struct REGEX_CLAUSE { - bool present; // clause is present - bool negate; // regex is negated (starts with !) - regex_t regex; // compiled regex + bool present; // clause is present + bool negate; // regex is negated (starts with !) + std::string expr; // the regex string + regex_t regex; // compiled regex REGEX_CLAUSE() { present = 0; @@ -36,7 +39,7 @@ struct REGEX_CLAUSE { int init(const char* p); // p is the regex, possibly preceded by ! bool mismatch(const char*); - // clause is present, and the string doesn't match it + // return true if clause is present and the string doesn't match it }; // if you add anything here, initialize it in the constructor