1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "icingadb/icingadb.hpp"
4 #include "base/configtype.hpp"
5 #include "base/object-packer.hpp"
6 #include "base/logger.hpp"
7 #include "base/serializer.hpp"
8 #include "base/tlsutility.hpp"
9 #include "base/initialize.hpp"
10 #include "base/objectlock.hpp"
11 #include "base/array.hpp"
12 #include "base/scriptglobal.hpp"
13 #include "base/convert.hpp"
14 #include "base/json.hpp"
15 #include "icinga/customvarobject.hpp"
16 #include "icinga/checkcommand.hpp"
17 #include "icinga/notificationcommand.hpp"
18 #include "icinga/eventcommand.hpp"
19 #include "icinga/host.hpp"
20 #include <boost/algorithm/string.hpp>
21 #include <map>
22 #include <utility>
23 #include <vector>
24
25 using namespace icinga;
26
FormatCheckSumBinary(const String & str)27 String IcingaDB::FormatCheckSumBinary(const String& str)
28 {
29 char output[20*2+1];
30 for (int i = 0; i < 20; i++)
31 sprintf(output + 2 * i, "%02x", str[i]);
32
33 return output;
34 }
35
FormatCommandLine(const Value & commandLine)36 String IcingaDB::FormatCommandLine(const Value& commandLine)
37 {
38 String result;
39 if (commandLine.IsObjectType<Array>()) {
40 Array::Ptr args = commandLine;
41 bool first = true;
42
43 ObjectLock olock(args);
44 for (const Value& arg : args) {
45 String token = "'" + Convert::ToString(arg) + "'";
46
47 if (first)
48 first = false;
49 else
50 result += String(1, ' ');
51
52 result += token;
53 }
54 } else if (!commandLine.IsEmpty()) {
55 result = commandLine;
56 boost::algorithm::replace_all(result, "\'", "\\'");
57 result = "'" + result + "'";
58 }
59
60 return result;
61 }
62
GetObjectIdentifier(const ConfigObject::Ptr & object)63 String IcingaDB::GetObjectIdentifier(const ConfigObject::Ptr& object)
64 {
65 return HashValue(new Array({m_EnvironmentId, object->GetName()}));
66 }
67
68 /**
69 * Calculates a deterministic history event ID like SHA1(env, eventType, x...[, nt][, eventTime])
70 *
71 * Where SHA1(env, x...) = GetObjectIdentifier(object)
72 */
CalcEventID(const char * eventType,const ConfigObject::Ptr & object,double eventTime,NotificationType nt)73 String IcingaDB::CalcEventID(const char* eventType, const ConfigObject::Ptr& object, double eventTime, NotificationType nt)
74 {
75 Array::Ptr rawId = new Array({object->GetName()});
76 rawId->Insert(0, m_EnvironmentId);
77 rawId->Insert(1, eventType);
78
79 if (nt) {
80 rawId->Add(GetNotificationTypeByEnum(nt));
81 }
82
83 if (eventTime) {
84 rawId->Add(TimestampToMilliseconds(eventTime));
85 }
86
87 return HashValue(std::move(rawId));
88 }
89
90 static const std::set<String> metadataWhitelist ({"package", "source_location", "templates"});
91
92 /**
93 * Prepare custom vars for being written to Redis
94 *
95 * object.vars = {
96 * "disks": {
97 * "disk": {},
98 * "disk /": {
99 * "disk_partitions": "/"
100 * }
101 * }
102 * }
103 *
104 * return {
105 * SHA1(PackObject([
106 * EnvironmentId,
107 * "disks",
108 * {
109 * "disk": {},
110 * "disk /": {
111 * "disk_partitions": "/"
112 * }
113 * }
114 * ])): {
115 * "environment_id": EnvironmentId,
116 * "name_checksum": SHA1("disks"),
117 * "name": "disks",
118 * "value": {
119 * "disk": {},
120 * "disk /": {
121 * "disk_partitions": "/"
122 * }
123 * }
124 * }
125 * }
126 *
127 * @param Dictionary Config object with custom vars
128 *
129 * @return JSON-like data structure for Redis
130 */
SerializeVars(const Dictionary::Ptr & vars)131 Dictionary::Ptr IcingaDB::SerializeVars(const Dictionary::Ptr& vars)
132 {
133 if (!vars)
134 return nullptr;
135
136 Dictionary::Ptr res = new Dictionary();
137
138 ObjectLock olock(vars);
139
140 for (auto& kv : vars) {
141 res->Set(
142 SHA1(PackObject((Array::Ptr)new Array({m_EnvironmentId, kv.first, kv.second}))),
143 (Dictionary::Ptr)new Dictionary({
144 {"environment_id", m_EnvironmentId},
145 {"name_checksum", SHA1(kv.first)},
146 {"name", kv.first},
147 {"value", JsonEncode(kv.second)},
148 })
149 );
150 }
151
152 return res;
153 }
154
GetNotificationTypeByEnum(NotificationType type)155 const char* IcingaDB::GetNotificationTypeByEnum(NotificationType type)
156 {
157 switch (type) {
158 case NotificationDowntimeStart:
159 return "downtime_start";
160 case NotificationDowntimeEnd:
161 return "downtime_end";
162 case NotificationDowntimeRemoved:
163 return "downtime_removed";
164 case NotificationCustom:
165 return "custom";
166 case NotificationAcknowledgement:
167 return "acknowledgement";
168 case NotificationProblem:
169 return "problem";
170 case NotificationRecovery:
171 return "recovery";
172 case NotificationFlappingStart:
173 return "flapping_start";
174 case NotificationFlappingEnd:
175 return "flapping_end";
176 }
177
178 VERIFY(!"Invalid notification type.");
179 }
180
181 static const std::set<String> propertiesBlacklistEmpty;
182
HashValue(const Value & value)183 String IcingaDB::HashValue(const Value& value)
184 {
185 return HashValue(value, propertiesBlacklistEmpty);
186 }
187
HashValue(const Value & value,const std::set<String> & propertiesBlacklist,bool propertiesWhitelist)188 String IcingaDB::HashValue(const Value& value, const std::set<String>& propertiesBlacklist, bool propertiesWhitelist)
189 {
190 Value temp;
191 bool mutabl;
192
193 Type::Ptr type = value.GetReflectionType();
194
195 if (ConfigObject::TypeInstance->IsAssignableFrom(type)) {
196 temp = Serialize(value, FAConfig);
197 mutabl = true;
198 } else {
199 temp = value;
200 mutabl = false;
201 }
202
203 if (propertiesBlacklist.size() && temp.IsObject()) {
204 Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>((Object::Ptr)temp);
205
206 if (dict) {
207 if (!mutabl)
208 dict = dict->ShallowClone();
209
210 ObjectLock olock(dict);
211
212 if (propertiesWhitelist) {
213 auto current = dict->Begin();
214 auto propertiesBlacklistEnd = propertiesBlacklist.end();
215
216 while (current != dict->End()) {
217 if (propertiesBlacklist.find(current->first) == propertiesBlacklistEnd) {
218 dict->Remove(current++);
219 } else {
220 ++current;
221 }
222 }
223 } else {
224 for (auto& property : propertiesBlacklist)
225 dict->Remove(property);
226 }
227
228 if (!mutabl)
229 temp = dict;
230 }
231 }
232
233 return SHA1(PackObject(temp));
234 }
235
GetLowerCaseTypeNameDB(const ConfigObject::Ptr & obj)236 String IcingaDB::GetLowerCaseTypeNameDB(const ConfigObject::Ptr& obj)
237 {
238 return obj->GetReflectionType()->GetName().ToLower();
239 }
240
TimestampToMilliseconds(double timestamp)241 long long IcingaDB::TimestampToMilliseconds(double timestamp) {
242 return static_cast<long long>(timestamp * 1000);
243 }
244
IcingaToStreamValue(const Value & value)245 String IcingaDB::IcingaToStreamValue(const Value& value)
246 {
247 switch (value.GetType()) {
248 case ValueBoolean:
249 return Convert::ToString(int(value));
250 case ValueString:
251 return Utility::ValidateUTF8(value);
252 case ValueNumber:
253 case ValueEmpty:
254 return Convert::ToString(value);
255 default:
256 return JsonEncode(value);
257 }
258 }
259
260 // Returns the items that exist in "arrayOld" but not in "arrayNew"
GetArrayDeletedValues(const Array::Ptr & arrayOld,const Array::Ptr & arrayNew)261 std::vector<Value> IcingaDB::GetArrayDeletedValues(const Array::Ptr& arrayOld, const Array::Ptr& arrayNew) {
262 std::vector<Value> deletedValues;
263
264 if (!arrayOld) {
265 return deletedValues;
266 }
267
268 if (!arrayNew) {
269 return std::vector<Value>(arrayOld->Begin(), arrayOld->End());
270 }
271
272 std::vector<Value> vectorOld(arrayOld->Begin(), arrayOld->End());
273 std::sort(vectorOld.begin(), vectorOld.end());
274 vectorOld.erase(std::unique(vectorOld.begin(), vectorOld.end()), vectorOld.end());
275
276 std::vector<Value> vectorNew(arrayNew->Begin(), arrayNew->End());
277 std::sort(vectorNew.begin(), vectorNew.end());
278 vectorNew.erase(std::unique(vectorNew.begin(), vectorNew.end()), vectorNew.end());
279
280 std::set_difference(vectorOld.begin(), vectorOld.end(), vectorNew.begin(), vectorNew.end(), std::back_inserter(deletedValues));
281
282 return deletedValues;
283 }
284
285 // Returns the keys that exist in "dictOld" but not in "dictNew"
GetDictionaryDeletedKeys(const Dictionary::Ptr & dictOld,const Dictionary::Ptr & dictNew)286 std::vector<String> IcingaDB::GetDictionaryDeletedKeys(const Dictionary::Ptr& dictOld, const Dictionary::Ptr& dictNew) {
287 std::vector<String> deletedKeys;
288
289 if (!dictOld) {
290 return deletedKeys;
291 }
292
293 std::vector<String> oldKeys = dictOld->GetKeys();
294
295 if (!dictNew) {
296 return oldKeys;
297 }
298
299 std::vector<String> newKeys = dictNew->GetKeys();
300
301 std::set_difference(oldKeys.begin(), oldKeys.end(), newKeys.begin(), newKeys.end(), std::back_inserter(deletedKeys));
302
303 return deletedKeys;
304 }
305