]> granicus.if.org Git - icinga2/blob - lib/db_ido/dbconnection.cpp
Fix Zone::IsGlobal()
[icinga2] / lib / db_ido / dbconnection.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)    *
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 "db_ido/dbconnection.hpp"
21 #include "db_ido/dbvalue.hpp"
22 #include "icinga/icingaapplication.hpp"
23 #include "icinga/host.hpp"
24 #include "icinga/service.hpp"
25 #include "config/configcompilercontext.hpp"
26 #include "base/dynamictype.hpp"
27 #include "base/convert.hpp"
28 #include "base/objectlock.hpp"
29 #include "base/utility.hpp"
30 #include "base/initialize.hpp"
31 #include "base/logger.hpp"
32 #include "base/scriptfunction.hpp"
33 #include <boost/foreach.hpp>
34
35 using namespace icinga;
36
37 REGISTER_TYPE(DbConnection);
38 REGISTER_SCRIPTFUNCTION(ValidateFailoverTimeout, &DbConnection::ValidateFailoverTimeout);
39
40 Timer::Ptr DbConnection::m_ProgramStatusTimer;
41
42 INITIALIZE_ONCE(&DbConnection::StaticInitialize);
43
44 void DbConnection::OnConfigLoaded(void)
45 {
46         DynamicObject::OnConfigLoaded();
47
48         if (!GetEnableHa()) {
49                 Log(LogDebug, "DbConnection")
50                     << "HA functionality disabled. Won't pause IDO connection: " << GetName();
51
52                 SetHAMode(HARunEverywhere);
53         }
54 }
55
56 void DbConnection::Start(void)
57 {
58         DynamicObject::Start();
59
60         DbObject::OnQuery.connect(boost::bind(&DbConnection::ExecuteQuery, this, _1));
61 }
62
63 void DbConnection::Resume(void)
64 {
65         DynamicObject::Resume();
66
67         Log(LogInformation, "DbConnection")
68             << "Resuming IDO connection: " << GetName();
69
70         m_CleanUpTimer = make_shared<Timer>();
71         m_CleanUpTimer->SetInterval(60);
72         m_CleanUpTimer->OnTimerExpired.connect(boost::bind(&DbConnection::CleanUpHandler, this));
73         m_CleanUpTimer->Start();
74 }
75
76 void DbConnection::Pause(void)
77 {
78         DynamicObject::Pause();
79
80         Log(LogInformation, "DbConnection")
81              << "Pausing IDO connection: " << GetName();
82
83         m_CleanUpTimer.reset();
84 }
85
86 void DbConnection::StaticInitialize(void)
87 {
88         m_ProgramStatusTimer = make_shared<Timer>();
89         m_ProgramStatusTimer->SetInterval(10);
90         m_ProgramStatusTimer->OnTimerExpired.connect(boost::bind(&DbConnection::ProgramStatusHandler));
91         m_ProgramStatusTimer->Start();
92 }
93
94 void DbConnection::InsertRuntimeVariable(const String& key, const Value& value)
95 {
96         DbQuery query;
97         query.Table = "runtimevariables";
98         query.Type = DbQueryInsert;
99         query.Category = DbCatProgramStatus;
100         query.Fields = make_shared<Dictionary>();
101         query.Fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
102         query.Fields->Set("varname", key);
103         query.Fields->Set("varvalue", value);
104         DbObject::OnQuery(query);
105 }
106
107 void DbConnection::ProgramStatusHandler(void)
108 {
109         DbQuery query1;
110         query1.Table = "programstatus";
111         query1.Type = DbQueryDelete;
112         query1.Category = DbCatProgramStatus;
113         query1.WhereCriteria = make_shared<Dictionary>();
114         query1.WhereCriteria->Set("instance_id", 0);  /* DbConnection class fills in real ID */
115         DbObject::OnQuery(query1);
116
117         DbQuery query2;
118         query2.Table = "programstatus";
119         query2.IdColumn = "programstatus_id";
120         query2.Type = DbQueryInsert;
121         query2.Category = DbCatProgramStatus;
122
123         query2.Fields = make_shared<Dictionary>();
124         query2.Fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
125         query2.Fields->Set("program_version", Application::GetVersion());
126         query2.Fields->Set("status_update_time", DbValue::FromTimestamp(Utility::GetTime()));
127         query2.Fields->Set("program_start_time", DbValue::FromTimestamp(Application::GetStartTime()));
128         query2.Fields->Set("is_currently_running", 1);
129         query2.Fields->Set("endpoint_name", IcingaApplication::GetInstance()->GetNodeName());
130         query2.Fields->Set("process_id", Utility::GetPid());
131         query2.Fields->Set("daemon_mode", 1);
132         query2.Fields->Set("last_command_check", DbValue::FromTimestamp(Utility::GetTime()));
133         query2.Fields->Set("notifications_enabled", (IcingaApplication::GetInstance()->GetEnableNotifications() ? 1 : 0));
134         query2.Fields->Set("active_host_checks_enabled", (IcingaApplication::GetInstance()->GetEnableHostChecks() ? 1 : 0));
135         query2.Fields->Set("passive_host_checks_enabled", 1);
136         query2.Fields->Set("active_service_checks_enabled", (IcingaApplication::GetInstance()->GetEnableServiceChecks() ? 1 : 0));
137         query2.Fields->Set("passive_service_checks_enabled", 1);
138         query2.Fields->Set("event_handlers_enabled", (IcingaApplication::GetInstance()->GetEnableEventHandlers() ? 1 : 0));
139         query2.Fields->Set("flap_detection_enabled", (IcingaApplication::GetInstance()->GetEnableFlapping() ? 1 : 0));
140         query2.Fields->Set("process_performance_data", (IcingaApplication::GetInstance()->GetEnablePerfdata() ? 1 : 0));
141         DbObject::OnQuery(query2);
142
143         DbQuery query3;
144         query3.Table = "runtimevariables";
145         query3.Type = DbQueryDelete;
146         query3.Category = DbCatProgramStatus;
147         query3.WhereCriteria = make_shared<Dictionary>();
148         query3.WhereCriteria->Set("instance_id", 0);  /* DbConnection class fills in real ID */
149         DbObject::OnQuery(query3);
150
151         InsertRuntimeVariable("total_services", std::distance(DynamicType::GetObjectsByType<Service>().first, DynamicType::GetObjectsByType<Service>().second));
152         InsertRuntimeVariable("total_scheduled_services", std::distance(DynamicType::GetObjectsByType<Service>().first, DynamicType::GetObjectsByType<Service>().second));
153         InsertRuntimeVariable("total_hosts", std::distance(DynamicType::GetObjectsByType<Host>().first, DynamicType::GetObjectsByType<Host>().second));
154         InsertRuntimeVariable("total_scheduled_hosts", std::distance(DynamicType::GetObjectsByType<Host>().first, DynamicType::GetObjectsByType<Host>().second));
155
156         Dictionary::Ptr vars = IcingaApplication::GetInstance()->GetVars();
157
158         if (!vars)
159                 return;
160
161         Log(LogDebug, "DbConnection", "Dumping global vars for icinga application");
162
163         ObjectLock olock(vars);
164
165         BOOST_FOREACH(const Dictionary::Pair& kv, vars) {
166                 if (!kv.first.IsEmpty()) {
167                         Log(LogDebug, "DbConnection")
168                             << "icinga application customvar key: '" << kv.first << "' value: '" << kv.second << "'";
169
170                         Dictionary::Ptr fields4 = make_shared<Dictionary>();
171                         fields4->Set("varname", Convert::ToString(kv.first));
172                         fields4->Set("varvalue", Convert::ToString(kv.second));
173                         fields4->Set("config_type", 1);
174                         fields4->Set("has_been_modified", 0);
175                         fields4->Set("instance_id", 0); /* DbConnection class fills in real ID */
176
177                         DbQuery query4;
178                         query4.Table = "customvariables";
179                         query4.Type = DbQueryInsert;
180                         query4.Category = DbCatConfig;
181                         query4.Fields = fields4;
182                         DbObject::OnQuery(query4);
183                 }
184         }
185 }
186
187 void DbConnection::CleanUpHandler(void)
188 {
189         long now = static_cast<long>(Utility::GetTime());
190
191         struct {
192                 String name;
193                 String time_column;
194         } tables[] = {
195                 { "acknowledgements", "entry_time" },
196                 { "commenthistory", "entry_time" },
197                 { "contactnotifications", "start_time" },
198                 { "contactnotificationmethods", "start_time" },
199                 { "downtimehistory", "entry_time" },
200                 { "eventhandlers", "start_time" },
201                 { "externalcommands", "entry_time" },
202                 { "flappinghistory" "event_time" },
203                 { "hostchecks", "start_time" },
204                 { "logentries", "logentry_time" },
205                 { "notifications", "start_time" },
206                 { "processevents", "event_time" },
207                 { "statehistory", "state_time" },
208                 { "servicechecks", "start_time" },
209                 { "systemcommands", "start_time" }
210         };
211
212         for (size_t i = 0; i < sizeof(tables) / sizeof(tables[0]); i++) {
213                 double max_age = GetCleanup()->Get(tables[i].name + "_age");
214
215                 if (max_age == 0)
216                         continue;
217
218                 CleanUpExecuteQuery(tables[i].name, tables[i].time_column, now - max_age);
219                 Log(LogNotice, "DbConnection")
220                     << "Cleanup (" << tables[i].name << "): " << max_age
221                     << " now: " << now
222                     << " old: " << now - max_age;
223         }
224
225 }
226
227 void DbConnection::CleanUpExecuteQuery(const String&, const String&, double)
228 {
229         /* Default handler does nothing. */
230 }
231
232 void DbConnection::SetObjectID(const DbObject::Ptr& dbobj, const DbReference& dbref)
233 {
234         if (dbref.IsValid())
235                 m_ObjectIDs[dbobj] = dbref;
236         else
237                 m_ObjectIDs.erase(dbobj);
238 }
239
240 DbReference DbConnection::GetObjectID(const DbObject::Ptr& dbobj) const
241 {
242         std::map<DbObject::Ptr, DbReference>::const_iterator it;
243
244         it = m_ObjectIDs.find(dbobj);
245
246         if (it == m_ObjectIDs.end())
247                 return DbReference();
248
249         return it->second;
250 }
251
252 void DbConnection::SetInsertID(const DbObject::Ptr& dbobj, const DbReference& dbref)
253 {
254         SetInsertID(dbobj->GetType(), GetObjectID(dbobj), dbref);
255 }
256
257 void DbConnection::SetInsertID(const DbType::Ptr& type, const DbReference& objid, const DbReference& dbref)
258 {
259         if (!objid.IsValid())
260                 return;
261
262         if (dbref.IsValid())
263                 m_InsertIDs[std::make_pair(type, objid)] = dbref;
264         else
265                 m_InsertIDs.erase(std::make_pair(type, objid));
266 }
267
268 DbReference DbConnection::GetInsertID(const DbObject::Ptr& dbobj) const
269 {
270         return GetInsertID(dbobj->GetType(), GetObjectID(dbobj));
271 }
272
273 DbReference DbConnection::GetInsertID(const DbType::Ptr& type, const DbReference& objid) const
274 {
275         if (!objid.IsValid())
276                 return DbReference();
277
278         std::map<std::pair<DbType::Ptr, DbReference>, DbReference>::const_iterator it;
279
280         it = m_InsertIDs.find(std::make_pair(type, objid));
281
282         if (it == m_InsertIDs.end())
283                 return DbReference();
284
285         return it->second;
286 }
287
288 void DbConnection::SetNotificationInsertID(const CustomVarObject::Ptr& obj, const DbReference& dbref)
289 {
290         if (dbref.IsValid())
291                 m_NotificationInsertIDs[obj] = dbref;
292         else
293                 m_NotificationInsertIDs.erase(obj);
294 }
295
296 DbReference DbConnection::GetNotificationInsertID(const CustomVarObject::Ptr& obj) const
297 {
298         std::map<CustomVarObject::Ptr, DbReference>::const_iterator it;
299
300         it = m_NotificationInsertIDs.find(obj);
301
302         if (it == m_NotificationInsertIDs.end())
303                 return DbReference();
304
305         return it->second;
306 }
307
308 void DbConnection::SetObjectActive(const DbObject::Ptr& dbobj, bool active)
309 {
310         if (active)
311                 m_ActiveObjects.insert(dbobj);
312         else
313                 m_ActiveObjects.erase(dbobj);
314 }
315
316 bool DbConnection::GetObjectActive(const DbObject::Ptr& dbobj) const
317 {
318         return (m_ActiveObjects.find(dbobj) != m_ActiveObjects.end());
319 }
320
321 void DbConnection::ClearIDCache(void)
322 {
323         m_ObjectIDs.clear();
324         m_InsertIDs.clear();
325         m_NotificationInsertIDs.clear();
326         m_ActiveObjects.clear();
327         m_ConfigUpdates.clear();
328         m_StatusUpdates.clear();
329 }
330
331 void DbConnection::SetConfigUpdate(const DbObject::Ptr& dbobj, bool hasupdate)
332 {
333         if (hasupdate)
334                 m_ConfigUpdates.insert(dbobj);
335         else
336                 m_ConfigUpdates.erase(dbobj);
337 }
338
339 bool DbConnection::GetConfigUpdate(const DbObject::Ptr& dbobj) const
340 {
341         return (m_ConfigUpdates.find(dbobj) != m_ConfigUpdates.end());
342 }
343
344 void DbConnection::SetStatusUpdate(const DbObject::Ptr& dbobj, bool hasupdate)
345 {
346         if (hasupdate)
347                 m_StatusUpdates.insert(dbobj);
348         else
349                 m_StatusUpdates.erase(dbobj);
350 }
351
352 bool DbConnection::GetStatusUpdate(const DbObject::Ptr& dbobj) const
353 {
354         return (m_StatusUpdates.find(dbobj) != m_StatusUpdates.end());
355 }
356
357 void DbConnection::ExecuteQuery(const DbQuery&)
358 {
359         /* Default handler does nothing. */
360 }
361
362 void DbConnection::UpdateAllObjects(void)
363 {
364         DynamicType::Ptr type;
365         BOOST_FOREACH(const DynamicType::Ptr& dt, DynamicType::GetTypes()) {
366                 BOOST_FOREACH(const DynamicObject::Ptr& object, dt->GetObjects()) {
367                         DbObject::Ptr dbobj = DbObject::GetOrCreateByObject(object);
368
369                         if (dbobj) {
370                                 if (!GetObjectActive(dbobj))
371                                         ActivateObject(dbobj);
372
373                                 dbobj->SendConfigUpdate();
374                                 dbobj->SendStatusUpdate();
375                         }
376                 }
377         }
378 }
379
380 void DbConnection::PrepareDatabase(void)
381 {
382         /*
383          * only clear tables on reconnect which
384          * cannot be updated by their existing ids
385          * for details check https://dev.icinga.org/issues/5565
386          */
387
388         //ClearConfigTable("commands");
389         ClearConfigTable("comments");
390         ClearConfigTable("contact_addresses");
391         ClearConfigTable("contact_notificationcommands");
392         ClearConfigTable("contactgroup_members");
393         //ClearConfigTable("contactgroups");
394         //ClearConfigTable("contacts");
395         //ClearConfigTable("contactstatus");
396         ClearConfigTable("customvariables");
397         ClearConfigTable("customvariablestatus");
398         ClearConfigTable("endpoints");
399         ClearConfigTable("endpointstatus");
400         ClearConfigTable("host_contactgroups");
401         ClearConfigTable("host_contacts");
402         ClearConfigTable("host_parenthosts");
403         ClearConfigTable("hostdependencies");
404         ClearConfigTable("hostgroup_members");
405         //ClearConfigTable("hostgroups");
406         //ClearConfigTable("hosts");
407         //ClearConfigTable("hoststatus");
408         ClearConfigTable("scheduleddowntime");
409         ClearConfigTable("service_contactgroups");
410         ClearConfigTable("service_contacts");
411         ClearConfigTable("servicedependencies");
412         ClearConfigTable("servicegroup_members");
413         //ClearConfigTable("servicegroups");
414         //ClearConfigTable("services");
415         //ClearConfigTable("servicestatus");
416         ClearConfigTable("timeperiod_timeranges");
417         //ClearConfigTable("timeperiods");
418
419         BOOST_FOREACH(const DbType::Ptr& type, DbType::GetAllTypes()) {
420                 FillIDCache(type);
421         }
422 }
423
424 void DbConnection::ValidateFailoverTimeout(const String& location, const Dictionary::Ptr& attrs)
425 {
426         if (!attrs->Contains("failover_timeout"))
427                 return;
428
429         if (attrs->Get("failover_timeout") < 60) {
430                 ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
431                     location + ": Failover timeout minimum is 60s.");
432         }
433 }