1 /******************************************************************************
3 * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
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. *
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. *
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 ******************************************************************************/
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"
30 using namespace icinga;
33 - icinga:config:<type> as hash
34 key: sha1 checksum(name)
35 value: JsonEncode(Serialize(object, FAConfig)) + config_checksum
37 Diff between calculated config_checksum and Redis json config_checksum
38 Alternative: Replace into.
41 - icinga:status:<type> as hash
42 key: sha1 checksum(name)
43 value: JsonEncode(Serialize(object, FAState))
46 INITIALIZE_ONCE(&RedisWriter::ConfigStaticInitialize);
48 void RedisWriter::ConfigStaticInitialize(void)
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));
54 /* triggered on create, update and delete objects */
55 ConfigObject::OnVersionChanged.connect(boost::bind(&RedisWriter::VersionChangedHandler, _1));
58 void RedisWriter::UpdateAllConfigObjects(void)
62 for (const Type::Ptr& type : Type::GetAllTypes()) {
63 if (!ConfigObject::TypeInstance->IsAssignableFrom(type))
66 String typeName = type->GetName();
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()));
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;
82 if (reply->type == REDIS_REPLY_ERROR) {
83 freeReplyObject(reply);
87 freeReplyObject(reply);
89 /* fetch all objects and dump them */
90 ConfigType *ctype = dynamic_cast<ConfigType *>(type.get());
93 for (const ConfigObject::Ptr& object : ctype->GetObjects()) {
94 SendConfigUpdate(object, typeName);
95 SendStatusUpdate(object, typeName);
100 void RedisWriter::SendConfigUpdate(const ConfigObject::Ptr& object, const String& typeName)
104 /* Serialize config object attributes */
105 Dictionary::Ptr objectAttrs = SerializeObjectAttrs(object, FAConfig);
107 String jsonBody = JsonEncode(objectAttrs);
110 String objectName = object->GetName();
112 redisReply *reply1 = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:config:%s %s %s", typeName.CStr(), objectName.CStr(), jsonBody.CStr()));
115 redisFree(m_Context);
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;
125 if (reply1->type == REDIS_REPLY_ERROR) {
126 freeReplyObject(reply1);
130 freeReplyObject(reply1);
134 /* hset icinga:config:Host:checksums localhost { "name_checksum": "...", "properties_checksum": "...", "groups_checksum": "...", "vars_checksum": null } */
135 Dictionary::Ptr checkSum = new Dictionary();
137 checkSum->Set("name_checksum", CalculateCheckSumString(object->GetName()));
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()));
147 String checkSumBody = JsonEncode(checkSum);
149 redisReply *reply2 = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:config:%s:checksum %s %s", typeName.CStr(), objectName.CStr(), checkSumBody.CStr()));
152 redisFree(m_Context);
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;
162 if (reply2->type == REDIS_REPLY_ERROR) {
163 freeReplyObject(reply2);
167 freeReplyObject(reply2);
170 void RedisWriter::SendStatusUpdate(const ConfigObject::Ptr& object, const String& typeName)
174 /* Serialize config object attributes */
175 Dictionary::Ptr objectAttrs = SerializeObjectAttrs(object, FAState);
177 String jsonBody = JsonEncode(objectAttrs);
180 String objectName = object->GetName();
182 redisReply *reply = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:status:%s %s %s", typeName.CStr(), objectName.CStr(), jsonBody.CStr()));
185 redisFree(m_Context);
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;
195 if (reply->type == REDIS_REPLY_ERROR) {
196 freeReplyObject(reply);
200 freeReplyObject(reply);
203 void RedisWriter::StateChangedHandler(const ConfigObject::Ptr& object)
205 Type::Ptr type = object->GetReflectionType();
207 for (const RedisWriter::Ptr& rw : ConfigType::GetObjectsByType<RedisWriter>()) {
208 rw->m_WorkQueue.Enqueue(boost::bind(&RedisWriter::SendStatusUpdate, rw, object, type->GetName()));
212 void RedisWriter::VarsChangedHandler(const ConfigObject::Ptr& object)
214 Type::Ptr type = object->GetReflectionType();
216 for (const RedisWriter::Ptr& rw : ConfigType::GetObjectsByType<RedisWriter>()) {
217 rw->m_WorkQueue.Enqueue(boost::bind(&RedisWriter::SendConfigUpdate, rw, object, type->GetName()));
221 void RedisWriter::VersionChangedHandler(const ConfigObject::Ptr& object)
223 Type::Ptr type = object->GetReflectionType();
225 for (const RedisWriter::Ptr& rw : ConfigType::GetObjectsByType<RedisWriter>()) {
226 rw->m_WorkQueue.Enqueue(boost::bind(&RedisWriter::SendConfigUpdate, rw.get(), object, type->GetName()));