]> granicus.if.org Git - icinga2/blob - lib/redis/rediswriter-config.cpp
Update log message
[icinga2] / lib / redis / rediswriter-config.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/)  *
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 "redis/rediswriter.hpp"
21 #include "icinga/customvarobject.hpp"
22 #include "icinga/host.hpp"
23 #include "icinga/service.hpp"
24 #include "base/json.hpp"
25 #include "base/logger.hpp"
26 #include "base/serializer.hpp"
27 #include "base/tlsutility.hpp"
28 #include "base/initialize.hpp"
29
30 using namespace icinga;
31
32 /*
33 - icinga:config:<type> as hash
34 key: sha1 checksum(name)
35 value: JsonEncode(Serialize(object, FAConfig)) + config_checksum
36
37 Diff between calculated config_checksum and Redis json config_checksum
38 Alternative: Replace into.
39
40
41 - icinga:status:<type> as hash
42 key: sha1 checksum(name)
43 value: JsonEncode(Serialize(object, FAState))
44 */
45
46 INITIALIZE_ONCE(&RedisWriter::ConfigStaticInitialize);
47
48 void RedisWriter::ConfigStaticInitialize(void)
49 {
50         /* triggered in ProcessCheckResult(), requires UpdateNextCheck() to be called before */
51         ConfigObject::OnStateChanged.connect(boost::bind(&RedisWriter::StateChangedHandler, _1));
52         CustomVarObject::OnVarsChanged.connect(boost::bind(&RedisWriter::VarsChangedHandler, _1));
53
54         /* triggered on create, update and delete objects */
55         ConfigObject::OnVersionChanged.connect(boost::bind(&RedisWriter::VersionChangedHandler, _1));
56 }
57
58 void RedisWriter::UpdateAllConfigObjects(void)
59 {
60         AssertOnWorkQueue();
61
62         for (const Type::Ptr& type : Type::GetAllTypes()) {
63                 if (!ConfigObject::TypeInstance->IsAssignableFrom(type))
64                         continue;
65
66                 String typeName = type->GetName();
67
68                 /* replace into aka delete insert is faster than a full diff */
69                 redisReply *reply = reinterpret_cast<redisReply *>(redisCommand(m_Context, "DEL icinga:config:%s icinga:status:%s", typeName.CStr(), typeName.CStr()));
70
71                 if (!reply) {
72                         redisFree(m_Context);
73                         m_Context = NULL;
74                         return;
75                 }
76
77                 if (reply->type == REDIS_REPLY_STATUS || reply->type == REDIS_REPLY_ERROR) {
78                         Log(LogInformation, "RedisWriter")
79                             << "DEL icinga:config:" << typeName << " icinga:status:" << typeName << ": " << reply->str;
80                 }
81
82                 if (reply->type == REDIS_REPLY_ERROR) {
83                         freeReplyObject(reply);
84                         return;
85                 }
86
87                 freeReplyObject(reply);
88
89                 /* fetch all objects and dump them */
90                 ConfigType *ctype = dynamic_cast<ConfigType *>(type.get());
91                 VERIFY(ctype);
92
93                 for (const ConfigObject::Ptr& object : ctype->GetObjects()) {
94                         SendConfigUpdate(object, typeName);
95                         SendStatusUpdate(object, typeName);
96                 }
97         }
98 }
99
100 void RedisWriter::SendConfigUpdate(const ConfigObject::Ptr& object, const String& typeName)
101 {
102         AssertOnWorkQueue();
103
104         /* Serialize config object attributes */
105         Dictionary::Ptr objectAttrs = SerializeObjectAttrs(object, FAConfig);
106
107         String jsonBody = JsonEncode(objectAttrs);
108
109         //TODO: checksum
110         String objectName = object->GetName();
111
112         redisReply *reply1 = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:config:%s %s %s", typeName.CStr(), objectName.CStr(), jsonBody.CStr()));
113
114         if (!reply1) {
115                 redisFree(m_Context);
116                 m_Context = NULL;
117                 return;
118         }
119
120         if (reply1->type == REDIS_REPLY_STATUS || reply1->type == REDIS_REPLY_ERROR) {
121                 Log(LogInformation, "RedisWriter")
122                     << "HSET icinga:config:" << typeName << " " << objectName << " " << jsonBody << ": " << reply1->str;
123         }
124
125         if (reply1->type == REDIS_REPLY_ERROR) {
126                 freeReplyObject(reply1);
127                 return;
128         }
129
130         freeReplyObject(reply1);
131
132
133         /* check sums */
134         /* hset icinga:config:Host:checksums localhost { "name_checksum": "...", "properties_checksum": "...", "groups_checksum": "...", "vars_checksum": null } */
135         Dictionary::Ptr checkSum = new Dictionary();
136
137         checkSum->Set("name_checksum", CalculateCheckSumString(object->GetName()));
138
139         if (object->GetReflectionType() == Host::TypeInstance) {
140                 Host::Ptr host = static_pointer_cast<Host>(object);
141                 checkSum->Set("groups_checksum", CalculateCheckSumGroups(host->GetGroups()));
142         } else if (object->GetReflectionType() == Service::TypeInstance) {
143                 Service::Ptr service = static_pointer_cast<Service>(object);
144                 checkSum->Set("groups_checksum", CalculateCheckSumGroups(service->GetGroups()));
145         }
146
147         String checkSumBody = JsonEncode(checkSum);
148
149         redisReply *reply2 = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:config:%s:checksum %s %s", typeName.CStr(), objectName.CStr(), checkSumBody.CStr()));
150
151         if (!reply2) {
152                 redisFree(m_Context);
153                 m_Context = NULL;
154                 return;
155         }
156
157         if (reply2->type == REDIS_REPLY_STATUS || reply2->type == REDIS_REPLY_ERROR) {
158                 Log(LogInformation, "RedisWriter")
159                     << "HSET icinga:config:" << typeName << " " << objectName << " " << jsonBody << ": " << reply2->str;
160         }
161
162         if (reply2->type == REDIS_REPLY_ERROR) {
163                 freeReplyObject(reply2);
164                 return;
165         }
166
167         freeReplyObject(reply2);
168 }
169
170 void RedisWriter::SendStatusUpdate(const ConfigObject::Ptr& object, const String& typeName)
171 {
172         AssertOnWorkQueue();
173
174         /* Serialize config object attributes */
175         Dictionary::Ptr objectAttrs = SerializeObjectAttrs(object, FAState);
176
177         String jsonBody = JsonEncode(objectAttrs);
178
179         //TODO: checksum
180         String objectName = object->GetName();
181
182         redisReply *reply = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:status:%s %s %s", typeName.CStr(), objectName.CStr(), jsonBody.CStr()));
183
184         if (!reply) {
185                 redisFree(m_Context);
186                 m_Context = NULL;
187                 return;
188         }
189
190         if (reply->type == REDIS_REPLY_STATUS || reply->type == REDIS_REPLY_ERROR) {
191                 Log(LogInformation, "RedisWriter")
192                     << "HSET icinga:status:" << typeName << " " << objectName << " " << jsonBody << ": " << reply->str;
193         }
194
195         if (reply->type == REDIS_REPLY_ERROR) {
196                 freeReplyObject(reply);
197                 return;
198         }
199
200         freeReplyObject(reply);
201 }
202
203 void RedisWriter::StateChangedHandler(const ConfigObject::Ptr& object)
204 {
205         Type::Ptr type = object->GetReflectionType();
206
207         for (const RedisWriter::Ptr& rw : ConfigType::GetObjectsByType<RedisWriter>()) {
208                 rw->m_WorkQueue.Enqueue(boost::bind(&RedisWriter::SendStatusUpdate, rw, object, type->GetName()));
209         }
210 }
211
212 void RedisWriter::VarsChangedHandler(const ConfigObject::Ptr& object)
213 {
214         Type::Ptr type = object->GetReflectionType();
215
216         for (const RedisWriter::Ptr& rw : ConfigType::GetObjectsByType<RedisWriter>()) {
217                 rw->m_WorkQueue.Enqueue(boost::bind(&RedisWriter::SendConfigUpdate, rw, object, type->GetName()));
218         }
219 }
220
221 void RedisWriter::VersionChangedHandler(const ConfigObject::Ptr& object)
222 {
223         Type::Ptr type = object->GetReflectionType();
224
225         for (const RedisWriter::Ptr& rw : ConfigType::GetObjectsByType<RedisWriter>()) {
226                 rw->m_WorkQueue.Enqueue(boost::bind(&RedisWriter::SendConfigUpdate, rw.get(), object, type->GetName()));
227         }
228 }