diff --git a/doc/19-technical-concepts.md b/doc/19-technical-concepts.md
index 1048e21d3..1da32b6f2 100644
--- a/doc/19-technical-concepts.md
+++ b/doc/19-technical-concepts.md
@@ -1370,6 +1370,43 @@ Message updates will be dropped when:
* Checkable does not exist.
* Origin endpoint's zone is not allowed to access this checkable.
+#### event::SetStateBeforeSuppression
+
+> Location: `clusterevents.cpp`
+
+##### Message Body
+
+Key | Value
+----------|---------------------------------
+jsonrpc | 2.0
+method | event::SetStateBeforeSuppression
+params | Dictionary
+
+##### Params
+
+Key | Type | Description
+---------------------------|--------|-----------------------------------------------
+host | String | Host name
+service | String | Service name
+state\_before\_suppression | Number | Checkable state before the current suppression
+
+##### Functions
+
+Event Sender: `Checkable::OnStateBeforeSuppressionChanged`
+Event Receiver: `StateBeforeSuppressionChangedAPIHandler`
+
+Used to sync the checkable state from before a notification suppression (for example
+because the checkable is in a downtime) started within the same HA zone.
+
+##### Permissions
+
+The receiver will not process messages from not configured endpoints.
+
+Message updates will be dropped when:
+
+* Checkable does not exist.
+* Origin endpoint is not within the local zone.
+
#### event::SetSuppressedNotifications
> Location: `clusterevents.cpp`
diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp
index bade7e5a5..e7fb9f3e0 100644
--- a/lib/icinga/clusterevents.cpp
+++ b/lib/icinga/clusterevents.cpp
@@ -25,6 +25,7 @@ INITIALIZE_ONCE(&ClusterEvents::StaticInitialize);
REGISTER_APIFUNCTION(CheckResult, event, &ClusterEvents::CheckResultAPIHandler);
REGISTER_APIFUNCTION(SetNextCheck, event, &ClusterEvents::NextCheckChangedAPIHandler);
REGISTER_APIFUNCTION(SetLastCheckStarted, event, &ClusterEvents::LastCheckStartedChangedAPIHandler);
+REGISTER_APIFUNCTION(SetStateBeforeSuppression, event, &ClusterEvents::StateBeforeSuppressionChangedAPIHandler);
REGISTER_APIFUNCTION(SetSuppressedNotifications, event, &ClusterEvents::SuppressedNotificationsChangedAPIHandler);
REGISTER_APIFUNCTION(SetSuppressedNotificationTypes, event, &ClusterEvents::SuppressedNotificationTypesChangedAPIHandler);
REGISTER_APIFUNCTION(SetNextNotification, event, &ClusterEvents::NextNotificationChangedAPIHandler);
@@ -45,6 +46,7 @@ void ClusterEvents::StaticInitialize()
Checkable::OnNewCheckResult.connect(&ClusterEvents::CheckResultHandler);
Checkable::OnNextCheckChanged.connect(&ClusterEvents::NextCheckChangedHandler);
Checkable::OnLastCheckStartedChanged.connect(&ClusterEvents::LastCheckStartedChangedHandler);
+ Checkable::OnStateBeforeSuppressionChanged.connect(&ClusterEvents::StateBeforeSuppressionChangedHandler);
Checkable::OnSuppressedNotificationsChanged.connect(&ClusterEvents::SuppressedNotificationsChangedHandler);
Notification::OnSuppressedNotificationsChanged.connect(&ClusterEvents::SuppressedNotificationTypesChangedHandler);
Notification::OnNextNotificationChanged.connect(&ClusterEvents::NextNotificationChangedHandler);
@@ -306,6 +308,68 @@ Value ClusterEvents::LastCheckStartedChangedAPIHandler(const MessageOrigin::Ptr&
return Empty;
}
+void ClusterEvents::StateBeforeSuppressionChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
+{
+ ApiListener::Ptr listener = ApiListener::GetInstance();
+
+ if (!listener)
+ return;
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ Dictionary::Ptr params = new Dictionary();
+ params->Set("host", host->GetName());
+ if (service)
+ params->Set("service", service->GetShortName());
+ params->Set("state_before_suppression", checkable->GetStateBeforeSuppression());
+
+ Dictionary::Ptr message = new Dictionary();
+ message->Set("jsonrpc", "2.0");
+ message->Set("method", "event::SetStateBeforeSuppression");
+ message->Set("params", params);
+
+ listener->RelayMessage(origin, nullptr, message, true);
+}
+
+Value ClusterEvents::StateBeforeSuppressionChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
+{
+ Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
+
+ if (!endpoint) {
+ Log(LogNotice, "ClusterEvents")
+ << "Discarding 'state before suppression changed' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
+ return Empty;
+ }
+
+ Host::Ptr host = Host::GetByName(params->Get("host"));
+
+ if (!host)
+ return Empty;
+
+ Checkable::Ptr checkable;
+
+ if (params->Contains("service"))
+ checkable = host->GetServiceByShortName(params->Get("service"));
+ else
+ checkable = host;
+
+ if (!checkable)
+ return Empty;
+
+ if (origin->FromZone && origin->FromZone != Zone::GetLocalZone()) {
+ Log(LogNotice, "ClusterEvents")
+ << "Discarding 'state before suppression changed' message for checkable '" << checkable->GetName()
+ << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
+ return Empty;
+ }
+
+ checkable->SetStateBeforeSuppression(ServiceState(int(params->Get("state_before_suppression"))), false, origin);
+
+ return Empty;
+}
+
void ClusterEvents::SuppressedNotificationsChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
diff --git a/lib/icinga/clusterevents.hpp b/lib/icinga/clusterevents.hpp
index a6c21971a..4cdadacc9 100644
--- a/lib/icinga/clusterevents.hpp
+++ b/lib/icinga/clusterevents.hpp
@@ -29,6 +29,9 @@ public:
static void LastCheckStartedChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin);
static Value LastCheckStartedChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
+ static void StateBeforeSuppressionChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin);
+ static Value StateBeforeSuppressionChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);
+
static void SuppressedNotificationsChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin);
static Value SuppressedNotificationsChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params);