Skip to content

Commit

Permalink
Merge pull request #9243 from Icinga/bugfix/multi-ido-notification-id…
Browse files Browse the repository at this point in the history
…-212

IDO: fix incorrect contacts in notification history with multiple IDO instances on a single node
  • Loading branch information
julianbrost authored Mar 7, 2022
2 parents 0ccd7b7 + 69f181f commit 4bdde97
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 56 deletions.
15 changes: 13 additions & 2 deletions lib/db_ido/dbconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,19 @@ void DbConnection::Start(bool runtimeCreated)
Log(LogInformation, "DbConnection")
<< "'" << GetName() << "' started.";

DbObject::OnQuery.connect(std::bind(&DbConnection::ExecuteQuery, this, _1));
DbObject::OnMultipleQueries.connect(std::bind(&DbConnection::ExecuteMultipleQueries, this, _1));
auto onQuery = [this](const DbQuery& query) { ExecuteQuery(query); };
DbObject::OnQuery.connect(onQuery);

auto onMultipleQueries = [this](const std::vector<DbQuery>& multiQueries) { ExecuteMultipleQueries(multiQueries); };
DbObject::OnMultipleQueries.connect(onMultipleQueries);

DbObject::QueryCallbacks queryCallbacks;
queryCallbacks.Query = onQuery;
queryCallbacks.MultipleQueries = onMultipleQueries;

DbObject::OnMakeQueries.connect([queryCallbacks](const std::function<void (const DbObject::QueryCallbacks&)>& queryFunc) {
queryFunc(queryCallbacks);
});
}

void DbConnection::Stop(bool runtimeRemoved)
Expand Down
115 changes: 61 additions & 54 deletions lib/db_ido/dbevents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -791,72 +791,79 @@ void DbEvents::AddAcknowledgementInternal(const Checkable::Ptr& checkable, Ackno
void DbEvents::AddNotificationHistory(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)
{
/* start and end happen at the same time */
std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(Utility::GetTime());

DbQuery query1;
query1.Table = "notifications";
query1.Type = DbQueryInsert;
query1.Category = DbCatNotification;
query1.NotificationInsertID = new DbValue(DbValueObjectInsertID, -1);
/* NotificationInsertID has to be tracked per IDO instance, therefore the OnQuery and OnMultipleQueries signals
* cannot be called directly as all IDO instances would insert rows with the same ID which is (most likely) only
* correct in one database. Instead, pass a lambda which generates the queries with new DbValue for
* NotificationInsertID each IDO instance.
*/
DbObject::OnMakeQueries([&checkable, &users, &type, &cr](const DbObject::QueryCallbacks& callbacks) {
/* start and end happen at the same time */
std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(Utility::GetTime());

Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
DbQuery query1;
query1.Table = "notifications";
query1.Type = DbQueryInsert;
query1.Category = DbCatNotification;
query1.NotificationInsertID = new DbValue(DbValueObjectInsertID, -1);

Dictionary::Ptr fields1 = new Dictionary();
fields1->Set("notification_type", 1); /* service */
fields1->Set("notification_reason", MapNotificationReasonType(type));
fields1->Set("object_id", checkable);
fields1->Set("start_time", DbValue::FromTimestamp(timeBag.first));
fields1->Set("start_time_usec", timeBag.second);
fields1->Set("end_time", DbValue::FromTimestamp(timeBag.first));
fields1->Set("end_time_usec", timeBag.second);
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);

if (service)
fields1->Set("state", service->GetState());
else
fields1->Set("state", GetHostState(host));
Dictionary::Ptr fields1 = new Dictionary();
fields1->Set("notification_type", 1); /* service */
fields1->Set("notification_reason", MapNotificationReasonType(type));
fields1->Set("object_id", checkable);
fields1->Set("start_time", DbValue::FromTimestamp(timeBag.first));
fields1->Set("start_time_usec", timeBag.second);
fields1->Set("end_time", DbValue::FromTimestamp(timeBag.first));
fields1->Set("end_time_usec", timeBag.second);

if (service)
fields1->Set("state", service->GetState());
else
fields1->Set("state", GetHostState(host));

if (cr) {
fields1->Set("output", CompatUtility::GetCheckResultOutput(cr));
fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
}

if (cr) {
fields1->Set("output", CompatUtility::GetCheckResultOutput(cr));
fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
}
fields1->Set("escalated", 0);
fields1->Set("contacts_notified", static_cast<long>(users.size()));
fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */

fields1->Set("escalated", 0);
fields1->Set("contacts_notified", static_cast<long>(users.size()));
fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());

Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());
if (endpoint)
fields1->Set("endpoint_object_id", endpoint);

if (endpoint)
fields1->Set("endpoint_object_id", endpoint);
query1.Fields = fields1;
callbacks.Query(query1);

query1.Fields = fields1;
DbObject::OnQuery(query1);
std::vector<DbQuery> queries;

std::vector<DbQuery> queries;
for (const User::Ptr& user : users) {
DbQuery query2;
query2.Table = "contactnotifications";
query2.Type = DbQueryInsert;
query2.Category = DbCatNotification;

for (const User::Ptr& user : users) {
DbQuery query2;
query2.Table = "contactnotifications";
query2.Type = DbQueryInsert;
query2.Category = DbCatNotification;

query2.Fields = new Dictionary({
{ "contact_object_id", user },
{ "start_time", DbValue::FromTimestamp(timeBag.first) },
{ "start_time_usec", timeBag.second },
{ "end_time", DbValue::FromTimestamp(timeBag.first) },
{ "end_time_usec", timeBag.second },
{ "notification_id", query1.NotificationInsertID },
{ "instance_id", 0 } /* DbConnection class fills in real ID */
});
query2.Fields = new Dictionary({
{ "contact_object_id", user },
{ "start_time", DbValue::FromTimestamp(timeBag.first) },
{ "start_time_usec", timeBag.second },
{ "end_time", DbValue::FromTimestamp(timeBag.first) },
{ "end_time_usec", timeBag.second },
{ "notification_id", query1.NotificationInsertID },
{ "instance_id", 0 } /* DbConnection class fills in real ID */
});

queries.emplace_back(std::move(query2));
}
queries.emplace_back(std::move(query2));
}

DbObject::OnMultipleQueries(queries);
callbacks.MultipleQueries(queries);
});
}

/* statehistory */
Expand Down
1 change: 1 addition & 0 deletions lib/db_ido/dbobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ using namespace icinga;

boost::signals2::signal<void (const DbQuery&)> DbObject::OnQuery;
boost::signals2::signal<void (const std::vector<DbQuery>&)> DbObject::OnMultipleQueries;
boost::signals2::signal<void (const std::function<void (const DbObject::QueryCallbacks&)>&)> DbObject::OnMakeQueries;

INITIALIZE_ONCE(&DbObject::StaticInitialize);

Expand Down
6 changes: 6 additions & 0 deletions lib/db_ido/dbobject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,14 @@ class DbObject : public Object

static DbObject::Ptr GetOrCreateByObject(const ConfigObject::Ptr& object);

struct QueryCallbacks {
std::function<void(const DbQuery&)> Query;
std::function<void(const std::vector<DbQuery>&)> MultipleQueries;
};

static boost::signals2::signal<void (const DbQuery&)> OnQuery;
static boost::signals2::signal<void (const std::vector<DbQuery>&)> OnMultipleQueries;
static boost::signals2::signal<void (const std::function<void (const QueryCallbacks&)>&)> OnMakeQueries;

void SendConfigUpdateHeavy(const Dictionary::Ptr& configFields);
void SendConfigUpdateLight();
Expand Down

0 comments on commit 4bdde97

Please sign in to comment.