]> granicus.if.org Git - icinga2/blob - lib/redis/rediswriter-config.cpp
Remove some log messages
[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 "base/json.hpp"
23 #include "base/logger.hpp"
24 #include "base/serializer.hpp"
25 #include "base/initialize.hpp"
26
27 using namespace icinga;
28
29 /*
30 - icinga:config:<type> as hash
31 key: sha1 checksum(name)
32 value: JsonEncode(Serialize(object, FAConfig)) + config_checksum
33
34 Diff between calculated config_checksum and Redis json config_checksum
35 Alternative: Replace into.
36
37
38 - icinga:status:<type> as hash
39 key: sha1 checksum(name)
40 value: JsonEncode(Serialize(object, FAState))
41 */
42
43 INITIALIZE_ONCE(&RedisWriter::ConfigStaticInitialize);
44
45 void RedisWriter::ConfigStaticInitialize(void)
46 {
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));
50
51         /* triggered on create, update and delete objects */
52         ConfigObject::OnVersionChanged.connect(boost::bind(&RedisWriter::VersionChangedHandler, _1));
53 }
54
55 //TODO: OnActiveChanged handling.
56 void RedisWriter::UpdateAllConfigObjects(void)
57 {
58         //TODO: Just use config types
59         for (const Type::Ptr& type : Type::GetAllTypes()) {
60                 if (!ConfigObject::TypeInstance->IsAssignableFrom(type))
61                         continue;
62
63                 String typeName = type->GetName();
64
65                 /* replace into aka delete insert is faster than a full diff */
66                 redisReply *reply1 = reinterpret_cast<redisReply *>(redisCommand(m_Context, "DEL icinga:config:%s", typeName.CStr()));
67
68                 if (!reply1) {
69                         redisFree(m_Context);
70                         m_Context = NULL;
71                         return;
72                 }
73
74                 if (reply1->type == REDIS_REPLY_STATUS || reply1->type == REDIS_REPLY_ERROR) {
75                         Log(LogInformation, "RedisWriter")
76                             << "DEL icinga:config:" << typeName << ": " << reply1->str;
77                 }
78
79                 if (reply1->type == REDIS_REPLY_ERROR) {
80                         freeReplyObject(reply1);
81                         return;
82                 }
83
84                 freeReplyObject(reply1);
85
86                 redisReply *reply2 = reinterpret_cast<redisReply *>(redisCommand(m_Context, "DEL icinga:status:%s", typeName.CStr()));
87
88                 if (!reply2) {
89                         redisFree(m_Context);
90                         m_Context = NULL;
91                         return;
92                 }
93
94                 if (reply2->type == REDIS_REPLY_STATUS || reply2->type == REDIS_REPLY_ERROR) {
95                         Log(LogInformation, "RedisWriter")
96                             << "DEL icinga:status:" << typeName << ": " << reply2->str;
97                 }
98
99                 if (reply2->type == REDIS_REPLY_ERROR) {
100                         freeReplyObject(reply2);
101                         return;
102                 }
103
104                 freeReplyObject(reply2);
105
106                 /* fetch all objects and dump them */
107                 ConfigType *ctype = dynamic_cast<ConfigType *>(type.get());
108                 VERIFY(ctype);
109
110                 for (const ConfigObject::Ptr& object : ctype->GetObjects()) {
111                         SendConfigUpdate(object, typeName);
112                         SendStatusUpdate(object, typeName);
113                 }
114         }
115 }
116
117 void RedisWriter::SendConfigUpdate(const ConfigObject::Ptr& object, const String& typeName)
118 {
119         /* Serialize config object attributes */
120         Dictionary::Ptr objectAttrs = SerializeObjectAttrs(object, FAConfig);
121
122         String jsonBody = JsonEncode(objectAttrs);
123
124         //TODO: checksum
125         String objectName = object->GetName();
126
127         redisReply *reply = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:config:%s %s %s", typeName.CStr(), objectName.CStr(), jsonBody.CStr()));
128
129         if (!reply) {
130                 redisFree(m_Context);
131                 m_Context = NULL;
132                 return;
133         }
134
135         if (reply->type == REDIS_REPLY_STATUS || reply->type == REDIS_REPLY_ERROR) {
136                 Log(LogInformation, "RedisWriter")
137                     << "HSET icinga:config:" << typeName << " " << objectName << " " << jsonBody << ": " << reply->str;
138         }
139
140         if (reply->type == REDIS_REPLY_ERROR) {
141                 freeReplyObject(reply);
142                 return;
143         }
144
145         freeReplyObject(reply);
146 }
147
148 void RedisWriter::SendStatusUpdate(const ConfigObject::Ptr& object, const String& typeName)
149 {
150         /* Serialize config object attributes */
151         Dictionary::Ptr objectAttrs = SerializeObjectAttrs(object, FAState);
152
153         String jsonBody = JsonEncode(objectAttrs);
154
155         //TODO: checksum
156         String objectName = object->GetName();
157
158         redisReply *reply = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:status:%s %s %s", typeName.CStr(), objectName.CStr(), jsonBody.CStr()));
159
160         if (!reply) {
161                 redisFree(m_Context);
162                 m_Context = NULL;
163                 return;
164         }
165
166         if (reply->type == REDIS_REPLY_STATUS || reply->type == REDIS_REPLY_ERROR) {
167                 Log(LogInformation, "RedisWriter")
168                     << "HSET icinga:status:" << typeName << " " << objectName << " " << jsonBody << ": " << reply->str;
169         }
170
171         if (reply->type == REDIS_REPLY_ERROR) {
172                 freeReplyObject(reply);
173                 return;
174         }
175
176         freeReplyObject(reply);
177 }
178
179 Dictionary::Ptr RedisWriter::SerializeObjectAttrs(const Object::Ptr& object, int fieldType)
180 {
181         Type::Ptr type = object->GetReflectionType();
182
183         std::vector<int> fids;
184
185         for (int fid = 0; fid < type->GetFieldCount(); fid++) {
186                 fids.push_back(fid);
187         }
188
189         Dictionary::Ptr resultAttrs = new Dictionary();
190
191         for (int& fid : fids) {
192                 Field field = type->GetFieldInfo(fid);
193
194                 if ((field.Attributes & fieldType) == 0)
195                         continue;
196
197                 Value val = object->GetField(fid);
198
199                 /* hide attributes which shouldn't be user-visible */
200                 if (field.Attributes & FANoUserView)
201                         continue;
202
203                 /* hide internal navigation fields */
204                 if (field.Attributes & FANavigation && !(field.Attributes & (FAConfig | FAState)))
205                         continue;
206
207                 Value sval = Serialize(val);
208                 resultAttrs->Set(field.Name, sval);
209         }
210
211         return resultAttrs;
212 }
213
214 void RedisWriter::StateChangedHandler(const ConfigObject::Ptr& object)
215 {
216         Type::Ptr type = object->GetReflectionType();
217
218         for (const RedisWriter::Ptr& rw : ConfigType::GetObjectsByType<RedisWriter>()) {
219                 rw->m_WorkQueue.Enqueue(boost::bind(&RedisWriter::SendStatusUpdate, rw.get(), object, type->GetName()));
220         }
221 }
222
223 void RedisWriter::VarsChangedHandler(const ConfigObject::Ptr& object)
224 {
225         Type::Ptr type = object->GetReflectionType();
226
227         for (const RedisWriter::Ptr& rw : ConfigType::GetObjectsByType<RedisWriter>()) {
228                 rw->m_WorkQueue.Enqueue(boost::bind(&RedisWriter::SendConfigUpdate, rw.get(), object, type->GetName()));
229         }
230 }
231
232 void RedisWriter::VersionChangedHandler(const ConfigObject::Ptr& object)
233 {
234         Type::Ptr type = object->GetReflectionType();
235
236         for (const RedisWriter::Ptr& rw : ConfigType::GetObjectsByType<RedisWriter>()) {
237                 rw->m_WorkQueue.Enqueue(boost::bind(&RedisWriter::SendConfigUpdate, rw.get(), object, type->GetName()));
238         }
239 }