]> granicus.if.org Git - icinga2/blob - lib/base/dynamicobject.cpp
Use BOOST_THROW_EXCEPTION instead of boost::throw_exception()
[icinga2] / lib / base / dynamicobject.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012 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 "i2-base.h"
21
22 using namespace icinga;
23
24 double DynamicObject::m_CurrentTx = 0;
25 set<DynamicObject::Ptr> DynamicObject::m_ModifiedObjects;
26
27 boost::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnRegistered;
28 boost::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnUnregistered;
29 boost::signal<void (const set<DynamicObject::Ptr>&)> DynamicObject::OnTransactionClosing;
30
31 DynamicObject::DynamicObject(const Dictionary::Ptr& serializedObject)
32         : m_ConfigTx(0)
33 {
34         RegisterAttribute("__name", Attribute_Config);
35         RegisterAttribute("__type", Attribute_Config);
36         RegisterAttribute("__local", Attribute_Config);
37         RegisterAttribute("__abstract", Attribute_Config);
38         RegisterAttribute("__source", Attribute_Local);
39         RegisterAttribute("methods", Attribute_Config);
40
41         if (!serializedObject->Contains("configTx"))
42                 BOOST_THROW_EXCEPTION(invalid_argument("Serialized object must contain a config snapshot."));
43
44         /* apply config state from the config item/remote update;
45          * The DynamicObject::Create function takes care of restoring
46          * non-config state after the object has been fully constructed */
47         InternalApplyUpdate(serializedObject, Attribute_Config, true);
48 }
49
50 Dictionary::Ptr DynamicObject::BuildUpdate(double sinceTx, int attributeTypes) const
51 {
52         DynamicObject::AttributeConstIterator it;
53
54         Dictionary::Ptr attrs = boost::make_shared<Dictionary>();
55
56         for (it = m_Attributes.begin(); it != m_Attributes.end(); it++) {
57                 if (it->second.Type == Attribute_Transient)
58                         continue;
59
60                 if ((it->second.Type & attributeTypes) == 0)
61                         continue;
62
63                 if (it->second.Tx == 0)
64                         continue;
65
66                 if (it->second.Tx < sinceTx && !(it->second.Type == Attribute_Config && m_ConfigTx >= sinceTx))
67                         continue;
68
69                 Dictionary::Ptr attr = boost::make_shared<Dictionary>();
70                 attr->Set("data", it->second.Data);
71                 attr->Set("type", it->second.Type);
72                 attr->Set("tx", it->second.Tx);
73
74                 attrs->Set(it->first, attr);
75         }
76
77         Dictionary::Ptr update = boost::make_shared<Dictionary>();
78         update->Set("attrs", attrs);
79
80         if (m_ConfigTx >= sinceTx && attributeTypes & Attribute_Config)
81                 update->Set("configTx", m_ConfigTx);
82         else if (attrs->GetLength() == 0)
83                 return Dictionary::Ptr();
84
85         return update;
86 }
87
88 void DynamicObject::ApplyUpdate(const Dictionary::Ptr& serializedUpdate,
89     int allowedTypes)
90 {
91         InternalApplyUpdate(serializedUpdate, allowedTypes, false);
92 }
93
94 void DynamicObject::InternalApplyUpdate(const Dictionary::Ptr& serializedUpdate,
95     int allowedTypes, bool suppressEvents)
96 {
97         double configTx = 0;
98         if ((allowedTypes & Attribute_Config) != 0 &&
99             serializedUpdate->Contains("configTx")) {
100                 configTx = serializedUpdate->Get("configTx");
101
102                 if (configTx > m_ConfigTx)
103                         ClearAttributesByType(Attribute_Config);
104         }
105
106         Dictionary::Ptr attrs = serializedUpdate->Get("attrs");
107
108         Dictionary::Iterator it;
109         for (it = attrs->Begin(); it != attrs->End(); it++) {
110                 if (!it->second.IsObjectType<Dictionary>())
111                         continue;
112
113                 Dictionary::Ptr attr = it->second;
114
115                 int type = attr->Get("type");
116
117                 if ((type & ~allowedTypes) != 0)
118                         continue;
119
120                 Value data = attr->Get("data");
121                 double tx = attr->Get("tx");
122
123                 if (type & Attribute_Config)
124                         RegisterAttribute(it->first, Attribute_Config);
125
126                 if (!HasAttribute(it->first))
127                         RegisterAttribute(it->first, static_cast<DynamicAttributeType>(type));
128
129                 InternalSetAttribute(it->first, data, tx, suppressEvents, true);
130         }
131 }
132
133 void DynamicObject::RegisterAttribute(const String& name,
134     DynamicAttributeType type)
135 {
136         DynamicAttribute attr;
137         attr.Type = type;
138         attr.Tx = 0;
139
140         pair<DynamicObject::AttributeIterator, bool> tt;
141         tt = m_Attributes.insert(make_pair(name, attr));
142
143         if (!tt.second)
144                 tt.first->second.Type = type;
145 }
146
147 void DynamicObject::Set(const String& name, const Value& data)
148 {
149         InternalSetAttribute(name, data, GetCurrentTx());
150 }
151
152 void DynamicObject::Touch(const String& name)
153 {
154         InternalSetAttribute(name, InternalGetAttribute(name), GetCurrentTx());
155 }
156
157 Value DynamicObject::Get(const String& name) const
158 {
159         return InternalGetAttribute(name);
160 }
161
162 void DynamicObject::InternalSetAttribute(const String& name, const Value& data,
163     double tx, bool suppressEvent, bool allowEditConfig)
164 {
165         DynamicAttribute attr;
166         attr.Type = Attribute_Transient;
167         attr.Data = data;
168         attr.Tx = tx;
169
170         pair<DynamicObject::AttributeIterator, bool> tt;
171         tt = m_Attributes.insert(make_pair(name, attr));
172
173         Value oldValue;
174
175         if (!allowEditConfig && (tt.first->second.Type & Attribute_Config))
176                 BOOST_THROW_EXCEPTION(runtime_error("Config properties are immutable: '" + name + "'."));
177
178         if (!tt.second && tx >= tt.first->second.Tx) {
179                 oldValue = tt.first->second.Data;
180                 tt.first->second.Data = data;
181                 tt.first->second.Tx = tx;
182         }
183
184         if (tt.first->second.Type & Attribute_Config)
185                 m_ConfigTx = tx;
186
187         if (!suppressEvent) {
188                 m_ModifiedObjects.insert(GetSelf());
189                 OnAttributeChanged(name, oldValue);
190         }
191 }
192
193 Value DynamicObject::InternalGetAttribute(const String& name) const
194 {
195         DynamicObject::AttributeConstIterator it;
196         it = m_Attributes.find(name);
197
198         if (it == m_Attributes.end())
199                 return Empty;
200
201         return it->second.Data;
202 }
203
204 bool DynamicObject::HasAttribute(const String& name) const
205 {
206         return (m_Attributes.find(name) != m_Attributes.end());
207 }
208
209 void DynamicObject::ClearAttributesByType(DynamicAttributeType type)
210 {
211         DynamicObject::AttributeIterator at;
212         for (at = m_Attributes.begin(); at != m_Attributes.end(); at++) {
213                 if (at->second.Type != type)
214                         continue;
215
216                 at->second.Tx = 0;
217                 at->second.Data = Empty;
218         }
219 }
220
221 DynamicType::Ptr DynamicObject::GetType(void) const
222 {
223         String name = Get("__type");
224         return DynamicType::GetByName(name);
225 }
226
227 String DynamicObject::GetName(void) const
228 {
229         return Get("__name");
230 }
231
232 bool DynamicObject::IsLocal(void) const
233 {
234         Value value = Get("__local");
235
236         if (value.IsEmpty())
237                 return false;
238
239         return (value != 0);
240 }
241
242 bool DynamicObject::IsAbstract(void) const
243 {
244         Value value = Get("__abstract");
245
246         if (value.IsEmpty())
247                 return false;
248
249         return (value != 0);
250 }
251
252 void DynamicObject::SetSource(const String& value)
253 {
254         Set("__source", value);
255 }
256
257 String DynamicObject::GetSource(void) const
258 {
259         return Get("__source");
260 }
261
262 void DynamicObject::Register(void)
263 {
264         assert(Application::IsMainThread());
265
266         DynamicType::Ptr dtype = GetType();
267
268         DynamicObject::Ptr dobj = dtype->GetObject(GetName());
269         DynamicObject::Ptr self = GetSelf();
270         assert(!dobj || dobj == self);
271
272         dtype->RegisterObject(self);
273
274         OnRegistered(GetSelf());
275
276         Start();
277 }
278
279 void DynamicObject::Start(void)
280 {
281         /* Nothing to do here. */
282 }
283
284 void DynamicObject::Unregister(void)
285 {
286         assert(Application::IsMainThread());
287
288         DynamicType::Ptr dtype = GetType();
289
290         if (!dtype || !dtype->GetObject(GetName()))
291                 return;
292
293         dtype->UnregisterObject(GetSelf());
294
295         OnUnregistered(GetSelf());
296 }
297
298 ScriptTask::Ptr DynamicObject::InvokeMethod(const String& method,
299     const vector<Value>& arguments, ScriptTask::CompletionCallback callback)
300 {
301         Value value = Get("methods");
302
303         if (!value.IsObjectType<Dictionary>())
304                 return ScriptTask::Ptr();
305
306         Dictionary::Ptr methods = value;
307         if (!methods->Contains(method))
308                 return ScriptTask::Ptr();
309
310         String funcName = methods->Get(method);
311
312         ScriptFunction::Ptr func = ScriptFunction::GetByName(funcName);
313
314         if (!func)
315                 BOOST_THROW_EXCEPTION(invalid_argument("Function '" + funcName + "' does not exist."));
316
317         ScriptTask::Ptr task = boost::make_shared<ScriptTask>(func, arguments);
318         task->Start(callback);
319
320         return task;
321 }
322
323 void DynamicObject::DumpObjects(const String& filename)
324 {
325         Logger::Write(LogInformation, "base", "Dumping program state to file '" + filename + "'");
326
327         String tempFilename = filename + ".tmp";
328
329         fstream fp;
330         fp.open(tempFilename.CStr(), std::ios_base::out);
331
332         if (!fp)
333                 BOOST_THROW_EXCEPTION(runtime_error("Could not open '" + filename + "' file"));
334
335         StdioStream::Ptr sfp = boost::make_shared<StdioStream>(&fp, false);
336         sfp->Start();
337
338         DynamicType::Ptr type;
339         BOOST_FOREACH(tie(tuples::ignore, type), DynamicType::GetTypes()) {
340                 DynamicObject::Ptr object;
341                 BOOST_FOREACH(tie(tuples::ignore, object), type->GetObjects()) {
342                         if (object->IsLocal())
343                                 continue;
344
345                         Dictionary::Ptr persistentObject = boost::make_shared<Dictionary>();
346
347                         persistentObject->Set("type", object->GetType()->GetName());
348                         persistentObject->Set("name", object->GetName());
349
350                         int types = Attribute_Local | Attribute_Replicated;
351
352                         /* only persist properties for replicated objects or for objects
353                          * that are marked as persistent */
354                         if (!object->GetSource().IsEmpty() /*|| object->IsPersistent()*/)
355                                 types |= Attribute_Config;
356
357                         Dictionary::Ptr update = object->BuildUpdate(0, types);
358
359                         if (!update)
360                                 continue;
361
362                         persistentObject->Set("update", update);
363
364                         Value value = persistentObject;
365                         String json = value.Serialize();
366
367                         NetString::WriteStringToStream(sfp, json);
368                 }
369         }
370
371         sfp->Close();
372
373         fp.close();
374
375 #ifdef _WIN32
376         _unlink(filename.CStr());
377 #endif /* _WIN32 */
378
379         if (rename(tempFilename.CStr(), filename.CStr()) < 0)
380                 BOOST_THROW_EXCEPTION(PosixException("rename() failed", errno));
381 }
382
383 void DynamicObject::RestoreObjects(const String& filename)
384 {
385         Logger::Write(LogInformation, "base", "Restoring program state from file '" + filename + "'");
386
387         std::fstream fp;
388         fp.open(filename.CStr(), std::ios_base::in);
389
390         StdioStream::Ptr sfp = boost::make_shared<StdioStream>(&fp, false);
391         sfp->Start();
392
393         unsigned long restored = 0;
394
395         String message;
396         while (NetString::ReadStringFromStream(sfp, &message)) {
397                 Dictionary::Ptr persistentObject = Value::Deserialize(message);
398
399                 String type = persistentObject->Get("type");
400                 String name = persistentObject->Get("name");
401                 Dictionary::Ptr update = persistentObject->Get("update");
402
403                 bool hasConfig = update->Contains("configTx");
404
405                 DynamicType::Ptr dt = DynamicType::GetByName(type);
406
407                 if (!dt)
408                         BOOST_THROW_EXCEPTION(invalid_argument("Invalid type: " + type));
409
410                 DynamicObject::Ptr object = dt->GetObject(name);
411
412                 if (hasConfig && !object) {
413                         object = dt->CreateObject(update);
414                         object->Register();
415                 } else if (object) {
416                         object->ApplyUpdate(update, Attribute_All);
417                 }
418
419                 restored++;
420         }
421
422         sfp->Close();
423
424         stringstream msgbuf;
425         msgbuf << "Restored " << restored << " objects";
426         Logger::Write(LogDebug, "base", msgbuf.str());
427 }
428
429 void DynamicObject::DeactivateObjects(void)
430 {
431         DynamicType::TypeMap::iterator tt;
432         for (tt = DynamicType::GetTypes().begin(); tt != DynamicType::GetTypes().end(); tt++) {
433                 DynamicType::NameMap::iterator nt;
434
435                 while ((nt = tt->second->GetObjects().begin()) != tt->second->GetObjects().end()) {
436                         DynamicObject::Ptr object = nt->second;
437
438                         object->Unregister();
439                 }
440         }
441 }
442
443 double DynamicObject::GetCurrentTx(void)
444 {
445         assert(m_CurrentTx != 0);
446
447         return m_CurrentTx;
448 }
449
450 void DynamicObject::BeginTx(void)
451 {
452         m_CurrentTx = Utility::GetTime();
453 }
454
455 void DynamicObject::FinishTx(void)
456 {
457         OnTransactionClosing(m_ModifiedObjects);
458         m_ModifiedObjects.clear();
459
460         m_CurrentTx = 0;
461 }
462
463 void DynamicObject::FlushTx(void)
464 {
465         FinishTx();
466         BeginTx();
467 }
468
469 void DynamicObject::OnAttributeChanged(const String&, const Value&)
470 { }
471
472 DynamicObject::Ptr DynamicObject::GetObject(const String& type, const String& name)
473 {
474         DynamicType::Ptr dtype = DynamicType::GetByName(type);
475         return dtype->GetObject(name);
476 }
477
478 const DynamicObject::AttributeMap& DynamicObject::GetAttributes(void) const
479 {
480         return m_Attributes;
481 }