]> granicus.if.org Git - icinga2/blob - lib/icinga/clusterevents.cpp
Merge pull request #7064 from widhalmt/feature/icingacli-elasticsearch-7063
[icinga2] / lib / icinga / clusterevents.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "icinga/clusterevents.hpp"
4 #include "icinga/service.hpp"
5 #include "remote/apilistener.hpp"
6 #include "remote/endpoint.hpp"
7 #include "remote/messageorigin.hpp"
8 #include "remote/zone.hpp"
9 #include "remote/apifunction.hpp"
10 #include "remote/eventqueue.hpp"
11 #include "base/application.hpp"
12 #include "base/configtype.hpp"
13 #include "base/utility.hpp"
14 #include "base/perfdatavalue.hpp"
15 #include "base/exception.hpp"
16 #include "base/initialize.hpp"
17 #include "base/serializer.hpp"
18 #include "base/json.hpp"
19 #include <fstream>
20
21 using namespace icinga;
22
23 INITIALIZE_ONCE(&ClusterEvents::StaticInitialize);
24
25 REGISTER_APIFUNCTION(CheckResult, event, &ClusterEvents::CheckResultAPIHandler);
26 REGISTER_APIFUNCTION(SetNextCheck, event, &ClusterEvents::NextCheckChangedAPIHandler);
27 REGISTER_APIFUNCTION(SetNextNotification, event, &ClusterEvents::NextNotificationChangedAPIHandler);
28 REGISTER_APIFUNCTION(SetForceNextCheck, event, &ClusterEvents::ForceNextCheckChangedAPIHandler);
29 REGISTER_APIFUNCTION(SetForceNextNotification, event, &ClusterEvents::ForceNextNotificationChangedAPIHandler);
30 REGISTER_APIFUNCTION(SetAcknowledgement, event, &ClusterEvents::AcknowledgementSetAPIHandler);
31 REGISTER_APIFUNCTION(ClearAcknowledgement, event, &ClusterEvents::AcknowledgementClearedAPIHandler);
32 REGISTER_APIFUNCTION(ExecuteCommand, event, &ClusterEvents::ExecuteCommandAPIHandler);
33 REGISTER_APIFUNCTION(SendNotifications, event, &ClusterEvents::SendNotificationsAPIHandler);
34 REGISTER_APIFUNCTION(NotificationSentUser, event, &ClusterEvents::NotificationSentUserAPIHandler);
35 REGISTER_APIFUNCTION(NotificationSentToAllUsers, event, &ClusterEvents::NotificationSentToAllUsersAPIHandler);
36
37 void ClusterEvents::StaticInitialize()
38 {
39         Checkable::OnNewCheckResult.connect(&ClusterEvents::CheckResultHandler);
40         Checkable::OnNextCheckChanged.connect(&ClusterEvents::NextCheckChangedHandler);
41         Notification::OnNextNotificationChanged.connect(&ClusterEvents::NextNotificationChangedHandler);
42         Checkable::OnForceNextCheckChanged.connect(&ClusterEvents::ForceNextCheckChangedHandler);
43         Checkable::OnForceNextNotificationChanged.connect(&ClusterEvents::ForceNextNotificationChangedHandler);
44         Checkable::OnNotificationsRequested.connect(&ClusterEvents::SendNotificationsHandler);
45         Checkable::OnNotificationSentToUser.connect(&ClusterEvents::NotificationSentUserHandler);
46         Checkable::OnNotificationSentToAllUsers.connect(&ClusterEvents::NotificationSentToAllUsersHandler);
47
48         Checkable::OnAcknowledgementSet.connect(&ClusterEvents::AcknowledgementSetHandler);
49         Checkable::OnAcknowledgementCleared.connect(&ClusterEvents::AcknowledgementClearedHandler);
50 }
51
52 Dictionary::Ptr ClusterEvents::MakeCheckResultMessage(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr)
53 {
54         Dictionary::Ptr message = new Dictionary();
55         message->Set("jsonrpc", "2.0");
56         message->Set("method", "event::CheckResult");
57
58         Host::Ptr host;
59         Service::Ptr service;
60         tie(host, service) = GetHostService(checkable);
61
62         Dictionary::Ptr params = new Dictionary();
63         params->Set("host", host->GetName());
64         if (service)
65                 params->Set("service", service->GetShortName());
66         else {
67                 Value agent_service_name = checkable->GetExtension("agent_service_name");
68
69                 if (!agent_service_name.IsEmpty())
70                         params->Set("service", agent_service_name);
71         }
72         params->Set("cr", Serialize(cr));
73
74         message->Set("params", params);
75
76         return message;
77 }
78
79 void ClusterEvents::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const MessageOrigin::Ptr& origin)
80 {
81         ApiListener::Ptr listener = ApiListener::GetInstance();
82
83         if (!listener)
84                 return;
85
86         Dictionary::Ptr message = MakeCheckResultMessage(checkable, cr);
87         listener->RelayMessage(origin, checkable, message, true);
88 }
89
90 Value ClusterEvents::CheckResultAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
91 {
92         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
93
94         if (!endpoint) {
95                 Log(LogNotice, "ClusterEvents")
96                         << "Discarding 'check result' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
97                 return Empty;
98         }
99
100         CheckResult::Ptr cr;
101         Array::Ptr vperf;
102
103         if (params->Contains("cr")) {
104                 cr = new CheckResult();
105                 Dictionary::Ptr vcr = params->Get("cr");
106
107                 if (vcr && vcr->Contains("performance_data")) {
108                         vperf = vcr->Get("performance_data");
109
110                         if (vperf)
111                                 vcr->Remove("performance_data");
112
113                         Deserialize(cr, vcr, true);
114                 }
115         }
116
117         if (!cr)
118                 return Empty;
119
120         ArrayData rperf;
121
122         if (vperf) {
123                 ObjectLock olock(vperf);
124                 for (const Value& vp : vperf) {
125                         Value p;
126
127                         if (vp.IsObjectType<Dictionary>()) {
128                                 PerfdataValue::Ptr val = new PerfdataValue();
129                                 Deserialize(val, vp, true);
130                                 rperf.push_back(val);
131                         } else
132                                 rperf.push_back(vp);
133                 }
134         }
135
136         cr->SetPerformanceData(new Array(std::move(rperf)));
137
138         Host::Ptr host = Host::GetByName(params->Get("host"));
139
140         if (!host)
141                 return Empty;
142
143         Checkable::Ptr checkable;
144
145         if (params->Contains("service"))
146                 checkable = host->GetServiceByShortName(params->Get("service"));
147         else
148                 checkable = host;
149
150         if (!checkable)
151                 return Empty;
152
153         if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable) && endpoint != checkable->GetCommandEndpoint()) {
154                 Log(LogNotice, "ClusterEvents")
155                         << "Discarding 'check result' message for checkable '" << checkable->GetName()
156                         << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
157                 return Empty;
158         }
159
160         if (!checkable->IsPaused() && Zone::GetLocalZone() == checkable->GetZone() && endpoint == checkable->GetCommandEndpoint())
161                 checkable->ProcessCheckResult(cr);
162         else
163                 checkable->ProcessCheckResult(cr, origin);
164
165         return Empty;
166 }
167
168 void ClusterEvents::NextCheckChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
169 {
170         ApiListener::Ptr listener = ApiListener::GetInstance();
171
172         if (!listener)
173                 return;
174
175         Host::Ptr host;
176         Service::Ptr service;
177         tie(host, service) = GetHostService(checkable);
178
179         Dictionary::Ptr params = new Dictionary();
180         params->Set("host", host->GetName());
181         if (service)
182                 params->Set("service", service->GetShortName());
183         params->Set("next_check", checkable->GetNextCheck());
184
185         Dictionary::Ptr message = new Dictionary();
186         message->Set("jsonrpc", "2.0");
187         message->Set("method", "event::SetNextCheck");
188         message->Set("params", params);
189
190         listener->RelayMessage(origin, checkable, message, true);
191 }
192
193 Value ClusterEvents::NextCheckChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
194 {
195         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
196
197         if (!endpoint) {
198                 Log(LogNotice, "ClusterEvents")
199                         << "Discarding 'next check changed' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
200                 return Empty;
201         }
202
203         Host::Ptr host = Host::GetByName(params->Get("host"));
204
205         if (!host)
206                 return Empty;
207
208         Checkable::Ptr checkable;
209
210         if (params->Contains("service"))
211                 checkable = host->GetServiceByShortName(params->Get("service"));
212         else
213                 checkable = host;
214
215         if (!checkable)
216                 return Empty;
217
218         if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) {
219                 Log(LogNotice, "ClusterEvents")
220                         << "Discarding 'next check changed' message for checkable '" << checkable->GetName()
221                         << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
222                 return Empty;
223         }
224
225         double nextCheck = params->Get("next_check");
226
227         if (nextCheck < Application::GetStartTime() + 60)
228                 return Empty;
229
230         checkable->SetNextCheck(params->Get("next_check"), false, origin);
231
232         return Empty;
233 }
234
235 void ClusterEvents::NextNotificationChangedHandler(const Notification::Ptr& notification, const MessageOrigin::Ptr& origin)
236 {
237         ApiListener::Ptr listener = ApiListener::GetInstance();
238
239         if (!listener)
240                 return;
241
242         Dictionary::Ptr params = new Dictionary();
243         params->Set("notification", notification->GetName());
244         params->Set("next_notification", notification->GetNextNotification());
245
246         Dictionary::Ptr message = new Dictionary();
247         message->Set("jsonrpc", "2.0");
248         message->Set("method", "event::SetNextNotification");
249         message->Set("params", params);
250
251         listener->RelayMessage(origin, notification, message, true);
252 }
253
254 Value ClusterEvents::NextNotificationChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
255 {
256         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
257
258         if (!endpoint) {
259                 Log(LogNotice, "ClusterEvents")
260                         << "Discarding 'next notification changed' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
261                 return Empty;
262         }
263
264         Notification::Ptr notification = Notification::GetByName(params->Get("notification"));
265
266         if (!notification)
267                 return Empty;
268
269         if (origin->FromZone && !origin->FromZone->CanAccessObject(notification)) {
270                 Log(LogNotice, "ClusterEvents")
271                         << "Discarding 'next notification changed' message for notification '" << notification->GetName()
272                         << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
273                 return Empty;
274         }
275
276         double nextNotification = params->Get("next_notification");
277
278         if (nextNotification < Utility::GetTime())
279                 return Empty;
280
281         notification->SetNextNotification(nextNotification, false, origin);
282
283         return Empty;
284 }
285
286 void ClusterEvents::ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
287 {
288         ApiListener::Ptr listener = ApiListener::GetInstance();
289
290         if (!listener)
291                 return;
292
293         Host::Ptr host;
294         Service::Ptr service;
295         tie(host, service) = GetHostService(checkable);
296
297         Dictionary::Ptr params = new Dictionary();
298         params->Set("host", host->GetName());
299         if (service)
300                 params->Set("service", service->GetShortName());
301         params->Set("forced", checkable->GetForceNextCheck());
302
303         Dictionary::Ptr message = new Dictionary();
304         message->Set("jsonrpc", "2.0");
305         message->Set("method", "event::SetForceNextCheck");
306         message->Set("params", params);
307
308         listener->RelayMessage(origin, checkable, message, true);
309 }
310
311 Value ClusterEvents::ForceNextCheckChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
312 {
313         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
314
315         if (!endpoint) {
316                 Log(LogNotice, "ClusterEvents")
317                         << "Discarding 'force next check changed' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
318                 return Empty;
319         }
320
321         Host::Ptr host = Host::GetByName(params->Get("host"));
322
323         if (!host)
324                 return Empty;
325
326         Checkable::Ptr checkable;
327
328         if (params->Contains("service"))
329                 checkable = host->GetServiceByShortName(params->Get("service"));
330         else
331                 checkable = host;
332
333         if (!checkable)
334                 return Empty;
335
336         if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) {
337                 Log(LogNotice, "ClusterEvents")
338                         << "Discarding 'force next check' message for checkable '" << checkable->GetName()
339                         << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
340                 return Empty;
341         }
342
343         checkable->SetForceNextCheck(params->Get("forced"), false, origin);
344
345         return Empty;
346 }
347
348 void ClusterEvents::ForceNextNotificationChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
349 {
350         ApiListener::Ptr listener = ApiListener::GetInstance();
351
352         if (!listener)
353                 return;
354
355         Host::Ptr host;
356         Service::Ptr service;
357         tie(host, service) = GetHostService(checkable);
358
359         Dictionary::Ptr params = new Dictionary();
360         params->Set("host", host->GetName());
361         if (service)
362                 params->Set("service", service->GetShortName());
363         params->Set("forced", checkable->GetForceNextNotification());
364
365         Dictionary::Ptr message = new Dictionary();
366         message->Set("jsonrpc", "2.0");
367         message->Set("method", "event::SetForceNextNotification");
368         message->Set("params", params);
369
370         listener->RelayMessage(origin, checkable, message, true);
371 }
372
373 Value ClusterEvents::ForceNextNotificationChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
374 {
375         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
376
377         if (!endpoint) {
378                 Log(LogNotice, "ClusterEvents")
379                         << "Discarding 'force next notification changed' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
380                 return Empty;
381         }
382
383         Host::Ptr host = Host::GetByName(params->Get("host"));
384
385         if (!host)
386                 return Empty;
387
388         Checkable::Ptr checkable;
389
390         if (params->Contains("service"))
391                 checkable = host->GetServiceByShortName(params->Get("service"));
392         else
393                 checkable = host;
394
395         if (!checkable)
396                 return Empty;
397
398         if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) {
399                 Log(LogNotice, "ClusterEvents")
400                         << "Discarding 'force next notification' message for checkable '" << checkable->GetName()
401                         << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
402                 return Empty;
403         }
404
405         checkable->SetForceNextNotification(params->Get("forced"), false, origin);
406
407         return Empty;
408 }
409
410 void ClusterEvents::AcknowledgementSetHandler(const Checkable::Ptr& checkable,
411         const String& author, const String& comment, AcknowledgementType type,
412         bool notify, bool persistent, double expiry, const MessageOrigin::Ptr& origin)
413 {
414         ApiListener::Ptr listener = ApiListener::GetInstance();
415
416         if (!listener)
417                 return;
418
419         Host::Ptr host;
420         Service::Ptr service;
421         tie(host, service) = GetHostService(checkable);
422
423         Dictionary::Ptr params = new Dictionary();
424         params->Set("host", host->GetName());
425         if (service)
426                 params->Set("service", service->GetShortName());
427         params->Set("author", author);
428         params->Set("comment", comment);
429         params->Set("acktype", type);
430         params->Set("notify", notify);
431         params->Set("persistent", persistent);
432         params->Set("expiry", expiry);
433
434         Dictionary::Ptr message = new Dictionary();
435         message->Set("jsonrpc", "2.0");
436         message->Set("method", "event::SetAcknowledgement");
437         message->Set("params", params);
438
439         listener->RelayMessage(origin, checkable, message, true);
440 }
441
442 Value ClusterEvents::AcknowledgementSetAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
443 {
444         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
445
446         if (!endpoint) {
447                 Log(LogNotice, "ClusterEvents")
448                         << "Discarding 'acknowledgement set' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
449                 return Empty;
450         }
451
452         Host::Ptr host = Host::GetByName(params->Get("host"));
453
454         if (!host)
455                 return Empty;
456
457         Checkable::Ptr checkable;
458
459         if (params->Contains("service"))
460                 checkable = host->GetServiceByShortName(params->Get("service"));
461         else
462                 checkable = host;
463
464         if (!checkable)
465                 return Empty;
466
467         if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) {
468                 Log(LogNotice, "ClusterEvents")
469                         << "Discarding 'acknowledgement set' message for checkable '" << checkable->GetName()
470                         << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
471                 return Empty;
472         }
473
474         checkable->AcknowledgeProblem(params->Get("author"), params->Get("comment"),
475                 static_cast<AcknowledgementType>(static_cast<int>(params->Get("acktype"))),
476                 params->Get("notify"), params->Get("persistent"), params->Get("expiry"), origin);
477
478         return Empty;
479 }
480
481 void ClusterEvents::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin)
482 {
483         ApiListener::Ptr listener = ApiListener::GetInstance();
484
485         if (!listener)
486                 return;
487
488         Host::Ptr host;
489         Service::Ptr service;
490         tie(host, service) = GetHostService(checkable);
491
492         Dictionary::Ptr params = new Dictionary();
493         params->Set("host", host->GetName());
494         if (service)
495                 params->Set("service", service->GetShortName());
496
497         Dictionary::Ptr message = new Dictionary();
498         message->Set("jsonrpc", "2.0");
499         message->Set("method", "event::ClearAcknowledgement");
500         message->Set("params", params);
501
502         listener->RelayMessage(origin, checkable, message, true);
503 }
504
505 Value ClusterEvents::AcknowledgementClearedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
506 {
507         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
508
509         if (!endpoint) {
510                 Log(LogNotice, "ClusterEvents")
511                         << "Discarding 'acknowledgement cleared' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
512                 return Empty;
513         }
514
515         Host::Ptr host = Host::GetByName(params->Get("host"));
516
517         if (!host)
518                 return Empty;
519
520         Checkable::Ptr checkable;
521
522         if (params->Contains("service"))
523                 checkable = host->GetServiceByShortName(params->Get("service"));
524         else
525                 checkable = host;
526
527         if (!checkable)
528                 return Empty;
529
530         if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) {
531                 Log(LogNotice, "ClusterEvents")
532                         << "Discarding 'acknowledgement cleared' message for checkable '" << checkable->GetName()
533                         << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
534                 return Empty;
535         }
536
537         checkable->ClearAcknowledgement(origin);
538
539         return Empty;
540 }
541
542 Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
543 {
544         EnqueueCheck(origin, params);
545
546         return Empty;
547 }
548
549 void ClusterEvents::SendNotificationsHandler(const Checkable::Ptr& checkable, NotificationType type,
550         const CheckResult::Ptr& cr, const String& author, const String& text, const MessageOrigin::Ptr& origin)
551 {
552         ApiListener::Ptr listener = ApiListener::GetInstance();
553
554         if (!listener)
555                 return;
556
557         Dictionary::Ptr message = MakeCheckResultMessage(checkable, cr);
558         message->Set("method", "event::SendNotifications");
559
560         Dictionary::Ptr params = message->Get("params");
561         params->Set("type", type);
562         params->Set("author", author);
563         params->Set("text", text);
564
565         listener->RelayMessage(origin, nullptr, message, true);
566 }
567
568 Value ClusterEvents::SendNotificationsAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
569 {
570         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
571
572         if (!endpoint) {
573                 Log(LogNotice, "ClusterEvents")
574                         << "Discarding 'send notification' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
575                 return Empty;
576         }
577
578         Host::Ptr host = Host::GetByName(params->Get("host"));
579
580         if (!host)
581                 return Empty;
582
583         Checkable::Ptr checkable;
584
585         if (params->Contains("service"))
586                 checkable = host->GetServiceByShortName(params->Get("service"));
587         else
588                 checkable = host;
589
590         if (!checkable)
591                 return Empty;
592
593         if (origin->FromZone && origin->FromZone != Zone::GetLocalZone()) {
594                 Log(LogNotice, "ClusterEvents")
595                         << "Discarding 'send custom notification' message for checkable '" << checkable->GetName()
596                         << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
597                 return Empty;
598         }
599
600         CheckResult::Ptr cr;
601         Array::Ptr vperf;
602
603         if (params->Contains("cr")) {
604                 cr = new CheckResult();
605                 Dictionary::Ptr vcr = params->Get("cr");
606
607                 if (vcr && vcr->Contains("performance_data")) {
608                         vperf = vcr->Get("performance_data");
609
610                         if (vperf)
611                                 vcr->Remove("performance_data");
612
613                         Deserialize(cr, vcr, true);
614                 }
615         }
616
617         NotificationType type = static_cast<NotificationType>(static_cast<int>(params->Get("type")));
618         String author = params->Get("author");
619         String text = params->Get("text");
620
621         Checkable::OnNotificationsRequested(checkable, type, cr, author, text, origin);
622
623         return Empty;
624 }
625
626 void ClusterEvents::NotificationSentUserHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const User::Ptr& user,
627         NotificationType notificationType, const CheckResult::Ptr& cr, const String& author, const String& commentText, const String& command,
628         const MessageOrigin::Ptr& origin)
629 {
630         ApiListener::Ptr listener = ApiListener::GetInstance();
631
632         if (!listener)
633                 return;
634
635         Host::Ptr host;
636         Service::Ptr service;
637         tie(host, service) = GetHostService(checkable);
638
639         Dictionary::Ptr params = new Dictionary();
640         params->Set("host", host->GetName());
641         if (service)
642                 params->Set("service", service->GetShortName());
643         params->Set("notification", notification->GetName());
644         params->Set("user", user->GetName());
645         params->Set("type", notificationType);
646         params->Set("cr", Serialize(cr));
647         params->Set("author", author);
648         params->Set("text", commentText);
649         params->Set("command", command);
650
651         Dictionary::Ptr message = new Dictionary();
652         message->Set("jsonrpc", "2.0");
653         message->Set("method", "event::NotificationSentUser");
654         message->Set("params", params);
655
656         listener->RelayMessage(origin, nullptr, message, true);
657 }
658
659 Value ClusterEvents::NotificationSentUserAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
660 {
661         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
662
663         if (!endpoint) {
664                 Log(LogNotice, "ClusterEvents")
665                         << "Discarding 'sent notification to user' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
666                 return Empty;
667         }
668
669         Host::Ptr host = Host::GetByName(params->Get("host"));
670
671         if (!host)
672                 return Empty;
673
674         Checkable::Ptr checkable;
675
676         if (params->Contains("service"))
677                 checkable = host->GetServiceByShortName(params->Get("service"));
678         else
679                 checkable = host;
680
681         if (!checkable)
682                 return Empty;
683
684         if (origin->FromZone && origin->FromZone != Zone::GetLocalZone()) {
685                 Log(LogNotice, "ClusterEvents")
686                         << "Discarding 'send notification to user' message for checkable '" << checkable->GetName()
687                         << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
688                 return Empty;
689         }
690
691         CheckResult::Ptr cr;
692         Array::Ptr vperf;
693
694         if (params->Contains("cr")) {
695                 cr = new CheckResult();
696                 Dictionary::Ptr vcr = params->Get("cr");
697
698                 if (vcr && vcr->Contains("performance_data")) {
699                         vperf = vcr->Get("performance_data");
700
701                         if (vperf)
702                                 vcr->Remove("performance_data");
703
704                         Deserialize(cr, vcr, true);
705                 }
706         }
707
708         NotificationType type = static_cast<NotificationType>(static_cast<int>(params->Get("type")));
709         String author = params->Get("author");
710         String text = params->Get("text");
711
712         Notification::Ptr notification = Notification::GetByName(params->Get("notification"));
713
714         if (!notification)
715                 return Empty;
716
717         User::Ptr user = User::GetByName(params->Get("user"));
718
719         if (!user)
720                 return Empty;
721
722         String command = params->Get("command");
723
724         Checkable::OnNotificationSentToUser(notification, checkable, user, type, cr, author, text, command, origin);
725
726         return Empty;
727 }
728
729 void ClusterEvents::NotificationSentToAllUsersHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set<User::Ptr>& users,
730         NotificationType notificationType, const CheckResult::Ptr& cr, const String& author, const String& commentText, const MessageOrigin::Ptr& origin)
731 {
732         ApiListener::Ptr listener = ApiListener::GetInstance();
733
734         if (!listener)
735                 return;
736
737         Host::Ptr host;
738         Service::Ptr service;
739         tie(host, service) = GetHostService(checkable);
740
741         Dictionary::Ptr params = new Dictionary();
742         params->Set("host", host->GetName());
743         if (service)
744                 params->Set("service", service->GetShortName());
745         params->Set("notification", notification->GetName());
746
747         ArrayData ausers;
748         for (const User::Ptr& user : users) {
749                 ausers.push_back(user->GetName());
750         }
751         params->Set("users", new Array(std::move(ausers)));
752
753         params->Set("type", notificationType);
754         params->Set("cr", Serialize(cr));
755         params->Set("author", author);
756         params->Set("text", commentText);
757
758         params->Set("last_notification", notification->GetLastNotification());
759         params->Set("next_notification", notification->GetNextNotification());
760         params->Set("notification_number", notification->GetNotificationNumber());
761         params->Set("last_problem_notification", notification->GetLastProblemNotification());
762         params->Set("no_more_notifications", notification->GetNoMoreNotifications());
763
764         Dictionary::Ptr message = new Dictionary();
765         message->Set("jsonrpc", "2.0");
766         message->Set("method", "event::NotificationSentToAllUsers");
767         message->Set("params", params);
768
769         listener->RelayMessage(origin, nullptr, message, true);
770 }
771
772 Value ClusterEvents::NotificationSentToAllUsersAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
773 {
774         Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint();
775
776         if (!endpoint) {
777                 Log(LogNotice, "ClusterEvents")
778                         << "Discarding 'sent notification to all users' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed).";
779                 return Empty;
780         }
781
782         Host::Ptr host = Host::GetByName(params->Get("host"));
783
784         if (!host)
785                 return Empty;
786
787         Checkable::Ptr checkable;
788
789         if (params->Contains("service"))
790                 checkable = host->GetServiceByShortName(params->Get("service"));
791         else
792                 checkable = host;
793
794         if (!checkable)
795                 return Empty;
796
797         if (origin->FromZone && origin->FromZone != Zone::GetLocalZone()) {
798                 Log(LogNotice, "ClusterEvents")
799                         << "Discarding 'sent notification to all users' message for checkable '" << checkable->GetName()
800                         << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access.";
801                 return Empty;
802         }
803
804         CheckResult::Ptr cr;
805         Array::Ptr vperf;
806
807         if (params->Contains("cr")) {
808                 cr = new CheckResult();
809                 Dictionary::Ptr vcr = params->Get("cr");
810
811                 if (vcr && vcr->Contains("performance_data")) {
812                         vperf = vcr->Get("performance_data");
813
814                         if (vperf)
815                                 vcr->Remove("performance_data");
816
817                         Deserialize(cr, vcr, true);
818                 }
819         }
820
821         NotificationType type = static_cast<NotificationType>(static_cast<int>(params->Get("type")));
822         String author = params->Get("author");
823         String text = params->Get("text");
824
825         Notification::Ptr notification = Notification::GetByName(params->Get("notification"));
826
827         if (!notification)
828                 return Empty;
829
830         Array::Ptr ausers = params->Get("users");
831
832         if (!ausers)
833                 return Empty;
834
835         std::set<User::Ptr> users;
836
837         {
838                 ObjectLock olock(ausers);
839                 for (const String& auser : ausers) {
840                         User::Ptr user = User::GetByName(auser);
841
842                         if (!user)
843                                 continue;
844
845                         users.insert(user);
846                 }
847         }
848
849         notification->SetLastNotification(params->Get("last_notification"));
850         notification->SetNextNotification(params->Get("next_notification"));
851         notification->SetNotificationNumber(params->Get("notification_number"));
852         notification->SetLastProblemNotification(params->Get("last_problem_notification"));
853         notification->SetNoMoreNotifications(params->Get("no_more_notifications"));
854
855         ArrayData notifiedProblemUsers;
856         for (const User::Ptr& user : users) {
857                 notifiedProblemUsers.push_back(user->GetName());
858         }
859
860         notification->SetNotifiedProblemUsers(new Array(std::move(notifiedProblemUsers)));
861
862         Checkable::OnNotificationSentToAllUsers(notification, checkable, users, type, cr, author, text, origin);
863
864         return Empty;
865 }