]> granicus.if.org Git - icinga2/blob - lib/db_ido/dbobject.cpp
Merge pull request #7124 from Icinga/bugfix/namespace-thread-safe
[icinga2] / lib / db_ido / dbobject.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "db_ido/dbobject.hpp"
4 #include "db_ido/dbtype.hpp"
5 #include "db_ido/dbvalue.hpp"
6 #include "icinga/customvarobject.hpp"
7 #include "icinga/service.hpp"
8 #include "icinga/compatutility.hpp"
9 #include "icinga/checkcommand.hpp"
10 #include "icinga/eventcommand.hpp"
11 #include "icinga/notificationcommand.hpp"
12 #include "remote/endpoint.hpp"
13 #include "base/configobject.hpp"
14 #include "base/configtype.hpp"
15 #include "base/json.hpp"
16 #include "base/serializer.hpp"
17 #include "base/json.hpp"
18 #include "base/convert.hpp"
19 #include "base/objectlock.hpp"
20 #include "base/utility.hpp"
21 #include "base/initialize.hpp"
22 #include "base/logger.hpp"
23
24 using namespace icinga;
25
26 boost::signals2::signal<void (const DbQuery&)> DbObject::OnQuery;
27 boost::signals2::signal<void (const std::vector<DbQuery>&)> DbObject::OnMultipleQueries;
28
29 INITIALIZE_ONCE(&DbObject::StaticInitialize);
30
31 DbObject::DbObject(intrusive_ptr<DbType> type, String name1, String name2)
32         : m_Name1(std::move(name1)), m_Name2(std::move(name2)), m_Type(std::move(type)), m_LastConfigUpdate(0), m_LastStatusUpdate(0)
33 { }
34
35 void DbObject::StaticInitialize()
36 {
37         /* triggered in ProcessCheckResult(), requires UpdateNextCheck() to be called before */
38         ConfigObject::OnStateChanged.connect(std::bind(&DbObject::StateChangedHandler, _1));
39         CustomVarObject::OnVarsChanged.connect(std::bind(&DbObject::VarsChangedHandler, _1));
40
41         /* triggered on create, update and delete objects */
42         ConfigObject::OnVersionChanged.connect(std::bind(&DbObject::VersionChangedHandler, _1));
43 }
44
45 void DbObject::SetObject(const ConfigObject::Ptr& object)
46 {
47         m_Object = object;
48 }
49
50 ConfigObject::Ptr DbObject::GetObject() const
51 {
52         return m_Object;
53 }
54
55 String DbObject::GetName1() const
56 {
57         return m_Name1;
58 }
59
60 String DbObject::GetName2() const
61 {
62         return m_Name2;
63 }
64
65 DbType::Ptr DbObject::GetType() const
66 {
67         return m_Type;
68 }
69
70 String DbObject::CalculateConfigHash(const Dictionary::Ptr& configFields) const
71 {
72         Dictionary::Ptr configFieldsDup = configFields->ShallowClone();
73
74         {
75                 ObjectLock olock(configFieldsDup);
76
77                 for (const Dictionary::Pair& kv : configFieldsDup) {
78                         if (kv.second.IsObjectType<ConfigObject>()) {
79                                 ConfigObject::Ptr obj = kv.second;
80                                 configFieldsDup->Set(kv.first, obj->GetName());
81                         }
82                 }
83         }
84
85         Array::Ptr data = new Array();
86         data->Add(configFieldsDup);
87
88         CustomVarObject::Ptr custom_var_object = dynamic_pointer_cast<CustomVarObject>(GetObject());
89
90         if (custom_var_object)
91                 data->Add(custom_var_object->GetVars());
92
93         return HashValue(data);
94 }
95
96 String DbObject::HashValue(const Value& value)
97 {
98         Value temp;
99
100         Type::Ptr type = value.GetReflectionType();
101
102         if (ConfigObject::TypeInstance->IsAssignableFrom(type))
103                 temp = Serialize(value, FAConfig);
104         else
105                 temp = value;
106
107         return SHA256(JsonEncode(temp));
108 }
109
110 void DbObject::SendConfigUpdateHeavy(const Dictionary::Ptr& configFields)
111 {
112         /* update custom var config and status */
113         SendVarsConfigUpdateHeavy();
114         SendVarsStatusUpdate();
115
116         /* config attributes */
117         if (!configFields)
118                 return;
119
120         ASSERT(configFields->Contains("config_hash"));
121
122         ConfigObject::Ptr object = GetObject();
123
124         DbQuery query;
125         query.Table = GetType()->GetTable() + "s";
126         query.Type = DbQueryInsert | DbQueryUpdate;
127         query.Category = DbCatConfig;
128         query.Fields = configFields;
129         query.Fields->Set(GetType()->GetIDColumn(), object);
130         query.Fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
131         query.Fields->Set("config_type", 1);
132         query.WhereCriteria = new Dictionary({
133                 { GetType()->GetIDColumn(), object }
134         });
135         query.Object = this;
136         query.ConfigUpdate = true;
137         OnQuery(query);
138
139         m_LastConfigUpdate = Utility::GetTime();
140
141         OnConfigUpdateHeavy();
142 }
143
144 void DbObject::SendConfigUpdateLight()
145 {
146         OnConfigUpdateLight();
147 }
148
149 void DbObject::SendStatusUpdate()
150 {
151         /* status attributes */
152         Dictionary::Ptr fields = GetStatusFields();
153
154         if (!fields)
155                 return;
156
157         DbQuery query;
158         query.Table = GetType()->GetTable() + "status";
159         query.Type = DbQueryInsert | DbQueryUpdate;
160         query.Category = DbCatState;
161         query.Fields = fields;
162         query.Fields->Set(GetType()->GetIDColumn(), GetObject());
163
164         /* do not override endpoint_object_id for endpoints & zones */
165         if (query.Table != "endpointstatus" && query.Table != "zonestatus") {
166                 String node = IcingaApplication::GetInstance()->GetNodeName();
167
168                 Endpoint::Ptr endpoint = Endpoint::GetByName(node);
169                 if (endpoint)
170                         query.Fields->Set("endpoint_object_id", endpoint);
171         }
172
173         query.Fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
174
175         query.Fields->Set("status_update_time", DbValue::FromTimestamp(Utility::GetTime()));
176         query.WhereCriteria = new Dictionary({
177                 { GetType()->GetIDColumn(), GetObject() }
178         });
179         query.Object = this;
180         query.StatusUpdate = true;
181         OnQuery(query);
182
183         m_LastStatusUpdate = Utility::GetTime();
184
185         OnStatusUpdate();
186 }
187
188 void DbObject::SendVarsConfigUpdateHeavy()
189 {
190         ConfigObject::Ptr obj = GetObject();
191
192         CustomVarObject::Ptr custom_var_object = dynamic_pointer_cast<CustomVarObject>(obj);
193
194         if (!custom_var_object)
195                 return;
196
197         std::vector<DbQuery> queries;
198
199         DbQuery query1;
200         query1.Table = "customvariables";
201         query1.Type = DbQueryDelete;
202         query1.Category = DbCatConfig;
203         query1.WhereCriteria = new Dictionary({
204                 { "object_id", obj }
205         });
206         queries.emplace_back(std::move(query1));
207
208         DbQuery query2;
209         query2.Table = "customvariablestatus";
210         query2.Type = DbQueryDelete;
211         query2.Category = DbCatConfig;
212         query2.WhereCriteria = new Dictionary({
213                 { "object_id", obj }
214         });
215         queries.emplace_back(std::move(query2));
216
217         Dictionary::Ptr vars = custom_var_object->GetVars();
218
219         if (vars) {
220                 ObjectLock olock (vars);
221
222                 for (const Dictionary::Pair& kv : vars) {
223                         if (kv.first.IsEmpty())
224                                 continue;
225
226                         String value;
227                         int is_json = 0;
228
229                         if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) {
230                                 value = JsonEncode(kv.second);
231                                 is_json = 1;
232                         } else
233                                 value = kv.second;
234
235                         DbQuery query3;
236                         query3.Table = "customvariables";
237                         query3.Type = DbQueryInsert;
238                         query3.Category = DbCatConfig;
239                         query3.Fields = new Dictionary({
240                                 { "varname", kv.first },
241                                 { "varvalue", value },
242                                 { "is_json", is_json },
243                                 { "config_type", 1 },
244                                 { "object_id", obj },
245                                 { "instance_id", 0 } /* DbConnection class fills in real ID */
246                         });
247                         queries.emplace_back(std::move(query3));
248                 }
249         }
250
251         OnMultipleQueries(queries);
252 }
253
254 void DbObject::SendVarsStatusUpdate()
255 {
256         ConfigObject::Ptr obj = GetObject();
257
258         CustomVarObject::Ptr custom_var_object = dynamic_pointer_cast<CustomVarObject>(obj);
259
260         if (!custom_var_object)
261                 return;
262
263         Dictionary::Ptr vars = custom_var_object->GetVars();
264
265         if (vars) {
266                 std::vector<DbQuery> queries;
267                 ObjectLock olock (vars);
268
269                 for (const Dictionary::Pair& kv : vars) {
270                         if (kv.first.IsEmpty())
271                                 continue;
272
273                         String value;
274                         int is_json = 0;
275
276                         if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) {
277                                 value = JsonEncode(kv.second);
278                                 is_json = 1;
279                         } else
280                                 value = kv.second;
281
282                         DbQuery query;
283                         query.Table = "customvariablestatus";
284                         query.Type = DbQueryInsert | DbQueryUpdate;
285                         query.Category = DbCatState;
286
287                         query.Fields = new Dictionary({
288                                 { "varname", kv.first },
289                                 { "varvalue", value },
290                                 { "is_json", is_json },
291                                 { "status_update_time", DbValue::FromTimestamp(Utility::GetTime()) },
292                                 { "object_id", obj },
293                                 { "instance_id", 0 } /* DbConnection class fills in real ID */
294                         });
295
296                         query.WhereCriteria = new Dictionary({
297                                 { "object_id", obj },
298                                 { "varname", kv.first }
299                         });
300
301                         queries.emplace_back(std::move(query));
302                 }
303
304                 OnMultipleQueries(queries);
305         }
306 }
307
308 double DbObject::GetLastConfigUpdate() const
309 {
310         return m_LastConfigUpdate;
311 }
312
313 double DbObject::GetLastStatusUpdate() const
314 {
315         return m_LastStatusUpdate;
316 }
317
318 void DbObject::OnConfigUpdateHeavy()
319 {
320         /* Default handler does nothing. */
321 }
322
323 void DbObject::OnConfigUpdateLight()
324 {
325         /* Default handler does nothing. */
326 }
327
328 void DbObject::OnStatusUpdate()
329 {
330         /* Default handler does nothing. */
331 }
332
333 DbObject::Ptr DbObject::GetOrCreateByObject(const ConfigObject::Ptr& object)
334 {
335         boost::mutex::scoped_lock lock(GetStaticMutex());
336
337         DbObject::Ptr dbobj = object->GetExtension("DbObject");
338
339         if (dbobj)
340                 return dbobj;
341
342         DbType::Ptr dbtype = DbType::GetByName(object->GetReflectionType()->GetName());
343
344         if (!dbtype)
345                 return nullptr;
346
347         Service::Ptr service;
348         String name1, name2;
349
350         service = dynamic_pointer_cast<Service>(object);
351
352         if (service) {
353                 Host::Ptr host = service->GetHost();
354
355                 name1 = service->GetHost()->GetName();
356                 name2 = service->GetShortName();
357         } else {
358                 if (object->GetReflectionType() == CheckCommand::TypeInstance ||
359                         object->GetReflectionType() == EventCommand::TypeInstance ||
360                         object->GetReflectionType() == NotificationCommand::TypeInstance) {
361                         Command::Ptr command = dynamic_pointer_cast<Command>(object);
362                         name1 = CompatUtility::GetCommandName(command);
363                 }
364                 else
365                         name1 = object->GetName();
366         }
367
368         dbobj = dbtype->GetOrCreateObjectByName(name1, name2);
369
370         dbobj->SetObject(object);
371         object->SetExtension("DbObject", dbobj);
372
373         return dbobj;
374 }
375
376 void DbObject::StateChangedHandler(const ConfigObject::Ptr& object)
377 {
378         DbObject::Ptr dbobj = GetOrCreateByObject(object);
379
380         if (!dbobj)
381                 return;
382
383         dbobj->SendStatusUpdate();
384 }
385
386 void DbObject::VarsChangedHandler(const CustomVarObject::Ptr& object)
387 {
388         DbObject::Ptr dbobj = GetOrCreateByObject(object);
389
390         if (!dbobj)
391                 return;
392
393         dbobj->SendVarsStatusUpdate();
394 }
395
396 void DbObject::VersionChangedHandler(const ConfigObject::Ptr& object)
397 {
398         DbObject::Ptr dbobj = DbObject::GetOrCreateByObject(object);
399
400         if (dbobj) {
401                 Dictionary::Ptr configFields = dbobj->GetConfigFields();
402                 String configHash = dbobj->CalculateConfigHash(configFields);
403                 configFields->Set("config_hash", configHash);
404
405                 dbobj->SendConfigUpdateHeavy(configFields);
406                 dbobj->SendStatusUpdate();
407         }
408 }
409
410 boost::mutex& DbObject::GetStaticMutex()
411 {
412         static boost::mutex mutex;
413         return mutex;
414 }