]> granicus.if.org Git - icinga2/blob - lib/icinga/downtime.cpp
Remove unused includes
[icinga2] / lib / icinga / downtime.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/)  *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "icinga/downtime.hpp"
21 #include "icinga/downtime-ti.cpp"
22 #include "icinga/host.hpp"
23 #include "icinga/scheduleddowntime.hpp"
24 #include "remote/configobjectutility.hpp"
25 #include "base/configtype.hpp"
26 #include "base/utility.hpp"
27 #include "base/timer.hpp"
28 #include <boost/thread/once.hpp>
29
30 using namespace icinga;
31
32 static int l_NextDowntimeID = 1;
33 static boost::mutex l_DowntimeMutex;
34 static std::map<int, String> l_LegacyDowntimesCache;
35 static Timer::Ptr l_DowntimesExpireTimer;
36 static Timer::Ptr l_DowntimesStartTimer;
37
38 boost::signals2::signal<void (const Downtime::Ptr&)> Downtime::OnDowntimeAdded;
39 boost::signals2::signal<void (const Downtime::Ptr&)> Downtime::OnDowntimeRemoved;
40 boost::signals2::signal<void (const Downtime::Ptr&)> Downtime::OnDowntimeStarted;
41 boost::signals2::signal<void (const Downtime::Ptr&)> Downtime::OnDowntimeTriggered;
42
43 REGISTER_TYPE(Downtime);
44
45 String DowntimeNameComposer::MakeName(const String& shortName, const Object::Ptr& context) const
46 {
47         Downtime::Ptr downtime = dynamic_pointer_cast<Downtime>(context);
48
49         if (!downtime)
50                 return "";
51
52         String name = downtime->GetHostName();
53
54         if (!downtime->GetServiceName().IsEmpty())
55                 name += "!" + downtime->GetServiceName();
56
57         name += "!" + shortName;
58
59         return name;
60 }
61
62 Dictionary::Ptr DowntimeNameComposer::ParseName(const String& name) const
63 {
64         std::vector<String> tokens = name.Split("!");
65
66         if (tokens.size() < 2)
67                 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid Downtime name."));
68
69         Dictionary::Ptr result = new Dictionary();
70         result->Set("host_name", tokens[0]);
71
72         if (tokens.size() > 2) {
73                 result->Set("service_name", tokens[1]);
74                 result->Set("name", tokens[2]);
75         } else {
76                 result->Set("name", tokens[1]);
77         }
78
79         return result;
80 }
81
82 void Downtime::OnAllConfigLoaded()
83 {
84         ObjectImpl<Downtime>::OnAllConfigLoaded();
85
86         Host::Ptr host = Host::GetByName(GetHostName());
87
88         if (GetServiceName().IsEmpty())
89                 m_Checkable = host;
90         else
91                 m_Checkable = host->GetServiceByShortName(GetServiceName());
92
93         if (!m_Checkable)
94                 BOOST_THROW_EXCEPTION(ScriptError("Downtime '" + GetName() + "' references a host/service which doesn't exist.", GetDebugInfo()));
95 }
96
97 void Downtime::Start(bool runtimeCreated)
98 {
99         ObjectImpl<Downtime>::Start(runtimeCreated);
100
101         static boost::once_flag once = BOOST_ONCE_INIT;
102
103         boost::call_once(once, [this]() {
104                 l_DowntimesStartTimer = new Timer();
105                 l_DowntimesStartTimer->SetInterval(5);
106                 l_DowntimesStartTimer->OnTimerExpired.connect(std::bind(&Downtime::DowntimesStartTimerHandler));
107                 l_DowntimesStartTimer->Start();
108
109                 l_DowntimesExpireTimer = new Timer();
110                 l_DowntimesExpireTimer->SetInterval(60);
111                 l_DowntimesExpireTimer->OnTimerExpired.connect(std::bind(&Downtime::DowntimesExpireTimerHandler));
112                 l_DowntimesExpireTimer->Start();
113         });
114
115         {
116                 boost::mutex::scoped_lock lock(l_DowntimeMutex);
117
118                 SetLegacyId(l_NextDowntimeID);
119                 l_LegacyDowntimesCache[l_NextDowntimeID] = GetName();
120                 l_NextDowntimeID++;
121         }
122
123         Checkable::Ptr checkable = GetCheckable();
124
125         checkable->RegisterDowntime(this);
126
127         if (runtimeCreated)
128                 OnDowntimeAdded(this);
129
130         /* if this object is already in a NOT-OK state trigger
131          * this downtime now *after* it has been added (important
132          * for DB IDO, etc.)
133          */
134         if (!checkable->IsStateOK(checkable->GetStateRaw())) {
135                 Log(LogNotice, "Downtime")
136                         << "Checkable '" << checkable->GetName() << "' already in a NOT-OK state."
137                         << " Triggering downtime now.";
138                 TriggerDowntime();
139         }
140 }
141
142 void Downtime::Stop(bool runtimeRemoved)
143 {
144         GetCheckable()->UnregisterDowntime(this);
145
146         if (runtimeRemoved)
147                 OnDowntimeRemoved(this);
148
149         ObjectImpl<Downtime>::Stop(runtimeRemoved);
150 }
151
152 Checkable::Ptr Downtime::GetCheckable() const
153 {
154         return static_pointer_cast<Checkable>(m_Checkable);
155 }
156
157 bool Downtime::IsInEffect() const
158 {
159         double now = Utility::GetTime();
160
161         if (now < GetStartTime() ||
162                 now > GetEndTime())
163                 return false;
164
165         if (GetFixed())
166                 return true;
167
168         double triggerTime = GetTriggerTime();
169
170         if (triggerTime == 0)
171                 return false;
172
173         return (now < triggerTime + GetDuration());
174 }
175
176 bool Downtime::IsTriggered() const
177 {
178         double now = Utility::GetTime();
179
180         double triggerTime = GetTriggerTime();
181
182         return (triggerTime > 0 && triggerTime <= now);
183 }
184
185 bool Downtime::IsExpired() const
186 {
187         double now = Utility::GetTime();
188
189         if (GetFixed())
190                 return (GetEndTime() < now);
191         else {
192                 /* triggered flexible downtime not in effect anymore */
193                 if (IsTriggered() && !IsInEffect())
194                         return true;
195                 /* flexible downtime never triggered */
196                 else if (!IsTriggered() && (GetEndTime() < now))
197                         return true;
198                 else
199                         return false;
200         }
201 }
202
203 bool Downtime::HasValidConfigOwner() const
204 {
205         String configOwner = GetConfigOwner();
206         return configOwner.IsEmpty() || GetObject<ScheduledDowntime>(configOwner);
207 }
208
209 int Downtime::GetNextDowntimeID()
210 {
211         boost::mutex::scoped_lock lock(l_DowntimeMutex);
212
213         return l_NextDowntimeID;
214 }
215
216 String Downtime::AddDowntime(const Checkable::Ptr& checkable, const String& author,
217         const String& comment, double startTime, double endTime, bool fixed,
218         const String& triggeredBy, double duration,
219         const String& scheduledDowntime, const String& scheduledBy,
220         const String& id, const MessageOrigin::Ptr& origin)
221 {
222         String fullName;
223
224         if (id.IsEmpty())
225                 fullName = checkable->GetName() + "!" + Utility::NewUniqueID();
226         else
227                 fullName = id;
228
229         Dictionary::Ptr attrs = new Dictionary();
230
231         attrs->Set("author", author);
232         attrs->Set("comment", comment);
233         attrs->Set("start_time", startTime);
234         attrs->Set("end_time", endTime);
235         attrs->Set("fixed", fixed);
236         attrs->Set("duration", duration);
237         attrs->Set("triggered_by", triggeredBy);
238         attrs->Set("scheduled_by", scheduledBy);
239         attrs->Set("config_owner", scheduledDowntime);
240         attrs->Set("entry_time", Utility::GetTime());
241
242         Host::Ptr host;
243         Service::Ptr service;
244         tie(host, service) = GetHostService(checkable);
245
246         attrs->Set("host_name", host->GetName());
247         if (service)
248                 attrs->Set("service_name", service->GetShortName());
249
250         String zone = checkable->GetZoneName();
251
252         if (!zone.IsEmpty())
253                 attrs->Set("zone", zone);
254
255         String config = ConfigObjectUtility::CreateObjectConfig(Downtime::TypeInstance, fullName, true, nullptr, attrs);
256
257         Array::Ptr errors = new Array();
258
259         if (!ConfigObjectUtility::CreateObject(Downtime::TypeInstance, fullName, config, errors)) {
260                 ObjectLock olock(errors);
261                 for (const String& error : errors) {
262                         Log(LogCritical, "Downtime", error);
263                 }
264
265                 BOOST_THROW_EXCEPTION(std::runtime_error("Could not create downtime."));
266         }
267
268         if (!triggeredBy.IsEmpty()) {
269                 Downtime::Ptr parentDowntime = Downtime::GetByName(triggeredBy);
270                 Array::Ptr triggers = parentDowntime->GetTriggers();
271
272                 ObjectLock olock(triggers);
273                 if (!triggers->Contains(fullName))
274                         triggers->Add(fullName);
275         }
276
277         Downtime::Ptr downtime = Downtime::GetByName(fullName);
278
279         if (!downtime)
280                 BOOST_THROW_EXCEPTION(std::runtime_error("Could not create downtime object."));
281
282         Log(LogNotice, "Downtime")
283                 << "Added downtime '" << downtime->GetName()
284                 << "' between '" << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", startTime)
285                 << "' and '" << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", endTime) << "'.";
286
287         return fullName;
288 }
289
290 void Downtime::RemoveDowntime(const String& id, bool cancelled, bool expired, const MessageOrigin::Ptr& origin)
291 {
292         Downtime::Ptr downtime = Downtime::GetByName(id);
293
294         if (!downtime || downtime->GetPackage() != "_api")
295                 return;
296
297         String config_owner = downtime->GetConfigOwner();
298
299         if (!config_owner.IsEmpty() && !expired) {
300                 Log(LogWarning, "Downtime")
301                         << "Cannot remove downtime '" << downtime->GetName() << "'. It is owned by scheduled downtime object '" << config_owner << "'";
302                 return;
303         }
304
305         downtime->SetWasCancelled(cancelled);
306
307         Log(LogNotice, "Downtime")
308                 << "Removed downtime '" << downtime->GetName() << "' from object '" << downtime->GetCheckable()->GetName() << "'.";
309
310         Array::Ptr errors = new Array();
311
312         if (!ConfigObjectUtility::DeleteObject(downtime, false, errors)) {
313                 ObjectLock olock(errors);
314                 for (const String& error : errors) {
315                         Log(LogCritical, "Downtime", error);
316                 }
317
318                 BOOST_THROW_EXCEPTION(std::runtime_error("Could not remove downtime."));
319         }
320 }
321
322 bool Downtime::CanBeTriggered()
323 {
324         if (IsInEffect() && IsTriggered())
325                 return false;
326
327         if (IsExpired())
328                 return false;
329
330         double now = Utility::GetTime();
331
332         if (now < GetStartTime() || now > GetEndTime())
333                 return false;
334
335         return true;
336 }
337
338 void Downtime::TriggerDowntime()
339 {
340         if (!CanBeTriggered())
341                 return;
342
343         Log(LogNotice, "Downtime")
344                 << "Triggering downtime '" << GetName() << "'.";
345
346         if (GetTriggerTime() == 0)
347                 SetTriggerTime(Utility::GetTime());
348
349         Array::Ptr triggers = GetTriggers();
350
351         {
352                 ObjectLock olock(triggers);
353                 for (const String& triggerName : triggers) {
354                         Downtime::Ptr downtime = Downtime::GetByName(triggerName);
355
356                         if (!downtime)
357                                 continue;
358
359                         downtime->TriggerDowntime();
360                 }
361         }
362
363         OnDowntimeTriggered(this);
364 }
365
366 String Downtime::GetDowntimeIDFromLegacyID(int id)
367 {
368         boost::mutex::scoped_lock lock(l_DowntimeMutex);
369
370         auto it = l_LegacyDowntimesCache.find(id);
371
372         if (it == l_LegacyDowntimesCache.end())
373                 return Empty;
374
375         return it->second;
376 }
377
378 void Downtime::DowntimesStartTimerHandler()
379 {
380         /* Start fixed downtimes. Flexible downtimes will be triggered on-demand. */
381         for (const Downtime::Ptr& downtime : ConfigType::GetObjectsByType<Downtime>()) {
382                 if (downtime->IsActive() &&
383                         downtime->CanBeTriggered() &&
384                         downtime->GetFixed()) {
385                         /* Send notifications. */
386                         OnDowntimeStarted(downtime);
387
388                         /* Trigger fixed downtime immediately. */
389                         downtime->TriggerDowntime();
390                 }
391         }
392 }
393
394 void Downtime::DowntimesExpireTimerHandler()
395 {
396         std::vector<Downtime::Ptr> downtimes;
397
398         for (const Downtime::Ptr& downtime : ConfigType::GetObjectsByType<Downtime>()) {
399                 downtimes.push_back(downtime);
400         }
401
402         for (const Downtime::Ptr& downtime : downtimes) {
403                 /* Only remove downtimes which are activated after daemon start. */
404                 if (downtime->IsActive() && (downtime->IsExpired() || !downtime->HasValidConfigOwner()))
405                         RemoveDowntime(downtime->GetName(), false, true);
406         }
407 }
408
409 void Downtime::ValidateStartTime(const Lazy<Timestamp>& lvalue, const ValidationUtils& utils)
410 {
411         ObjectImpl<Downtime>::ValidateStartTime(lvalue, utils);
412
413         if (lvalue() <= 0)
414                 BOOST_THROW_EXCEPTION(ValidationError(this, { "start_time" }, "Start time must be greater than 0."));
415 }
416
417 void Downtime::ValidateEndTime(const Lazy<Timestamp>& lvalue, const ValidationUtils& utils)
418 {
419         ObjectImpl<Downtime>::ValidateEndTime(lvalue, utils);
420
421         if (lvalue() <= 0)
422                 BOOST_THROW_EXCEPTION(ValidationError(this, { "end_time" }, "End time must be greater than 0."));
423 }