[pkg-nagios-changes] [Git][nagios-team/pkg-icinga2][upstream] New upstream version 2.13.2
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Sat Nov 13 07:04:40 GMT 2021
Bas Couwenberg pushed to branch upstream at Debian Nagios Maintainer Group / pkg-icinga2
Commits:
ed7c657a by Bas Couwenberg at 2021-11-13T07:03:21+01:00
New upstream version 2.13.2
- - - - -
28 changed files:
- CHANGELOG.md
- ICINGA2_VERSION
- doc/02-installation.md
- lib/base/dictionary.cpp
- lib/base/tlsutility.cpp
- lib/base/tlsutility.hpp
- lib/icinga/command.ti
- lib/icinga/customvarobject.ti
- lib/icinga/host.ti
- lib/icinga/notification.cpp
- lib/icinga/notification.ti
- lib/icinga/service.ti
- lib/icinga/timeperiod.ti
- lib/icinga/user.ti
- lib/icinga/usergroup.cpp
- lib/icinga/usergroup.hpp
- lib/icingadb/icingadb-objects.cpp
- lib/icingadb/icingadb-stats.cpp
- lib/icingadb/icingadb-utility.cpp
- lib/icingadb/icingadb.cpp
- lib/icingadb/icingadb.hpp
- lib/icingadb/icingadb.ti
- lib/icingadb/redisconnection.cpp
- test/CMakeLists.txt
- test/base-dictionary.cpp
- tools/mkclass/class_lexer.ll
- tools/mkclass/classcompiler.cpp
- tools/mkclass/classcompiler.hpp
Changes:
=====================================
CHANGELOG.md
=====================================
@@ -7,6 +7,32 @@ documentation before upgrading to a new release.
Released closed milestones can be found on [GitHub](https://github.com/Icinga/icinga2/milestones?state=closed).
+## 2.13.2 (2021-11-12)
+
+This version only includes changes needed for the release of Icinga DB 1.0.0 RC2 and doesn't include any other bugfixes or features.
+
+### Icinga DB
+
+* Prefix command_id with command type #9085
+* Decouple environment from Icinga 2 Environment constant #9082
+* Make icinga:history:stream:*#event_id deterministic #9076
+* Add downtime.duration & service_state.host_id to Redis #9084
+* Sync checkables along with their states first #9081
+* Flush both buffered states and state checksums on initial dump #9079
+* Introduce icinga:history:stream:downtime#scheduled_by #9080
+* Actually write parent to parent_id of zones #9078
+* Set value in milliseconds for program_start in stats/heartbeat #9077
+* Clean up vanished objects from icinga:checksum:*:state #9074
+* Remove usernotification history stream #9073
+* Write IDs of notified users into notification history stream #9071
+* Make CheckResult#scheduling_source available to Icinga DB #9072
+* Stream runtime state updates only to icinga:runtime:state #9068
+* Publish Redis schema version via XADD icinga:schema #9069
+* Don't include checkable types in history IDs #9070
+* Remove unused Redis key 'icinga:zone:parent' #9075
+* Make sure object relationships are handled correctly during runtime updates #9089
+* Only log queries at debug level #9088
+
## 2.13.1 (2021-08-19)
The main focus of this version is a security vulnerability in the TLS certificate verification of our metrics writers ElasticsearchWriter, GelfWriter, InfluxdbWriter and Influxdb2Writer.
@@ -500,6 +526,19 @@ Thanks to all contributors:
* Code quality fixes
* Small documentation fixes
+## 2.11.11 (2021-08-19)
+
+The main focus of these versions is a security vulnerability in the TLS certificate verification of our metrics writers ElasticsearchWriter, GelfWriter and InfluxdbWriter.
+
+### Security
+
+* Add TLS server certificate validation to ElasticsearchWriter, GelfWriter and InfluxdbWriter
+
+Depending on your setup, manual intervention beyond installing the new versions
+may be required, so please read the more detailed information in the
+[release blog post](https://icinga.com/blog/2021/08/19/icinga-2-13-1-security-release//)
+carefully
+
## 2.11.10 (2021-07-15)
Version 2.11.10 fixes two security vulnerabilities that may lead to privilege
=====================================
ICINGA2_VERSION
=====================================
@@ -1,2 +1,2 @@
-Version: 2.13.1
+Version: 2.13.2
Revision: 1
=====================================
doc/02-installation.md
=====================================
@@ -151,7 +151,7 @@ CentOS 8 additionally needs the PowerTools repository for EPEL:
```bash
dnf install 'dnf-command(config-manager)'
-dnf config-manager --set-enabled PowerTools
+dnf config-manager --set-enabled powertools
dnf install epel-release
```
=====================================
lib/base/dictionary.cpp
=====================================
@@ -235,10 +235,10 @@ Object::Ptr Dictionary::Clone() const
}
/**
- * Returns an array containing all keys
+ * Returns an ordered vector containing all keys
* which are currently set in this directory.
*
- * @returns an array of key names
+ * @returns an ordered vector of key names
*/
std::vector<String> Dictionary::GetKeys() const
{
=====================================
lib/base/tlsutility.cpp
=====================================
@@ -844,15 +844,7 @@ String SHA1(const String& s, bool binary)
if (binary)
return String(reinterpret_cast<const char*>(digest), reinterpret_cast<const char *>(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<X509> &caCertificate, const std::shared_ptr<X509> &certificate, const String& crlFile)
{
X509_STORE *store = X509_STORE_new();
=====================================
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<X509>& caCertificate, const std::shared_ptr<X509>& certificate, const String& crlFile);
bool IsCa(const std::shared_ptr<X509>& cacert);
=====================================
lib/icinga/command.ti
=====================================
@@ -11,11 +11,11 @@ namespace icinga
abstract class Command : CustomVarObject
{
[config] Value command (CommandLine);
- [config] Value arguments;
+ [config, signal_with_old_value] Value arguments;
[config] int timeout {
default {{{ return 60; }}}
};
- [config] Dictionary::Ptr env;
+ [config, signal_with_old_value] Dictionary::Ptr env;
[config, required] Function::Ptr execute;
};
=====================================
lib/icinga/customvarobject.ti
=====================================
@@ -9,7 +9,7 @@ namespace icinga
abstract class CustomVarObject : ConfigObject
{
- [config] Dictionary::Ptr vars;
+ [config, signal_with_old_value] Dictionary::Ptr vars;
};
}
=====================================
lib/icinga/host.ti
=====================================
@@ -15,7 +15,7 @@ class Host : Checkable
load_after Endpoint;
load_after Zone;
- [config, no_user_modify, required] array(name(HostGroup)) groups {
+ [config, no_user_modify, required, signal_with_old_value] array(name(HostGroup)) groups {
default {{{ return new Array(); }}}
};
=====================================
lib/icinga/notification.cpp
=====================================
@@ -133,6 +133,9 @@ void Notification::Start(bool runtimeCreated)
if (ApiListener::IsHACluster() && GetNextNotification() < Utility::GetTime() + 60)
SetNextNotification(Utility::GetTime() + 60, true);
+ for (const UserGroup::Ptr& group : GetUserGroups())
+ group->AddNotification(this);
+
ObjectImpl<Notification>::Start(runtimeCreated);
}
@@ -144,6 +147,9 @@ void Notification::Stop(bool runtimeRemoved)
if (obj)
obj->UnregisterNotification(this);
+
+ for (const UserGroup::Ptr& group : GetUserGroups())
+ group->RemoveNotification(this);
}
Checkable::Ptr Notification::GetCheckable() const
=====================================
lib/icinga/notification.ti
=====================================
@@ -36,8 +36,8 @@ class Notification : CustomVarObject < NotificationNameComposer
return TimePeriod::GetByName(GetPeriodRaw());
}}}
};
- [config, protected] array(name(User)) users (UsersRaw);
- [config, protected] array(name(UserGroup)) user_groups (UserGroupsRaw);
+ [config, signal_with_old_value] array(name(User)) users (UsersRaw);
+ [config, signal_with_old_value] array(name(UserGroup)) user_groups (UserGroupsRaw);
[config] Dictionary::Ptr times;
[config] array(Value) types;
[no_user_view, no_user_modify] int type_filter_real (TypeFilter);
=====================================
lib/icinga/service.ti
=====================================
@@ -27,7 +27,7 @@ class Service : Checkable < ServiceNameComposer
load_after Host;
load_after Zone;
- [config, no_user_modify, required] array(name(ServiceGroup)) groups {
+ [config, no_user_modify, required, signal_with_old_value] array(name(ServiceGroup)) groups {
default {{{ return new Array(); }}}
};
=====================================
lib/icinga/timeperiod.ti
=====================================
@@ -18,15 +18,15 @@ class TimePeriod : CustomVarObject
return m_DisplayName.m_Value;
}}}
};
- [config] Dictionary::Ptr ranges;
+ [config, signal_with_old_value] Dictionary::Ptr ranges;
[config, required] Function::Ptr update;
[config] bool prefer_includes {
default {{{ return true; }}}
};
- [config, required] array(name(TimePeriod)) excludes {
+ [config, required, signal_with_old_value] array(name(TimePeriod)) excludes {
default {{{ return new Array(); }}}
};
- [config, required] array(name(TimePeriod)) includes {
+ [config, required, signal_with_old_value] array(name(TimePeriod)) includes {
default {{{ return new Array(); }}}
};
[state, no_user_modify] Value valid_begin;
=====================================
lib/icinga/user.ti
=====================================
@@ -19,7 +19,7 @@ class User : CustomVarObject
return m_DisplayName.m_Value;
}}}
};
- [config, no_user_modify, required] array(name(UserGroup)) groups {
+ [config, no_user_modify, required, signal_with_old_value] array(name(UserGroup)) groups {
default {{{ return new Array(); }}}
};
[config, navigation] name(TimePeriod) period (PeriodRaw) {
=====================================
lib/icinga/usergroup.cpp
=====================================
@@ -76,6 +76,24 @@ void UserGroup::RemoveMember(const User::Ptr& user)
m_Members.erase(user);
}
+std::set<Notification::Ptr> UserGroup::GetNotifications() const
+{
+ std::unique_lock<std::mutex> lock(m_UserGroupMutex);
+ return m_Notifications;
+}
+
+void UserGroup::AddNotification(const Notification::Ptr& notification)
+{
+ std::unique_lock<std::mutex> lock(m_UserGroupMutex);
+ m_Notifications.insert(notification);
+}
+
+void UserGroup::RemoveNotification(const Notification::Ptr& notification)
+{
+ std::unique_lock<std::mutex> lock(m_UserGroupMutex);
+ m_Notifications.erase(notification);
+}
+
bool UserGroup::ResolveGroupMembership(const User::Ptr& user, bool add, int rstack) {
if (add && rstack > 20) {
=====================================
lib/icinga/usergroup.hpp
=====================================
@@ -11,6 +11,7 @@ namespace icinga
{
class ConfigItem;
+class Notification;
/**
* An Icinga user group.
@@ -27,6 +28,10 @@ public:
void AddMember(const User::Ptr& user);
void RemoveMember(const User::Ptr& user);
+ std::set<intrusive_ptr<Notification>> GetNotifications() const;
+ void AddNotification(const intrusive_ptr<Notification>& notification);
+ void RemoveNotification(const intrusive_ptr<Notification>& notification);
+
bool ResolveGroupMembership(const User::Ptr& user, bool add = true, int rstack = 0);
static void EvaluateObjectRules(const User::Ptr& user);
@@ -34,6 +39,7 @@ public:
private:
mutable std::mutex m_UserGroupMutex;
std::set<User::Ptr> m_Members;
+ std::set<intrusive_ptr<Notification>> m_Notifications;
static bool EvaluateObjectRule(const User::Ptr& user, const intrusive_ptr<ConfigItem>& group);
};
=====================================
lib/icingadb/icingadb-objects.cpp
=====================================
@@ -15,6 +15,7 @@
#include "base/array.hpp"
#include "base/exception.hpp"
#include "base/utility.hpp"
+#include "base/object-packer.hpp"
#include "icinga/command.hpp"
#include "icinga/compatutility.hpp"
#include "icinga/customvarobject.hpp"
@@ -35,6 +36,7 @@
#include <mutex>
#include <set>
#include <utility>
+#include <type_traits>
using namespace icinga;
@@ -44,18 +46,23 @@ INITIALIZE_ONCE(&IcingaDB::ConfigStaticInitialize);
std::vector<Type::Ptr> IcingaDB::GetTypes()
{
+ // The initial config sync will queue the types in the following order.
return {
- CheckCommand::TypeInstance,
- Comment::TypeInstance,
+ // Sync them first to get their states ASAP.
+ Host::TypeInstance,
+ Service::TypeInstance,
+
+ // Then sync them for similar reasons.
Downtime::TypeInstance,
+ Comment::TypeInstance,
+
+ HostGroup::TypeInstance,
+ ServiceGroup::TypeInstance,
+ CheckCommand::TypeInstance,
Endpoint::TypeInstance,
EventCommand::TypeInstance,
- Host::TypeInstance,
- HostGroup::TypeInstance,
Notification::TypeInstance,
NotificationCommand::TypeInstance,
- Service::TypeInstance,
- ServiceGroup::TypeInstance,
TimePeriod::TypeInstance,
User::TypeInstance,
UserGroup::TypeInstance,
@@ -122,10 +129,47 @@ void IcingaDB::ConfigStaticInitialize()
Service::OnHostProblemChanged.connect([](const Service::Ptr& service, const CheckResult::Ptr&, const MessageOrigin::Ptr&) {
IcingaDB::StateChangeHandler(service);
});
+
+ Notification::OnUsersRawChangedWithOldValue.connect([](const Notification::Ptr& notification, const Value& oldValues, const Value& newValues) {
+ IcingaDB::NotificationUsersChangedHandler(notification, oldValues, newValues);
+ });
+ Notification::OnUserGroupsRawChangedWithOldValue.connect([](const Notification::Ptr& notification, const Value& oldValues, const Value& newValues) {
+ IcingaDB::NotificationUserGroupsChangedHandler(notification, oldValues, newValues);
+ });
+ TimePeriod::OnRangesChangedWithOldValue.connect([](const TimePeriod::Ptr& timeperiod, const Value& oldValues, const Value& newValues) {
+ IcingaDB::TimePeriodRangesChangedHandler(timeperiod, oldValues, newValues);
+ });
+ TimePeriod::OnIncludesChangedWithOldValue.connect([](const TimePeriod::Ptr& timeperiod, const Value& oldValues, const Value& newValues) {
+ IcingaDB::TimePeriodIncludesChangedHandler(timeperiod, oldValues, newValues);
+ });
+ TimePeriod::OnExcludesChangedWithOldValue.connect([](const TimePeriod::Ptr& timeperiod, const Value& oldValues, const Value& newValues) {
+ IcingaDB::TimePeriodExcludesChangedHandler(timeperiod, oldValues, newValues);
+ });
+ User::OnGroupsChangedWithOldValue.connect([](const User::Ptr& user, const Value& oldValues, const Value& newValues) {
+ IcingaDB::UserGroupsChangedHandler(user, oldValues, newValues);
+ });
+ Host::OnGroupsChangedWithOldValue.connect([](const Host::Ptr& host, const Value& oldValues, const Value& newValues) {
+ IcingaDB::HostGroupsChangedHandler(host, oldValues, newValues);
+ });
+ Service::OnGroupsChangedWithOldValue.connect([](const Service::Ptr& service, const Value& oldValues, const Value& newValues) {
+ IcingaDB::ServiceGroupsChangedHandler(service, oldValues, newValues);
+ });
+ Command::OnEnvChangedWithOldValue.connect([](const ConfigObject::Ptr& command, const Value& oldValues, const Value& newValues) {
+ IcingaDB::CommandEnvChangedHandler(command, oldValues, newValues);
+ });
+ Command::OnArgumentsChangedWithOldValue.connect([](const ConfigObject::Ptr& command, const Value& oldValues, const Value& newValues) {
+ IcingaDB::CommandArgumentsChangedHandler(command, oldValues, newValues);
+ });
+ CustomVarObject::OnVarsChangedWithOldValue.connect([](const ConfigObject::Ptr& object, const Value& oldValues, const Value& newValues) {
+ IcingaDB::CustomVarsChangedHandler(object, oldValues, newValues);
+ });
}
void IcingaDB::UpdateAllConfigObjects()
{
+ m_Rcon->Sync();
+ m_Rcon->FireAndForgetQuery({"XADD", "icinga:schema", "MAXLEN", "1", "*", "version", "4"}, Prio::Heartbeat);
+
Log(LogInformation, "IcingaDB") << "Starting initial config/status dump";
double startTime = Utility::GetTime();
@@ -310,8 +354,10 @@ void IcingaDB::UpdateAllConfigObjects()
}
}
- if (states.size() > 2)
+ if (states.size() > 2) {
transaction.emplace_back(std::move(states));
+ transaction.emplace_back(std::move(statesChksms));
+ }
if (transaction.size() > 1) {
transaction.push_back({"EXEC"});
@@ -533,12 +579,11 @@ std::vector<String> IcingaDB::GetTypeOverwriteKeys(const String& type)
if (type == "host" || type == "service" || type == "user") {
keys.emplace_back(m_PrefixConfigObject + type + "group:member");
keys.emplace_back(m_PrefixConfigObject + type + ":state");
+ keys.emplace_back(m_PrefixConfigCheckSum + type + ":state");
} else if (type == "timeperiod") {
keys.emplace_back(m_PrefixConfigObject + type + ":override:include");
keys.emplace_back(m_PrefixConfigObject + type + ":override:exclude");
keys.emplace_back(m_PrefixConfigObject + type + ":range");
- } else if (type == "zone") {
- keys.emplace_back(m_PrefixConfigObject + type + ":parent");
} else if (type == "notification") {
keys.emplace_back(m_PrefixConfigObject + type + ":user");
keys.emplace_back(m_PrefixConfigObject + type + ":usergroup");
@@ -571,8 +616,6 @@ std::vector<String> IcingaDB::GetTypeDumpSignalKeys(const Type::Ptr& type)
keys.emplace_back(m_PrefixConfigObject + lcType + ":override:include");
keys.emplace_back(m_PrefixConfigObject + lcType + ":override:exclude");
keys.emplace_back(m_PrefixConfigObject + lcType + ":range");
- } else if (type == Zone::TypeInstance) {
- keys.emplace_back(m_PrefixConfigObject + lcType + ":parent");
} else if (type == Notification::TypeInstance) {
keys.emplace_back(m_PrefixConfigObject + lcType + ":user");
keys.emplace_back(m_PrefixConfigObject + lcType + ":usergroup");
@@ -595,20 +638,14 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
std::vector<Dictionary::Ptr>& runtimeUpdates, bool runtimeUpdate)
{
String objectKey = GetObjectIdentifier(object);
- String objectKeyName;
+ String objectKeyName = typeName + "_id";
Type::Ptr type = object->GetReflectionType();
- if (type == CheckCommand::TypeInstance || type == NotificationCommand::TypeInstance || type == EventCommand::TypeInstance) {
- objectKeyName = "command_id";
- } else {
- objectKeyName = typeName + "_id";
- }
CustomVarObject::Ptr customVarObject = dynamic_pointer_cast<CustomVarObject>(object);
- auto env (GetEnvironment());
if (customVarObject) {
- auto vars(SerializeVars(customVarObject));
+ auto vars(SerializeVars(customVarObject->GetVars()));
if (vars) {
auto& typeCvs (hMSets[m_PrefixConfigObject + typeName + ":customvar"]);
auto& allCvs (hMSets[m_PrefixConfigObject + "customvar"]);
@@ -628,7 +665,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({m_EnvironmentId, kv.first, object->GetName()}));
typeCvs.emplace_back(id);
Dictionary::Ptr data = new Dictionary({{objectKeyName, objectKey}, {"environment_id", m_EnvironmentId}, {"customvar_id", kv.first}});
@@ -650,7 +687,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));
@@ -665,7 +702,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));
@@ -680,7 +717,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));
@@ -718,7 +755,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({m_EnvironmentId, groupObj->GetName(), object->GetName()}));
members.emplace_back(id);
Dictionary::Ptr data = new Dictionary({{objectKeyName, objectKey}, {"environment_id", m_EnvironmentId}, {typeName + "group_id", groupId}});
members.emplace_back(JsonEncode(data));
@@ -746,10 +783,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({m_EnvironmentId, kv.first, kv.second, object->GetName()}));
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));
@@ -779,7 +816,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({m_EnvironmentId, includeTp->GetName(), object->GetName()}));
includs.emplace_back(id);
Dictionary::Ptr data = new Dictionary({{"environment_id", m_EnvironmentId}, {"timeperiod_id", objectKey}, {"include_id", includeId}});
includs.emplace_back(JsonEncode(data));
@@ -809,7 +846,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({m_EnvironmentId, excludeTp->GetName(), object->GetName()}));
excluds.emplace_back(id);
Dictionary::Ptr data = new Dictionary({{"environment_id", m_EnvironmentId}, {"timeperiod_id", objectKey}, {"exclude_id", excludeId}});
excluds.emplace_back(JsonEncode(data));
@@ -822,40 +859,9 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
return;
}
- if (type == Zone::TypeInstance) {
- Zone::Ptr zone = static_pointer_cast<Zone>(object);
-
- Array::Ptr parents(new Array);
- auto parentsRaw (zone->GetAllParentsRaw());
-
- parents->Reserve(parentsRaw.size());
-
- auto& parnts (hMSets[m_PrefixConfigObject + typeName + ":parent"]);
-
- for (auto& parent : parentsRaw) {
- String id = HashValue(new Array(Prepend(env, Prepend(GetObjectIdentifiersWithoutEnv(parent), GetObjectIdentifiersWithoutEnv(object)))));
- parnts.emplace_back(id);
- Dictionary::Ptr data = new Dictionary({{"zone_id", objectKey}, {"environment_id", m_EnvironmentId}, {"parent_id", GetObjectIdentifier(parent)}});
- parnts.emplace_back(JsonEncode(data));
-
- if (runtimeUpdate) {
- AddObjectDataToRuntimeUpdates(runtimeUpdates, id, m_PrefixConfigObject + typeName + ":parent", data);
- }
-
- parents->Add(GetObjectIdentifier(parent));
- }
-
- return;
- }
-
if (type == User::TypeInstance) {
User::Ptr user = static_pointer_cast<User>(object);
-
- Array::Ptr groups;
- ConfigObject::Ptr (*getGroup)(const String& name);
-
- groups = user->GetGroups();
- getGroup = &::GetObjectByName<UserGroup>;
+ Array::Ptr groups = user->GetGroups();
if (groups) {
ObjectLock groupsLock(groups);
@@ -864,17 +870,28 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
groupIds->Reserve(groups->GetLength());
auto& members (hMSets[m_PrefixConfigObject + typeName + "group:member"]);
+ auto& notificationRecipients (hMSets[m_PrefixConfigObject + "notification:recipient"]);
for (auto& group : groups) {
- auto groupObj ((*getGroup)(group));
+ UserGroup::Ptr groupObj = UserGroup::GetByName(group);
String groupId = GetObjectIdentifier(groupObj);
- String id = HashValue(new Array(Prepend(env, Prepend(GetObjectIdentifiersWithoutEnv(groupObj), GetObjectIdentifiersWithoutEnv(object)))));
+ String id = HashValue(new Array({m_EnvironmentId, groupObj->GetName(), object->GetName()}));
members.emplace_back(id);
Dictionary::Ptr data = new Dictionary({{"user_id", objectKey}, {"environment_id", m_EnvironmentId}, {"usergroup_id", groupId}});
members.emplace_back(JsonEncode(data));
if (runtimeUpdate) {
AddObjectDataToRuntimeUpdates(runtimeUpdates, id, m_PrefixConfigObject + typeName + "group:member", data);
+
+ // Recipients are handled by notifications during initial dumps and only need to be handled here during runtime (e.g. User creation).
+ for (auto& notification : groupObj->GetNotifications()) {
+ String recipientId = HashValue(new Array({m_EnvironmentId, "usergroupuser", user->GetName(), groupObj->GetName(), notification->GetName()}));
+ notificationRecipients.emplace_back(recipientId);
+ Dictionary::Ptr recipientData = new Dictionary({{"notification_id", GetObjectIdentifier(notification)}, {"environment_id", m_EnvironmentId}, {"user_id", objectKey}, {"usergroup_id", groupId}});
+ notificationRecipients.emplace_back(JsonEncode(recipientData));
+
+ AddObjectDataToRuntimeUpdates(runtimeUpdates, recipientId, m_PrefixConfigObject + "notification:recipient", recipientData);
+ }
}
groupIds->Add(groupId);
@@ -888,10 +905,6 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
Notification::Ptr notification = static_pointer_cast<Notification>(object);
std::set<User::Ptr> users = notification->GetUsers();
-
- std::set<User::Ptr> allUsers;
- std::copy(users.begin(), users.end(), std::inserter(allUsers, allUsers.begin()));
-
Array::Ptr userIds = new Array();
auto usergroups(notification->GetUserGroups());
@@ -900,16 +913,22 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
userIds->Reserve(users.size());
auto& usrs (hMSets[m_PrefixConfigObject + typeName + ":user"]);
+ auto& notificationRecipients (hMSets[m_PrefixConfigObject + typeName + ":recipient"]);
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({m_EnvironmentId, "user", user->GetName(), object->GetName()}));
usrs.emplace_back(id);
+ notificationRecipients.emplace_back(id);
+
Dictionary::Ptr data = new Dictionary({{"notification_id", objectKey}, {"environment_id", m_EnvironmentId}, {"user_id", userId}});
- usrs.emplace_back(JsonEncode(data));
+ String dataJson = JsonEncode(data);
+ usrs.emplace_back(dataJson);
+ notificationRecipients.emplace_back(dataJson);
if (runtimeUpdate) {
AddObjectDataToRuntimeUpdates(runtimeUpdates, id, m_PrefixConfigObject + typeName + ":user", data);
+ AddObjectDataToRuntimeUpdates(runtimeUpdates, id, m_PrefixConfigObject + typeName + ":recipient", data);
}
userIds->Add(userId);
@@ -918,41 +937,36 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
usergroupIds->Reserve(usergroups.size());
auto& groups (hMSets[m_PrefixConfigObject + typeName + ":usergroup"]);
- auto& notificationRecipients (hMSets[m_PrefixConfigObject + typeName + ":recipient"]);
for (auto& usergroup : usergroups) {
String usergroupId = GetObjectIdentifier(usergroup);
-
- 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({m_EnvironmentId, "usergroup", usergroup->GetName(), object->GetName()}));
groups.emplace_back(id);
- Dictionary::Ptr groupData = new Dictionary({{"notification_id", objectKey}, {"environment_id", m_EnvironmentId}, {"usergroup_id", usergroupId}});
- groups.emplace_back(JsonEncode(groupData));
-
notificationRecipients.emplace_back(id);
- Dictionary::Ptr notificationRecipientData = new Dictionary({{"notification_id", objectKey}, {"environment_id", m_EnvironmentId}, {"usergroup_id", usergroupId}});
- notificationRecipients.emplace_back(JsonEncode(notificationRecipientData));
+
+ Dictionary::Ptr groupData = new Dictionary({{"notification_id", objectKey}, {"environment_id", m_EnvironmentId}, {"usergroup_id", usergroupId}});
+ String groupDataJson = JsonEncode(groupData);
+ groups.emplace_back(groupDataJson);
+ notificationRecipients.emplace_back(groupDataJson);
if (runtimeUpdate) {
AddObjectDataToRuntimeUpdates(runtimeUpdates, id, m_PrefixConfigObject + typeName + ":usergroup", groupData);
- AddObjectDataToRuntimeUpdates(runtimeUpdates, id, m_PrefixConfigObject + typeName + ":recipient", notificationRecipientData);
+ AddObjectDataToRuntimeUpdates(runtimeUpdates, id, m_PrefixConfigObject + typeName + ":recipient", groupData);
}
- usergroupIds->Add(usergroupId);
- }
+ for (const User::Ptr& user : usergroup->GetMembers()) {
+ String userId = GetObjectIdentifier(user);
+ String recipientId = HashValue(new Array({m_EnvironmentId, "usergroupuser", user->GetName(), usergroup->GetName(), notification->GetName()}));
+ notificationRecipients.emplace_back(recipientId);
+ Dictionary::Ptr userData = new Dictionary({{"notification_id", objectKey}, {"environment_id", m_EnvironmentId}, {"user_id", userId}, {"usergroup_id", usergroupId}});
+ notificationRecipients.emplace_back(JsonEncode(userData));
- for (auto& user : allUsers) {
- String userId = GetObjectIdentifier(user);
- String id = HashValue(new Array(Prepend(env, 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));
-
- if (runtimeUpdate) {
- AddObjectDataToRuntimeUpdates(runtimeUpdates, id, m_PrefixConfigObject + typeName + ":recipient", data);
+ if (runtimeUpdate) {
+ AddObjectDataToRuntimeUpdates(runtimeUpdates, recipientId, m_PrefixConfigObject + typeName + ":recipient", userData);
+ }
}
+
+ usergroupIds->Add(usergroupId);
}
return;
@@ -995,11 +1009,11 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
}
}
- values->Set("command_id", objectKey);
+ values->Set(objectKeyName, objectKey);
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({m_EnvironmentId, kv.first, object->GetName()}));
typeArgs.emplace_back(id);
typeArgs.emplace_back(JsonEncode(values));
@@ -1044,11 +1058,11 @@ void IcingaDB::InsertObjectDependencies(const ConfigObject::Ptr& object, const S
}
}
- values->Set("command_id", objectKey);
+ values->Set(objectKeyName, objectKey);
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({m_EnvironmentId, kv.first, object->GetName()}));
typeVars.emplace_back(id);
typeVars.emplace_back(JsonEncode(values));
@@ -1105,8 +1119,7 @@ void IcingaDB::SendConfigUpdate(const ConfigObject::Ptr& object, bool runtimeUpd
m_Rcon->FireAndForgetQuery({"HSET", m_PrefixConfigCheckSum + typeName + ":state", objectKey, JsonEncode(new Dictionary({{"checksum", checksum}}))}, Prio::RuntimeStateSync);
if (runtimeUpdate) {
- state->Set("checksum", checksum);
- AddObjectDataToRuntimeUpdates(runtimeUpdates, objectKey, m_PrefixConfigObject + typeName + ":state", state);
+ SendStatusUpdate(checkable);
}
}
@@ -1147,10 +1160,11 @@ void IcingaDB::SendConfigUpdate(const ConfigObject::Ptr& object, bool runtimeUpd
void IcingaDB::AddObjectDataToRuntimeUpdates(std::vector<Dictionary::Ptr>& runtimeUpdates, const String& objectKey,
const String& redisKey, const Dictionary::Ptr& data)
{
- data->Set("id", objectKey);
- data->Set("redis_key", redisKey);
- data->Set("runtime_type", "upsert");
- runtimeUpdates.emplace_back(data);
+ Dictionary::Ptr dataClone = data->ShallowClone();
+ dataClone->Set("id", objectKey);
+ dataClone->Set("redis_key", redisKey);
+ dataClone->Set("runtime_type", "upsert");
+ runtimeUpdates.emplace_back(dataClone);
}
// Takes object and collects IcingaDB relevant attributes and computes checksums. Returns whether the object is relevant
@@ -1186,7 +1200,7 @@ bool IcingaDB::PrepareObject(const ConfigObject::Ptr& object, Dictionary::Ptr& a
Zone::Ptr parent = zone->GetParent();
if (parent) {
- attributes->Set("parent_id", GetObjectIdentifier(zone));
+ attributes->Set("parent_id", GetObjectIdentifier(parent));
}
auto parentsRaw (zone->GetAllParentsRaw());
@@ -1239,11 +1253,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;
@@ -1297,7 +1311,7 @@ bool IcingaDB::PrepareObject(const ConfigObject::Ptr& object, Dictionary::Ptr& a
tie(host, service) = GetHostService(notification->GetCheckable());
- attributes->Set("command_id", GetObjectIdentifier(notification->GetCommand()));
+ attributes->Set("notificationcommand_id", GetObjectIdentifier(notification->GetCommand()));
attributes->Set("host_id", GetObjectIdentifier(host));
if (service)
@@ -1357,6 +1371,7 @@ 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("is_flexible", !downtime->GetFixed());
attributes->Set("is_in_effect", downtime->IsInEffect());
@@ -1365,6 +1380,12 @@ bool IcingaDB::PrepareObject(const ConfigObject::Ptr& object, Dictionary::Ptr& a
attributes->Set("end_time", TimestampToMilliseconds(downtime->GetFixed() ? downtime->GetEndTime() : (downtime->GetTriggerTime() + downtime->GetDuration())));
}
+ auto duration = downtime->GetDuration();
+ if (downtime->GetFixed()) {
+ duration = downtime->GetEndTime() - downtime->GetStartTime();
+ }
+ attributes->Set("duration", TimestampToMilliseconds(duration));
+
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(downtime->GetCheckable());
@@ -1475,7 +1496,8 @@ IcingaDB::CreateConfigUpdate(const ConfigObject::Ptr& object, const String typeN
void IcingaDB::SendConfigDelete(const ConfigObject::Ptr& object)
{
- String typeName = object->GetReflectionType()->GetName().ToLower();
+ Type::Ptr type = object->GetReflectionType();
+ String typeName = type->GetName().ToLower();
String objectKey = GetObjectIdentifier(object);
m_Rcon->FireAndForgetQueries({
@@ -1487,12 +1509,23 @@ void IcingaDB::SendConfigDelete(const ConfigObject::Ptr& object)
}
}, Prio::Config);
- auto checkable (dynamic_pointer_cast<Checkable>(object));
+ CustomVarObject::Ptr customVarObject = dynamic_pointer_cast<CustomVarObject>(object);
+
+ if (customVarObject) {
+ Dictionary::Ptr vars = customVarObject->GetVars();
+ SendCustomVarsChanged(object, vars, nullptr);
+ }
+
+ if (type == Host::TypeInstance || type == Service::TypeInstance) {
+ Checkable::Ptr checkable = static_pointer_cast<Checkable>(object);
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
- if (checkable) {
m_Rcon->FireAndForgetQuery({
"ZREM",
- dynamic_pointer_cast<Service>(checkable) ? "icinga:nextupdate:service" : "icinga:nextupdate:host",
+ service ? "icinga:nextupdate:service" : "icinga:nextupdate:host",
GetObjectIdentifier(checkable)
}, Prio::CheckResult);
@@ -1500,6 +1533,42 @@ void IcingaDB::SendConfigDelete(const ConfigObject::Ptr& object)
{"HDEL", m_PrefixConfigObject + typeName + ":state", objectKey},
{"HDEL", m_PrefixConfigCheckSum + typeName + ":state", objectKey}
}, Prio::RuntimeStateSync);
+
+ if (service) {
+ SendGroupsChanged<ServiceGroup>(checkable, service->GetGroups(), nullptr);
+ } else {
+ SendGroupsChanged<HostGroup>(checkable, host->GetGroups(), nullptr);
+ }
+
+ return;
+ }
+
+ if (type == TimePeriod::TypeInstance) {
+ TimePeriod::Ptr timeperiod = static_pointer_cast<TimePeriod>(object);
+ SendTimePeriodRangesChanged(timeperiod, timeperiod->GetRanges(), nullptr);
+ SendTimePeriodIncludesChanged(timeperiod, timeperiod->GetIncludes(), nullptr);
+ SendTimePeriodExcludesChanged(timeperiod, timeperiod->GetExcludes(), nullptr);
+ return;
+ }
+
+ if (type == User::TypeInstance) {
+ User::Ptr user = static_pointer_cast<User>(object);
+ SendGroupsChanged<UserGroup>(user, user->GetGroups(), nullptr);
+ return;
+ }
+
+ if (type == Notification::TypeInstance) {
+ Notification::Ptr notification = static_pointer_cast<Notification>(object);
+ SendNotificationUsersChanged(notification, notification->GetUsersRaw(), nullptr);
+ SendNotificationUserGroupsChanged(notification, notification->GetUserGroupsRaw(), nullptr);
+ return;
+ }
+
+ if (type == CheckCommand::TypeInstance || type == NotificationCommand::TypeInstance || type == EventCommand::TypeInstance) {
+ Command::Ptr command = static_pointer_cast<Command>(object);
+ SendCommandArgumentsChanged(command, command->GetArguments(), nullptr);
+ SendCommandEnvChanged(command, command->GetEnv(), nullptr);
+ return;
}
}
@@ -1549,6 +1618,9 @@ void IcingaDB::SendStateChange(const ConfigObject::Ptr& object, const CheckResul
if (!checkable)
return;
+ if (!cr)
+ return;
+
Host::Ptr host;
Service::Ptr service;
@@ -1563,9 +1635,15 @@ void IcingaDB::SendStateChange(const ConfigObject::Ptr& object, const CheckResul
hard_state = service ? Convert::ToLong(service->GetLastHardState()) : Convert::ToLong(host->GetLastHardState());
}
+ auto eventTime (cr->GetExecutionEnd());
+ auto eventTs (TimestampToMilliseconds(eventTime));
+
+ Array::Ptr rawId = new Array({m_EnvironmentId, object->GetName()});
+ rawId->Add(eventTs);
+
std::vector<String> xAdd ({
"XADD", "icinga:history:stream:state", "*",
- "id", Utility::NewUniqueID(),
+ "id", HashValue(rawId),
"environment_id", m_EnvironmentId,
"host_id", GetObjectIdentifier(host),
"state_type", Convert::ToString(type),
@@ -1575,8 +1653,8 @@ void IcingaDB::SendStateChange(const ConfigObject::Ptr& object, const CheckResul
"previous_soft_state", Convert::ToString(GetPreviousState(checkable, service, StateTypeSoft)),
"previous_hard_state", Convert::ToString(GetPreviousState(checkable, service, StateTypeHard)),
"max_check_attempts", Convert::ToString(checkable->GetMaxCheckAttempts()),
- "event_time", Convert::ToString(TimestampToMilliseconds(cr ? cr->GetExecutionEnd() : Utility::GetTime())),
- "event_id", Utility::NewUniqueID(),
+ "event_time", Convert::ToString(eventTs),
+ "event_id", CalcEventID("state_change", object, eventTime),
"event_type", "state_change"
});
@@ -1596,6 +1674,8 @@ void IcingaDB::SendStateChange(const ConfigObject::Ptr& object, const CheckResul
xAdd.emplace_back(Utility::ValidateUTF8(std::move(output)));
xAdd.emplace_back("check_source");
xAdd.emplace_back(cr->GetCheckSource());
+ xAdd.emplace_back("scheduling_source");
+ xAdd.emplace_back(cr->GetSchedulingSource());
}
if (service) {
@@ -1620,7 +1700,7 @@ void IcingaDB::SendStateChange(const ConfigObject::Ptr& object, const CheckResul
void IcingaDB::SendSentNotification(
const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set<User::Ptr>& users,
- NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text
+ NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text, double sendTime
)
{
if (!m_Rcon || !m_Rcon->IsConnected())
@@ -1636,7 +1716,13 @@ void IcingaDB::SendSentNotification(
}
auto usersAmount (users.size());
- auto notificationHistoryId = Utility::NewUniqueID();
+ auto sendTs (TimestampToMilliseconds(sendTime));
+
+ Array::Ptr rawId = new Array({m_EnvironmentId, notification->GetName()});
+ rawId->Add(GetNotificationTypeByEnum(type));
+ rawId->Add(sendTs);
+
+ auto notificationHistoryId (HashValue(rawId));
std::vector<String> xAdd ({
"XADD", "icinga:history:stream:notification", "*",
@@ -1650,8 +1736,8 @@ void IcingaDB::SendSentNotification(
"author", Utility::ValidateUTF8(author),
"text", Utility::ValidateUTF8(finalText),
"users_notified", Convert::ToString(usersAmount),
- "send_time", Convert::ToString(TimestampToMilliseconds(Utility::GetTime())),
- "event_id", Utility::NewUniqueID(),
+ "send_time", Convert::ToString(sendTs),
+ "event_id", CalcEventID("notification", notification, sendTime, type),
"event_type", "notification"
});
@@ -1672,20 +1758,16 @@ void IcingaDB::SendSentNotification(
xAdd.emplace_back(GetObjectIdentifier(endpoint));
}
- m_Rcon->FireAndForgetQuery(std::move(xAdd), Prio::History);
-
- for (const User::Ptr& user : users) {
- auto userId = GetObjectIdentifier(user);
- std::vector<String> xAddUser ({
- "XADD", "icinga:history:stream:usernotification", "*",
- "id", Utility::NewUniqueID(),
- "environment_id", m_EnvironmentId,
- "notification_history_id", notificationHistoryId,
- "user_id", GetObjectIdentifier(user),
- });
-
- m_Rcon->FireAndForgetQuery(std::move(xAddUser), Prio::History);
+ if (!users.empty()) {
+ Array::Ptr users_notified = new Array();
+ for (const User::Ptr& user : users) {
+ users_notified->Add(GetObjectIdentifier(user));
+ }
+ xAdd.emplace_back("users_notified_ids");
+ xAdd.emplace_back(JsonEncode(users_notified));
}
+
+ m_Rcon->FireAndForgetQuery(std::move(xAdd), Prio::History);
}
void IcingaDB::SendStartedDowntime(const Downtime::Ptr& downtime)
@@ -1711,12 +1793,12 @@ 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(downtime->GetDuration()),
+ "flexible_duration", Convert::ToString(TimestampToMilliseconds(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()),
"trigger_time", Convert::ToString(TimestampToMilliseconds(downtime->GetTriggerTime())),
- "event_id", Utility::NewUniqueID(),
+ "event_id", CalcEventID("downtime_start", downtime),
"event_type", "downtime_start"
});
@@ -1761,6 +1843,13 @@ void IcingaDB::SendStartedDowntime(const Downtime::Ptr& downtime)
xAdd.emplace_back(GetObjectIdentifier(parent));
}
+ auto scheduledBy (downtime->GetScheduledBy());
+
+ if (!scheduledBy.IsEmpty()) {
+ xAdd.emplace_back("scheduled_by");
+ xAdd.emplace_back(scheduledBy);
+ }
+
m_Rcon->FireAndForgetQuery(std::move(xAdd), Prio::History);
}
@@ -1790,13 +1879,13 @@ 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(downtime->GetDuration()),
+ "flexible_duration", Convert::ToString(TimestampToMilliseconds(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()),
"trigger_time", Convert::ToString(TimestampToMilliseconds(downtime->GetTriggerTime())),
"cancel_time", Convert::ToString(TimestampToMilliseconds(Utility::GetTime())),
- "event_id", Utility::NewUniqueID(),
+ "event_id", CalcEventID("downtime_end", downtime),
"event_type", "downtime_end"
});
@@ -1841,6 +1930,13 @@ void IcingaDB::SendRemovedDowntime(const Downtime::Ptr& downtime)
xAdd.emplace_back(GetObjectIdentifier(parent));
}
+ auto scheduledBy (downtime->GetScheduledBy());
+
+ if (!scheduledBy.IsEmpty()) {
+ xAdd.emplace_back("scheduled_by");
+ xAdd.emplace_back(scheduledBy);
+ }
+
m_Rcon->FireAndForgetQuery(std::move(xAdd), Prio::History);
}
@@ -1866,7 +1962,7 @@ void IcingaDB::SendAddedComment(const Comment::Ptr& comment)
"entry_type", Convert::ToString(comment->GetEntryType()),
"is_persistent", Convert::ToString((unsigned short)comment->GetPersistent()),
"is_sticky", Convert::ToString((unsigned short)(comment->GetEntryType() == CommentAcknowledgement && comment->GetCheckable()->GetAcknowledgement() == AcknowledgementSticky)),
- "event_id", Utility::NewUniqueID(),
+ "event_id", CalcEventID("comment_add", comment),
"event_type", "comment_add"
});
@@ -1923,7 +2019,7 @@ void IcingaDB::SendRemovedComment(const Comment::Ptr& comment)
"entry_type", Convert::ToString(comment->GetEntryType()),
"is_persistent", Convert::ToString((unsigned short)comment->GetPersistent()),
"is_sticky", Convert::ToString((unsigned short)(comment->GetEntryType() == CommentAcknowledgement && comment->GetCheckable()->GetAcknowledgement() == AcknowledgementSticky)),
- "event_id", Utility::NewUniqueID(),
+ "event_id", CalcEventID("comment_remove", comment),
"event_type", "comment_remove"
});
@@ -1984,8 +2080,7 @@ void IcingaDB::SendFlappingChange(const Checkable::Ptr& checkable, double change
"environment_id", m_EnvironmentId,
"host_id", GetObjectIdentifier(host),
"flapping_threshold_low", Convert::ToString(checkable->GetFlappingThresholdLow()),
- "flapping_threshold_high", Convert::ToString(checkable->GetFlappingThresholdHigh()),
- "event_id", Utility::NewUniqueID()
+ "flapping_threshold_high", Convert::ToString(checkable->GetFlappingThresholdHigh())
});
if (service) {
@@ -2027,8 +2122,10 @@ void IcingaDB::SendFlappingChange(const Checkable::Ptr& checkable, double change
xAdd.emplace_back("start_time");
xAdd.emplace_back(Convert::ToString(startTime));
+ 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->GetReflectionType()->GetName(), checkable->GetName(), startTime})));
+ xAdd.emplace_back(HashValue(new Array({m_EnvironmentId, checkable->GetName(), startTime})));
m_Rcon->FireAndForgetQuery(std::move(xAdd), Prio::History);
}
@@ -2071,7 +2168,6 @@ void IcingaDB::SendAcknowledgementSet(const Checkable::Ptr& checkable, const Str
std::vector<String> xAdd ({
"XADD", "icinga:history:stream:acknowledgement", "*",
- "event_id", Utility::NewUniqueID(),
"environment_id", m_EnvironmentId,
"host_id", GetObjectIdentifier(host),
"event_type", "ack_set",
@@ -2107,8 +2203,10 @@ void IcingaDB::SendAcknowledgementSet(const Checkable::Ptr& checkable, const Str
xAdd.emplace_back("set_time");
xAdd.emplace_back(Convert::ToString(setTime));
+ 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->GetReflectionType()->GetName(), checkable->GetName(), setTime})));
+ xAdd.emplace_back(HashValue(new Array({m_EnvironmentId, checkable->GetName(), setTime})));
m_Rcon->FireAndForgetQuery(std::move(xAdd), Prio::History);
}
@@ -2124,7 +2222,6 @@ void IcingaDB::SendAcknowledgementCleared(const Checkable::Ptr& checkable, const
std::vector<String> xAdd ({
"XADD", "icinga:history:stream:acknowledgement", "*",
- "event_id", Utility::NewUniqueID(),
"environment_id", m_EnvironmentId,
"host_id", GetObjectIdentifier(host),
"clear_time", Convert::ToString(TimestampToMilliseconds(changeTime)),
@@ -2152,8 +2249,10 @@ void IcingaDB::SendAcknowledgementCleared(const Checkable::Ptr& checkable, const
xAdd.emplace_back("set_time");
xAdd.emplace_back(Convert::ToString(setTime));
+ 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->GetReflectionType()->GetName(), checkable->GetName(), setTime})));
+ xAdd.emplace_back(HashValue(new Array({m_EnvironmentId, checkable->GetName(), setTime})));
if (!removedBy.IsEmpty()) {
xAdd.emplace_back("cleared_by");
@@ -2163,6 +2262,150 @@ void IcingaDB::SendAcknowledgementCleared(const Checkable::Ptr& checkable, const
m_Rcon->FireAndForgetQuery(std::move(xAdd), Prio::History);
}
+void IcingaDB::SendNotificationUsersChanged(const Notification::Ptr& notification, const Array::Ptr& oldValues, const Array::Ptr& newValues) {
+ if (!m_Rcon || !m_Rcon->IsConnected() || oldValues == newValues) {
+ return;
+ }
+
+ std::vector<Value> deletedUsers = GetArrayDeletedValues(oldValues, newValues);
+
+ for (const auto& userName : deletedUsers) {
+ String id = HashValue(new Array({m_EnvironmentId, "user", userName, notification->GetName()}));
+ DeleteRelationship(id, "notification:user");
+ DeleteRelationship(id, "notification:recipient");
+ }
+}
+
+void IcingaDB::SendNotificationUserGroupsChanged(const Notification::Ptr& notification, const Array::Ptr& oldValues, const Array::Ptr& newValues) {
+ if (!m_Rcon || !m_Rcon->IsConnected() || oldValues == newValues) {
+ return;
+ }
+
+ std::vector<Value> deletedUserGroups = GetArrayDeletedValues(oldValues, newValues);
+
+ for (const auto& userGroupName : deletedUserGroups) {
+ UserGroup::Ptr userGroup = UserGroup::GetByName(userGroupName);
+ String id = HashValue(new Array({m_EnvironmentId, "usergroup", userGroupName, notification->GetName()}));
+ DeleteRelationship(id, "notification:usergroup");
+ DeleteRelationship(id, "notification:recipient");
+
+ for (const User::Ptr& user : userGroup->GetMembers()) {
+ String userId = HashValue(new Array({m_EnvironmentId, "usergroupuser", user->GetName(), userGroupName, notification->GetName()}));
+ DeleteRelationship(userId, "notification:recipient");
+ }
+ }
+}
+
+void IcingaDB::SendTimePeriodRangesChanged(const TimePeriod::Ptr& timeperiod, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues) {
+ if (!m_Rcon || !m_Rcon->IsConnected() || oldValues == newValues) {
+ return;
+ }
+
+ std::vector<String> deletedKeys = GetDictionaryDeletedKeys(oldValues, newValues);
+ String typeName = GetLowerCaseTypeNameDB(timeperiod);
+
+ for (const auto& rangeKey : deletedKeys) {
+ String id = HashValue(new Array({m_EnvironmentId, rangeKey, oldValues->Get(rangeKey), timeperiod->GetName()}));
+ DeleteRelationship(id, "timeperiod:range");
+ }
+}
+
+void IcingaDB::SendTimePeriodIncludesChanged(const TimePeriod::Ptr& timeperiod, const Array::Ptr& oldValues, const Array::Ptr& newValues) {
+ if (!m_Rcon || !m_Rcon->IsConnected() || oldValues == newValues) {
+ return;
+ }
+
+ std::vector<Value> deletedIncludes = GetArrayDeletedValues(oldValues, newValues);
+
+ for (const auto& includeName : deletedIncludes) {
+ String id = HashValue(new Array({m_EnvironmentId, includeName, timeperiod->GetName()}));
+ DeleteRelationship(id, "timeperiod:override:include");
+ }
+}
+
+void IcingaDB::SendTimePeriodExcludesChanged(const TimePeriod::Ptr& timeperiod, const Array::Ptr& oldValues, const Array::Ptr& newValues) {
+ if (!m_Rcon || !m_Rcon->IsConnected() || oldValues == newValues) {
+ return;
+ }
+
+ std::vector<Value> deletedExcludes = GetArrayDeletedValues(oldValues, newValues);
+
+ for (const auto& excludeName : deletedExcludes) {
+ String id = HashValue(new Array({m_EnvironmentId, excludeName, timeperiod->GetName()}));
+ DeleteRelationship(id, "timeperiod:override:exclude");
+ }
+}
+
+template<typename T>
+void IcingaDB::SendGroupsChanged(const ConfigObject::Ptr& object, const Array::Ptr& oldValues, const Array::Ptr& newValues) {
+ if (!m_Rcon || !m_Rcon->IsConnected() || oldValues == newValues) {
+ return;
+ }
+
+ std::vector<Value> deletedGroups = GetArrayDeletedValues(oldValues, newValues);
+ String typeName = GetLowerCaseTypeNameDB(object);
+
+ for (const auto& groupName : deletedGroups) {
+ typename T::Ptr group = ConfigObject::GetObject<T>(groupName);
+ String id = HashValue(new Array({m_EnvironmentId, group->GetName(), object->GetName()}));
+ DeleteRelationship(id, typeName + "group:member");
+
+ if (std::is_same<T, UserGroup>::value) {
+ UserGroup::Ptr userGroup = dynamic_pointer_cast<UserGroup>(group);
+
+ for (const auto& notification : userGroup->GetNotifications()) {
+ String userId = HashValue(new Array({m_EnvironmentId, "usergroupuser", object->GetName(), groupName, notification->GetName()}));
+ DeleteRelationship(userId, "notification:recipient");
+ }
+ }
+ }
+}
+
+void IcingaDB::SendCommandEnvChanged(const ConfigObject::Ptr& command, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues) {
+ if (!m_Rcon || !m_Rcon->IsConnected() || oldValues == newValues) {
+ return;
+ }
+
+ std::vector<String> deletedKeys = GetDictionaryDeletedKeys(oldValues, newValues);
+ String typeName = GetLowerCaseTypeNameDB(command);
+
+ for (const auto& envvarKey : deletedKeys) {
+ String id = HashValue(new Array({m_EnvironmentId, envvarKey, command->GetName()}));
+ DeleteRelationship(id, typeName + ":envvar", true);
+ }
+}
+
+void IcingaDB::SendCommandArgumentsChanged(const ConfigObject::Ptr& command, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues) {
+ if (!m_Rcon || !m_Rcon->IsConnected() || oldValues == newValues) {
+ return;
+ }
+
+ std::vector<String> deletedKeys = GetDictionaryDeletedKeys(oldValues, newValues);
+ String typeName = GetLowerCaseTypeNameDB(command);
+
+ for (const auto& argumentKey : deletedKeys) {
+ String id = HashValue(new Array({m_EnvironmentId, argumentKey, command->GetName()}));
+ DeleteRelationship(id, typeName + ":argument", true);
+ }
+}
+
+void IcingaDB::SendCustomVarsChanged(const ConfigObject::Ptr& object, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues) {
+ if (!m_Rcon || !m_Rcon->IsConnected() || oldValues == newValues) {
+ return;
+ }
+
+ Dictionary::Ptr oldVars = SerializeVars(oldValues);
+ Dictionary::Ptr newVars = SerializeVars(newValues);
+
+ std::vector<String> deletedVars = GetDictionaryDeletedKeys(oldVars, newVars);
+ String typeName = GetLowerCaseTypeNameDB(object);
+
+ for (const auto& varId : deletedVars) {
+ String id = HashValue(new Array({m_EnvironmentId, varId, object->GetName()}));
+ DeleteRelationship(id, typeName + ":customvar");
+ }
+}
+
Dictionary::Ptr IcingaDB::SerializeState(const Checkable::Ptr& checkable)
{
Dictionary::Ptr attrs = new Dictionary();
@@ -2192,6 +2435,7 @@ Dictionary::Ptr IcingaDB::SerializeState(const Checkable::Ptr& checkable)
attrs->Set("state", state);
attrs->Set("hard_state", service->HasBeenChecked() ? service->GetLastHardState() : 99);
attrs->Set("severity", service->GetSeverity());
+ attrs->Set("host_id", GetObjectIdentifier(host));
} else {
attrs->Set("host_id", id);
auto state = host->HasBeenChecked() ? host->GetState() : 99;
@@ -2235,6 +2479,7 @@ Dictionary::Ptr IcingaDB::SerializeState(const Checkable::Ptr& checkable)
attrs->Set("execution_time", TimestampToMilliseconds(fmax(0.0, cr->CalculateExecutionTime())));
attrs->Set("latency", TimestampToMilliseconds(cr->CalculateLatency()));
attrs->Set("check_source", cr->GetCheckSource());
+ attrs->Set("scheduling_source", cr->GetSchedulingSource());
}
attrs->Set("is_problem", checkable->GetProblem());
@@ -2388,10 +2633,11 @@ void IcingaDB::NotificationSentToAllUsersHandler(
)
{
auto rws (ConfigType::GetObjectsByType<IcingaDB>());
+ auto sendTime (notification->GetLastNotification());
if (!rws.empty()) {
for (auto& rw : rws) {
- rw->SendSentNotification(notification, checkable, users, type, cr, author, text);
+ rw->SendSentNotification(notification, checkable, users, type, cr, author, text, sendTime);
}
}
}
@@ -2459,3 +2705,89 @@ void IcingaDB::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, co
}
}
}
+
+void IcingaDB::NotificationUsersChangedHandler(const Notification::Ptr& notification, const Array::Ptr& oldValues, const Array::Ptr& newValues) {
+ for (const IcingaDB::Ptr& rw : ConfigType::GetObjectsByType<IcingaDB>()) {
+ rw->SendNotificationUsersChanged(notification, oldValues, newValues);
+ }
+}
+
+void IcingaDB::NotificationUserGroupsChangedHandler(const Notification::Ptr& notification, const Array::Ptr& oldValues, const Array::Ptr& newValues) {
+ for (const IcingaDB::Ptr& rw : ConfigType::GetObjectsByType<IcingaDB>()) {
+ rw->SendNotificationUserGroupsChanged(notification, oldValues, newValues);
+ }
+}
+
+void IcingaDB::TimePeriodRangesChangedHandler(const TimePeriod::Ptr& timeperiod, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues) {
+ for (const IcingaDB::Ptr& rw : ConfigType::GetObjectsByType<IcingaDB>()) {
+ rw->SendTimePeriodRangesChanged(timeperiod, oldValues, newValues);
+ }
+}
+
+void IcingaDB::TimePeriodIncludesChangedHandler(const TimePeriod::Ptr& timeperiod, const Array::Ptr& oldValues, const Array::Ptr& newValues) {
+ for (const IcingaDB::Ptr& rw : ConfigType::GetObjectsByType<IcingaDB>()) {
+ rw->SendTimePeriodIncludesChanged(timeperiod, oldValues, newValues);
+ }
+}
+
+void IcingaDB::TimePeriodExcludesChangedHandler(const TimePeriod::Ptr& timeperiod, const Array::Ptr& oldValues, const Array::Ptr& newValues) {
+ for (const IcingaDB::Ptr& rw : ConfigType::GetObjectsByType<IcingaDB>()) {
+ rw->SendTimePeriodExcludesChanged(timeperiod, oldValues, newValues);
+ }
+}
+
+void IcingaDB::UserGroupsChangedHandler(const User::Ptr& user, const Array::Ptr& oldValues, const Array::Ptr& newValues) {
+ for (const IcingaDB::Ptr& rw : ConfigType::GetObjectsByType<IcingaDB>()) {
+ rw->SendGroupsChanged<UserGroup>(user, oldValues, newValues);
+ }
+}
+
+void IcingaDB::HostGroupsChangedHandler(const Host::Ptr& host, const Array::Ptr& oldValues, const Array::Ptr& newValues) {
+ for (const IcingaDB::Ptr& rw : ConfigType::GetObjectsByType<IcingaDB>()) {
+ rw->SendGroupsChanged<HostGroup>(host, oldValues, newValues);
+ }
+}
+
+void IcingaDB::ServiceGroupsChangedHandler(const Service::Ptr& service, const Array::Ptr& oldValues, const Array::Ptr& newValues) {
+ for (const IcingaDB::Ptr& rw : ConfigType::GetObjectsByType<IcingaDB>()) {
+ rw->SendGroupsChanged<ServiceGroup>(service, oldValues, newValues);
+ }
+}
+
+void IcingaDB::CommandEnvChangedHandler(const ConfigObject::Ptr& command, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues) {
+ for (const IcingaDB::Ptr& rw : ConfigType::GetObjectsByType<IcingaDB>()) {
+ rw->SendCommandEnvChanged(command, oldValues, newValues);
+ }
+}
+
+void IcingaDB::CommandArgumentsChangedHandler(const ConfigObject::Ptr& command, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues) {
+ for (const IcingaDB::Ptr& rw : ConfigType::GetObjectsByType<IcingaDB>()) {
+ rw->SendCommandArgumentsChanged(command, oldValues, newValues);
+ }
+}
+
+void IcingaDB::CustomVarsChangedHandler(const ConfigObject::Ptr& object, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues) {
+ for (const IcingaDB::Ptr& rw : ConfigType::GetObjectsByType<IcingaDB>()) {
+ rw->SendCustomVarsChanged(object, oldValues, newValues);
+ }
+}
+
+void IcingaDB::DeleteRelationship(const String& id, const String& redisKeyWithoutPrefix, bool hasChecksum) {
+ Log(LogNotice, "IcingaDB") << "Deleting relationship '" << redisKeyWithoutPrefix << " -> '" << id << "'";
+
+ String redisKey = m_PrefixConfigObject + redisKeyWithoutPrefix;
+
+ std::vector<std::vector<String>> queries;
+
+ if (hasChecksum) {
+ queries.push_back({"HDEL", m_PrefixConfigCheckSum + redisKeyWithoutPrefix, id});
+ }
+
+ queries.push_back({"HDEL", redisKey, id});
+ queries.push_back({
+ "XADD", "icinga:runtime", "MAXLEN", "~", "1000000", "*",
+ "redis_key", redisKey, "id", id, "runtime_type", "delete"
+ });
+
+ m_Rcon->FireAndForgetQueries(queries, Prio::Config);
+}
=====================================
lib/icingadb/icingadb-stats.cpp
=====================================
@@ -1,6 +1,7 @@
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
#include "icingadb/icingadb.hpp"
+#include "base/application.hpp"
#include "base/json.hpp"
#include "base/logger.hpp"
#include "base/serializer.hpp"
@@ -38,13 +39,14 @@ Dictionary::Ptr IcingaDB::GetStats()
}));
}
- auto localEndpoint (Endpoint::GetLocalEndpoint());
+ typedef Dictionary::Ptr DP;
+ DP app = DP(DP(DP(stats->Get("IcingaApplication"))->Get("status"))->Get("icingaapplication"))->Get("app");
- if (localEndpoint) {
- typedef Dictionary::Ptr DP;
+ app->Set("program_start", TimestampToMilliseconds(Application::GetStartTime()));
- DP(DP(DP(DP(stats->Get("IcingaApplication"))->Get("status"))->Get("icingaapplication"))->Get("app"))
- ->Set("endpoint_id", GetObjectIdentifier(localEndpoint));
+ auto localEndpoint (Endpoint::GetLocalEndpoint());
+ if (localEndpoint) {
+ app->Set("endpoint_id", GetObjectIdentifier(localEndpoint));
}
return stats;
=====================================
lib/icingadb/icingadb-utility.cpp
=====================================
@@ -60,30 +60,37 @@ String IcingaDB::FormatCommandLine(const Value& commandLine)
return result;
}
-String IcingaDB::GetEnvironment()
+String IcingaDB::GetObjectIdentifier(const ConfigObject::Ptr& object)
{
- return ConfigType::GetObjectsByType<IcingaApplication>()[0]->GetEnvironment();
+ return HashValue(new Array({m_EnvironmentId, object->GetName()}));
}
-ArrayData IcingaDB::GetObjectIdentifiersWithoutEnv(const ConfigObject::Ptr& object)
+/**
+ * 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)
{
- Type::Ptr type = object->GetReflectionType();
+ Array::Ptr rawId = new Array({object->GetName()});
+ rawId->Insert(0, m_EnvironmentId);
+ rawId->Insert(1, eventType);
- if (type == CheckCommand::TypeInstance || type == NotificationCommand::TypeInstance || type == EventCommand::TypeInstance)
- return {type->GetName(), object->GetName()};
- else
- return {object->GetName()};
-}
+ if (nt) {
+ rawId->Add(GetNotificationTypeByEnum(nt));
+ }
-String IcingaDB::GetObjectIdentifier(const ConfigObject::Ptr& object)
-{
- return HashValue(new Array(Prepend(GetEnvironment(), GetObjectIdentifiersWithoutEnv(object))));
+ if (eventTime) {
+ rawId->Add(TimestampToMilliseconds(eventTime));
+ }
+
+ return HashValue(std::move(rawId));
}
static const std::set<String> metadataWhitelist ({"package", "source_location", "templates"});
/**
- * Prepare object's custom vars for being written to Redis
+ * Prepare custom vars for being written to Redis
*
* object.vars = {
* "disks": {
@@ -96,7 +103,7 @@ static const std::set<String> metadataWhitelist ({"package", "source_location",
*
* return {
* SHA1(PackObject([
- * Environment,
+ * EnvironmentId,
* "disks",
* {
* "disk": {},
@@ -105,7 +112,7 @@ static const std::set<String> metadataWhitelist ({"package", "source_location",
* }
* }
* ])): {
- * "envId": SHA1(Environment),
+ * "environment_id": EnvironmentId,
* "name_checksum": SHA1("disks"),
* "name": "disks",
* "value": {
@@ -117,28 +124,24 @@ static const std::set<String> metadataWhitelist ({"package", "source_location",
* }
* }
*
- * @param object Config object with custom vars
+ * @param Dictionary Config object with custom vars
*
- * @return JSON-like data structure for Redis
+ * @return JSON-like data structure for Redis
*/
-Dictionary::Ptr IcingaDB::SerializeVars(const CustomVarObject::Ptr& object)
+Dictionary::Ptr IcingaDB::SerializeVars(const Dictionary::Ptr& vars)
{
- Dictionary::Ptr vars = object->GetVars();
-
if (!vars)
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)},
@@ -149,6 +152,32 @@ Dictionary::Ptr IcingaDB::SerializeVars(const CustomVarObject::Ptr& object)
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<String> propertiesBlacklistEmpty;
String IcingaDB::HashValue(const Value& value)
@@ -227,3 +256,49 @@ String IcingaDB::IcingaToStreamValue(const Value& value)
return JsonEncode(value);
}
}
+
+// Returns the items that exist in "arrayOld" but not in "arrayNew"
+std::vector<Value> IcingaDB::GetArrayDeletedValues(const Array::Ptr& arrayOld, const Array::Ptr& arrayNew) {
+ std::vector<Value> deletedValues;
+
+ if (!arrayOld) {
+ return deletedValues;
+ }
+
+ if (!arrayNew) {
+ return std::vector<Value>(arrayOld->Begin(), arrayOld->End());
+ }
+
+ std::vector<Value> vectorOld(arrayOld->Begin(), arrayOld->End());
+ std::sort(vectorOld.begin(), vectorOld.end());
+ vectorOld.erase(std::unique(vectorOld.begin(), vectorOld.end()), vectorOld.end());
+
+ std::vector<Value> vectorNew(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<String> IcingaDB::GetDictionaryDeletedKeys(const Dictionary::Ptr& dictOld, const Dictionary::Ptr& dictNew) {
+ std::vector<String> deletedKeys;
+
+ if (!dictOld) {
+ return deletedKeys;
+ }
+
+ std::vector<String> oldKeys = dictOld->GetKeys();
+
+ if (!dictNew) {
+ return oldKeys;
+ }
+
+ std::vector<String> newKeys = dictNew->GetKeys();
+
+ std::set_difference(oldKeys.begin(), oldKeys.end(), newKeys.begin(), newKeys.end(), std::back_inserter(deletedKeys));
+
+ return deletedKeys;
+}
=====================================
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 <boost/algorithm/string.hpp>
+#include <fstream>
#include <memory>
#include <utility>
@@ -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<IcingaDB>::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<X509> 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.";
@@ -151,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<String> query {"XADD", "icinga:stats", "MAXLEN", "1", "*"};
@@ -204,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<std::mutex> l (m_Mutex);
=====================================
lib/icingadb/icingadb.hpp
=====================================
@@ -12,7 +12,6 @@
#include "icinga/service.hpp"
#include "icinga/downtime.hpp"
#include "remote/messageorigin.hpp"
-#include <boost/thread/once.hpp>
#include <atomic>
#include <memory>
#include <mutex>
@@ -40,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<String>& lvalue, const ValidationUtils& utils) override;
void ValidateConnectTimeout(const Lazy<double>& lvalue, const ValidationUtils& utils) override;
@@ -78,10 +79,11 @@ private:
void SendStateChange(const ConfigObject::Ptr& object, const CheckResult::Ptr& cr, StateType type);
void AddObjectDataToRuntimeUpdates(std::vector<Dictionary::Ptr>& runtimeUpdates, const String& objectKey,
const String& redisKey, const Dictionary::Ptr& data);
+ void DeleteRelationship(const String& id, const String& redisKeyWithoutPrefix, bool hasChecksum = false);
void SendSentNotification(
const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set<User::Ptr>& users,
- NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text
+ NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text, double sendTime
);
void SendStartedDowntime(const Downtime::Ptr& downtime);
@@ -92,6 +94,16 @@ private:
void SendNextUpdate(const Checkable::Ptr& checkable);
void SendAcknowledgementSet(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, bool persistent, double changeTime, double expiry);
void SendAcknowledgementCleared(const Checkable::Ptr& checkable, const String& removedBy, double changeTime, double ackLastChange);
+ void SendNotificationUsersChanged(const Notification::Ptr& notification, const Array::Ptr& oldValues, const Array::Ptr& newValues);
+ void SendNotificationUserGroupsChanged(const Notification::Ptr& notification, const Array::Ptr& oldValues, const Array::Ptr& newValues);
+ void SendTimePeriodRangesChanged(const TimePeriod::Ptr& timeperiod, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues);
+ void SendTimePeriodIncludesChanged(const TimePeriod::Ptr& timeperiod, const Array::Ptr& oldValues, const Array::Ptr& newValues);
+ void SendTimePeriodExcludesChanged(const TimePeriod::Ptr& timeperiod, const Array::Ptr& oldValues, const Array::Ptr& newValues);
+ template<class T>
+ void SendGroupsChanged(const ConfigObject::Ptr& command, const Array::Ptr& oldValues, const Array::Ptr& newValues);
+ void SendCommandEnvChanged(const ConfigObject::Ptr& command, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues);
+ void SendCommandArgumentsChanged(const ConfigObject::Ptr& command, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues);
+ void SendCustomVarsChanged(const ConfigObject::Ptr& object, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues);
std::vector<String> UpdateObjectAttrs(const ConfigObject::Ptr& object, int fieldType, const String& typeNameOverride);
Dictionary::Ptr SerializeState(const Checkable::Ptr& checkable);
@@ -104,11 +116,13 @@ private:
static String FormatCommandLine(const Value& commandLine);
static long long TimestampToMilliseconds(double timestamp);
static String IcingaToStreamValue(const Value& value);
+ static std::vector<Value> GetArrayDeletedValues(const Array::Ptr& arrayOld, const Array::Ptr& arrayNew);
+ static std::vector<String> GetDictionaryDeletedKeys(const Dictionary::Ptr& dictOld, const Dictionary::Ptr& dictNew);
- static ArrayData GetObjectIdentifiersWithoutEnv(const ConfigObject::Ptr& object);
static String GetObjectIdentifier(const ConfigObject::Ptr& object);
- static String GetEnvironment();
- static Dictionary::Ptr SerializeVars(const CustomVarObject::Ptr& object);
+ static String CalcEventID(const char* eventType, const ConfigObject::Ptr& object, double eventTime = 0, NotificationType nt = NotificationType(0));
+ static const char* GetNotificationTypeByEnum(NotificationType type);
+ static Dictionary::Ptr SerializeVars(const Dictionary::Ptr& vars);
static String HashValue(const Value& value);
static String HashValue(const Value& value, const std::set<String>& propertiesBlacklist, bool propertiesWhitelist = false);
@@ -134,30 +148,22 @@ private:
static void NextCheckChangedHandler(const Checkable::Ptr& checkable);
static void AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, bool persistent, double changeTime, double expiry);
static void AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const String& removedBy, double changeTime);
+ static void NotificationUsersChangedHandler(const Notification::Ptr& notification, const Array::Ptr& oldValues, const Array::Ptr& newValues);
+ static void NotificationUserGroupsChangedHandler(const Notification::Ptr& notification, const Array::Ptr& oldValues, const Array::Ptr& newValues);
+ static void TimePeriodRangesChangedHandler(const TimePeriod::Ptr& timeperiod, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues);
+ static void TimePeriodIncludesChangedHandler(const TimePeriod::Ptr& timeperiod, const Array::Ptr& oldValues, const Array::Ptr& newValues);
+ static void TimePeriodExcludesChangedHandler(const TimePeriod::Ptr& timeperiod, const Array::Ptr& oldValues, const Array::Ptr& newValues);
+ static void UserGroupsChangedHandler(const User::Ptr& user, const Array::Ptr&, const Array::Ptr& newValues);
+ static void HostGroupsChangedHandler(const Host::Ptr& host, const Array::Ptr& oldValues, const Array::Ptr& newValues);
+ static void ServiceGroupsChangedHandler(const Service::Ptr& service, const Array::Ptr& oldValues, const Array::Ptr& newValues);
+ static void CommandEnvChangedHandler(const ConfigObject::Ptr& command, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues);
+ static void CommandArgumentsChangedHandler(const ConfigObject::Ptr& command, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues);
+ static void CustomVarsChangedHandler(const ConfigObject::Ptr& object, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues);
void AssertOnWorkQueue();
void ExceptionHandler(boost::exception_ptr exp);
- template<class T>
- static inline
- std::vector<T> Prepend(std::vector<T>&& needle, std::vector<T>&& haystack)
- {
- for (auto& hay : haystack) {
- needle.emplace_back(std::move(hay));
- }
-
- return std::move(needle);
- }
-
- template<class T, class Needle>
- static inline
- std::vector<T> Prepend(Needle&& needle, std::vector<T>&& haystack)
- {
- haystack.emplace(haystack.begin(), std::forward<Needle>(needle));
- return std::move(haystack);
- }
-
static std::vector<Type::Ptr> GetTypes();
Timer::Ptr m_StatsTimer;
@@ -178,7 +184,7 @@ private:
} m_DumpedGlobals;
static String m_EnvironmentId;
- static boost::once_flag m_EnvironmentIdOnce;
+ static std::once_flag m_EnvironmentIdOnce;
};
}
=====================================
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;
+ };
};
}
=====================================
lib/icingadb/redisconnection.cpp
=====================================
@@ -116,7 +116,7 @@ void LogQuery(RedisConnection::Query& query, Log& msg)
void RedisConnection::FireAndForgetQuery(RedisConnection::Query query, RedisConnection::QueryPriority priority)
{
{
- Log msg (LogNotice, "IcingaDB", "Firing and forgetting query:");
+ Log msg (LogDebug, "IcingaDB", "Firing and forgetting query:");
LogQuery(query, msg);
}
@@ -138,7 +138,7 @@ void RedisConnection::FireAndForgetQuery(RedisConnection::Query query, RedisConn
void RedisConnection::FireAndForgetQueries(RedisConnection::Queries queries, RedisConnection::QueryPriority priority)
{
for (auto& query : queries) {
- Log msg (LogNotice, "IcingaDB", "Firing and forgetting query:");
+ Log msg (LogDebug, "IcingaDB", "Firing and forgetting query:");
LogQuery(query, msg);
}
@@ -162,7 +162,7 @@ void RedisConnection::FireAndForgetQueries(RedisConnection::Queries queries, Red
RedisConnection::Reply RedisConnection::GetResultOfQuery(RedisConnection::Query query, RedisConnection::QueryPriority priority)
{
{
- Log msg (LogNotice, "IcingaDB", "Executing query:");
+ Log msg (LogDebug, "IcingaDB", "Executing query:");
LogQuery(query, msg);
}
@@ -192,7 +192,7 @@ RedisConnection::Reply RedisConnection::GetResultOfQuery(RedisConnection::Query
RedisConnection::Replies RedisConnection::GetResultsOfQueries(RedisConnection::Queries queries, RedisConnection::QueryPriority priority)
{
for (auto& query : queries) {
- Log msg (LogNotice, "IcingaDB", "Executing query:");
+ Log msg (LogDebug, "IcingaDB", "Executing query:");
LogQuery(query, msg);
}
=====================================
test/CMakeLists.txt
=====================================
@@ -70,6 +70,7 @@ add_boost_test(base
base_dictionary/remove
base_dictionary/clone
base_dictionary/json
+ base_dictionary/keys_ordered
base_fifo/construct
base_fifo/io
base_json/encode
=====================================
test/base-dictionary.cpp
=====================================
@@ -3,6 +3,8 @@
#include "base/dictionary.hpp"
#include "base/objectlock.hpp"
#include "base/json.hpp"
+#include "base/string.hpp"
+#include "base/utility.hpp"
#include <BoostTestTargetConfig.h>
using namespace icinga;
@@ -183,4 +185,16 @@ BOOST_AUTO_TEST_CASE(json)
BOOST_CHECK(deserialized->Get("test2") == "hello world");
}
+BOOST_AUTO_TEST_CASE(keys_ordered)
+{
+ Dictionary::Ptr dictionary = new Dictionary();
+
+ for (int i = 0; i < 100; i++) {
+ dictionary->Set(std::to_string(Utility::Random()), Utility::Random());
+ }
+
+ std::vector<String> keys = dictionary->GetKeys();
+ BOOST_CHECK(std::is_sorted(keys.begin(), keys.end()));
+}
+
BOOST_AUTO_TEST_SUITE_END()
=====================================
tools/mkclass/class_lexer.ll
=====================================
@@ -134,6 +134,7 @@ no_user_view { yylval->num = FANoUserView; return T_FIELD_ATTRIBUTE; }
deprecated { yylval->num = FADeprecated; return T_FIELD_ATTRIBUTE; }
get_virtual { yylval->num = FAGetVirtual; return T_FIELD_ATTRIBUTE; }
set_virtual { yylval->num = FASetVirtual; return T_FIELD_ATTRIBUTE; }
+signal_with_old_value { yylval->num = FASignalWithOldValue; return T_FIELD_ATTRIBUTE; }
virtual { yylval->num = FAGetVirtual | FASetVirtual; return T_FIELD_ATTRIBUTE; }
navigation { return T_NAVIGATION; }
validator { return T_VALIDATOR; }
=====================================
tools/mkclass/classcompiler.cpp
=====================================
@@ -830,10 +830,10 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
m_Impl << "void ObjectImpl<" << klass.Name << ">::Set" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " value, bool suppress_events, const Value& cookie)" << std::endl
<< "{" << std::endl;
- if (field.Type.IsName || !field.TrackAccessor.empty())
- m_Impl << "\t" << "Value oldValue = Get" << field.GetFriendlyName() << "();" << std::endl;
+ if (field.Type.IsName || !field.TrackAccessor.empty() || field.Attributes & FASignalWithOldValue)
+ m_Impl << "\t" << "Value oldValue = Get" << field.GetFriendlyName() << "();" << std::endl
+ << "\t" << "auto *dobj = dynamic_cast<ConfigObject *>(this);" << std::endl;
-
if (field.SetAccessor.empty() && !(field.Attributes & FANoStorage))
m_Impl << "\t" << "m_" << field.GetFriendlyName() << ".store(value);" << std::endl;
else
@@ -841,16 +841,22 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
if (field.Type.IsName || !field.TrackAccessor.empty()) {
if (field.Name != "active") {
- m_Impl << "\t" << "auto *dobj = dynamic_cast<ConfigObject *>(this);" << std::endl
- << "\t" << "if (!dobj || dobj->IsActive())" << std::endl
+ m_Impl << "\t" << "if (!dobj || dobj->IsActive())" << std::endl
<< "\t";
}
m_Impl << "\t" << "Track" << field.GetFriendlyName() << "(oldValue, value);" << std::endl;
}
- m_Impl << "\t" << "if (!suppress_events)" << std::endl
- << "\t\t" << "Notify" << field.GetFriendlyName() << "(cookie);" << std::endl
+ m_Impl << "\t" << "if (!suppress_events) {" << std::endl
+ << "\t\t" << "Notify" << field.GetFriendlyName() << "(cookie);" << std::endl;
+
+ if (field.Attributes & FASignalWithOldValue) {
+ m_Impl << "\t\t" << "if (!dobj || dobj->IsActive())" << std::endl
+ << "\t\t\t" << "On" << field.GetFriendlyName() << "ChangedWithOldValue(static_cast<" << klass.Name << " *>(this), oldValue, value);" << std::endl;
+ }
+
+ m_Impl << "\t" "}" << std::endl << std::endl
<< "}" << std::endl << std::endl;
}
}
@@ -1053,6 +1059,15 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
for (const Field& field : klass.Fields) {
m_Header << "\t" << "static boost::signals2::signal<void (const intrusive_ptr<" << klass.Name << ">&, const Value&)> On" << field.GetFriendlyName() << "Changed;" << std::endl;
m_Impl << std::endl << "boost::signals2::signal<void (const intrusive_ptr<" << klass.Name << ">&, const Value&)> ObjectImpl<" << klass.Name << ">::On" << field.GetFriendlyName() << "Changed;" << std::endl << std::endl;
+
+ if (field.Attributes & FASignalWithOldValue) {
+ m_Header << "\t" << "static boost::signals2::signal<void (const intrusive_ptr<" << klass.Name
+ << ">&, const Value&, const Value&)> On" << field.GetFriendlyName() << "ChangedWithOldValue;"
+ << std::endl;
+ m_Impl << std::endl << "boost::signals2::signal<void (const intrusive_ptr<" << klass.Name
+ << ">&, const Value&, const Value&)> ObjectImpl<" << klass.Name << ">::On"
+ << field.GetFriendlyName() << "ChangedWithOldValue;" << std::endl << std::endl;
+ }
}
}
=====================================
tools/mkclass/classcompiler.hpp
=====================================
@@ -60,7 +60,8 @@ enum FieldAttribute
FADeprecated = 4096,
FAGetVirtual = 8192,
FASetVirtual = 16384,
- FAActivationPriority = 32768
+ FAActivationPriority = 32768,
+ FASignalWithOldValue = 65536,
};
struct FieldType
View it on GitLab: https://salsa.debian.org/nagios-team/pkg-icinga2/-/commit/ed7c657aea415234ac5ba145893c1c8352ae3519
--
View it on GitLab: https://salsa.debian.org/nagios-team/pkg-icinga2/-/commit/ed7c657aea415234ac5ba145893c1c8352ae3519
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-nagios-changes/attachments/20211113/54eaea63/attachment-0001.htm>
More information about the pkg-nagios-changes
mailing list