From 56ae875861ab260b80a030f50c4aff9f9dc8fff0 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 13:32:39 +0200 Subject: Adding upstream version 2.14.2. Signed-off-by: Daniel Baumann --- lib/icingadb/icingadb-utility.cpp | 319 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 lib/icingadb/icingadb-utility.cpp (limited to 'lib/icingadb/icingadb-utility.cpp') diff --git a/lib/icingadb/icingadb-utility.cpp b/lib/icingadb/icingadb-utility.cpp new file mode 100644 index 0000000..b247ed8 --- /dev/null +++ b/lib/icingadb/icingadb-utility.cpp @@ -0,0 +1,319 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "icingadb/icingadb.hpp" +#include "base/configtype.hpp" +#include "base/object-packer.hpp" +#include "base/logger.hpp" +#include "base/serializer.hpp" +#include "base/tlsutility.hpp" +#include "base/initialize.hpp" +#include "base/objectlock.hpp" +#include "base/array.hpp" +#include "base/scriptglobal.hpp" +#include "base/convert.hpp" +#include "base/json.hpp" +#include "icinga/customvarobject.hpp" +#include "icinga/checkcommand.hpp" +#include "icinga/notificationcommand.hpp" +#include "icinga/eventcommand.hpp" +#include "icinga/host.hpp" +#include +#include +#include +#include + +using namespace icinga; + +String IcingaDB::FormatCheckSumBinary(const String& str) +{ + char output[20*2+1]; + for (int i = 0; i < 20; i++) + sprintf(output + 2 * i, "%02x", str[i]); + + return output; +} + +String IcingaDB::FormatCommandLine(const Value& commandLine) +{ + String result; + if (commandLine.IsObjectType()) { + Array::Ptr args = commandLine; + bool first = true; + + ObjectLock olock(args); + for (const Value& arg : args) { + String token = "'" + Convert::ToString(arg) + "'"; + + if (first) + first = false; + else + result += String(1, ' '); + + result += token; + } + } else if (!commandLine.IsEmpty()) { + result = commandLine; + boost::algorithm::replace_all(result, "\'", "\\'"); + result = "'" + result + "'"; + } + + return result; +} + +String IcingaDB::GetObjectIdentifier(const ConfigObject::Ptr& object) +{ + String identifier = object->GetIcingadbIdentifier(); + if (identifier.IsEmpty()) { + identifier = HashValue(new Array({m_EnvironmentId, object->GetName()})); + object->SetIcingadbIdentifier(identifier); + } + + return identifier; +} + +/** + * Calculates a deterministic history event ID like SHA1(env, eventType, x...[, nt][, eventTime]) + * + * Where SHA1(env, x...) = GetObjectIdentifier(object) + */ +String IcingaDB::CalcEventID(const char* eventType, const ConfigObject::Ptr& object, double eventTime, NotificationType nt) +{ + Array::Ptr rawId = new Array({object->GetName()}); + rawId->Insert(0, m_EnvironmentId); + rawId->Insert(1, eventType); + + if (nt) { + rawId->Add(GetNotificationTypeByEnum(nt)); + } + + if (eventTime) { + rawId->Add(TimestampToMilliseconds(eventTime)); + } + + return HashValue(std::move(rawId)); +} + +static const std::set metadataWhitelist ({"package", "source_location", "templates"}); + +/** + * Prepare custom vars for being written to Redis + * + * object.vars = { + * "disks": { + * "disk": {}, + * "disk /": { + * "disk_partitions": "/" + * } + * } + * } + * + * return { + * SHA1(PackObject([ + * EnvironmentId, + * "disks", + * { + * "disk": {}, + * "disk /": { + * "disk_partitions": "/" + * } + * } + * ])): { + * "environment_id": EnvironmentId, + * "name_checksum": SHA1("disks"), + * "name": "disks", + * "value": { + * "disk": {}, + * "disk /": { + * "disk_partitions": "/" + * } + * } + * } + * } + * + * @param Dictionary Config object with custom vars + * + * @return JSON-like data structure for Redis + */ +Dictionary::Ptr IcingaDB::SerializeVars(const Dictionary::Ptr& vars) +{ + if (!vars) + return nullptr; + + Dictionary::Ptr res = new Dictionary(); + + ObjectLock olock(vars); + + for (auto& kv : vars) { + res->Set( + SHA1(PackObject((Array::Ptr)new Array({m_EnvironmentId, kv.first, kv.second}))), + (Dictionary::Ptr)new Dictionary({ + {"environment_id", m_EnvironmentId}, + {"name_checksum", SHA1(kv.first)}, + {"name", kv.first}, + {"value", JsonEncode(kv.second)}, + }) + ); + } + + return res; +} + +const char* IcingaDB::GetNotificationTypeByEnum(NotificationType type) +{ + switch (type) { + case NotificationDowntimeStart: + return "downtime_start"; + case NotificationDowntimeEnd: + return "downtime_end"; + case NotificationDowntimeRemoved: + return "downtime_removed"; + case NotificationCustom: + return "custom"; + case NotificationAcknowledgement: + return "acknowledgement"; + case NotificationProblem: + return "problem"; + case NotificationRecovery: + return "recovery"; + case NotificationFlappingStart: + return "flapping_start"; + case NotificationFlappingEnd: + return "flapping_end"; + } + + VERIFY(!"Invalid notification type."); +} + +static const std::set propertiesBlacklistEmpty; + +String IcingaDB::HashValue(const Value& value) +{ + return HashValue(value, propertiesBlacklistEmpty); +} + +String IcingaDB::HashValue(const Value& value, const std::set& propertiesBlacklist, bool propertiesWhitelist) +{ + Value temp; + bool mutabl; + + Type::Ptr type = value.GetReflectionType(); + + if (ConfigObject::TypeInstance->IsAssignableFrom(type)) { + temp = Serialize(value, FAConfig); + mutabl = true; + } else { + temp = value; + mutabl = false; + } + + if (propertiesBlacklist.size() && temp.IsObject()) { + Dictionary::Ptr dict = dynamic_pointer_cast((Object::Ptr)temp); + + if (dict) { + if (!mutabl) + dict = dict->ShallowClone(); + + ObjectLock olock(dict); + + if (propertiesWhitelist) { + auto current = dict->Begin(); + auto propertiesBlacklistEnd = propertiesBlacklist.end(); + + while (current != dict->End()) { + if (propertiesBlacklist.find(current->first) == propertiesBlacklistEnd) { + dict->Remove(current++); + } else { + ++current; + } + } + } else { + for (auto& property : propertiesBlacklist) + dict->Remove(property); + } + + if (!mutabl) + temp = dict; + } + } + + return SHA1(PackObject(temp)); +} + +String IcingaDB::GetLowerCaseTypeNameDB(const ConfigObject::Ptr& obj) +{ + return obj->GetReflectionType()->GetName().ToLower(); +} + +long long IcingaDB::TimestampToMilliseconds(double timestamp) { + return static_cast(timestamp * 1000); +} + +String IcingaDB::IcingaToStreamValue(const Value& value) +{ + switch (value.GetType()) { + case ValueBoolean: + return Convert::ToString(int(value)); + case ValueString: + return Utility::ValidateUTF8(value); + case ValueNumber: + case ValueEmpty: + return Convert::ToString(value); + default: + return JsonEncode(value); + } +} + +// Returns the items that exist in "arrayOld" but not in "arrayNew" +std::vector IcingaDB::GetArrayDeletedValues(const Array::Ptr& arrayOld, const Array::Ptr& arrayNew) { + std::vector deletedValues; + + if (!arrayOld) { + return deletedValues; + } + + if (!arrayNew) { + ObjectLock olock (arrayOld); + return std::vector(arrayOld->Begin(), arrayOld->End()); + } + + std::vector vectorOld; + { + ObjectLock olock (arrayOld); + vectorOld.assign(arrayOld->Begin(), arrayOld->End()); + } + std::sort(vectorOld.begin(), vectorOld.end()); + vectorOld.erase(std::unique(vectorOld.begin(), vectorOld.end()), vectorOld.end()); + + std::vector vectorNew; + { + ObjectLock olock (arrayNew); + vectorNew.assign(arrayNew->Begin(), arrayNew->End()); + } + std::sort(vectorNew.begin(), vectorNew.end()); + vectorNew.erase(std::unique(vectorNew.begin(), vectorNew.end()), vectorNew.end()); + + std::set_difference(vectorOld.begin(), vectorOld.end(), vectorNew.begin(), vectorNew.end(), std::back_inserter(deletedValues)); + + return deletedValues; +} + +// Returns the keys that exist in "dictOld" but not in "dictNew" +std::vector IcingaDB::GetDictionaryDeletedKeys(const Dictionary::Ptr& dictOld, const Dictionary::Ptr& dictNew) { + std::vector deletedKeys; + + if (!dictOld) { + return deletedKeys; + } + + std::vector oldKeys = dictOld->GetKeys(); + + if (!dictNew) { + return oldKeys; + } + + std::vector newKeys = dictNew->GetKeys(); + + std::set_difference(oldKeys.begin(), oldKeys.end(), newKeys.begin(), newKeys.end(), std::back_inserter(deletedKeys)); + + return deletedKeys; +} -- cgit v1.2.3