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 "base/json.hpp"
23 #include "base/logger.hpp"
24 #include "base/serializer.hpp"
25 #include "base/initialize.hpp"
27 using namespace icinga;
30 - icinga:config:<type> as hash
31 key: sha1 checksum(name)
32 value: JsonEncode(Serialize(object, FAConfig)) + config_checksum
34 Diff between calculated config_checksum and Redis json config_checksum
35 Alternative: Replace into.
38 - icinga:status:<type> as hash
39 key: sha1 checksum(name)
40 value: JsonEncode(Serialize(object, FAState))
43 INITIALIZE_ONCE(&RedisWriter::ConfigStaticInitialize);
45 void RedisWriter::ConfigStaticInitialize(void)
47 /* triggered in ProcessCheckResult(), requires UpdateNextCheck() to be called before */
48 ConfigObject::OnStateChanged.connect(boost::bind(&RedisWriter::StateChangedHandler, _1));
49 CustomVarObject::OnVarsChanged.connect(boost::bind(&RedisWriter::VarsChangedHandler, _1));
51 /* triggered on create, update and delete objects */
52 ConfigObject::OnVersionChanged.connect(boost::bind(&RedisWriter::VersionChangedHandler, _1));
55 //TODO: OnActiveChanged handling.
56 void RedisWriter::UpdateAllConfigObjects(void)
60 //TODO: Just use config types
61 for (const Type::Ptr& type : Type::GetAllTypes()) {
62 if (!ConfigObject::TypeInstance->IsAssignableFrom(type))
65 String typeName = type->GetName();
67 /* replace into aka delete insert is faster than a full diff */
68 redisReply *reply1 = reinterpret_cast<redisReply *>(redisCommand(m_Context, "DEL icinga:config:%s", typeName.CStr()));
76 if (reply1->type == REDIS_REPLY_STATUS || reply1->type == REDIS_REPLY_ERROR) {
77 Log(LogInformation, "RedisWriter")
78 << "DEL icinga:config:" << typeName << ": " << reply1->str;
81 if (reply1->type == REDIS_REPLY_ERROR) {
82 freeReplyObject(reply1);
86 freeReplyObject(reply1);
88 redisReply *reply2 = reinterpret_cast<redisReply *>(redisCommand(m_Context, "DEL icinga:status:%s", typeName.CStr()));
96 if (reply2->type == REDIS_REPLY_STATUS || reply2->type == REDIS_REPLY_ERROR) {
97 Log(LogInformation, "RedisWriter")
98 << "DEL icinga:status:" << typeName << ": " << reply2->str;
101 if (reply2->type == REDIS_REPLY_ERROR) {
102 freeReplyObject(reply2);
106 freeReplyObject(reply2);
108 /* fetch all objects and dump them */
109 ConfigType *ctype = dynamic_cast<ConfigType *>(type.get());
112 for (const ConfigObject::Ptr& object : ctype->GetObjects()) {
113 SendConfigUpdate(object, typeName);
114 SendStatusUpdate(object, typeName);
119 void RedisWriter::SendConfigUpdate(const ConfigObject::Ptr& object, const String& typeName)
123 /* Serialize config object attributes */
124 Dictionary::Ptr objectAttrs = SerializeObjectAttrs(object, FAConfig);
126 String jsonBody = JsonEncode(objectAttrs);
129 String objectName = object->GetName();
131 redisReply *reply = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:config:%s %s %s", typeName.CStr(), objectName.CStr(), jsonBody.CStr()));
134 redisFree(m_Context);
139 if (reply->type == REDIS_REPLY_STATUS || reply->type == REDIS_REPLY_ERROR) {
140 Log(LogInformation, "RedisWriter")
141 << "HSET icinga:config:" << typeName << " " << objectName << " " << jsonBody << ": " << reply->str;
144 if (reply->type == REDIS_REPLY_ERROR) {
145 freeReplyObject(reply);
149 freeReplyObject(reply);
152 void RedisWriter::SendStatusUpdate(const ConfigObject::Ptr& object, const String& typeName)
156 /* Serialize config object attributes */
157 Dictionary::Ptr objectAttrs = SerializeObjectAttrs(object, FAState);
159 String jsonBody = JsonEncode(objectAttrs);
162 String objectName = object->GetName();
164 redisReply *reply = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:status:%s %s %s", typeName.CStr(), objectName.CStr(), jsonBody.CStr()));
167 redisFree(m_Context);
172 if (reply->type == REDIS_REPLY_STATUS || reply->type == REDIS_REPLY_ERROR) {
173 Log(LogInformation, "RedisWriter")
174 << "HSET icinga:status:" << typeName << " " << objectName << " " << jsonBody << ": " << reply->str;
177 if (reply->type == REDIS_REPLY_ERROR) {
178 freeReplyObject(reply);
182 freeReplyObject(reply);
185 Dictionary::Ptr RedisWriter::SerializeObjectAttrs(const Object::Ptr& object, int fieldType)
187 Type::Ptr type = object->GetReflectionType();
189 std::vector<int> fids;
191 for (int fid = 0; fid < type->GetFieldCount(); fid++) {
195 Dictionary::Ptr resultAttrs = new Dictionary();
197 for (int& fid : fids) {
198 Field field = type->GetFieldInfo(fid);
200 if ((field.Attributes & fieldType) == 0)
203 Value val = object->GetField(fid);
205 /* hide attributes which shouldn't be user-visible */
206 if (field.Attributes & FANoUserView)
209 /* hide internal navigation fields */
210 if (field.Attributes & FANavigation && !(field.Attributes & (FAConfig | FAState)))
213 Value sval = Serialize(val);
214 resultAttrs->Set(field.Name, sval);
220 void RedisWriter::StateChangedHandler(const ConfigObject::Ptr& object)
222 Type::Ptr type = object->GetReflectionType();
224 for (const RedisWriter::Ptr& rw : ConfigType::GetObjectsByType<RedisWriter>()) {
225 rw->m_WorkQueue.Enqueue(boost::bind(&RedisWriter::SendStatusUpdate, rw.get(), object, type->GetName()));
229 void RedisWriter::VarsChangedHandler(const ConfigObject::Ptr& object)
231 Type::Ptr type = object->GetReflectionType();
233 for (const RedisWriter::Ptr& rw : ConfigType::GetObjectsByType<RedisWriter>()) {
234 rw->m_WorkQueue.Enqueue(boost::bind(&RedisWriter::SendConfigUpdate, rw.get(), object, type->GetName()));
238 void RedisWriter::VersionChangedHandler(const ConfigObject::Ptr& object)
240 Type::Ptr type = object->GetReflectionType();
242 for (const RedisWriter::Ptr& rw : ConfigType::GetObjectsByType<RedisWriter>()) {
243 rw->m_WorkQueue.Enqueue(boost::bind(&RedisWriter::SendConfigUpdate, rw.get(), object, type->GetName()));