From 2d53e000c8fc9d1a3b220b90ca1521597510d3f5 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 20 Nov 2014 13:28:21 +0100 Subject: [PATCH] Refactor the config validator so that it doesn't require serialized objects refs #7701 --- lib/compat/compatlogger.cpp | 6 +- lib/compat/compatlogger.hpp | 2 +- lib/config/base-type.conf | 2 + lib/config/configitem.cpp | 3 +- lib/config/configtype.cpp | 153 ++++++++++++-------------- lib/config/configtype.hpp | 7 +- lib/db_ido/dbconnection.cpp | 7 +- lib/db_ido/dbconnection.hpp | 2 +- lib/icinga/command.cpp | 4 +- lib/icinga/command.hpp | 2 +- lib/icinga/dependency.cpp | 8 +- lib/icinga/dependency.hpp | 2 +- lib/icinga/notification.cpp | 10 +- lib/icinga/notification.hpp | 2 +- lib/livestatus/livestatuslistener.cpp | 6 +- lib/livestatus/livestatuslistener.hpp | 2 +- 16 files changed, 102 insertions(+), 116 deletions(-) diff --git a/lib/compat/compatlogger.cpp b/lib/compat/compatlogger.cpp index 1c3a3f076..01e479d48 100644 --- a/lib/compat/compatlogger.cpp +++ b/lib/compat/compatlogger.cpp @@ -563,11 +563,11 @@ void CompatLogger::RotationTimerHandler(void) ScheduleNextRotation(); } -void CompatLogger::ValidateRotationMethod(const String& location, const Dictionary::Ptr& attrs) +void CompatLogger::ValidateRotationMethod(const String& location, const CompatLogger::Ptr& object) { - Value rotation_method = attrs->Get("rotation_method"); + String rotation_method = object->GetRotationMethod(); - if (!rotation_method.IsEmpty() && rotation_method != "HOURLY" && rotation_method != "DAILY" && + if (rotation_method != "HOURLY" && rotation_method != "DAILY" && rotation_method != "WEEKLY" && rotation_method != "MONTHLY" && rotation_method != "NONE") { ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " + location + ": Rotation method '" + rotation_method + "' is invalid."); diff --git a/lib/compat/compatlogger.hpp b/lib/compat/compatlogger.hpp index 82f5c36ea..5c75934f0 100644 --- a/lib/compat/compatlogger.hpp +++ b/lib/compat/compatlogger.hpp @@ -41,7 +41,7 @@ public: static Value StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata); - static void ValidateRotationMethod(const String& location, const Dictionary::Ptr& attrs); + static void ValidateRotationMethod(const String& location, const CompatLogger::Ptr& object); protected: virtual void Start(void); diff --git a/lib/config/base-type.conf b/lib/config/base-type.conf index 542026eec..a45329d9b 100644 --- a/lib/config/base-type.conf +++ b/lib/config/base-type.conf @@ -21,6 +21,8 @@ %require "__name", %attribute %string "__name", + %attribute %string "name", + %require "type", %attribute %string "type", diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index 0b48c8a81..00beca8bf 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -209,8 +209,7 @@ DynamicObject::Ptr ConfigItem::Commit(bool discard) TypeRuleUtilities utils; try { - attrs->Remove("name"); - ctype->ValidateItem(GetName(), attrs, GetDebugInfo(), &utils); + ctype->ValidateItem(GetName(), dobj, GetDebugInfo(), &utils); } catch (const ConfigError& ex) { const DebugInfo *di = boost::get_error_info(ex); ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo()); diff --git a/lib/config/configtype.cpp b/lib/config/configtype.cpp index 9970a440c..9a5873339 100644 --- a/lib/config/configtype.cpp +++ b/lib/config/configtype.cpp @@ -19,6 +19,7 @@ #include "config/configtype.hpp" #include "config/configcompilercontext.hpp" +#include "config/expression.hpp" #include "base/objectlock.hpp" #include "base/convert.hpp" #include "base/singleton.hpp" @@ -72,7 +73,7 @@ void ConfigType::AddParentRules(std::vector& ruleLists, const } } -void ConfigType::ValidateItem(const String& name, const Dictionary::Ptr& attrs, const DebugInfo& debugInfo, const TypeRuleUtilities *utils) +void ConfigType::ValidateItem(const String& name, const Object::Ptr& object, const DebugInfo& debugInfo, const TypeRuleUtilities *utils) { String location = "Object '" + name + "' (Type: '" + GetName() + "')"; @@ -86,7 +87,7 @@ void ConfigType::ValidateItem(const String& name, const Dictionary::Ptr& attrs, AddParentRules(ruleLists, this); ruleLists.push_back(m_RuleList); - ValidateDictionary(attrs, ruleLists, locations, utils); + ValidateObject(object, ruleLists, locations, utils); } String ConfigType::LocationToString(const std::vector& locations) @@ -105,7 +106,55 @@ String ConfigType::LocationToString(const std::vector& locations) return stack; } -void ConfigType::ValidateDictionary(const Dictionary::Ptr& dictionary, +void ConfigType::ValidateAttribute(const String& key, const Value& value, + const std::vector& ruleLists, std::vector& locations, + const TypeRuleUtilities *utils) +{ + TypeValidationResult overallResult = ValidationUnknownField; + std::vector subRuleLists; + String hint; + + locations.push_back("Key " + key); + + BOOST_FOREACH(const TypeRuleList::Ptr& ruleList, ruleLists) { + TypeRuleList::Ptr subRuleList; + TypeValidationResult result = ruleList->ValidateAttribute(key, value, &subRuleList, &hint, utils); + + if (subRuleList) + subRuleLists.push_back(subRuleList); + + if (overallResult == ValidationOK) + continue; + + if (result == ValidationOK) { + overallResult = result; + continue; + } + + if (result == ValidationInvalidType) + overallResult = result; + } + + if (overallResult == ValidationUnknownField) + ConfigCompilerContext::GetInstance()->AddMessage(true, "Unknown attribute: " + LocationToString(locations)); + else if (overallResult == ValidationInvalidType) { + String message = "Invalid value: " + LocationToString(locations); + + if (!hint.IsEmpty()) + message += ": " + hint; + + ConfigCompilerContext::GetInstance()->AddMessage(true, message); + } + + if (!subRuleLists.empty() && value.IsObject() && !value.IsObjectType()) + ValidateObject(value, subRuleLists, locations, utils); + else if (!subRuleLists.empty() && value.IsObjectType()) + ValidateArray(value, subRuleLists, locations, utils); + + locations.pop_back(); +} + +void ConfigType::ValidateObject(const Object::Ptr& object, const std::vector& ruleLists, std::vector& locations, const TypeRuleUtilities *utils) { @@ -113,7 +162,7 @@ void ConfigType::ValidateDictionary(const Dictionary::Ptr& dictionary, BOOST_FOREACH(const String& require, ruleList->GetRequires()) { locations.push_back("Attribute '" + require + "'"); - Value value = dictionary->Get(require); + Value value = Expression::GetField(object, require); if (value.IsEmpty() || (value.IsString() && static_cast(value).IsEmpty())) { ConfigCompilerContext::GetInstance()->AddMessage(true, @@ -133,57 +182,34 @@ void ConfigType::ValidateDictionary(const Dictionary::Ptr& dictionary, std::vector arguments; arguments.push_back(LocationToString(locations)); - arguments.push_back(dictionary); + arguments.push_back(object); func->Invoke(arguments); } } - ObjectLock olock(dictionary); + Dictionary::Ptr dictionary = dynamic_pointer_cast(object); - BOOST_FOREACH(const Dictionary::Pair& kv, dictionary) { - TypeValidationResult overallResult = ValidationUnknownField; - std::vector subRuleLists; - String hint; + if (dictionary) { + ObjectLock olock(dictionary); - locations.push_back("Attribute '" + kv.first + "'"); + BOOST_FOREACH(const Dictionary::Pair& kv, dictionary) { + ValidateAttribute(kv.first, kv.second, ruleLists, locations, utils); + } + } else { + Type::Ptr type = object->GetReflectionType(); - BOOST_FOREACH(const TypeRuleList::Ptr& ruleList, ruleLists) { - TypeRuleList::Ptr subRuleList; - TypeValidationResult result = ruleList->ValidateAttribute(kv.first, kv.second, &subRuleList, &hint, utils); + if (!type) + return; - if (subRuleList) - subRuleLists.push_back(subRuleList); + for (int i = 0; i < type->GetFieldCount(); i++) { + Field field = type->GetFieldInfo(i); - if (overallResult == ValidationOK) + if (!(field.Attributes & FAConfig)) continue; - if (result == ValidationOK) { - overallResult = result; - continue; - } - - if (result == ValidationInvalidType) - overallResult = result; - } - - if (overallResult == ValidationUnknownField) - ConfigCompilerContext::GetInstance()->AddMessage(true, "Unknown attribute: " + LocationToString(locations)); - else if (overallResult == ValidationInvalidType) { - String message = "Invalid value for attribute: " + LocationToString(locations); - - if (!hint.IsEmpty()) - message += ": " + hint; - - ConfigCompilerContext::GetInstance()->AddMessage(true, message); + ValidateAttribute(field.Name, object->GetField(i), ruleLists, locations, utils); } - - if (!subRuleLists.empty() && kv.second.IsObjectType()) - ValidateDictionary(kv.second, subRuleLists, locations, utils); - else if (!subRuleLists.empty() && kv.second.IsObjectType()) - ValidateArray(kv.second, subRuleLists, locations, utils); - - locations.pop_back(); } } @@ -229,48 +255,7 @@ void ConfigType::ValidateArray(const Array::Ptr& array, key = Convert::ToString(index); index++; - TypeValidationResult overallResult = ValidationUnknownField; - std::vector subRuleLists; - String hint; - - locations.push_back("Index " + key); - - BOOST_FOREACH(const TypeRuleList::Ptr& ruleList, ruleLists) { - TypeRuleList::Ptr subRuleList; - TypeValidationResult result = ruleList->ValidateAttribute(key, value, &subRuleList, &hint, utils); - - if (subRuleList) - subRuleLists.push_back(subRuleList); - - if (overallResult == ValidationOK) - continue; - - if (result == ValidationOK) { - overallResult = result; - continue; - } - - if (result == ValidationInvalidType) - overallResult = result; - } - - if (overallResult == ValidationUnknownField) - ConfigCompilerContext::GetInstance()->AddMessage(true, "Unknown attribute: " + LocationToString(locations)); - else if (overallResult == ValidationInvalidType) { - String message = "Invalid value for array index: " + LocationToString(locations); - - if (!hint.IsEmpty()) - message += ": " + hint; - - ConfigCompilerContext::GetInstance()->AddMessage(true, message); - } - - if (!subRuleLists.empty() && value.IsObjectType()) - ValidateDictionary(value, subRuleLists, locations, utils); - else if (!subRuleLists.empty() && value.IsObjectType()) - ValidateArray(value, subRuleLists, locations, utils); - - locations.pop_back(); + ValidateAttribute(key, value, ruleLists, locations, utils); } } diff --git a/lib/config/configtype.hpp b/lib/config/configtype.hpp index aa8b770ad..b2dd527eb 100644 --- a/lib/config/configtype.hpp +++ b/lib/config/configtype.hpp @@ -50,7 +50,7 @@ public: DebugInfo GetDebugInfo(void) const; - void ValidateItem(const String& name, const Dictionary::Ptr& attrs, + void ValidateItem(const String& name, const Object::Ptr& object, const DebugInfo& debugInfo, const TypeRuleUtilities *utils); void Register(void); @@ -65,7 +65,10 @@ private: TypeRuleList::Ptr m_RuleList; DebugInfo m_DebugInfo; /**< Debug information. */ - static void ValidateDictionary(const Dictionary::Ptr& dictionary, + static void ValidateAttribute(const String& key, const Value& value, + const std::vector& ruleLists, std::vector& locations, + const TypeRuleUtilities *utils); + static void ValidateObject(const Object::Ptr& object, const std::vector& ruleLists, std::vector& locations, const TypeRuleUtilities *utils); static void ValidateArray(const Array::Ptr& array, diff --git a/lib/db_ido/dbconnection.cpp b/lib/db_ido/dbconnection.cpp index e8c2e1435..d754505f2 100644 --- a/lib/db_ido/dbconnection.cpp +++ b/lib/db_ido/dbconnection.cpp @@ -421,12 +421,9 @@ void DbConnection::PrepareDatabase(void) } } -void DbConnection::ValidateFailoverTimeout(const String& location, const Dictionary::Ptr& attrs) +void DbConnection::ValidateFailoverTimeout(const String& location, const DbConnection::Ptr& object) { - if (!attrs->Contains("failover_timeout")) - return; - - if (attrs->Get("failover_timeout") < 60) { + if (object->GetFailoverTimeout() < 60) { ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " + location + ": Failover timeout minimum is 60s."); } diff --git a/lib/db_ido/dbconnection.hpp b/lib/db_ido/dbconnection.hpp index cac7f210d..befe2fc61 100644 --- a/lib/db_ido/dbconnection.hpp +++ b/lib/db_ido/dbconnection.hpp @@ -63,7 +63,7 @@ public: void SetStatusUpdate(const DbObject::Ptr& dbobj, bool hasupdate); bool GetStatusUpdate(const DbObject::Ptr& dbobj) const; - static void ValidateFailoverTimeout(const String& location, const Dictionary::Ptr& attrs); + static void ValidateFailoverTimeout(const String& location, const DbConnection::Ptr& object); protected: virtual void OnConfigLoaded(void); diff --git a/lib/icinga/command.cpp b/lib/icinga/command.cpp index 4056c9037..947fcf3b8 100644 --- a/lib/icinga/command.cpp +++ b/lib/icinga/command.cpp @@ -44,9 +44,9 @@ void Command::SetModifiedAttributes(int flags, const MessageOrigin& origin) } } -void Command::ValidateAttributes(const String& location, const Dictionary::Ptr& attrs) +void Command::ValidateAttributes(const String& location, const Command::Ptr& object) { - if (attrs->Get("arguments") != Empty && !attrs->Get("command").IsObjectType()) { + if (object->GetArguments() != Empty && !object->GetCommandLine().IsObjectType()) { ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " + location + ": Attribute 'command' must be an array if the 'arguments' attribute is set."); } diff --git a/lib/icinga/command.hpp b/lib/icinga/command.hpp index ad21659b2..5bb38bed5 100644 --- a/lib/icinga/command.hpp +++ b/lib/icinga/command.hpp @@ -39,7 +39,7 @@ public: //virtual Dictionary::Ptr Execute(const Object::Ptr& context) = 0; - static void ValidateAttributes(const String& location, const Dictionary::Ptr& attrs); + static void ValidateAttributes(const String& location, const Command::Ptr& object); int GetModifiedAttributes(void) const; void SetModifiedAttributes(int flags, const MessageOrigin& origin = MessageOrigin()); diff --git a/lib/icinga/dependency.cpp b/lib/icinga/dependency.cpp index 3cf854f15..4bafab4e1 100644 --- a/lib/icinga/dependency.cpp +++ b/lib/icinga/dependency.cpp @@ -203,16 +203,16 @@ TimePeriod::Ptr Dependency::GetPeriod(void) const return TimePeriod::GetByName(GetPeriodRaw()); } -void Dependency::ValidateFilters(const String& location, const Dictionary::Ptr& attrs) +void Dependency::ValidateFilters(const String& location, const Dependency::Ptr& object) { - int sfilter = FilterArrayToInt(attrs->Get("states"), 0); + int sfilter = FilterArrayToInt(object->GetStates(), 0); - if (attrs->Get("parent_service_name") == Empty && (sfilter & ~(StateFilterUp | StateFilterDown)) != 0) { + if (object->GetParentServiceName().IsEmpty() && (sfilter & ~(StateFilterUp | StateFilterDown)) != 0) { ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " + location + ": State filter is invalid for host dependency."); } - if (attrs->Get("parent_service_name") != Empty && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) { + if (!object->GetParentServiceName().IsEmpty() && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) { ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " + location + ": State filter is invalid for service dependency."); } diff --git a/lib/icinga/dependency.hpp b/lib/icinga/dependency.hpp index b35b2b07b..a3d9b8a71 100644 --- a/lib/icinga/dependency.hpp +++ b/lib/icinga/dependency.hpp @@ -49,7 +49,7 @@ public: static void RegisterApplyRuleHandler(void); - static void ValidateFilters(const String& location, const Dictionary::Ptr& attrs); + static void ValidateFilters(const String& location, const Dependency::Ptr& object); protected: virtual void OnConfigLoaded(void); diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp index 1c21c08b7..6d0f5d487 100644 --- a/lib/icinga/notification.cpp +++ b/lib/icinga/notification.cpp @@ -493,21 +493,21 @@ int icinga::FilterArrayToInt(const Array::Ptr& typeFilters, int defaultValue) return resultTypeFilter; } -void Notification::ValidateFilters(const String& location, const Dictionary::Ptr& attrs) +void Notification::ValidateFilters(const String& location, const Notification::Ptr& object) { - int sfilter = FilterArrayToInt(attrs->Get("states"), 0); + int sfilter = FilterArrayToInt(object->GetStates(), 0); - if (attrs->Get("service_name") == Empty && (sfilter & ~(StateFilterUp | StateFilterDown)) != 0) { + if (object->GetServiceName().IsEmpty() && (sfilter & ~(StateFilterUp | StateFilterDown)) != 0) { ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " + location + ": State filter is invalid."); } - if (attrs->Get("service_name") != Empty && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) { + if (!object->GetServiceName().IsEmpty() && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) { ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " + location + ": State filter is invalid."); } - int tfilter = FilterArrayToInt(attrs->Get("types"), 0); + int tfilter = FilterArrayToInt(object->GetTypes(), 0); if ((tfilter & ~(1 << NotificationDowntimeStart | 1 << NotificationDowntimeEnd | 1 << NotificationDowntimeRemoved | 1 << NotificationCustom | 1 << NotificationAcknowledgement | 1 << NotificationProblem | 1 << NotificationRecovery | diff --git a/lib/icinga/notification.hpp b/lib/icinga/notification.hpp index 2bf6c0957..6e27e8fd4 100644 --- a/lib/icinga/notification.hpp +++ b/lib/icinga/notification.hpp @@ -106,7 +106,7 @@ public: static void RegisterApplyRuleHandler(void); - static void ValidateFilters(const String& location, const Dictionary::Ptr& attrs); + static void ValidateFilters(const String& location, const Notification::Ptr& object); protected: virtual void OnConfigLoaded(void); diff --git a/lib/livestatus/livestatuslistener.cpp b/lib/livestatus/livestatuslistener.cpp index 7a7568aad..5a595adec 100644 --- a/lib/livestatus/livestatuslistener.cpp +++ b/lib/livestatus/livestatuslistener.cpp @@ -183,11 +183,11 @@ void LivestatusListener::ClientHandler(const Socket::Ptr& client) } -void LivestatusListener::ValidateSocketType(const String& location, const Dictionary::Ptr& attrs) +void LivestatusListener::ValidateSocketType(const String& location, const LivestatusListener::Ptr& object) { - Value socket_type = attrs->Get("socket_type"); + String socket_type = object->GetSocketType(); - if (!socket_type.IsEmpty() && socket_type != "unix" && socket_type != "tcp") { + if (socket_type != "unix" && socket_type != "tcp") { ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " + location + ": Socket type '" + socket_type + "' is invalid."); } diff --git a/lib/livestatus/livestatuslistener.hpp b/lib/livestatus/livestatuslistener.hpp index 14cf5527d..ba298d4cd 100644 --- a/lib/livestatus/livestatuslistener.hpp +++ b/lib/livestatus/livestatuslistener.hpp @@ -44,7 +44,7 @@ public: static int GetClientsConnected(void); static int GetConnections(void); - static void ValidateSocketType(const String& location, const Dictionary::Ptr& attrs); + static void ValidateSocketType(const String& location, const LivestatusListener::Ptr& object); protected: virtual void Start(void); -- 2.50.0