mirror of
https://github.com/Icinga/icinga2.git
synced 2025-09-26 11:08:51 +02:00
In order to avoid changes to the environment ID, it is now no longer derived from the Environment constant but instead from the public key of the CA certificate. This ensures that it is different between clusters by default, so no additional changes have to be done to allow two clusters to use Icinga DB to write into the same database. To prevent the ID from changing when the CA certificate is replaced, it is also persisted into the file /var/lib/icinga2/icingadb.env, so if that file exists, it takes precedence over the CA certificate.
271 lines
6.2 KiB
C++
271 lines
6.2 KiB
C++
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
|
|
|
|
#include "icingadb/icingadb.hpp"
|
|
#include "base/configtype.hpp"
|
|
#include "base/object-packer.hpp"
|
|
#include "base/logger.hpp"
|
|
#include "base/serializer.hpp"
|
|
#include "base/tlsutility.hpp"
|
|
#include "base/initialize.hpp"
|
|
#include "base/objectlock.hpp"
|
|
#include "base/array.hpp"
|
|
#include "base/scriptglobal.hpp"
|
|
#include "base/convert.hpp"
|
|
#include "base/json.hpp"
|
|
#include "icinga/customvarobject.hpp"
|
|
#include "icinga/checkcommand.hpp"
|
|
#include "icinga/notificationcommand.hpp"
|
|
#include "icinga/eventcommand.hpp"
|
|
#include "icinga/host.hpp"
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
using namespace icinga;
|
|
|
|
String IcingaDB::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 IcingaDB::FormatCommandLine(const Value& commandLine)
|
|
{
|
|
String result;
|
|
if (commandLine.IsObjectType<Array>()) {
|
|
Array::Ptr args = commandLine;
|
|
bool first = true;
|
|
|
|
ObjectLock olock(args);
|
|
for (const Value& arg : args) {
|
|
String token = "'" + Convert::ToString(arg) + "'";
|
|
|
|
if (first)
|
|
first = false;
|
|
else
|
|
result += String(1, ' ');
|
|
|
|
result += token;
|
|
}
|
|
} else if (!commandLine.IsEmpty()) {
|
|
result = commandLine;
|
|
boost::algorithm::replace_all(result, "\'", "\\'");
|
|
result = "'" + result + "'";
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
ArrayData IcingaDB::GetObjectIdentifiersWithoutEnv(const ConfigObject::Ptr& object)
|
|
{
|
|
Type::Ptr type = object->GetReflectionType();
|
|
|
|
if (type == CheckCommand::TypeInstance || type == NotificationCommand::TypeInstance || type == EventCommand::TypeInstance)
|
|
return {type->GetName(), object->GetName()};
|
|
else
|
|
return {object->GetName()};
|
|
}
|
|
|
|
String IcingaDB::GetObjectIdentifier(const ConfigObject::Ptr& object)
|
|
{
|
|
return HashValue(new Array(Prepend(m_EnvironmentId, GetObjectIdentifiersWithoutEnv(object))));
|
|
}
|
|
|
|
/**
|
|
* Calculates a deterministic history event ID like SHA1(env, eventType, x...[, nt][, eventTime])
|
|
*
|
|
* Where SHA1(env, x...) = GetObjectIdentifier(object)
|
|
*/
|
|
String IcingaDB::CalcEventID(const char* eventType, const ConfigObject::Ptr& object, double eventTime, NotificationType nt)
|
|
{
|
|
Array::Ptr rawId = new Array(GetObjectIdentifiersWithoutEnv(object));
|
|
rawId->Insert(0, m_EnvironmentId);
|
|
rawId->Insert(1, eventType);
|
|
|
|
if (nt) {
|
|
rawId->Add(GetNotificationTypeByEnum(nt));
|
|
}
|
|
|
|
if (eventTime) {
|
|
rawId->Add(TimestampToMilliseconds(eventTime));
|
|
}
|
|
|
|
return HashValue(std::move(rawId));
|
|
}
|
|
|
|
static const std::set<String> metadataWhitelist ({"package", "source_location", "templates"});
|
|
|
|
/**
|
|
* Prepare object's custom vars for being written to Redis
|
|
*
|
|
* object.vars = {
|
|
* "disks": {
|
|
* "disk": {},
|
|
* "disk /": {
|
|
* "disk_partitions": "/"
|
|
* }
|
|
* }
|
|
* }
|
|
*
|
|
* return {
|
|
* SHA1(PackObject([
|
|
* EnvironmentId,
|
|
* "disks",
|
|
* {
|
|
* "disk": {},
|
|
* "disk /": {
|
|
* "disk_partitions": "/"
|
|
* }
|
|
* }
|
|
* ])): {
|
|
* "environment_id": EnvironmentId,
|
|
* "name_checksum": SHA1("disks"),
|
|
* "name": "disks",
|
|
* "value": {
|
|
* "disk": {},
|
|
* "disk /": {
|
|
* "disk_partitions": "/"
|
|
* }
|
|
* }
|
|
* }
|
|
* }
|
|
*
|
|
* @param object Config object with custom vars
|
|
*
|
|
* @return JSON-like data structure for Redis
|
|
*/
|
|
Dictionary::Ptr IcingaDB::SerializeVars(const CustomVarObject::Ptr& object)
|
|
{
|
|
Dictionary::Ptr vars = object->GetVars();
|
|
|
|
if (!vars)
|
|
return nullptr;
|
|
|
|
Dictionary::Ptr res = new Dictionary();
|
|
|
|
ObjectLock olock(vars);
|
|
|
|
for (auto& kv : vars) {
|
|
res->Set(
|
|
SHA1(PackObject((Array::Ptr)new Array({m_EnvironmentId, kv.first, kv.second}))),
|
|
(Dictionary::Ptr)new Dictionary({
|
|
{"environment_id", m_EnvironmentId},
|
|
{"name_checksum", SHA1(kv.first)},
|
|
{"name", kv.first},
|
|
{"value", JsonEncode(kv.second)},
|
|
})
|
|
);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
const char* IcingaDB::GetNotificationTypeByEnum(NotificationType type)
|
|
{
|
|
switch (type) {
|
|
case NotificationDowntimeStart:
|
|
return "downtime_start";
|
|
case NotificationDowntimeEnd:
|
|
return "downtime_end";
|
|
case NotificationDowntimeRemoved:
|
|
return "downtime_removed";
|
|
case NotificationCustom:
|
|
return "custom";
|
|
case NotificationAcknowledgement:
|
|
return "acknowledgement";
|
|
case NotificationProblem:
|
|
return "problem";
|
|
case NotificationRecovery:
|
|
return "recovery";
|
|
case NotificationFlappingStart:
|
|
return "flapping_start";
|
|
case NotificationFlappingEnd:
|
|
return "flapping_end";
|
|
}
|
|
|
|
VERIFY(!"Invalid notification type.");
|
|
}
|
|
|
|
static const std::set<String> propertiesBlacklistEmpty;
|
|
|
|
String IcingaDB::HashValue(const Value& value)
|
|
{
|
|
return HashValue(value, propertiesBlacklistEmpty);
|
|
}
|
|
|
|
String IcingaDB::HashValue(const Value& value, const std::set<String>& propertiesBlacklist, bool propertiesWhitelist)
|
|
{
|
|
Value temp;
|
|
bool mutabl;
|
|
|
|
Type::Ptr type = value.GetReflectionType();
|
|
|
|
if (ConfigObject::TypeInstance->IsAssignableFrom(type)) {
|
|
temp = Serialize(value, FAConfig);
|
|
mutabl = true;
|
|
} else {
|
|
temp = value;
|
|
mutabl = false;
|
|
}
|
|
|
|
if (propertiesBlacklist.size() && temp.IsObject()) {
|
|
Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>((Object::Ptr)temp);
|
|
|
|
if (dict) {
|
|
if (!mutabl)
|
|
dict = dict->ShallowClone();
|
|
|
|
ObjectLock olock(dict);
|
|
|
|
if (propertiesWhitelist) {
|
|
auto current = dict->Begin();
|
|
auto propertiesBlacklistEnd = propertiesBlacklist.end();
|
|
|
|
while (current != dict->End()) {
|
|
if (propertiesBlacklist.find(current->first) == propertiesBlacklistEnd) {
|
|
dict->Remove(current++);
|
|
} else {
|
|
++current;
|
|
}
|
|
}
|
|
} else {
|
|
for (auto& property : propertiesBlacklist)
|
|
dict->Remove(property);
|
|
}
|
|
|
|
if (!mutabl)
|
|
temp = dict;
|
|
}
|
|
}
|
|
|
|
return SHA1(PackObject(temp));
|
|
}
|
|
|
|
String IcingaDB::GetLowerCaseTypeNameDB(const ConfigObject::Ptr& obj)
|
|
{
|
|
return obj->GetReflectionType()->GetName().ToLower();
|
|
}
|
|
|
|
long long IcingaDB::TimestampToMilliseconds(double timestamp) {
|
|
return static_cast<long long>(timestamp * 1000);
|
|
}
|
|
|
|
String IcingaDB::IcingaToStreamValue(const Value& value)
|
|
{
|
|
switch (value.GetType()) {
|
|
case ValueBoolean:
|
|
return Convert::ToString(int(value));
|
|
case ValueString:
|
|
return Utility::ValidateUTF8(value);
|
|
case ValueNumber:
|
|
case ValueEmpty:
|
|
return Convert::ToString(value);
|
|
default:
|
|
return JsonEncode(value);
|
|
}
|
|
}
|