Start working on checksum config dump

refs #4991
This commit is contained in:
Michael Friedrich 2017-03-20 09:56:54 +01:00
parent 6e70e2ddf0
commit 0b466aabc0
6 changed files with 206 additions and 47 deletions

View File

@ -591,6 +591,46 @@ String PBKDF2_SHA1(const String& password, const String& salt, int iterations)
return output;
}
String SHA1(const String& s, bool binary)
{
char errbuf[120];
SHA_CTX context;
unsigned char digest[SHA_DIGEST_LENGTH];
if (!SHA1_Init(&context)) {
Log(LogCritical, "SSL")
<< "Error on SHA Init: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
BOOST_THROW_EXCEPTION(openssl_error()
<< boost::errinfo_api_function("SHA1_Init")
<< errinfo_openssl_error(ERR_peek_error()));
}
if (!SHA1_Update(&context, (unsigned char*)s.CStr(), s.GetLength())) {
Log(LogCritical, "SSL")
<< "Error on SHA Update: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
BOOST_THROW_EXCEPTION(openssl_error()
<< boost::errinfo_api_function("SHA1_Update")
<< errinfo_openssl_error(ERR_peek_error()));
}
if (!SHA1_Final(digest, &context)) {
Log(LogCritical, "SSL")
<< "Error on SHA Final: " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
BOOST_THROW_EXCEPTION(openssl_error()
<< boost::errinfo_api_function("SHA1_Final")
<< errinfo_openssl_error(ERR_peek_error()));
}
if (binary)
return String(reinterpret_cast<const char*>(digest));
char output[SHA_DIGEST_LENGTH*2+1];
for (int i = 0; i < 20; i++)
sprintf(output + 2 * i, "%02x", digest[i]);
return output;
}
String SHA256(const String& s)
{
char errbuf[120];

View File

@ -50,7 +50,7 @@ String I2_BASE_API GetIcingaCADir(void);
String I2_BASE_API CertificateToString(const boost::shared_ptr<X509>& cert);
boost::shared_ptr<X509> I2_BASE_API CreateCertIcingaCA(EVP_PKEY *pubkey, X509_NAME *subject);
String I2_BASE_API PBKDF2_SHA1(const String& password, const String& salt, int iterations);
String I2_BASE_API SHA1(const String& s);
String I2_BASE_API SHA1(const String& s, bool binary = false);
String I2_BASE_API SHA256(const String& s);
String I2_BASE_API RandomString(int length);

View File

@ -18,7 +18,7 @@
mkclass_target(rediswriter.ti rediswriter.tcpp rediswriter.thpp)
set(redis_SOURCES
rediswriter.cpp rediswriter-config.cpp rediswriter.thpp
rediswriter.cpp rediswriter-config.cpp rediswriter-utility.cpp rediswriter.thpp
)
if(ICINGA2_UNITY_BUILD)

View File

@ -19,9 +19,12 @@
#include "redis/rediswriter.hpp"
#include "icinga/customvarobject.hpp"
#include "icinga/host.hpp"
#include "icinga/service.hpp"
#include "base/json.hpp"
#include "base/logger.hpp"
#include "base/serializer.hpp"
#include "base/tlsutility.hpp"
#include "base/initialize.hpp"
using namespace icinga;
@ -52,12 +55,10 @@ void RedisWriter::ConfigStaticInitialize(void)
ConfigObject::OnVersionChanged.connect(boost::bind(&RedisWriter::VersionChangedHandler, _1));
}
//TODO: OnActiveChanged handling.
void RedisWriter::UpdateAllConfigObjects(void)
{
AssertOnWorkQueue();
//TODO: Just use config types
for (const Type::Ptr& type : Type::GetAllTypes()) {
if (!ConfigObject::TypeInstance->IsAssignableFrom(type))
continue;
@ -108,25 +109,62 @@ void RedisWriter::SendConfigUpdate(const ConfigObject::Ptr& object, const String
//TODO: checksum
String objectName = object->GetName();
redisReply *reply = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:config:%s %s %s", typeName.CStr(), objectName.CStr(), jsonBody.CStr()));
redisReply *reply1 = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:config:%s %s %s", typeName.CStr(), objectName.CStr(), jsonBody.CStr()));
if (!reply) {
if (!reply1) {
redisFree(m_Context);
m_Context = NULL;
return;
}
if (reply->type == REDIS_REPLY_STATUS || reply->type == REDIS_REPLY_ERROR) {
if (reply1->type == REDIS_REPLY_STATUS || reply1->type == REDIS_REPLY_ERROR) {
Log(LogInformation, "RedisWriter")
<< "HSET icinga:config:" << typeName << " " << objectName << " " << jsonBody << ": " << reply->str;
<< "HSET icinga:config:" << typeName << " " << objectName << " " << jsonBody << ": " << reply1->str;
}
if (reply->type == REDIS_REPLY_ERROR) {
freeReplyObject(reply);
if (reply1->type == REDIS_REPLY_ERROR) {
freeReplyObject(reply1);
return;
}
freeReplyObject(reply);
freeReplyObject(reply1);
/* check sums */
/* hset icinga:config:Host:checksums localhost { "name_checksum": "...", "properties_checksum": "...", "groups_checksum": "...", "vars_checksum": null } */
Dictionary::Ptr checkSum = new Dictionary();
checkSum->Set("name_checksum", CalculateCheckSumString(object->GetName()));
if (object->GetReflectionType() == Host::TypeInstance) {
Host::Ptr host = static_pointer_cast<Host>(object);
checkSum->Set("groups_checksum", CalculateCheckSumGroups(host->GetGroups()));
} else if (object->GetReflectionType() == Service::TypeInstance) {
Service::Ptr service = static_pointer_cast<Service>(object);
checkSum->Set("groups_checksum", CalculateCheckSumGroups(service->GetGroups()));
}
String checkSumBody = JsonEncode(checkSum);
redisReply *reply2 = reinterpret_cast<redisReply *>(redisCommand(m_Context, "HSET icinga:config:%s:checksum %s %s", typeName.CStr(), objectName.CStr(), checkSumBody.CStr()));
if (!reply2) {
redisFree(m_Context);
m_Context = NULL;
return;
}
if (reply2->type == REDIS_REPLY_STATUS || reply2->type == REDIS_REPLY_ERROR) {
Log(LogInformation, "RedisWriter")
<< "HSET icinga:config:" << typeName << " " << objectName << " " << jsonBody << ": " << reply2->str;
}
if (reply2->type == REDIS_REPLY_ERROR) {
freeReplyObject(reply2);
return;
}
freeReplyObject(reply2);
}
void RedisWriter::SendStatusUpdate(const ConfigObject::Ptr& object, const String& typeName)
@ -162,41 +200,6 @@ void RedisWriter::SendStatusUpdate(const ConfigObject::Ptr& object, const String
freeReplyObject(reply);
}
Dictionary::Ptr RedisWriter::SerializeObjectAttrs(const Object::Ptr& object, int fieldType)
{
Type::Ptr type = object->GetReflectionType();
std::vector<int> fids;
for (int fid = 0; fid < type->GetFieldCount(); fid++) {
fids.push_back(fid);
}
Dictionary::Ptr resultAttrs = new Dictionary();
for (int& fid : fids) {
Field field = type->GetFieldInfo(fid);
if ((field.Attributes & fieldType) == 0)
continue;
Value val = object->GetField(fid);
/* hide attributes which shouldn't be user-visible */
if (field.Attributes & FANoUserView)
continue;
/* hide internal navigation fields */
if (field.Attributes & FANavigation && !(field.Attributes & (FAConfig | FAState)))
continue;
Value sval = Serialize(val);
resultAttrs->Set(field.Name, sval);
}
return resultAttrs;
}
void RedisWriter::StateChangedHandler(const ConfigObject::Ptr& object)
{
Type::Ptr type = object->GetReflectionType();

View File

@ -0,0 +1,110 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "redis/rediswriter.hpp"
#include "icinga/customvarobject.hpp"
#include "base/json.hpp"
#include "base/logger.hpp"
#include "base/serializer.hpp"
#include "base/tlsutility.hpp"
#include "base/initialize.hpp"
using namespace icinga;
String RedisWriter::FormatCheckSumBinary(const String& str)
{
char output[20*2+1];
for (int i = 0; i < 20; i++)
sprintf(output + 2 * i, "%02x", str[i]);
return output;
}
String RedisWriter::CalculateCheckSumString(const String& str)
{
return SHA1(str, true);
}
String RedisWriter::CalculateCheckSumGroups(const Array::Ptr& groups)
{
String output;
bool first = true;
for (const String& group : groups) {
if (first)
first = false;
else
output += ";";
output += SHA1(group, true); //binary checksum required here
}
return SHA1(output, false);
}
String RedisWriter::CalculateCheckSumAttrs(const Dictionary::Ptr& attrs)
{
String output;
//TODO: Implement
for (const Dictionary::Pair& kv: attrs) {
if (kv.second.IsNumber()) {
//use a precision of 6 for floating point numbers
}
}
return output;
}
Dictionary::Ptr RedisWriter::SerializeObjectAttrs(const Object::Ptr& object, int fieldType)
{
Type::Ptr type = object->GetReflectionType();
std::vector<int> fids;
for (int fid = 0; fid < type->GetFieldCount(); fid++) {
fids.push_back(fid);
}
Dictionary::Ptr resultAttrs = new Dictionary();
for (int& fid : fids) {
Field field = type->GetFieldInfo(fid);
if ((field.Attributes & fieldType) == 0)
continue;
Value val = object->GetField(fid);
/* hide attributes which shouldn't be user-visible */
if (field.Attributes & FANoUserView)
continue;
/* hide internal navigation fields */
if (field.Attributes & FANavigation && !(field.Attributes & (FAConfig | FAState)))
continue;
Value sval = Serialize(val);
resultAttrs->Set(field.Name, sval);
}
return resultAttrs;
}

View File

@ -59,10 +59,16 @@ private:
void UpdateSubscriptionsTimerHandler(void);
void UpdateSubscriptions(void);
/* config dump */
/* config & status dump */
void UpdateAllConfigObjects(void);
void SendConfigUpdate(const ConfigObject::Ptr& object, const String& typeName);
void SendStatusUpdate(const ConfigObject::Ptr& object, const String& typeName);
/* utilities */
static String FormatCheckSumBinary(const String& str);
static String CalculateCheckSumString(const String& str);
static String CalculateCheckSumGroups(const Array::Ptr& groups);
static String CalculateCheckSumAttrs(const Dictionary::Ptr& attrs);
static Dictionary::Ptr SerializeObjectAttrs(const Object::Ptr& object, int fieldType);
static void StateChangedHandler(const ConfigObject::Ptr& object);