From 1dcba7d1276793bb4038a0fe13d8a24915b2fc69 Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Tue, 21 Sep 2021 12:56:10 +0200 Subject: [PATCH 1/5] tlsutility: move hex encoding into a separate function BinaryToHex --- lib/base/tlsutility.cpp | 22 +++++++++++++--------- lib/base/tlsutility.hpp | 1 + 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/base/tlsutility.cpp b/lib/base/tlsutility.cpp index 02d677ee0..7032c7a3a 100644 --- a/lib/base/tlsutility.cpp +++ b/lib/base/tlsutility.cpp @@ -844,15 +844,7 @@ String SHA1(const String& s, bool binary) if (binary) return String(reinterpret_cast(digest), reinterpret_cast(digest + SHA_DIGEST_LENGTH)); - static const char hexdigits[] = "0123456789abcdef"; - char output[SHA_DIGEST_LENGTH*2+1]; - for (int i = 0; i < SHA_DIGEST_LENGTH; i++) { - output[2*i] = hexdigits[digest[i] >> 4]; - output[2*i + 1] = hexdigits[digest[i] & 0xf]; - } - output[2*SHA_DIGEST_LENGTH] = 0; - - return output; + return BinaryToHex(digest, SHA_DIGEST_LENGTH); } String SHA256(const String& s) @@ -930,6 +922,18 @@ String RandomString(int length) return result; } +String BinaryToHex(const unsigned char* data, size_t length) { + static const char hexdigits[] = "0123456789abcdef"; + + String output(2*length, 0); + for (int i = 0; i < SHA_DIGEST_LENGTH; i++) { + output[2 * i] = hexdigits[data[i] >> 4]; + output[2 * i + 1] = hexdigits[data[i] & 0xf]; + } + + return output; +} + bool VerifyCertificate(const std::shared_ptr &caCertificate, const std::shared_ptr &certificate, const String& crlFile) { X509_STORE *store = X509_STORE_new(); diff --git a/lib/base/tlsutility.hpp b/lib/base/tlsutility.hpp index 51bed97c1..c62f5cfad 100644 --- a/lib/base/tlsutility.hpp +++ b/lib/base/tlsutility.hpp @@ -61,6 +61,7 @@ String PBKDF2_SHA256(const String& password, const String& salt, int iterations) String SHA1(const String& s, bool binary = false); String SHA256(const String& s); String RandomString(int length); +String BinaryToHex(const unsigned char* data, size_t length); bool VerifyCertificate(const std::shared_ptr& caCertificate, const std::shared_ptr& certificate, const String& crlFile); bool IsCa(const std::shared_ptr& cacert); From 42fc3cdcfc9061abf81ed639dbb6ae22988ad51f Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Tue, 21 Sep 2021 12:56:20 +0200 Subject: [PATCH 2/5] IcingaDB: introduce a new environment ID derived from the CA public key In order to avoid changes to the environment ID, it is now no longer derived from the Environment constant but instead from the public key of the CA certificate. This ensures that it is different between clusters by default, so no additional changes have to be done to allow two clusters to use Icinga DB to write into the same database. To prevent the ID from changing when the CA certificate is replaced, it is also persisted into the file /var/lib/icinga2/icingadb.env, so if that file exists, it takes precedence over the CA certificate. --- lib/icingadb/icingadb-objects.cpp | 47 +++++++++++++++---------------- lib/icingadb/icingadb-utility.cpp | 19 ++++--------- lib/icingadb/icingadb.cpp | 43 +++++++++++++++++++++++++--- lib/icingadb/icingadb.hpp | 4 +-- 4 files changed, 69 insertions(+), 44 deletions(-) diff --git a/lib/icingadb/icingadb-objects.cpp b/lib/icingadb/icingadb-objects.cpp index a71974a1c..e488a55d9 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -612,7 +612,6 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S } CustomVarObject::Ptr customVarObject = dynamic_pointer_cast(object); - auto env (GetEnvironment()); if (customVarObject) { auto vars(SerializeVars(customVarObject)); @@ -635,7 +634,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S } } - String id = HashValue(new Array(Prepend(env, Prepend(kv.first, GetObjectIdentifiersWithoutEnv(object))))); + String id = HashValue(new Array(Prepend(m_EnvironmentId, Prepend(kv.first, GetObjectIdentifiersWithoutEnv(object))))); typeCvs.emplace_back(id); Dictionary::Ptr data = new Dictionary({{objectKeyName, objectKey}, {"environment_id", m_EnvironmentId}, {"customvar_id", kv.first}}); @@ -657,7 +656,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S if (!actionUrl.IsEmpty()) { auto& actionUrls (hMSets[m_PrefixConfigObject + "action:url"]); - auto id (HashValue(new Array({env, actionUrl}))); + auto id (HashValue(new Array({m_EnvironmentId, actionUrl}))); if (runtimeUpdate || m_DumpedGlobals.ActionUrl.IsNew(id)) { actionUrls.emplace_back(std::move(id)); @@ -672,7 +671,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S if (!notesUrl.IsEmpty()) { auto& notesUrls (hMSets[m_PrefixConfigObject + "notes:url"]); - auto id (HashValue(new Array({env, notesUrl}))); + auto id (HashValue(new Array({m_EnvironmentId, notesUrl}))); if (runtimeUpdate || m_DumpedGlobals.NotesUrl.IsNew(id)) { notesUrls.emplace_back(std::move(id)); @@ -687,7 +686,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S if (!iconImage.IsEmpty()) { auto& iconImages (hMSets[m_PrefixConfigObject + "icon:image"]); - auto id (HashValue(new Array({env, iconImage}))); + auto id (HashValue(new Array({m_EnvironmentId, iconImage}))); if (runtimeUpdate || m_DumpedGlobals.IconImage.IsNew(id)) { iconImages.emplace_back(std::move(id)); @@ -725,7 +724,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S for (auto& group : groups) { auto groupObj ((*getGroup)(group)); String groupId = GetObjectIdentifier(groupObj); - String id = HashValue(new Array(Prepend(env, Prepend(GetObjectIdentifiersWithoutEnv(groupObj), GetObjectIdentifiersWithoutEnv(object))))); + String id = HashValue(new Array(Prepend(m_EnvironmentId, Prepend(GetObjectIdentifiersWithoutEnv(groupObj), GetObjectIdentifiersWithoutEnv(object))))); members.emplace_back(id); Dictionary::Ptr data = new Dictionary({{objectKeyName, objectKey}, {"environment_id", m_EnvironmentId}, {typeName + "group_id", groupId}}); members.emplace_back(JsonEncode(data)); @@ -753,10 +752,10 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S rangeIds->Reserve(ranges->GetLength()); for (auto& kv : ranges) { - String rangeId = HashValue(new Array({env, kv.first, kv.second})); + String rangeId = HashValue(new Array({m_EnvironmentId, kv.first, kv.second})); rangeIds->Add(rangeId); - String id = HashValue(new Array(Prepend(env, Prepend(kv.first, Prepend(kv.second, GetObjectIdentifiersWithoutEnv(object)))))); + String id = HashValue(new Array(Prepend(m_EnvironmentId, Prepend(kv.first, Prepend(kv.second, GetObjectIdentifiersWithoutEnv(object)))))); typeRanges.emplace_back(id); Dictionary::Ptr data = new Dictionary({{"environment_id", m_EnvironmentId}, {"timeperiod_id", objectKey}, {"range_key", kv.first}, {"range_value", kv.second}}); typeRanges.emplace_back(JsonEncode(data)); @@ -786,7 +785,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S String includeId = GetObjectIdentifier(includeTp); includeChecksums->Add(includeId); - String id = HashValue(new Array(Prepend(env, Prepend(GetObjectIdentifiersWithoutEnv(includeTp), GetObjectIdentifiersWithoutEnv(object))))); + String id = HashValue(new Array(Prepend(m_EnvironmentId, Prepend(GetObjectIdentifiersWithoutEnv(includeTp), GetObjectIdentifiersWithoutEnv(object))))); includs.emplace_back(id); Dictionary::Ptr data = new Dictionary({{"environment_id", m_EnvironmentId}, {"timeperiod_id", objectKey}, {"include_id", includeId}}); includs.emplace_back(JsonEncode(data)); @@ -816,7 +815,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S String excludeId = GetObjectIdentifier(excludeTp); excludeChecksums->Add(excludeId); - String id = HashValue(new Array(Prepend(env, Prepend(GetObjectIdentifiersWithoutEnv(excludeTp), GetObjectIdentifiersWithoutEnv(object))))); + String id = HashValue(new Array(Prepend(m_EnvironmentId, Prepend(GetObjectIdentifiersWithoutEnv(excludeTp), GetObjectIdentifiersWithoutEnv(object))))); excluds.emplace_back(id); Dictionary::Ptr data = new Dictionary({{"environment_id", m_EnvironmentId}, {"timeperiod_id", objectKey}, {"exclude_id", excludeId}}); excluds.emplace_back(JsonEncode(data)); @@ -849,7 +848,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S for (auto& group : groups) { auto groupObj ((*getGroup)(group)); String groupId = GetObjectIdentifier(groupObj); - String id = HashValue(new Array(Prepend(env, Prepend(GetObjectIdentifiersWithoutEnv(groupObj), GetObjectIdentifiersWithoutEnv(object))))); + String id = HashValue(new Array(Prepend(m_EnvironmentId, Prepend(GetObjectIdentifiersWithoutEnv(groupObj), GetObjectIdentifiersWithoutEnv(object))))); members.emplace_back(id); Dictionary::Ptr data = new Dictionary({{"user_id", objectKey}, {"environment_id", m_EnvironmentId}, {"usergroup_id", groupId}}); members.emplace_back(JsonEncode(data)); @@ -884,7 +883,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S for (auto& user : users) { String userId = GetObjectIdentifier(user); - String id = HashValue(new Array(Prepend(env, Prepend(GetObjectIdentifiersWithoutEnv(user), GetObjectIdentifiersWithoutEnv(object))))); + String id = HashValue(new Array(Prepend(m_EnvironmentId, Prepend(GetObjectIdentifiersWithoutEnv(user), GetObjectIdentifiersWithoutEnv(object))))); usrs.emplace_back(id); Dictionary::Ptr data = new Dictionary({{"notification_id", objectKey}, {"environment_id", m_EnvironmentId}, {"user_id", userId}}); usrs.emplace_back(JsonEncode(data)); @@ -907,7 +906,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S auto groupMembers = usergroup->GetMembers(); std::copy(groupMembers.begin(), groupMembers.end(), std::inserter(allUsers, allUsers.begin())); - String id = HashValue(new Array(Prepend(env, Prepend("usergroup", Prepend(GetObjectIdentifiersWithoutEnv(usergroup), GetObjectIdentifiersWithoutEnv(object)))))); + String id = HashValue(new Array(Prepend(m_EnvironmentId, Prepend("usergroup", Prepend(GetObjectIdentifiersWithoutEnv(usergroup), GetObjectIdentifiersWithoutEnv(object)))))); groups.emplace_back(id); Dictionary::Ptr groupData = new Dictionary({{"notification_id", objectKey}, {"environment_id", m_EnvironmentId}, {"usergroup_id", usergroupId}}); groups.emplace_back(JsonEncode(groupData)); @@ -926,7 +925,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S for (auto& user : allUsers) { String userId = GetObjectIdentifier(user); - String id = HashValue(new Array(Prepend(env, Prepend("user", Prepend(GetObjectIdentifiersWithoutEnv(user), GetObjectIdentifiersWithoutEnv(object)))))); + String id = HashValue(new Array(Prepend(m_EnvironmentId, Prepend("user", Prepend(GetObjectIdentifiersWithoutEnv(user), GetObjectIdentifiersWithoutEnv(object)))))); notificationRecipients.emplace_back(id); Dictionary::Ptr data = new Dictionary({{"notification_id", objectKey}, {"environment_id", m_EnvironmentId}, {"user_id", userId}}); notificationRecipients.emplace_back(JsonEncode(data)); @@ -980,7 +979,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S values->Set("argument_key", kv.first); values->Set("environment_id", m_EnvironmentId); - String id = HashValue(new Array(Prepend(env, Prepend(kv.first, GetObjectIdentifiersWithoutEnv(object))))); + String id = HashValue(new Array(Prepend(m_EnvironmentId, Prepend(kv.first, GetObjectIdentifiersWithoutEnv(object))))); typeArgs.emplace_back(id); typeArgs.emplace_back(JsonEncode(values)); @@ -1029,7 +1028,7 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S values->Set("envvar_key", kv.first); values->Set("environment_id", m_EnvironmentId); - String id = HashValue(new Array(Prepend(env, Prepend(kv.first, GetObjectIdentifiersWithoutEnv(object))))); + String id = HashValue(new Array(Prepend(m_EnvironmentId, Prepend(kv.first, GetObjectIdentifiersWithoutEnv(object))))); typeVars.emplace_back(id); typeVars.emplace_back(JsonEncode(values)); @@ -1219,11 +1218,11 @@ bool IcingaDB::PrepareObject(const ConfigObject::Ptr& object, Dictionary::Ptr& a String notesUrl = checkable->GetNotesUrl(); String iconImage = checkable->GetIconImage(); if (!actionUrl.IsEmpty()) - attributes->Set("action_url_id", HashValue(new Array({GetEnvironment(), actionUrl}))); + attributes->Set("action_url_id", HashValue(new Array({m_EnvironmentId, actionUrl}))); if (!notesUrl.IsEmpty()) - attributes->Set("notes_url_id", HashValue(new Array({GetEnvironment(), notesUrl}))); + attributes->Set("notes_url_id", HashValue(new Array({m_EnvironmentId, notesUrl}))); if (!iconImage.IsEmpty()) - attributes->Set("icon_image_id", HashValue(new Array({GetEnvironment(), iconImage}))); + attributes->Set("icon_image_id", HashValue(new Array({m_EnvironmentId, iconImage}))); Host::Ptr host; @@ -1556,7 +1555,7 @@ void IcingaDB::SendStateChange(const ConfigObject::Ptr& object, const CheckResul auto eventTime (cr->GetExecutionEnd()); auto eventTs (TimestampToMilliseconds(eventTime)); - Array::Ptr rawId = new Array(Prepend(GetEnvironment(), GetObjectIdentifiersWithoutEnv(object))); + Array::Ptr rawId = new Array(Prepend(m_EnvironmentId, GetObjectIdentifiersWithoutEnv(object))); rawId->Add(eventTs); std::vector xAdd ({ @@ -1636,7 +1635,7 @@ void IcingaDB::SendSentNotification( auto usersAmount (users.size()); auto sendTs (TimestampToMilliseconds(sendTime)); - Array::Ptr rawId = new Array(Prepend(GetEnvironment(), GetObjectIdentifiersWithoutEnv(notification))); + Array::Ptr rawId = new Array(Prepend(m_EnvironmentId, GetObjectIdentifiersWithoutEnv(notification))); rawId->Add(GetNotificationTypeByEnum(type)); rawId->Add(sendTs); @@ -2043,7 +2042,7 @@ void IcingaDB::SendFlappingChange(const Checkable::Ptr& checkable, double change xAdd.emplace_back("event_id"); xAdd.emplace_back(CalcEventID(checkable->IsFlapping() ? "flapping_start" : "flapping_end", checkable, startTime)); xAdd.emplace_back("id"); - xAdd.emplace_back(HashValue(new Array({GetEnvironment(), checkable->GetName(), startTime}))); + xAdd.emplace_back(HashValue(new Array({m_EnvironmentId, checkable->GetName(), startTime}))); m_Rcon->FireAndForgetQuery(std::move(xAdd), Prio::History); } @@ -2124,7 +2123,7 @@ void IcingaDB::SendAcknowledgementSet(const Checkable::Ptr& checkable, const Str xAdd.emplace_back("event_id"); xAdd.emplace_back(CalcEventID("ack_set", checkable, setTime)); xAdd.emplace_back("id"); - xAdd.emplace_back(HashValue(new Array({GetEnvironment(), checkable->GetName(), setTime}))); + xAdd.emplace_back(HashValue(new Array({m_EnvironmentId, checkable->GetName(), setTime}))); m_Rcon->FireAndForgetQuery(std::move(xAdd), Prio::History); } @@ -2170,7 +2169,7 @@ void IcingaDB::SendAcknowledgementCleared(const Checkable::Ptr& checkable, const xAdd.emplace_back("event_id"); xAdd.emplace_back(CalcEventID("ack_clear", checkable, setTime)); xAdd.emplace_back("id"); - xAdd.emplace_back(HashValue(new Array({GetEnvironment(), checkable->GetName(), setTime}))); + xAdd.emplace_back(HashValue(new Array({m_EnvironmentId, checkable->GetName(), setTime}))); if (!removedBy.IsEmpty()) { xAdd.emplace_back("cleared_by"); diff --git a/lib/icingadb/icingadb-utility.cpp b/lib/icingadb/icingadb-utility.cpp index 3b1713cde..769b4b5aa 100644 --- a/lib/icingadb/icingadb-utility.cpp +++ b/lib/icingadb/icingadb-utility.cpp @@ -60,11 +60,6 @@ String IcingaDB::FormatCommandLine(const Value& commandLine) return result; } -String IcingaDB::GetEnvironment() -{ - return ConfigType::GetObjectsByType()[0]->GetEnvironment(); -} - ArrayData IcingaDB::GetObjectIdentifiersWithoutEnv(const ConfigObject::Ptr& object) { Type::Ptr type = object->GetReflectionType(); @@ -77,7 +72,7 @@ ArrayData IcingaDB::GetObjectIdentifiersWithoutEnv(const ConfigObject::Ptr& obje String IcingaDB::GetObjectIdentifier(const ConfigObject::Ptr& object) { - return HashValue(new Array(Prepend(GetEnvironment(), GetObjectIdentifiersWithoutEnv(object)))); + return HashValue(new Array(Prepend(m_EnvironmentId, GetObjectIdentifiersWithoutEnv(object)))); } /** @@ -88,7 +83,7 @@ String IcingaDB::GetObjectIdentifier(const ConfigObject::Ptr& object) String IcingaDB::CalcEventID(const char* eventType, const ConfigObject::Ptr& object, double eventTime, NotificationType nt) { Array::Ptr rawId = new Array(GetObjectIdentifiersWithoutEnv(object)); - rawId->Insert(0, GetEnvironment()); + rawId->Insert(0, m_EnvironmentId); rawId->Insert(1, eventType); if (nt) { @@ -118,7 +113,7 @@ static const std::set metadataWhitelist ({"package", "source_location", * * return { * SHA1(PackObject([ - * Environment, + * EnvironmentId, * "disks", * { * "disk": {}, @@ -127,7 +122,7 @@ static const std::set metadataWhitelist ({"package", "source_location", * } * } * ])): { - * "envId": SHA1(Environment), + * "environment_id": EnvironmentId, * "name_checksum": SHA1("disks"), * "name": "disks", * "value": { @@ -151,16 +146,14 @@ Dictionary::Ptr IcingaDB::SerializeVars(const CustomVarObject::Ptr& object) return nullptr; Dictionary::Ptr res = new Dictionary(); - auto env (GetEnvironment()); - auto envChecksum (SHA1(env)); ObjectLock olock(vars); for (auto& kv : vars) { res->Set( - SHA1(PackObject((Array::Ptr)new Array({env, kv.first, kv.second}))), + SHA1(PackObject((Array::Ptr)new Array({m_EnvironmentId, kv.first, kv.second}))), (Dictionary::Ptr)new Dictionary({ - {"environment_id", envChecksum}, + {"environment_id", m_EnvironmentId}, {"name_checksum", SHA1(kv.first)}, {"name", kv.first}, {"value", JsonEncode(kv.second)}, diff --git a/lib/icingadb/icingadb.cpp b/lib/icingadb/icingadb.cpp index dec804f3d..7b5fe251b 100644 --- a/lib/icingadb/icingadb.cpp +++ b/lib/icingadb/icingadb.cpp @@ -3,11 +3,16 @@ #include "icingadb/icingadb.hpp" #include "icingadb/icingadb-ti.cpp" #include "icingadb/redisconnection.hpp" +#include "remote/apilistener.hpp" #include "remote/eventqueue.hpp" +#include "base/configuration.hpp" #include "base/json.hpp" +#include "base/tlsutility.hpp" +#include "base/utility.hpp" #include "icinga/checkable.hpp" #include "icinga/host.hpp" #include +#include #include #include @@ -18,7 +23,7 @@ using namespace icinga; using Prio = RedisConnection::QueryPriority; String IcingaDB::m_EnvironmentId; -boost::once_flag IcingaDB::m_EnvironmentIdOnce = BOOST_ONCE_INIT; +std::once_flag IcingaDB::m_EnvironmentIdOnce; REGISTER_TYPE(IcingaDB); @@ -52,9 +57,39 @@ void IcingaDB::Start(bool runtimeCreated) { ObjectImpl::Start(runtimeCreated); - boost::call_once([]() { - m_EnvironmentId = SHA1(GetEnvironment()); - }, m_EnvironmentIdOnce); + std::call_once(m_EnvironmentIdOnce, []() { + String path = Configuration::DataDir + "/icingadb.env"; + + if (Utility::PathExists(path)) { + m_EnvironmentId = Utility::LoadJsonFile(path); + + if (m_EnvironmentId.GetLength() != 2*SHA_DIGEST_LENGTH) { + throw std::runtime_error("Wrong length of stored Icinga DB environment"); + } + + for (unsigned char c : m_EnvironmentId) { + if (!std::isxdigit(c)) { + throw std::runtime_error("Stored Icinga DB environment is not a hex string"); + } + } + } else { + std::shared_ptr cert = GetX509Certificate(ApiListener::GetDefaultCaPath()); + + unsigned int n; + unsigned char digest[EVP_MAX_MD_SIZE]; + if (X509_pubkey_digest(cert.get(), EVP_sha1(), digest, &n) != 1) { + BOOST_THROW_EXCEPTION(openssl_error() + << boost::errinfo_api_function("X509_pubkey_digest") + << errinfo_openssl_error(ERR_peek_error())); + } + + m_EnvironmentId = BinaryToHex(digest, n); + + Utility::SaveJsonFile(path, 0600, m_EnvironmentId); + } + + m_EnvironmentId = m_EnvironmentId.ToLower(); + }); Log(LogInformation, "IcingaDB") << "'" << GetName() << "' started."; diff --git a/lib/icingadb/icingadb.hpp b/lib/icingadb/icingadb.hpp index eef4267bf..4bd6e6ad2 100644 --- a/lib/icingadb/icingadb.hpp +++ b/lib/icingadb/icingadb.hpp @@ -12,7 +12,6 @@ #include "icinga/service.hpp" #include "icinga/downtime.hpp" #include "remote/messageorigin.hpp" -#include #include #include #include @@ -108,7 +107,6 @@ private: static ArrayData GetObjectIdentifiersWithoutEnv(const ConfigObject::Ptr& object); static String GetObjectIdentifier(const ConfigObject::Ptr& object); static String CalcEventID(const char* eventType, const ConfigObject::Ptr& object, double eventTime = 0, NotificationType nt = NotificationType(0)); - static String GetEnvironment(); static Dictionary::Ptr SerializeVars(const CustomVarObject::Ptr& object); static const char* GetNotificationTypeByEnum(NotificationType type); @@ -180,7 +178,7 @@ private: } m_DumpedGlobals; static String m_EnvironmentId; - static boost::once_flag m_EnvironmentIdOnce; + static std::once_flag m_EnvironmentIdOnce; }; } From b98b951f85aef1e2c6114101280ae0674b51266a Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Wed, 13 Oct 2021 12:40:56 +0200 Subject: [PATCH 3/5] IcingaDB: write new environment to icinga:stats stream --- lib/icingadb/icingadb.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/icingadb/icingadb.cpp b/lib/icingadb/icingadb.cpp index 7b5fe251b..25bd83665 100644 --- a/lib/icingadb/icingadb.cpp +++ b/lib/icingadb/icingadb.cpp @@ -186,6 +186,7 @@ void IcingaDB::PublishStats() Dictionary::Ptr status = GetStats(); status->Set("config_dump_in_progress", m_ConfigDumpInProgress); status->Set("timestamp", TimestampToMilliseconds(Utility::GetTime())); + status->Set("icingadb_environment", m_EnvironmentId); std::vector query {"XADD", "icinga:stats", "MAXLEN", "1", "*"}; From d3ec8a9ba86d2824a21650fc219be77bef936f3c Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Thu, 14 Oct 2021 12:00:59 +0200 Subject: [PATCH 4/5] IcingaDB: export environment_id via API Primarily required for Icinga DB integration tests at the moment, but could also be helpful in other situations. --- lib/icingadb/icingadb.cpp | 4 ++++ lib/icingadb/icingadb.hpp | 2 ++ lib/icingadb/icingadb.ti | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/lib/icingadb/icingadb.cpp b/lib/icingadb/icingadb.cpp index 25bd83665..d5f725b6f 100644 --- a/lib/icingadb/icingadb.cpp +++ b/lib/icingadb/icingadb.cpp @@ -240,6 +240,10 @@ void IcingaDB::DumpedGlobals::Reset() m_Ids.clear(); } +String IcingaDB::GetEnvironmentId() const { + return m_EnvironmentId; +} + bool IcingaDB::DumpedGlobals::IsNew(const String& id) { std::lock_guard l (m_Mutex); diff --git a/lib/icingadb/icingadb.hpp b/lib/icingadb/icingadb.hpp index 4bd6e6ad2..fba1a2857 100644 --- a/lib/icingadb/icingadb.hpp +++ b/lib/icingadb/icingadb.hpp @@ -39,6 +39,8 @@ public: virtual void Start(bool runtimeCreated) override; virtual void Stop(bool runtimeRemoved) override; + String GetEnvironmentId() const override; + protected: void ValidateTlsProtocolmin(const Lazy& lvalue, const ValidationUtils& utils) override; void ValidateConnectTimeout(const Lazy& lvalue, const ValidationUtils& utils) override; diff --git a/lib/icingadb/icingadb.ti b/lib/icingadb/icingadb.ti index 3e020282a..00ca95691 100644 --- a/lib/icingadb/icingadb.ti +++ b/lib/icingadb/icingadb.ti @@ -44,6 +44,10 @@ class IcingaDB : ConfigObject [config] double connect_timeout { default {{{ return DEFAULT_CONNECT_TIMEOUT; }}} }; + + [no_storage] String environment_id { + get; + }; }; } From 9a5ba28e0cfdb4d095881e06dd1341644489f651 Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Wed, 3 Nov 2021 14:44:54 +0100 Subject: [PATCH 5/5] Icinga DB: increase Redis schema version PR #9036 introduces some incompatible changes to the Redis schema, most importantly where Icinga DB has to read the environment from: now it has to use a new top-level key of the icinga:stats message instead of a value in the IcingaApplication part of that message. --- 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 e488a55d9..622281b0d 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -132,7 +132,7 @@ void IcingaDB::ConfigStaticInitialize() void IcingaDB::UpdateAllConfigObjects() { m_Rcon->Sync(); - m_Rcon->FireAndForgetQuery({"XADD", "icinga:schema", "MAXLEN", "1", "*", "version", "2"}, Prio::Heartbeat); + m_Rcon->FireAndForgetQuery({"XADD", "icinga:schema", "MAXLEN", "1", "*", "version", "3"}, Prio::Heartbeat); Log(LogInformation, "IcingaDB") << "Starting initial config/status dump"; double startTime = Utility::GetTime();