From 99350e6b274c105baf34a92eb77e14e497c05fe7 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 24 May 2023 16:04:14 +0200 Subject: [PATCH 1/7] Icinga DB feature: normalize *Command.arguments[*].{required,skip_key,repeat_key} to boolean At the moment, the Icinga DB feature will use that value as-is and serialize it to JSON, resulting in a crash in Icinga DB down the road because it expects a boolean. --- lib/icingadb/icingadb-objects.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/icingadb/icingadb-objects.cpp b/lib/icingadb/icingadb-objects.cpp index ac1469ba5d9..15fb1888a45 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -1019,6 +1019,15 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S } } + for (const char *attr : {"repeat_key", "required", "skip_key"}) { + Value value; + + // Boolify if set. + if (values->Get(attr, &value)) { + values->Set(attr, value.ToBool()); + } + } + { Value order; From f0176001fed93151be44b1d2acc300feca6d8c37 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 30 May 2023 17:56:03 +0200 Subject: [PATCH 2/7] Icinga DB: don't write negative Downtime durations into Redis via `std::max(0, x)` not to crash the Go daemon which can't handle such. --- lib/icingadb/icingadb-objects.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/icingadb/icingadb-objects.cpp b/lib/icingadb/icingadb-objects.cpp index 15fb1888a45..6e39ec0cfec 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -30,6 +30,7 @@ #include "icinga/timeperiod.hpp" #include "icinga/pluginutility.hpp" #include "remote/zone.hpp" +#include #include #include #include @@ -1431,20 +1432,23 @@ bool IcingaDB::PrepareObject(const ConfigObject::Ptr& object, Dictionary::Ptr& a attributes->Set("entry_time", TimestampToMilliseconds(downtime->GetEntryTime())); attributes->Set("scheduled_start_time", TimestampToMilliseconds(downtime->GetStartTime())); attributes->Set("scheduled_end_time", TimestampToMilliseconds(downtime->GetEndTime())); - attributes->Set("scheduled_duration", TimestampToMilliseconds(downtime->GetEndTime() - downtime->GetStartTime())); - attributes->Set("flexible_duration", TimestampToMilliseconds(downtime->GetDuration())); + attributes->Set("scheduled_duration", TimestampToMilliseconds(std::max(0.0, downtime->GetEndTime() - downtime->GetStartTime()))); + attributes->Set("flexible_duration", TimestampToMilliseconds(std::max(0.0, downtime->GetDuration()))); attributes->Set("is_flexible", !downtime->GetFixed()); attributes->Set("is_in_effect", downtime->IsInEffect()); if (downtime->IsInEffect()) { attributes->Set("start_time", TimestampToMilliseconds(downtime->GetTriggerTime())); - attributes->Set("end_time", TimestampToMilliseconds(downtime->GetFixed() ? downtime->GetEndTime() : (downtime->GetTriggerTime() + downtime->GetDuration()))); + + attributes->Set("end_time", TimestampToMilliseconds( + downtime->GetFixed() ? downtime->GetEndTime() : (downtime->GetTriggerTime() + std::max(0.0, downtime->GetDuration())) + )); } auto duration = downtime->GetDuration(); if (downtime->GetFixed()) { duration = downtime->GetEndTime() - downtime->GetStartTime(); } - attributes->Set("duration", TimestampToMilliseconds(duration)); + attributes->Set("duration", TimestampToMilliseconds(std::max(0.0, duration))); Host::Ptr host; Service::Ptr service; @@ -1834,7 +1838,7 @@ void IcingaDB::SendStartedDowntime(const Downtime::Ptr& downtime) "author", Utility::ValidateUTF8(downtime->GetAuthor()), "comment", Utility::ValidateUTF8(downtime->GetComment()), "is_flexible", Convert::ToString((unsigned short)!downtime->GetFixed()), - "flexible_duration", Convert::ToString(TimestampToMilliseconds(downtime->GetDuration())), + "flexible_duration", Convert::ToString(TimestampToMilliseconds(std::max(0.0, downtime->GetDuration()))), "scheduled_start_time", Convert::ToString(TimestampToMilliseconds(downtime->GetStartTime())), "scheduled_end_time", Convert::ToString(TimestampToMilliseconds(downtime->GetEndTime())), "has_been_cancelled", Convert::ToString((unsigned short)downtime->GetWasCancelled()), @@ -1867,7 +1871,7 @@ void IcingaDB::SendStartedDowntime(const Downtime::Ptr& downtime) xAdd.emplace_back("start_time"); xAdd.emplace_back(Convert::ToString(TimestampToMilliseconds(downtime->GetTriggerTime()))); xAdd.emplace_back("end_time"); - xAdd.emplace_back(Convert::ToString(TimestampToMilliseconds(downtime->GetTriggerTime() + downtime->GetDuration()))); + xAdd.emplace_back(Convert::ToString(TimestampToMilliseconds(downtime->GetTriggerTime() + std::max(0.0, downtime->GetDuration())))); } auto endpoint (Endpoint::GetLocalEndpoint()); @@ -1924,7 +1928,7 @@ void IcingaDB::SendRemovedDowntime(const Downtime::Ptr& downtime) "cancelled_by", Utility::ValidateUTF8(downtime->GetRemovedBy()), "comment", Utility::ValidateUTF8(downtime->GetComment()), "is_flexible", Convert::ToString((unsigned short)!downtime->GetFixed()), - "flexible_duration", Convert::ToString(TimestampToMilliseconds(downtime->GetDuration())), + "flexible_duration", Convert::ToString(TimestampToMilliseconds(std::max(0.0, downtime->GetDuration()))), "scheduled_start_time", Convert::ToString(TimestampToMilliseconds(downtime->GetStartTime())), "scheduled_end_time", Convert::ToString(TimestampToMilliseconds(downtime->GetEndTime())), "has_been_cancelled", Convert::ToString((unsigned short)downtime->GetWasCancelled()), @@ -1958,7 +1962,7 @@ void IcingaDB::SendRemovedDowntime(const Downtime::Ptr& downtime) xAdd.emplace_back("start_time"); xAdd.emplace_back(Convert::ToString(TimestampToMilliseconds(downtime->GetTriggerTime()))); xAdd.emplace_back("end_time"); - xAdd.emplace_back(Convert::ToString(TimestampToMilliseconds(downtime->GetTriggerTime() + downtime->GetDuration()))); + xAdd.emplace_back(Convert::ToString(TimestampToMilliseconds(downtime->GetTriggerTime() + std::max(0.0, downtime->GetDuration())))); } auto endpoint (Endpoint::GetLocalEndpoint()); From 766e28e1aa3774fef2000c855d7c5ca090f2d4d0 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 15 Jun 2023 12:29:42 +0200 Subject: [PATCH 3/7] IcingaDB::PrepareObject(): convert non-null Checkable#check_timeout to number and, in case of null, fall back to Checkable#check_command.timeout, just like IcingaDB#SerializeState(). Otherwise the Go daemon crashes. It expects a number. --- lib/icingadb/icingadb-objects.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/icingadb/icingadb-objects.cpp b/lib/icingadb/icingadb-objects.cpp index 6e39ec0cfec..9693a1ce0a0 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -1272,10 +1272,11 @@ bool IcingaDB::PrepareObject(const ConfigObject::Ptr& object, Dictionary::Ptr& a if (type == Host::TypeInstance || type == Service::TypeInstance) { Checkable::Ptr checkable = static_pointer_cast(object); + auto checkTimeout (checkable->GetCheckTimeout()); attributes->Set("checkcommand_name", checkable->GetCheckCommand()->GetName()); attributes->Set("max_check_attempts", checkable->GetMaxCheckAttempts()); - attributes->Set("check_timeout", checkable->GetCheckTimeout()); + attributes->Set("check_timeout", checkTimeout.IsEmpty() ? checkable->GetCheckCommand()->GetTimeout() : (double)checkTimeout); attributes->Set("check_interval", checkable->GetCheckInterval()); attributes->Set("check_retry_interval", checkable->GetRetryInterval()); attributes->Set("active_checks_enabled", checkable->GetEnableActiveChecks()); From 2f1732e7e6c79acee480b749432e2f7334bd5137 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 26 Jun 2023 15:36:47 +0200 Subject: [PATCH 4/7] IcingaDB::PrepareObject(): cut off (0) negative Command#timeout for Redis not to crash the Go daemon which expects positive values there. --- lib/icingadb/icingadb-objects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icingadb/icingadb-objects.cpp b/lib/icingadb/icingadb-objects.cpp index 9693a1ce0a0..e04522c6e1a 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -1508,7 +1508,7 @@ bool IcingaDB::PrepareObject(const ConfigObject::Ptr& object, Dictionary::Ptr& a Command::Ptr command = static_pointer_cast(object); attributes->Set("command", JsonEncode(command->GetCommandLine())); - attributes->Set("timeout", command->GetTimeout()); + attributes->Set("timeout", std::max(0, command->GetTimeout())); return true; } From b7ecefb3c097130bb744b89306f94978348b4ed6 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 19 Jun 2023 12:46:40 +0200 Subject: [PATCH 5/7] IcingaDB::PrepareObject(): round Notification#interval and limit it to >=0 otherwise, e.g. with -42.5, the Go daemon crashes. It expects uints there. --- lib/icingadb/icingadb-objects.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/icingadb/icingadb-objects.cpp b/lib/icingadb/icingadb-objects.cpp index e04522c6e1a..a475f4bb243 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -32,6 +32,7 @@ #include "remote/zone.hpp" #include #include +#include #include #include #include @@ -1388,7 +1389,7 @@ bool IcingaDB::PrepareObject(const ConfigObject::Ptr& object, Dictionary::Ptr& a attributes->Set("times_end",notification->GetTimes()->Get("end")); } - attributes->Set("notification_interval", notification->GetInterval()); + attributes->Set("notification_interval", std::max(0.0, std::round(notification->GetInterval()))); attributes->Set("states", notification->GetStates()); attributes->Set("types", notification->GetTypes()); From 04457f5f16e705e1533ab06186f3cf3de86a8526 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 27 Jun 2023 12:53:08 +0200 Subject: [PATCH 6/7] IcingaDB::PrepareObject(): round Notification#times.{begin,end} not to crash Go daemon The latter expects ints, not floats - not to mention strings. Luckily Icinga already enforces numeric strings so that we can cast it to number. --- lib/icingadb/icingadb-objects.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/icingadb/icingadb-objects.cpp b/lib/icingadb/icingadb-objects.cpp index a475f4bb243..3687af75277 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -1385,8 +1385,16 @@ bool IcingaDB::PrepareObject(const ConfigObject::Ptr& object, Dictionary::Ptr& a attributes->Set("timeperiod_id", GetObjectIdentifier(timeperiod)); if (notification->GetTimes()) { - attributes->Set("times_begin", notification->GetTimes()->Get("begin")); - attributes->Set("times_end",notification->GetTimes()->Get("end")); + auto begin (notification->GetTimes()->Get("begin")); + auto end (notification->GetTimes()->Get("end")); + + if (begin != Empty) { + attributes->Set("times_begin", std::round((double)begin)); + } + + if (end != Empty) { + attributes->Set("times_end", std::round((double)end)); + } } attributes->Set("notification_interval", std::max(0.0, std::round(notification->GetInterval()))); From 0735966e23fae5db74bd4506f4f6d7396c5b9911 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 27 Jun 2023 11:44:14 +0200 Subject: [PATCH 7/7] IcingaDB::PrepareObject(): cut off (null) negative Notification#times.{begin,end} not to crash Go daemon At least our PostgreSQL schema enforces positive values. --- lib/icingadb/icingadb-objects.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/icingadb/icingadb-objects.cpp b/lib/icingadb/icingadb-objects.cpp index 3687af75277..8b5575af441 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -1388,11 +1388,11 @@ bool IcingaDB::PrepareObject(const ConfigObject::Ptr& object, Dictionary::Ptr& a auto begin (notification->GetTimes()->Get("begin")); auto end (notification->GetTimes()->Get("end")); - if (begin != Empty) { + if (begin != Empty && (double)begin >= 0) { attributes->Set("times_begin", std::round((double)begin)); } - if (end != Empty) { + if (end != Empty && (double)end >= 0) { attributes->Set("times_end", std::round((double)end)); } }