From 6cd3a483a0414526a67f502f7615028cf8648e3d 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 525dd50859109d9347691ff98c58c2e00661fd16 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 000860617..cdf4405b3 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -607,7 +607,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)); @@ -630,7 +629,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}}); @@ -652,7 +651,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)); @@ -667,7 +666,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)); @@ -682,7 +681,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)); @@ -720,7 +719,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)); @@ -748,10 +747,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)); @@ -781,7 +780,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)); @@ -811,7 +810,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)); @@ -844,7 +843,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)); @@ -879,7 +878,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)); @@ -902,7 +901,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)); @@ -921,7 +920,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)); @@ -975,7 +974,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)); @@ -1024,7 +1023,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)); @@ -1214,11 +1213,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; @@ -1544,7 +1543,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 ({ @@ -1624,7 +1623,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); @@ -2031,7 +2030,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); } @@ -2112,7 +2111,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); } @@ -2158,7 +2157,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 4ade4c757b996ea8cbc0fcc30cc589a64c014744 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 60078481461d13d381457516e95844be18e2175d 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 3c8672b4dcf65a63d3501e77091c8c789a205596 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 cdf4405b3..a98d1dc80 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -127,7 +127,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();