1 /******************************************************************************
3 * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) *
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. *
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. *
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 ******************************************************************************/
20 #include "icinga/scheduleddowntime.hpp"
21 #include "icinga/scheduleddowntime-ti.cpp"
22 #include "icinga/legacytimeperiod.hpp"
23 #include "icinga/downtime.hpp"
24 #include "icinga/service.hpp"
25 #include "base/timer.hpp"
26 #include "base/configtype.hpp"
27 #include "base/utility.hpp"
28 #include "base/objectlock.hpp"
29 #include "base/convert.hpp"
30 #include "base/logger.hpp"
31 #include "base/exception.hpp"
32 #include <boost/thread/once.hpp>
34 using namespace icinga;
36 REGISTER_TYPE(ScheduledDowntime);
38 static Timer::Ptr l_Timer;
40 String ScheduledDowntimeNameComposer::MakeName(const String& shortName, const Object::Ptr& context) const
42 ScheduledDowntime::Ptr downtime = dynamic_pointer_cast<ScheduledDowntime>(context);
47 String name = downtime->GetHostName();
49 if (!downtime->GetServiceName().IsEmpty())
50 name += "!" + downtime->GetServiceName();
52 name += "!" + shortName;
57 Dictionary::Ptr ScheduledDowntimeNameComposer::ParseName(const String& name) const
59 std::vector<String> tokens = name.Split("!");
61 if (tokens.size() < 2)
62 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid ScheduledDowntime name."));
64 Dictionary::Ptr result = new Dictionary();
65 result->Set("host_name", tokens[0]);
67 if (tokens.size() > 2) {
68 result->Set("service_name", tokens[1]);
69 result->Set("name", tokens[2]);
71 result->Set("name", tokens[1]);
77 void ScheduledDowntime::OnAllConfigLoaded()
79 ObjectImpl<ScheduledDowntime>::OnAllConfigLoaded();
82 BOOST_THROW_EXCEPTION(ScriptError("ScheduledDowntime '" + GetName() + "' references a host/service which doesn't exist.", GetDebugInfo()));
85 void ScheduledDowntime::Start(bool runtimeCreated)
87 ObjectImpl<ScheduledDowntime>::Start(runtimeCreated);
89 static boost::once_flag once = BOOST_ONCE_INIT;
91 boost::call_once(once, [this]() {
92 l_Timer = new Timer();
93 l_Timer->SetInterval(60);
94 l_Timer->OnTimerExpired.connect(std::bind(&ScheduledDowntime::TimerProc));
98 Utility::QueueAsyncCallback(std::bind(&ScheduledDowntime::CreateNextDowntime, this));
101 void ScheduledDowntime::TimerProc()
103 for (const ScheduledDowntime::Ptr& sd : ConfigType::GetObjectsByType<ScheduledDowntime>()) {
105 sd->CreateNextDowntime();
109 Checkable::Ptr ScheduledDowntime::GetCheckable() const
111 Host::Ptr host = Host::GetByName(GetHostName());
113 if (GetServiceName().IsEmpty())
116 return host->GetServiceByShortName(GetServiceName());
119 std::pair<double, double> ScheduledDowntime::FindNextSegment()
121 time_t refts = Utility::GetTime();
122 tm reference = Utility::LocalTime(refts);
124 Log(LogDebug, "ScheduledDowntime")
125 << "Finding next scheduled downtime segment for time " << refts;
127 Dictionary::Ptr ranges = GetRanges();
130 return std::make_pair(0, 0);
132 Array::Ptr segments = new Array();
134 Dictionary::Ptr bestSegment;
136 double now = Utility::GetTime();
138 ObjectLock olock(ranges);
139 for (const Dictionary::Pair& kv : ranges) {
140 Log(LogDebug, "ScheduledDowntime")
141 << "Evaluating segment: " << kv.first << ": " << kv.second << " at ";
143 Dictionary::Ptr segment = LegacyTimePeriod::FindNextSegment(kv.first, kv.second, &reference);
148 Log(LogDebug, "ScheduledDowntime")
149 << "Considering segment: " << Utility::FormatDateTime("%c", segment->Get("begin")) << " -> " << Utility::FormatDateTime("%c", segment->Get("end"));
151 double begin = segment->Get("begin");
156 if (!bestSegment || begin < bestBegin) {
157 bestSegment = segment;
163 return std::make_pair(bestSegment->Get("begin"), bestSegment->Get("end"));
165 return std::make_pair(0, 0);
168 void ScheduledDowntime::CreateNextDowntime()
170 for (const Downtime::Ptr& downtime : GetCheckable()->GetDowntimes()) {
171 if (downtime->GetScheduledBy() != GetName() ||
172 downtime->GetStartTime() < Utility::GetTime())
175 /* We've found a downtime that is owned by us and that hasn't started yet - we're done. */
179 std::pair<double, double> segment = FindNextSegment();
181 if (segment.first == 0 && segment.second == 0) {
182 tm reference = Utility::LocalTime(Utility::GetTime());
184 reference.tm_hour = 0;
185 reference.tm_min = 0;
186 reference.tm_sec = 0;
191 Downtime::AddDowntime(GetCheckable(), GetAuthor(), GetComment(),
192 segment.first, segment.second,
193 GetFixed(), String(), GetDuration(), GetName(), GetName());
196 void ScheduledDowntime::ValidateRanges(const Lazy<Dictionary::Ptr>& lvalue, const ValidationUtils& utils)
198 ObjectImpl<ScheduledDowntime>::ValidateRanges(lvalue, utils);
203 /* create a fake time environment to validate the definitions */
204 time_t refts = Utility::GetTime();
205 tm reference = Utility::LocalTime(refts);
206 Array::Ptr segments = new Array();
208 ObjectLock olock(lvalue());
209 for (const Dictionary::Pair& kv : lvalue()) {
213 LegacyTimePeriod::ParseTimeRange(kv.first, &begin_tm, &end_tm, &stride, &reference);
214 } catch (const std::exception& ex) {
215 BOOST_THROW_EXCEPTION(ValidationError(this, { "ranges" }, "Invalid time specification '" + kv.first + "': " + ex.what()));
219 LegacyTimePeriod::ProcessTimeRanges(kv.second, &reference, segments);
220 } catch (const std::exception& ex) {
221 BOOST_THROW_EXCEPTION(ValidationError(this, { "ranges" }, "Invalid time range definition '" + kv.second + "': " + ex.what()));