diff --git a/lib/base/dynamicobject.cpp b/lib/base/dynamicobject.cpp index 7176cab98..e29e51022 100644 --- a/lib/base/dynamicobject.cpp +++ b/lib/base/dynamicobject.cpp @@ -56,16 +56,6 @@ DynamicType::Ptr DynamicObject::GetType(void) const return DynamicType::GetByName(GetTypeNameV()); } -DebugInfo DynamicObject::GetDebugInfo(void) const -{ - return m_DebugInfo; -} - -void DynamicObject::SetDebugInfo(const DebugInfo& di) -{ - m_DebugInfo = di; -} - bool DynamicObject::IsActive(void) const { return GetActive(); diff --git a/lib/base/dynamicobject.hpp b/lib/base/dynamicobject.hpp index c7d3649bf..7ea7f4fa1 100644 --- a/lib/base/dynamicobject.hpp +++ b/lib/base/dynamicobject.hpp @@ -52,9 +52,6 @@ public: intrusive_ptr GetType(void) const; - DebugInfo GetDebugInfo(void) const; - void SetDebugInfo(const DebugInfo& di); - bool IsActive(void) const; bool IsPaused(void) const; @@ -97,8 +94,6 @@ protected: private: static DynamicObject::Ptr GetObject(const String& type, const String& name); static void RestoreObject(const String& message, int attributeTypes); - - DebugInfo m_DebugInfo; }; #define DECLARE_OBJECTNAME(klass) \ diff --git a/lib/base/dynamicobject.ti b/lib/base/dynamicobject.ti index b541820ad..df8bdb154 100644 --- a/lib/base/dynamicobject.ti +++ b/lib/base/dynamicobject.ti @@ -33,10 +33,32 @@ public: }; }}} -abstract class DynamicObject +abstract class DynamicObjectBase +{ }; + +code {{{ +class I2_BASE_API DynamicObjectBase : public ObjectImpl +{ +public: + inline DebugInfo GetDebugInfo(void) const + { + return m_DebugInfo; + } + + void SetDebugInfo(const DebugInfo& di) + { + m_DebugInfo = di; + } + +private: + DebugInfo m_DebugInfo; +}; +}}} + +abstract class DynamicObject : DynamicObjectBase { [config, internal] String __name (Name); - [config] String name (ShortName) { + [config] String "name" (ShortName) { get {{{ if (m_ShortName.IsEmpty()) return GetName(); @@ -45,7 +67,7 @@ abstract class DynamicObject }}} }; [config, internal, get_protected] String type (TypeNameV); - [config] String zone (ZoneName); + [config] name(Zone) zone (ZoneName); [config, internal, get_protected] Array::Ptr templates; [get_protected] bool active; [get_protected] bool paused { diff --git a/lib/base/exception.cpp b/lib/base/exception.cpp index c34ab587b..a68e0da59 100644 --- a/lib/base/exception.cpp +++ b/lib/base/exception.cpp @@ -18,6 +18,7 @@ ******************************************************************************/ #include "base/exception.hpp" +#include "base/dynamicobject.hpp" #include #ifdef HAVE_CXXABI_H @@ -136,6 +137,8 @@ String icinga::DiagnosticInformation(const std::exception& ex, bool verbose, Sta String message = ex.what(); + const ValidationError *vex = dynamic_cast(&ex); + if (message.IsEmpty()) result << boost::diagnostic_information(ex); else @@ -148,6 +151,46 @@ String icinga::DiagnosticInformation(const std::exception& ex, bool verbose, Sta ShowCodeFragment(result, dex->GetDebugInfo()); } + if (vex) { + DebugInfo di; + + DynamicObject::Ptr dobj = dynamic_pointer_cast(vex->GetObject()); + if (dobj) + di = dobj->GetDebugInfo(); + + Dictionary::Ptr currentHint = vex->GetDebugHint(); + Array::Ptr messages; + + BOOST_FOREACH(const String& attr, vex->GetAttributePath()) { + Dictionary::Ptr props = currentHint->Get("properties"); + + if (!props) + break; + + currentHint = props->Get(attr); + + if (!currentHint) + break; + + messages = currentHint->Get("messages"); + } + + if (messages && messages->GetLength() > 0) { + Array::Ptr message = messages->Get(messages->GetLength() - 1); + + di.Path = message->Get(1); + di.FirstLine = message->Get(2); + di.FirstColumn = message->Get(3); + di.LastLine = message->Get(4); + di.LastColumn = message->Get(5); + } + + if (!di.Path.IsEmpty()) { + result << "\nLocation:\n"; + ShowCodeFragment(result, di); + } + } + const user_error *uex = dynamic_cast(&ex); const posix_error *pex = dynamic_cast(&ex); @@ -270,3 +313,57 @@ const char *posix_error::what(void) const throw() return m_Message; } +ValidationError::ValidationError(const intrusive_ptr >& object, const std::vector& attributePath, const String& message) + : m_Object(object), m_AttributePath(attributePath), m_Message(message) +{ + String path; + + BOOST_FOREACH(const String& attribute, attributePath) { + if (!path.IsEmpty()) + path += " -> "; + + path += "'" + attribute + "'"; + } + + Type::Ptr type = object->GetReflectionType(); + m_What = "Validation failed for object '" + object->GetName() + "' of type '" + type->GetName() + "'"; + + if (!path.IsEmpty()) + m_What += "; Attribute " + path; + + m_What += ": " + message; +} + +ValidationError::~ValidationError(void) throw() +{ } + +const char *ValidationError::what(void) const throw() +{ + return m_What.CStr(); +} + +intrusive_ptr > ValidationError::GetObject(void) const +{ + return m_Object; +} + +std::vector ValidationError::GetAttributePath(void) const +{ + return m_AttributePath; +} + +String ValidationError::GetMessage(void) const +{ + return m_Message; +} + +void ValidationError::SetDebugHint(const Dictionary::Ptr& dhint) +{ + m_DebugHint = dhint; +} + +Dictionary::Ptr ValidationError::GetDebugHint(void) const +{ + return m_DebugHint; +} + diff --git a/lib/base/exception.hpp b/lib/base/exception.hpp index 3270a332b..15f449f12 100644 --- a/lib/base/exception.hpp +++ b/lib/base/exception.hpp @@ -26,6 +26,7 @@ #include "base/context.hpp" #include "base/utility.hpp" #include "base/debuginfo.hpp" +#include "base/dictionary.hpp" #include #include #include @@ -64,6 +65,35 @@ private: bool m_IncompleteExpr; }; +class DynamicObject; +template<> class ObjectImpl; + +/* + * @ingroup base + */ +class I2_BASE_API ValidationError : virtual public user_error +{ +public: + ValidationError(const intrusive_ptr >& object, const std::vector& attributePath, const String& message); + ~ValidationError(void) throw(); + + virtual const char *what(void) const throw(); + + intrusive_ptr > GetObject(void) const; + std::vector GetAttributePath(void) const; + String GetMessage(void) const; + + void SetDebugHint(const Dictionary::Ptr& dhint); + Dictionary::Ptr GetDebugHint(void) const; + +private: + intrusive_ptr > m_Object; + std::vector m_AttributePath; + String m_Message; + String m_What; + Dictionary::Ptr m_DebugHint; +}; + I2_BASE_API StackTrace *GetLastExceptionStack(void); I2_BASE_API void SetLastExceptionStack(const StackTrace& trace); diff --git a/lib/base/filelogger.ti b/lib/base/filelogger.ti index d341088a3..eb579cf18 100644 --- a/lib/base/filelogger.ti +++ b/lib/base/filelogger.ti @@ -24,7 +24,7 @@ namespace icinga class FileLogger : StreamLogger { - [config] String path; + [config, required] String path; }; } diff --git a/lib/base/scriptutils.cpp b/lib/base/scriptutils.cpp index cf4808903..b6b9df8c6 100644 --- a/lib/base/scriptutils.cpp +++ b/lib/base/scriptutils.cpp @@ -26,7 +26,6 @@ #include "base/objectlock.hpp" #include "base/dynamictype.hpp" #include "base/application.hpp" -#include "base/exception.hpp" #include #include #include diff --git a/lib/base/type.cpp b/lib/base/type.cpp index 95bc97412..dd2fcd68e 100644 --- a/lib/base/type.cpp +++ b/lib/base/type.cpp @@ -137,7 +137,7 @@ int TypeType::GetFieldId(const String& name) const Field TypeType::GetFieldInfo(int id) const { if (id == 0) - return Field(0, "Object", "prototype", 0); + return Field(0, "Object", "prototype", NULL, 0); throw std::runtime_error("Invalid field ID."); } diff --git a/lib/base/type.hpp b/lib/base/type.hpp index 1c123fb1b..b636ecc7f 100644 --- a/lib/base/type.hpp +++ b/lib/base/type.hpp @@ -37,6 +37,7 @@ enum FieldAttribute FAConfig = 2, FAState = 4, FAInternal = 64, + FARequired = 512 }; class Type; @@ -46,10 +47,11 @@ struct Field int ID; const char *TypeName; const char *Name; + const char *RefTypeName; int Attributes; - Field(int id, const char *type, const char *name, int attributes) - : ID(id), TypeName(type), Name(name), Attributes(attributes) + Field(int id, const char *type, const char *name, const char *reftype, int attributes) + : ID(id), TypeName(type), Name(name), RefTypeName(reftype), Attributes(attributes) { } }; @@ -58,6 +60,12 @@ enum TypeAttribute TAAbstract = 1 }; +class ValidationUtils +{ +public: + virtual bool ValidateName(const String& type, const String& name) const = 0; +}; + class I2_BASE_API Type : public Object { public: diff --git a/lib/checker/CMakeLists.txt b/lib/checker/CMakeLists.txt index 5463c7912..d8f422dfb 100644 --- a/lib/checker/CMakeLists.txt +++ b/lib/checker/CMakeLists.txt @@ -17,10 +17,8 @@ mkclass_target(checkercomponent.ti checkercomponent.thpp) -mkembedconfig_target(checker-type.conf checker-type.cpp) - set(checker_SOURCES - checkercomponent.cpp checkercomponent.thpp checker-type.cpp + checkercomponent.cpp checkercomponent.thpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/checker/checker-type.conf b/lib/checker/checker-type.conf deleted file mode 100644 index f86a57f03..000000000 --- a/lib/checker/checker-type.conf +++ /dev/null @@ -1,21 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -%type CheckerComponent { -} diff --git a/lib/cli/consolecommand.cpp b/lib/cli/consolecommand.cpp index 23471c49d..b5845b681 100644 --- a/lib/cli/consolecommand.cpp +++ b/lib/cli/consolecommand.cpp @@ -26,6 +26,7 @@ #include "base/unixsocket.hpp" #include "base/utility.hpp" #include "base/networkstream.hpp" +#include "base/exception.hpp" #include #ifdef HAVE_EDITLINE #include "cli/editline.hpp" diff --git a/lib/cli/repositoryutility.cpp b/lib/cli/repositoryutility.cpp index 3cccc7368..d7b0e0ecd 100644 --- a/lib/cli/repositoryutility.cpp +++ b/lib/cli/repositoryutility.cpp @@ -19,8 +19,6 @@ #include "cli/repositoryutility.hpp" #include "cli/clicommand.hpp" -#include "config/configtype.hpp" -#include "config/configcompiler.hpp" #include "base/logger.hpp" #include "base/application.hpp" #include "base/convert.hpp" @@ -189,10 +187,10 @@ void RepositoryUtility::PrintChangeLog(std::ostream& fp) } } -class RepositoryTypeRuleUtilities : public TypeRuleUtilities +class RepositoryValidationUtils : public ValidationUtils { public: - virtual bool ValidateName(const String& type, const String& name, String *hint) const + virtual bool ValidateName(const String& type, const String& name) const { return true; } @@ -228,31 +226,19 @@ bool RepositoryUtility::AddObject(const std::vector& object_paths, const change->Set("command", "add"); change->Set("attrs", attrs); + Type::Ptr utype = Type::GetByName(type); + ASSERT(utype); + if (check_config) { - ConfigType::Ptr ctype = ConfigType::GetByName(type); + try { + Object::Ptr object = utype->Instantiate(); + Deserialize(object, attrs, false, FAConfig); - if (!ctype) - Log(LogCritical, "cli") - << "No validation type available for '" << type << "'."; - else { - Dictionary::Ptr vattrs = attrs->ShallowClone(); - vattrs->Set("__name", vattrs->Get("name")); - vattrs->Remove("name"); - vattrs->Remove("import"); - vattrs->Set("type", type); - - Type::Ptr dtype = Type::GetByName(type); - - Object::Ptr object = dtype->Instantiate(); - Deserialize(object, vattrs, false, FAConfig); - - try { - RepositoryTypeRuleUtilities utils; - ctype->ValidateItem(name, object, DebugInfo(), &utils); - } catch (const ScriptError& ex) { - Log(LogCritical, "config", DiagnosticInformation(ex)); - return false; - } + RepositoryValidationUtils utils; + static_pointer_cast(object)->Validate(FAConfig, utils); + } catch (const ScriptError& ex) { + Log(LogCritical, "config", DiagnosticInformation(ex)); + return false; } } diff --git a/lib/compat/CMakeLists.txt b/lib/compat/CMakeLists.txt index 163d77779..60a4d9a1e 100644 --- a/lib/compat/CMakeLists.txt +++ b/lib/compat/CMakeLists.txt @@ -20,12 +20,10 @@ mkclass_target(compatlogger.ti compatlogger.thpp) mkclass_target(externalcommandlistener.ti externalcommandlistener.thpp) mkclass_target(statusdatawriter.ti statusdatawriter.thpp) -mkembedconfig_target(compat-type.conf compat-type.cpp) - set(compat_SOURCES checkresultreader.cpp checkresultreader.thpp compatlogger.cpp compatlogger.thpp externalcommandlistener.cpp externalcommandlistener.thpp - statusdatawriter.cpp statusdatawriter.thpp compat-type.cpp + statusdatawriter.cpp statusdatawriter.thpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/compat/compat-type.conf b/lib/compat/compat-type.conf deleted file mode 100644 index 7782ff9b5..000000000 --- a/lib/compat/compat-type.conf +++ /dev/null @@ -1,39 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -%type StatusDataWriter { - %attribute %string "status_path", - %attribute %string "objects_path", - %attribute %number "update_interval" -} - -%type ExternalCommandListener { - %attribute %string "command_path" -} - -%type CompatLogger { - %validator "ValidateRotationMethod", - - %attribute %string "log_dir", - %attribute %string "rotation_method" -} - -%type CheckResultReader { - %attribute %string "spool_dir" -} diff --git a/lib/compat/compatlogger.cpp b/lib/compat/compatlogger.cpp index 4d88edbaa..c5cc9b424 100644 --- a/lib/compat/compatlogger.cpp +++ b/lib/compat/compatlogger.cpp @@ -32,7 +32,6 @@ #include "base/convert.hpp" #include "base/application.hpp" #include "base/utility.hpp" -#include "base/function.hpp" #include "base/statsfunction.hpp" #include #include @@ -40,7 +39,6 @@ using namespace icinga; REGISTER_TYPE(CompatLogger); -REGISTER_SCRIPTFUNCTION(ValidateRotationMethod, &CompatLogger::ValidateRotationMethod); REGISTER_STATSFUNCTION(CompatLoggerStats, &CompatLogger::StatsFunc); @@ -561,14 +559,12 @@ void CompatLogger::RotationTimerHandler(void) ScheduleNextRotation(); } -void CompatLogger::ValidateRotationMethod(const String& location, const CompatLogger::Ptr& object) +void CompatLogger::ValidateRotationMethod(const String& value, const ValidationUtils& utils) { - String rotation_method = object->GetRotationMethod(); + ObjectImpl::ValidateRotationMethod(value, utils); - if (rotation_method != "HOURLY" && rotation_method != "DAILY" && - rotation_method != "WEEKLY" && rotation_method != "MONTHLY" && rotation_method != "NONE") { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Rotation method '" + rotation_method + "' is invalid.", object->GetDebugInfo())); + if (value != "HOURLY" && value != "DAILY" && + value != "WEEKLY" && value != "MONTHLY" && value != "NONE") { + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("rotation_method"), "Rotation method '" + value + "' is invalid.")); } } - diff --git a/lib/compat/compatlogger.hpp b/lib/compat/compatlogger.hpp index 24ae233c2..b0fc5dc79 100644 --- a/lib/compat/compatlogger.hpp +++ b/lib/compat/compatlogger.hpp @@ -41,7 +41,7 @@ public: static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata); - static void ValidateRotationMethod(const String& location, const CompatLogger::Ptr& object); + virtual void ValidateRotationMethod(const String& value, const ValidationUtils& utils) override; protected: virtual void Start(void); diff --git a/lib/config/CMakeLists.txt b/lib/config/CMakeLists.txt index 1a54fc986..ab9d80927 100644 --- a/lib/config/CMakeLists.txt +++ b/lib/config/CMakeLists.txt @@ -31,15 +31,13 @@ endif() add_flex_bison_dependency(config_lexer config_parser) -mkembedconfig_target(base-type.conf base-type.cpp) - include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) set(config_SOURCES - applyrule.cpp base-type.conf base-type.cpp + applyrule.cpp configcompilercontext.cpp configcompiler.cpp configitembuilder.cpp configitem.cpp ${FLEX_config_lexer_OUTPUTS} ${BISON_config_parser_OUTPUTS} - configtype.cpp expression.cpp objectrule.cpp typerule.cpp typerulelist.cpp + expression.cpp objectrule.cpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/config/base-type.conf b/lib/config/base-type.conf deleted file mode 100644 index 2b38c736c..000000000 --- a/lib/config/base-type.conf +++ /dev/null @@ -1,46 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -%type DynamicObject { - %require "__name", - %attribute %string "__name", - - %attribute %string "name", - - %require "type", - %attribute %string "type", - - %attribute %name(Zone) "zone", - - %attribute %array "templates" { - %attribute %string "*" - }, -} - -%type Logger { - %attribute %string "severity" -} - -%type FileLogger %inherits Logger { - %require "path", - %attribute %string "path" -} - -%type SyslogLogger %inherits Logger { -} diff --git a/lib/config/config_lexer.ll b/lib/config/config_lexer.ll index 25310d650..dc0a7eed5 100644 --- a/lib/config/config_lexer.ll +++ b/lib/config/config_lexer.ll @@ -19,8 +19,8 @@ ******************************************************************************/ #include "config/configcompiler.hpp" -#include "config/typerule.hpp" #include "config/expression.hpp" +#include "base/exception.hpp" using namespace icinga; @@ -167,19 +167,6 @@ do { \ [ \t] /* ignore whitespace */ { -%type return T_TYPE; -%dictionary { yylval->type = TypeDictionary; return T_TYPE_DICTIONARY; } -%array { yylval->type = TypeArray; return T_TYPE_ARRAY; } -%number { yylval->type = TypeNumber; return T_TYPE_NUMBER; } -%string { yylval->type = TypeString; return T_TYPE_STRING; } -%scalar { yylval->type = TypeScalar; return T_TYPE_SCALAR; } -%any { yylval->type = TypeAny; return T_TYPE_ANY; } -%function { yylval->type = TypeFunction; return T_TYPE_FUNCTION; } -%name { yylval->type = TypeName; return T_TYPE_NAME; } -%validator { return T_VALIDATOR; } -%require { return T_REQUIRE; } -%attribute { return T_ATTRIBUTE; } -%inherits return T_INHERITS; object return T_OBJECT; template return T_TEMPLATE; include return T_INCLUDE; diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index f84d4d5e5..b262f7594 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -23,8 +23,6 @@ #include "config/i2-config.hpp" #include "config/configitembuilder.hpp" #include "config/configcompiler.hpp" -#include "config/typerule.hpp" -#include "config/typerulelist.hpp" #include "config/expression.hpp" #include "config/applyrule.hpp" #include "config/objectrule.hpp" @@ -92,9 +90,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig bool boolean; icinga::Expression *expr; icinga::DictExpression *dexpr; - icinga::Value *variant; CombinedSetOp csop; - icinga::TypeSpecifier type; std::vector *slist; std::vector > *llist; std::vector *elist; @@ -149,18 +145,6 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig %token T_LOCALS "locals (T_LOCALS)" %token T_CONST "const (T_CONST)" %token T_USE "use (T_USE)" -%token T_TYPE_DICTIONARY "dictionary (T_TYPE_DICTIONARY)" -%token T_TYPE_ARRAY "array (T_TYPE_ARRAY)" -%token T_TYPE_NUMBER "number (T_TYPE_NUMBER)" -%token T_TYPE_STRING "string (T_TYPE_STRING)" -%token T_TYPE_SCALAR "scalar (T_TYPE_SCALAR)" -%token T_TYPE_ANY "any (T_TYPE_ANY)" -%token T_TYPE_FUNCTION "function (T_TYPE_FUNCTION)" -%token T_TYPE_NAME "name (T_TYPE_NAME)" -%token T_VALIDATOR "%validator (T_VALIDATOR)" -%token T_REQUIRE "%require (T_REQUIRE)" -%token T_ATTRIBUTE "%attribute (T_ATTRIBUTE)" -%token T_TYPE "type (T_TYPE)" %token T_OBJECT "object (T_OBJECT)" %token T_TEMPLATE "template (T_TEMPLATE)" %token T_INCLUDE "include (T_INCLUDE)" @@ -190,9 +174,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig %type rterm_items_inner %type identifier_items %type identifier_items_inner -%type typerulelist %type combined_set_op -%type type %type statements %type lterm_items %type lterm_items_inner @@ -352,106 +334,6 @@ identifier: T_IDENTIFIER | T_STRING ; -type: T_TYPE identifier - { - String name = String($2); - free($2); - - context->m_Type = ConfigType::GetByName(name); - - if (!context->m_Type) { - context->m_Type = new ConfigType(name, @$); - context->m_Type->Register(); - } - } - type_inherits_specifier typerulelist - { - TypeRuleList::Ptr ruleList = *$5; - delete $5; - - context->m_Type->GetRuleList()->AddRules(ruleList); - context->m_Type->GetRuleList()->AddRequires(ruleList); - - BOOST_FOREACH(const String& validator, ruleList->GetValidators()) { - context->m_Type->GetRuleList()->AddValidator(validator); - } - } - ; - -typerulelist: '{' - { - context->m_OpenBraces++; - context->m_RuleLists.push(new TypeRuleList()); - } - typerules - '}' - { - context->m_OpenBraces--; - $$ = new Value(context->m_RuleLists.top()); - context->m_RuleLists.pop(); - } - ; - -typerules: typerules_inner - | typerules_inner sep - -typerules_inner: /* empty */ - | typerule - | typerules_inner sep typerule - ; - -typerule: T_REQUIRE T_STRING - { - context->m_RuleLists.top()->AddRequire($2); - free($2); - } - | T_VALIDATOR T_STRING - { - context->m_RuleLists.top()->AddValidator($2); - free($2); - } - | T_ATTRIBUTE type T_STRING - { - TypeRule rule($2, String(), $3, TypeRuleList::Ptr(), @$); - free($3); - - context->m_RuleLists.top()->AddRule(rule); - } - | T_ATTRIBUTE T_TYPE_NAME '(' identifier ')' T_STRING - { - TypeRule rule($2, $4, $6, TypeRuleList::Ptr(), @$); - free($4); - free($6); - - context->m_RuleLists.top()->AddRule(rule); - } - | T_ATTRIBUTE type T_STRING typerulelist - { - TypeRule rule($2, String(), $3, *$4, @$); - free($3); - delete $4; - context->m_RuleLists.top()->AddRule(rule); - } - ; - -type_inherits_specifier: /* empty */ - | T_INHERITS identifier - { - context->m_Type->SetParent($2); - free($2); - } - ; - -type: T_TYPE_DICTIONARY - | T_TYPE_ARRAY - | T_TYPE_NUMBER - | T_TYPE_STRING - | T_TYPE_SCALAR - | T_TYPE_ANY - | T_TYPE_FUNCTION - | T_TYPE_NAME - ; - object: { context->m_ObjectAssign.push(true); @@ -483,7 +365,7 @@ object: if (seen_assign) { if (!ObjectRule::IsValidSourceType(type)) - BOOST_THROW_EXCEPTION(ScriptError("object rule 'assign' cannot be used for type '" + type + "'", DebugInfoRange(@2, @3))); + BOOST_THROW_EXCEPTION(ScriptError("object rule 'assign' cannot be used for type '" + type + "'", DebugInfoRange(@2, @4))); if (ignore) { Expression *rex = new LogicalNegateExpression(ignore, DebugInfoRange(@2, @5)); @@ -544,11 +426,7 @@ combined_set_op: T_SET | T_SET_BINARY_OR ; -lterm: type - { - $$ = MakeLiteral(); // ASTify this - } - | library +lterm: library { $$ = MakeLiteral(); // ASTify this } diff --git a/lib/config/configcompiler.hpp b/lib/config/configcompiler.hpp index 6f4cdb4fa..981c44744 100644 --- a/lib/config/configcompiler.hpp +++ b/lib/config/configcompiler.hpp @@ -22,7 +22,6 @@ #include "config/i2-config.hpp" #include "config/expression.hpp" -#include "config/configtype.hpp" #include "base/debuginfo.hpp" #include "base/registry.hpp" #include "base/initialize.hpp" @@ -124,9 +123,6 @@ public: std::ostringstream m_LexBuffer; CompilerDebugInfo m_LocationBegin; - std::stack m_RuleLists; - ConfigType::Ptr m_Type; - std::stack m_Apply; std::stack m_ObjectAssign; std::stack m_SeenAssign; diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index b5ed42731..373ced031 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -21,7 +21,6 @@ #include "config/configcompilercontext.hpp" #include "config/applyrule.hpp" #include "config/objectrule.hpp" -#include "config/configtype.hpp" #include "base/application.hpp" #include "base/dynamictype.hpp" #include "base/objectlock.hpp" @@ -135,6 +134,15 @@ boost::shared_ptr ConfigItem::GetFilter(void) const return m_Filter; } +class DefaultValidationUtils : public ValidationUtils +{ +public: + virtual bool ValidateName(const String& type, const String& name) const override + { + return ConfigItem::GetObject(type, name) != ConfigItem::Ptr(); + } +}; + /** * Commits the configuration item by creating a DynamicObject * object. @@ -210,7 +218,9 @@ DynamicObject::Ptr ConfigItem::Commit(bool discard) persistentItem->Set("type", GetType()); persistentItem->Set("name", GetName()); persistentItem->Set("properties", Serialize(dobj, FAConfig)); - persistentItem->Set("debug_hints", debugHints.ToDictionary()); + + Dictionary::Ptr dhint = debugHints.ToDictionary(); + persistentItem->Set("debug_hints", dhint); Array::Ptr di = new Array(); di->Add(m_DebugInfo.Path); @@ -223,13 +233,16 @@ DynamicObject::Ptr ConfigItem::Commit(bool discard) ConfigCompilerContext::GetInstance()->WriteObject(persistentItem); persistentItem.reset(); - ConfigType::Ptr ctype = ConfigType::GetByName(GetType()); - - if (ctype) { - TypeRuleUtilities utils; - ctype->ValidateItem(GetName(), dobj, GetDebugInfo(), &utils); + try { + DefaultValidationUtils utils; + dobj->Validate(FAConfig, utils); + } catch (ValidationError& ex) { + ex.SetDebugHint(dhint); + throw; } + dhint.reset(); + dobj->Register(); m_Object = dobj; diff --git a/lib/config/configtype.cpp b/lib/config/configtype.cpp deleted file mode 100644 index 03f0c690b..000000000 --- a/lib/config/configtype.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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 "config/configtype.hpp" -#include "config/vmops.hpp" -#include "base/objectlock.hpp" -#include "base/convert.hpp" -#include "base/singleton.hpp" -#include "base/function.hpp" -#include - -using namespace icinga; - -ConfigType::ConfigType(const String& name, const DebugInfo& debuginfo) - : m_Name(name), m_RuleList(new TypeRuleList()), m_DebugInfo(debuginfo) -{ } - -String ConfigType::GetName(void) const -{ - return m_Name; -} - -String ConfigType::GetParent(void) const -{ - return m_Parent; -} - -void ConfigType::SetParent(const String& parent) -{ - m_Parent = parent; -} - -TypeRuleList::Ptr ConfigType::GetRuleList(void) const -{ - return m_RuleList; -} - -DebugInfo ConfigType::GetDebugInfo(void) const -{ - return m_DebugInfo; -} - -void ConfigType::AddParentRules(std::vector& ruleLists, const ConfigType::Ptr& item) -{ - ConfigType::Ptr parent; - if (item->m_Parent.IsEmpty()) { - if (item->GetName() != "DynamicObject") - parent = ConfigType::GetByName("DynamicObject"); - } else { - parent = ConfigType::GetByName(item->m_Parent); - } - - if (parent) { - AddParentRules(ruleLists, parent); - ruleLists.push_back(parent->m_RuleList); - } -} - -void ConfigType::ValidateItem(const String& name, const Object::Ptr& object, const DebugInfo& debugInfo, const TypeRuleUtilities *utils) -{ - String location = "Object '" + name + "' (Type: '" + GetName() + "')"; - - if (!debugInfo.Path.IsEmpty()) - location += " at " + debugInfo.Path + ":" + Convert::ToString(debugInfo.FirstLine); - - std::vector locations; - locations.push_back(location); - - std::vector ruleLists; - AddParentRules(ruleLists, this); - ruleLists.push_back(m_RuleList); - - ValidateObject(object, ruleLists, locations, utils); -} - -String ConfigType::LocationToString(const std::vector& locations) -{ - bool first = true; - String stack; - BOOST_FOREACH(const String& location, locations) { - if (!first) - stack += " -> "; - else - first = false; - - stack += location; - } - - return stack; -} - -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) - BOOST_THROW_EXCEPTION(ScriptError("Unknown attribute: " + LocationToString(locations))); - else if (overallResult == ValidationInvalidType) { - String message = "Invalid value: " + LocationToString(locations); - - if (!hint.IsEmpty()) - message += ": " + hint; - - BOOST_THROW_EXCEPTION(ScriptError(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) -{ - BOOST_FOREACH(const TypeRuleList::Ptr& ruleList, ruleLists) { - BOOST_FOREACH(const String& require, ruleList->GetRequires()) { - locations.push_back("Attribute '" + require + "'"); - - Value value = VMOps::GetField(object, require); - - if (value.IsEmpty()) - BOOST_THROW_EXCEPTION(ScriptError("Required attribute is missing: " + LocationToString(locations))); - - locations.pop_back(); - } - - BOOST_FOREACH(const String& validator, ruleList->GetValidators()) { - Function::Ptr func = ScriptGlobal::Get(validator, &Empty); - - if (!func) - BOOST_THROW_EXCEPTION(std::invalid_argument("Validator function '" + validator + "' does not exist.")); - - std::vector arguments; - arguments.push_back(LocationToString(locations)); - arguments.push_back(object); - - func->Invoke(arguments); - } - } - - Dictionary::Ptr dictionary = dynamic_pointer_cast(object); - - if (dictionary) { - ObjectLock olock(dictionary); - - BOOST_FOREACH(const Dictionary::Pair& kv, dictionary) { - ValidateAttribute(kv.first, kv.second, ruleLists, locations, utils); - } - } else { - Type::Ptr type = object->GetReflectionType(); - - if (!type) - return; - - for (int i = 0; i < type->GetFieldCount(); i++) { - Field field = type->GetFieldInfo(i); - - if (!(field.Attributes & FAConfig)) - continue; - - ValidateAttribute(field.Name, object->GetField(i), ruleLists, locations, utils); - } - } -} - -void ConfigType::ValidateArray(const Array::Ptr& array, - const std::vector& ruleLists, std::vector& locations, - const TypeRuleUtilities *utils) -{ - BOOST_FOREACH(const TypeRuleList::Ptr& ruleList, ruleLists) { - BOOST_FOREACH(const String& require, ruleList->GetRequires()) { - size_t index = Convert::ToLong(require); - - locations.push_back("Attribute '" + require + "'"); - - if (array->GetLength() < index) - BOOST_THROW_EXCEPTION(ScriptError("Required array index is missing: " + LocationToString(locations))); - - locations.pop_back(); - } - - BOOST_FOREACH(const String& validator, ruleList->GetValidators()) { - Function::Ptr func = ScriptGlobal::Get(validator, &Empty); - - if (!func) - BOOST_THROW_EXCEPTION(std::invalid_argument("Validator function '" + validator + "' does not exist.")); - - std::vector arguments; - arguments.push_back(LocationToString(locations)); - arguments.push_back(array); - - func->Invoke(arguments); - } - } - - ObjectLock olock(array); - - int index = 0; - String key; - BOOST_FOREACH(const Value& value, array) { - key = Convert::ToString(index); - index++; - - ValidateAttribute(key, value, ruleLists, locations, utils); - } -} - -void ConfigType::Register(void) -{ - ConfigTypeRegistry::GetInstance()->Register(GetName(), this); -} - -ConfigType::Ptr ConfigType::GetByName(const String& name) -{ - return ConfigTypeRegistry::GetInstance()->GetItem(name); -} - -ConfigTypeRegistry::ItemMap ConfigType::GetTypes(void) -{ - return ConfigTypeRegistry::GetInstance()->GetItems(); -} - -ConfigTypeRegistry *ConfigTypeRegistry::GetInstance(void) -{ - return Singleton::GetInstance(); -} diff --git a/lib/config/configtype.hpp b/lib/config/configtype.hpp deleted file mode 100644 index d4a63564b..000000000 --- a/lib/config/configtype.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -#ifndef CONFIGTYPE_H -#define CONFIGTYPE_H - -#include "config/i2-config.hpp" -#include "config/typerulelist.hpp" -#include "config/typerule.hpp" -#include "config/configitem.hpp" -#include "base/array.hpp" -#include "base/registry.hpp" - -namespace icinga -{ - -/** - * A configuration type. Used to validate config objects. - * - * @ingroup config - */ -class I2_CONFIG_API ConfigType : public Object { -public: - DECLARE_PTR_TYPEDEFS(ConfigType); - - ConfigType(const String& name, const DebugInfo& debuginfo); - - String GetName(void) const; - - String GetParent(void) const; - void SetParent(const String& parent); - - TypeRuleList::Ptr GetRuleList(void) const; - - DebugInfo GetDebugInfo(void) const; - - void ValidateItem(const String& name, const Object::Ptr& object, - const DebugInfo& debugInfo, const TypeRuleUtilities *utils); - - void Register(void); - static ConfigType::Ptr GetByName(const String& name); - static Registry::ItemMap GetTypes(void); - -private: - String m_Name; /**< The type name. */ - String m_Parent; /**< The parent type. */ - - TypeRuleList::Ptr m_RuleList; - DebugInfo m_DebugInfo; /**< Debug information. */ - - 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, - const std::vector& ruleLists, std::vector& locations, - const TypeRuleUtilities *utils); - - static String LocationToString(const std::vector& locations); - - static void AddParentRules(std::vector& ruleLists, const ConfigType::Ptr& item); -}; - -class I2_CONFIG_API ConfigTypeRegistry : public Registry -{ -public: - static ConfigTypeRegistry *GetInstance(void); -}; - -} - -#endif /* CONFIGTYPE_H */ diff --git a/lib/config/typerule.cpp b/lib/config/typerule.cpp deleted file mode 100644 index 513d666ac..000000000 --- a/lib/config/typerule.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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 "config/typerule.hpp" -#include "config/configitem.hpp" -#include "base/convert.hpp" -#include "base/utility.hpp" -#include "base/dictionary.hpp" -#include "base/array.hpp" - -using namespace icinga; - -TypeRule::TypeRule(TypeSpecifier type, const String& nameType, - const String& namePattern, const TypeRuleList::Ptr& subRules, - const DebugInfo& debuginfo) - : m_Type(type), m_NameType(nameType), m_NamePattern(namePattern), m_SubRules(subRules), m_DebugInfo(debuginfo) -{ } - -TypeRuleList::Ptr TypeRule::GetSubRules(void) const -{ - return m_SubRules; -} - -bool TypeRule::MatchName(const String& name) const -{ - return (Utility::Match(m_NamePattern, name)); -} - -bool TypeRule::MatchValue(const Value& value, String *hint, const TypeRuleUtilities *utils) const -{ - ConfigItem::Ptr item; - - if (value.IsEmpty()) - return true; - - switch (m_Type) { - case TypeAny: - return true; - - case TypeString: - /* fall through; any scalar can be converted to a string */ - case TypeScalar: - return value.IsScalar(); - - case TypeNumber: - try { - Convert::ToDouble(value); - } catch (...) { - return false; - } - - return true; - - case TypeDictionary: - return value.IsObjectType(); - - case TypeArray: - return value.IsObjectType(); - - case TypeFunction: - return value.IsObjectType(); - - case TypeName: - if (!value.IsScalar()) - return false; - - return utils->ValidateName(m_NameType, value, hint); - - default: - return false; - } -} - -bool TypeRuleUtilities::ValidateName(const String& type, const String& name, String *hint) const -{ - if (name.IsEmpty()) - return true; - - ConfigItem::Ptr item = ConfigItem::GetObject(type, name); - - if (!item) { - *hint = "Object '" + name + "' of type '" + type + "' does not exist."; - return false; - } - - if (item->IsAbstract()) { - *hint = "Object '" + name + "' of type '" + type + "' must not be a template."; - return false; - } - - return true; -} diff --git a/lib/config/typerule.hpp b/lib/config/typerule.hpp deleted file mode 100644 index a4fe8faa2..000000000 --- a/lib/config/typerule.hpp +++ /dev/null @@ -1,85 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -#ifndef TYPERULE_H -#define TYPERULE_H - -#include "config/i2-config.hpp" -#include "config/typerulelist.hpp" -#include "base/debuginfo.hpp" - -namespace icinga -{ - -/** - * Utilities for type rules. - * - * @ingroup config - */ -class I2_CONFIG_API TypeRuleUtilities -{ -public: - virtual bool ValidateName(const String& type, const String& name, String *hint) const; -}; - -/** - * The allowed type for a type rule. - * - * @ingroup config - */ -enum TypeSpecifier -{ - TypeAny, - TypeScalar, - TypeNumber, - TypeString, - TypeDictionary, - TypeArray, - TypeFunction, - TypeName -}; - -/** - * A configuration type rule. - * - * @ingroup config - */ -struct I2_CONFIG_API TypeRule -{ -public: - TypeRule(TypeSpecifier type, const String& nameType, - const String& namePattern, const TypeRuleList::Ptr& subRules, - const DebugInfo& debuginfo); - - TypeRuleList::Ptr GetSubRules(void) const; - - bool MatchName(const String& name) const; - bool MatchValue(const Value& value, String *hint, const TypeRuleUtilities *utils) const; - -private: - TypeSpecifier m_Type; - String m_NameType; - String m_NamePattern; - TypeRuleList::Ptr m_SubRules; - DebugInfo m_DebugInfo; -}; - -} - -#endif /* TYPERULE_H */ diff --git a/lib/config/typerulelist.cpp b/lib/config/typerulelist.cpp deleted file mode 100644 index eb67bf5ed..000000000 --- a/lib/config/typerulelist.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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 "config/typerulelist.hpp" -#include "config/typerule.hpp" -#include - -using namespace icinga; - -/** - * Adds a validator method for a rule list. - * - * @param validator The validator. - */ -void TypeRuleList::AddValidator(const String& validator) -{ - m_Validators.push_back(validator); -} - -/** - * Retrieves the validator methods. - * - * @returns The validator methods. - */ -std::vector TypeRuleList::GetValidators(void) const -{ - return m_Validators; -} - -/** - * Adds an attribute to the list of required attributes. - * - * @param attr The new required attribute. - */ -void TypeRuleList::AddRequire(const String& attr) -{ - m_Requires.push_back(attr); -} - -/** - * Retrieves the list of required attributes. - * - * @returns The list of required attributes. - */ -std::vector TypeRuleList::GetRequires(void) const -{ - return m_Requires; -} - -/** - * Adds all requires from the specified rule list. - * - * @param ruleList The rule list to copy requires from. - */ -void TypeRuleList::AddRequires(const TypeRuleList::Ptr& ruleList) -{ - BOOST_FOREACH(const String& require, ruleList->m_Requires) { - AddRequire(require); - } -} - -/** - * Adds a rule to a rule list. - * - * @param rule The rule that should be added. - */ -void TypeRuleList::AddRule(const TypeRule& rule) -{ - m_Rules.push_back(rule); -} - -/** - * Adds all rules from the specified rule list. - * - * @param ruleList The rule list to copy rules from. - */ -void TypeRuleList::AddRules(const TypeRuleList::Ptr& ruleList) -{ - BOOST_FOREACH(const TypeRule& rule, ruleList->m_Rules) { - AddRule(rule); - } -} - -/** - * Returns the number of rules currently contained in the list. - * - * @returns The length of the list. - */ -size_t TypeRuleList::GetLength(void) const -{ - return m_Rules.size(); -} - -/** - * Validates a field. - * - * @param name The name of the attribute. - * @param value The value of the attribute. - * @param[out] subRules The list of sub-rules for the matching rule. - * @param[out] hint A hint describing the validation failure. - * @returns The validation result. - */ -TypeValidationResult TypeRuleList::ValidateAttribute(const String& name, - const Value& value, TypeRuleList::Ptr *subRules, String *hint, - const TypeRuleUtilities *utils) const -{ - bool foundField = false; - BOOST_FOREACH(const TypeRule& rule, m_Rules) { - if (!rule.MatchName(name)) - continue; - - foundField = true; - - if (rule.MatchValue(value, hint, utils)) { - *subRules = rule.GetSubRules(); - return ValidationOK; - } - } - - if (foundField) - return ValidationInvalidType; - else - return ValidationUnknownField; -} diff --git a/lib/config/typerulelist.hpp b/lib/config/typerulelist.hpp deleted file mode 100644 index c96f11f34..000000000 --- a/lib/config/typerulelist.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -#ifndef TYPERULELIST_H -#define TYPERULELIST_H - -#include "config/i2-config.hpp" -#include "base/value.hpp" -#include - -namespace icinga -{ - -struct TypeRule; -class TypeRuleUtilities; - -/** - * @ingroup config - */ -enum TypeValidationResult -{ - ValidationOK, - ValidationInvalidType, - ValidationUnknownField -}; - -/** - * A list of configuration type rules. - * - * @ingroup config - */ -class I2_CONFIG_API TypeRuleList : public Object -{ -public: - DECLARE_PTR_TYPEDEFS(TypeRuleList); - - void AddValidator(const String& validator); - std::vector GetValidators(void) const; - - void AddRequire(const String& attr); - void AddRequires(const TypeRuleList::Ptr& ruleList); - std::vector GetRequires(void) const; - - void AddRule(const TypeRule& rule); - void AddRules(const TypeRuleList::Ptr& ruleList); - - TypeValidationResult ValidateAttribute(const String& name, const Value& value, - TypeRuleList::Ptr *subRules, String *hint, const TypeRuleUtilities *utils) const; - - size_t GetLength(void) const; - -private: - std::vector m_Validators; - std::vector m_Requires; - std::vector m_Rules; -}; - -} - -#endif /* TYPERULELIST_H */ diff --git a/lib/config/vmops.hpp b/lib/config/vmops.hpp index 9d7307cdd..a0f3845bd 100644 --- a/lib/config/vmops.hpp +++ b/lib/config/vmops.hpp @@ -317,9 +317,13 @@ public: try { context->SetField(fid, value); } catch (const boost::bad_lexical_cast&) { - BOOST_THROW_EXCEPTION(ScriptError("Attribute '" + field + "' cannot be set to value of type '" + value.GetTypeName() + "'", debugInfo)); + Field fieldInfo = type->GetFieldInfo(fid); + Type::Ptr ftype = Type::GetByName(fieldInfo.TypeName); + BOOST_THROW_EXCEPTION(ScriptError("Attribute '" + field + "' cannot be set to value of type '" + value.GetTypeName() + "', expected '" + ftype->GetName() + "'", debugInfo)); } catch (const std::bad_cast&) { - BOOST_THROW_EXCEPTION(ScriptError("Attribute '" + field + "' cannot be set to value of type '" + value.GetTypeName() + "'", debugInfo)); + Field fieldInfo = type->GetFieldInfo(fid); + Type::Ptr ftype = Type::GetByName(fieldInfo.TypeName); + BOOST_THROW_EXCEPTION(ScriptError("Attribute '" + field + "' cannot be set to value of type '" + value.GetTypeName() + "', expected '" + ftype->GetName() + "'", debugInfo)); } } diff --git a/lib/db_ido/CMakeLists.txt b/lib/db_ido/CMakeLists.txt index fcc74ea60..0cec9ea02 100644 --- a/lib/db_ido/CMakeLists.txt +++ b/lib/db_ido/CMakeLists.txt @@ -17,12 +17,11 @@ mkclass_target(dbconnection.ti dbconnection.thpp) -mkembedconfig_target(db_ido-type.conf db_ido-type.cpp) mkembedconfig_target(db_ido-itl.conf db_ido-itl.cpp) set(db_ido_SOURCES commanddbobject.cpp dbconnection.cpp dbconnection.thpp dbconnection.thpp - db_ido-type.cpp db_ido-itl.cpp dbevents.cpp dbobject.cpp dbquery.cpp + db_ido-itl.cpp dbevents.cpp dbobject.cpp dbquery.cpp dbreference.cpp dbtype.cpp dbvalue.cpp endpointdbobject.cpp hostdbobject.cpp hostgroupdbobject.cpp idochecktask.cpp servicedbobject.cpp servicegroupdbobject.cpp timeperioddbobject.cpp userdbobject.cpp diff --git a/lib/db_ido/db_ido-type.conf b/lib/db_ido/db_ido-type.conf deleted file mode 100644 index 331b89141..000000000 --- a/lib/db_ido/db_ido-type.conf +++ /dev/null @@ -1,48 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -%type DbConnection { - %validator "ValidateFailoverTimeout" - - %attribute %string "table_prefix", - - %attribute %dictionary "cleanup" { - %attribute %number "acknowledgements_age", - %attribute %number "commenthistory_age", - %attribute %number "contactnotifications_age", - %attribute %number "contactnotificationmethods_age", - %attribute %number "downtimehistory_age", - %attribute %number "eventhandlers_age", - %attribute %number "externalcommands_age", - %attribute %number "flappinghistory_age", - %attribute %number "hostchecks_age", - %attribute %number "logentries_age", - %attribute %number "notifications_age", - %attribute %number "processevents_age", - %attribute %number "statehistory_age", - %attribute %number "servicechecks_age", - %attribute %number "systemcommands_age", - }, - - %attribute %number "categories", - - %attribute %number "enable_ha", - - %attribute %number "failover_timeout", -} diff --git a/lib/db_ido/dbconnection.cpp b/lib/db_ido/dbconnection.cpp index 2baa0175f..fe8e011b1 100644 --- a/lib/db_ido/dbconnection.cpp +++ b/lib/db_ido/dbconnection.cpp @@ -27,14 +27,12 @@ #include "base/objectlock.hpp" #include "base/utility.hpp" #include "base/logger.hpp" -#include "base/function.hpp" #include "base/exception.hpp" #include using namespace icinga; REGISTER_TYPE(DbConnection); -REGISTER_SCRIPTFUNCTION(ValidateFailoverTimeout, &DbConnection::ValidateFailoverTimeout); Timer::Ptr DbConnection::m_ProgramStatusTimer; boost::once_flag DbConnection::m_OnceFlag = BOOST_ONCE_INIT; @@ -440,12 +438,12 @@ void DbConnection::PrepareDatabase(void) } } -void DbConnection::ValidateFailoverTimeout(const String& location, const DbConnection::Ptr& object) +void DbConnection::ValidateFailoverTimeout(double value, const ValidationUtils& utils) { - if (object->GetFailoverTimeout() < 60) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Failover timeout minimum is 60s.", object->GetDebugInfo())); - } + ObjectImpl::ValidateFailoverTimeout(value, utils); + + if (value < 60) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("failover_timeout"), "Failover timeout minimum is 60s.")); } void DbConnection::IncreaseQueryCount(void) diff --git a/lib/db_ido/dbconnection.hpp b/lib/db_ido/dbconnection.hpp index 243744258..e31660967 100644 --- a/lib/db_ido/dbconnection.hpp +++ b/lib/db_ido/dbconnection.hpp @@ -74,7 +74,7 @@ public: int GetQueryCount(RingBuffer::SizeType span) const; virtual int GetPendingQueryCount(void) const = 0; - static void ValidateFailoverTimeout(const String& location, const DbConnection::Ptr& object); + virtual void ValidateFailoverTimeout(double value, const ValidationUtils& utils) override; protected: virtual void OnConfigLoaded(void); diff --git a/lib/db_ido/dbconnection.ti b/lib/db_ido/dbconnection.ti index 498490e03..51c98456f 100644 --- a/lib/db_ido/dbconnection.ti +++ b/lib/db_ido/dbconnection.ti @@ -56,4 +56,25 @@ abstract class DbConnection : DynamicObject }; }; + +validator DbConnection { + Dictionary cleanup { + Number acknowledgements_age; + Number commenthistory_age; + Number contactnotifications_age; + Number contactnotificationmethods_age; + Number downtimehistory_age; + Number eventhandlers_age; + Number externalcommands_age; + Number flappinghistory_age; + Number hostchecks_age; + Number logentries_age; + Number notifications_age; + Number processevents_age; + Number statehistory_age; + Number servicechecks_age; + Number systemcommands_age; + }; +}; + } diff --git a/lib/db_ido_mysql/CMakeLists.txt b/lib/db_ido_mysql/CMakeLists.txt index 9f382fa5e..61d57232a 100644 --- a/lib/db_ido_mysql/CMakeLists.txt +++ b/lib/db_ido_mysql/CMakeLists.txt @@ -20,10 +20,8 @@ find_package(MySQL) if(MYSQL_FOUND) mkclass_target(idomysqlconnection.ti idomysqlconnection.thpp) - mkembedconfig_target(db_ido_mysql-type.conf db_ido_mysql-type.cpp) - set(db_ido_mysql_SOURCES - idomysqlconnection.cpp idomysqlconnection.thpp db_ido_mysql-type.cpp + idomysqlconnection.cpp idomysqlconnection.thpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/db_ido_mysql/db_ido_mysql-type.conf b/lib/db_ido_mysql/db_ido_mysql-type.conf deleted file mode 100644 index 829be8fb5..000000000 --- a/lib/db_ido_mysql/db_ido_mysql-type.conf +++ /dev/null @@ -1,33 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -%type IdoMysqlConnection %inherits DbConnection { - %attribute %string "host", - %attribute %number "port", - - %attribute %string "socket_path", - - %attribute %string "user", - %attribute %string "password", - - %attribute %string "database", - - %attribute %string "instance_name", - %attribute %string "instance_description" -} diff --git a/lib/db_ido_pgsql/CMakeLists.txt b/lib/db_ido_pgsql/CMakeLists.txt index 985532c1e..06da5143d 100644 --- a/lib/db_ido_pgsql/CMakeLists.txt +++ b/lib/db_ido_pgsql/CMakeLists.txt @@ -20,13 +20,11 @@ find_package(PostgreSQL) if(PostgreSQL_FOUND) mkclass_target(idopgsqlconnection.ti idopgsqlconnection.thpp) - mkembedconfig_target(db_ido_pgsql-type.conf db_ido_pgsql-type.cpp) - link_directories(${PostgreSQL_LIBRARY_DIRS}) include_directories(${PostgreSQL_INCLUDE_DIRS}) set(db_ido_pgsql_SOURCES - idopgsqlconnection.cpp idopgsqlconnection.thpp db_ido_pgsql-type.cpp + idopgsqlconnection.cpp idopgsqlconnection.thpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/db_ido_pgsql/db_ido_pgsql-type.conf b/lib/db_ido_pgsql/db_ido_pgsql-type.conf deleted file mode 100644 index b0d6abc72..000000000 --- a/lib/db_ido_pgsql/db_ido_pgsql-type.conf +++ /dev/null @@ -1,31 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -%type IdoPgsqlConnection %inherits DbConnection { - %attribute %string "host", - %attribute %number "port", - - %attribute %string "user", - %attribute %string "password", - - %attribute %string "database", - - %attribute %string "instance_name", - %attribute %string "instance_description" -} diff --git a/lib/demo/CMakeLists.txt b/lib/demo/CMakeLists.txt index fb41b88a9..2484d356a 100644 --- a/lib/demo/CMakeLists.txt +++ b/lib/demo/CMakeLists.txt @@ -17,10 +17,8 @@ mkclass_target(demo.ti demo.thpp) -mkembedconfig_target(demo-type.conf demo-type.cpp) - set(demo_SOURCES - demo.cpp demo.thpp demo-type.cpp + demo.cpp demo.thpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/demo/demo-type.conf b/lib/demo/demo-type.conf deleted file mode 100644 index 6cec1b8fc..000000000 --- a/lib/demo/demo-type.conf +++ /dev/null @@ -1,21 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -%type Demo { -} diff --git a/lib/hello/CMakeLists.txt b/lib/hello/CMakeLists.txt index 4e5e4a779..d9ff7017a 100644 --- a/lib/hello/CMakeLists.txt +++ b/lib/hello/CMakeLists.txt @@ -17,11 +17,10 @@ mkclass_target(hello.ti hello.thpp) -mkembedconfig_target(hello-type.conf hello-type.cpp) mkembedconfig_target(hello-app.conf hello-app.cpp) set(hello_SOURCES - hello.cpp hello.thpp hello-type.cpp hello-app.cpp + hello.cpp hello.thpp hello-app.cpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/hello/hello-type.conf b/lib/hello/hello-type.conf deleted file mode 100644 index c17a0934d..000000000 --- a/lib/hello/hello-type.conf +++ /dev/null @@ -1,21 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -%type Hello { -} diff --git a/lib/icinga/CMakeLists.txt b/lib/icinga/CMakeLists.txt index 14cd7095a..10648c885 100644 --- a/lib/icinga/CMakeLists.txt +++ b/lib/icinga/CMakeLists.txt @@ -38,7 +38,6 @@ mkclass_target(timeperiod.ti timeperiod.thpp) mkclass_target(usergroup.ti usergroup.thpp) mkclass_target(user.ti user.thpp) -mkembedconfig_target(icinga-type.conf icinga-type.cpp) mkembedconfig_target(icinga-app.conf icinga-app.cpp) set(icinga_SOURCES @@ -52,7 +51,7 @@ set(icinga_SOURCES notification.thpp notification-apply.cpp objectutils.cpp perfdatavalue.cpp perfdatavalue.thpp pluginutility.cpp scheduleddowntime.cpp scheduleddowntime.thpp scheduleddowntime-apply.cpp service-apply.cpp checkable-check.cpp checkable-comment.cpp service.cpp service.thpp servicegroup.cpp servicegroup.thpp checkable-notification.cpp timeperiod.cpp timeperiod.thpp - user.cpp user.thpp usergroup.cpp usergroup.thpp icinga-type.cpp icinga-app.cpp + user.cpp user.thpp usergroup.cpp usergroup.thpp icinga-app.cpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/icinga/checkable.cpp b/lib/icinga/checkable.cpp index c023ae42f..27b5c3628 100644 --- a/lib/icinga/checkable.cpp +++ b/lib/icinga/checkable.cpp @@ -20,7 +20,6 @@ #include "icinga/checkable.hpp" #include "base/objectlock.hpp" #include "base/utility.hpp" -#include "base/function.hpp" #include "base/exception.hpp" #include #include @@ -28,7 +27,6 @@ using namespace icinga; REGISTER_TYPE(Checkable); -REGISTER_SCRIPTFUNCTION(ValidateCheckableCheckInterval, &Checkable::ValidateCheckInterval); boost::signals2::signal Checkable::OnEnablePerfdataChanged; boost::signals2::signal Checkable::OnAcknowledgementSet; @@ -263,10 +261,10 @@ Endpoint::Ptr Checkable::GetCommandEndpoint(void) const return Endpoint::GetByName(GetCommandEndpointRaw()); } -void Checkable::ValidateCheckInterval(const String& location, const Checkable::Ptr& object) +void Checkable::ValidateCheckIntervalRaw(double value, const ValidationUtils& utils) { - if (object->GetCheckInterval() <= 0) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": check_interval must be greater than 0.", object->GetDebugInfo())); - } + ObjectImpl::ValidateCheckIntervalRaw(value, utils); + + if (value <= 0) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("check_interval"), "Interval must be greater than 0.")); } diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp index 96a43830c..41c9187c4 100644 --- a/lib/icinga/checkable.hpp +++ b/lib/icinga/checkable.hpp @@ -277,7 +277,7 @@ public: void RemoveReverseDependency(const intrusive_ptr& dep); std::set > GetReverseDependencies(void) const; - static void ValidateCheckInterval(const String& location, const Checkable::Ptr& object); + virtual void ValidateCheckIntervalRaw(double value, const ValidationUtils& utils) override; protected: virtual void Start(void); diff --git a/lib/icinga/checkable.ti b/lib/icinga/checkable.ti index f0e61594e..6c868300e 100644 --- a/lib/icinga/checkable.ti +++ b/lib/icinga/checkable.ti @@ -43,18 +43,18 @@ abstract class Checkable : CustomVarObject [config] Array::Ptr groups { default {{{ return new Array(); }}} }; - [config, protected] String check_command (CheckCommandRaw); + [config, protected, required] name(CheckCommand) check_command (CheckCommandRaw); [config] int max_check_attempts (MaxCheckAttemptsRaw) { default {{{ return 3; }}} }; - [config, protected] String check_period (CheckPeriodRaw); + [config, protected] name(TimePeriod) check_period (CheckPeriodRaw); [config] double check_interval (CheckIntervalRaw) { default {{{ return 5 * 60; }}} }; [config] double retry_interval (RetryIntervalRaw) { default {{{ return 60; }}} }; - [config] String event_command (EventCommandRaw); + [config] name(EventCommand) event_command (EventCommandRaw); [config] bool volatile; [config] double flapping_threshold { default {{{ return 30; }}} @@ -147,7 +147,7 @@ abstract class Checkable : CustomVarObject [state] Value override_max_check_attempts; [state] Value override_check_period; - [config] String command_endpoint (CommandEndpointRaw); + [config] name(Endpoint) command_endpoint (CommandEndpointRaw); }; } diff --git a/lib/icinga/command.cpp b/lib/icinga/command.cpp index 6d6bafcd4..0641e1d57 100644 --- a/lib/icinga/command.cpp +++ b/lib/icinga/command.cpp @@ -19,7 +19,6 @@ #include "icinga/command.hpp" #include "icinga/macroprocessor.hpp" -#include "base/function.hpp" #include "base/exception.hpp" #include "base/objectlock.hpp" #include @@ -27,9 +26,6 @@ using namespace icinga; REGISTER_TYPE(Command); -REGISTER_SCRIPTFUNCTION(ValidateCommandAttributes, &Command::ValidateAttributes); -REGISTER_SCRIPTFUNCTION(ValidateCommandArguments, &Command::ValidateArguments); -REGISTER_SCRIPTFUNCTION(ValidateEnvironmentVariables, &Command::ValidateEnvironmentVariables); int Command::GetModifiedAttributes(void) const { @@ -49,81 +45,68 @@ void Command::SetModifiedAttributes(int flags, const MessageOrigin& origin) } } -void Command::ValidateAttributes(const String& location, const Command::Ptr& object) +void Command::Validate(int types, const ValidationUtils& utils) { - if (object->GetArguments() != Empty && !object->GetCommandLine().IsObjectType()) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Attribute 'command' must be an array if the 'arguments' attribute is set.", object->GetDebugInfo())); - } -} + ObjectImpl::Validate(types, utils); -void Command::ValidateArguments(const String& location, const Command::Ptr& object) -{ - Dictionary::Ptr arguments = object->GetArguments(); + Dictionary::Ptr arguments = GetArguments(); - if (!arguments) + if (!(types & FAConfig)) return; - ObjectLock olock(arguments); - BOOST_FOREACH(const Dictionary::Pair& kv, arguments) { - const Value& arginfo = kv.second; - Value argval; + if (arguments) { + if (!GetCommandLine().IsObjectType()) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("command"), "Attribute 'command' must be an array if the 'arguments' attribute is set.")); - if (arginfo.IsObjectType()) { - Dictionary::Ptr argdict = arginfo; + ObjectLock olock(arguments); + BOOST_FOREACH(const Dictionary::Pair& kv, arguments) { + const Value& arginfo = kv.second; + Value argval; - if (argdict->Contains("value")) { - String argvalue = argdict->Get("value"); + if (arginfo.IsObjectType()) { + Dictionary::Ptr argdict = arginfo; - if (!MacroProcessor::ValidateMacroString(argvalue)) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Closing $ not found in macro format string '" + argvalue + "'.", object->GetDebugInfo())); + if (argdict->Contains("value")) { + String argvalue = argdict->Get("value"); + + if (!MacroProcessor::ValidateMacroString(argvalue)) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("arguments")(kv.first)("value"), "Validation failed: Closing $ not found in macro format string '" + argvalue + "'.")); } - } - if (argdict->Contains("set_if")) { - String argsetif = argdict->Get("set_if"); + if (argdict->Contains("set_if")) { + String argsetif = argdict->Get("set_if"); - if (!MacroProcessor::ValidateMacroString(argsetif)) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Closing $ not found in macro format string '" + argsetif + "'.", object->GetDebugInfo())); + if (!MacroProcessor::ValidateMacroString(argsetif)) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("arguments")(kv.first)("set_if"), "Closing $ not found in macro format string '" + argsetif + "'.")); } - } - } else if (arginfo.IsObjectType()) { - continue; /* we cannot evaluate macros in functions */ - } else { - argval = arginfo; + } else if (arginfo.IsObjectType()) { + continue; /* we cannot evaluate macros in functions */ + } else { + argval = arginfo; - if (argval.IsEmpty()) + if (argval.IsEmpty()) + continue; + + String argstr = argval; + + if (!MacroProcessor::ValidateMacroString(argstr)) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("arguments")(kv.first), "Closing $ not found in macro format string '" + argstr + "'.")); + } + } + } + + Dictionary::Ptr env = GetEnv(); + + if (env) { + ObjectLock olock(env); + BOOST_FOREACH(const Dictionary::Pair& kv, env) { + const Value& envval = kv.second; + + if (!envval.IsString() || envval.IsEmpty()) continue; - String argstr = argval; - - if (!MacroProcessor::ValidateMacroString(argstr)) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Closing $ not found in macro format string '" + argstr + "'.", object->GetDebugInfo())); - } - } - } -} - -void Command::ValidateEnvironmentVariables(const String& location, const Command::Ptr& object) -{ - Dictionary::Ptr env = object->GetEnv(); - - if (!env) - return; - - ObjectLock olock(env); - BOOST_FOREACH(const Dictionary::Pair& kv, env) { - const Value& envval = kv.second; - - if (!envval.IsString() || envval.IsEmpty()) - continue; - - if (!MacroProcessor::ValidateMacroString(envval)) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Closing $ not found in macro format string '" + envval + "'.", object->GetDebugInfo())); + if (!MacroProcessor::ValidateMacroString(envval)) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("env")(kv.first), "Closing $ not found in macro format string '" + envval + "'.")); } } } diff --git a/lib/icinga/command.hpp b/lib/icinga/command.hpp index 4a23c4619..4b85452d8 100644 --- a/lib/icinga/command.hpp +++ b/lib/icinga/command.hpp @@ -39,9 +39,7 @@ public: //virtual Dictionary::Ptr Execute(const Object::Ptr& context) = 0; - static void ValidateAttributes(const String& location, const Command::Ptr& object); - static void ValidateArguments(const String& location, const Command::Ptr& object); - static void ValidateEnvironmentVariables(const String& location, const Command::Ptr& object); + virtual void Validate(int types, const ValidationUtils& utils) override; int GetModifiedAttributes(void) const; void SetModifiedAttributes(int flags, const MessageOrigin& origin = MessageOrigin()); diff --git a/lib/icinga/command.ti b/lib/icinga/command.ti index 84267ab1e..3d3c53066 100644 --- a/lib/icinga/command.ti +++ b/lib/icinga/command.ti @@ -27,11 +27,41 @@ abstract class Command : CustomVarObject { [config] Value command (CommandLine); [config] Value arguments; - [config] Value timeout { + [config] int timeout { default {{{ return 60; }}} }; [config] Dictionary::Ptr env; - [config] Function::Ptr execute; + [config, required] Function::Ptr execute; +}; + +validator Command { + String command; + Function command; + Array command { + String "*"; + Function "*"; + }; + + Dictionary arguments { + String "*"; + Dictionary "*" { + String key; + String value; + Function value; + String description; + Number "required"; + Number skip_key; + Number repeat_key; + String set_if; + Function set_if; + Number order; + }; + }; + + Dictionary env { + String "*"; + Function "*"; + }; }; } diff --git a/lib/icinga/customvarobject.cpp b/lib/icinga/customvarobject.cpp index 929139897..8388cea29 100644 --- a/lib/icinga/customvarobject.cpp +++ b/lib/icinga/customvarobject.cpp @@ -28,7 +28,6 @@ using namespace icinga; REGISTER_TYPE(CustomVarObject); -REGISTER_SCRIPTFUNCTION(ValidateCustomAttributes, &CustomVarObject::ValidateCustomAttributes); boost::signals2::signal CustomVarObject::OnVarsChanged; @@ -68,16 +67,14 @@ bool CustomVarObject::IsVarOverridden(const String& name) const return vars_override->Contains(name); } -void CustomVarObject::ValidateCustomAttributes(const String& location, const CustomVarObject::Ptr& object) +void CustomVarObject::ValidateVarsRaw(const Dictionary::Ptr& value, const ValidationUtils& utils) { - Dictionary::Ptr vars = object->GetVars(); - - if (!vars) + if (!value) return; /* string, array, dictionary */ - ObjectLock olock(vars); - BOOST_FOREACH(const Dictionary::Pair& kv, vars) { + ObjectLock olock(value); + BOOST_FOREACH(const Dictionary::Pair& kv, value) { const Value& varval = kv.second; if (varval.IsObjectType()) { @@ -89,10 +86,8 @@ void CustomVarObject::ValidateCustomAttributes(const String& location, const Cus if (kv_var.second.IsEmpty()) continue; - if (!MacroProcessor::ValidateMacroString(kv_var.second)) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Closing $ not found in macro format string '" + kv_var.second + "'.", object->GetDebugInfo())); - } + if (!MacroProcessor::ValidateMacroString(kv_var.second)) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("vars")(kv.first)(kv_var.first), "Closing $ not found in macro format string '" + kv_var.second + "'.")); } } else if (varval.IsObjectType()) { /* check all array entries */ @@ -104,8 +99,7 @@ void CustomVarObject::ValidateCustomAttributes(const String& location, const Cus continue; if (!MacroProcessor::ValidateMacroString(arrval)) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Closing $ not found in macro format string '" + arrval + "'.", object->GetDebugInfo())); + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("vars")(kv.first), "Closing $ not found in macro format string '" + arrval + "'.")); } } } else { @@ -114,10 +108,8 @@ void CustomVarObject::ValidateCustomAttributes(const String& location, const Cus String varstr = varval; - if (!MacroProcessor::ValidateMacroString(varstr)) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Closing $ not found in macro format string '" + varstr + "'.", object->GetDebugInfo())); - } + if (!MacroProcessor::ValidateMacroString(varstr)) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("vars")(kv.first), "Closing $ not found in macro format string '" + varstr + "'.")); } } } diff --git a/lib/icinga/customvarobject.hpp b/lib/icinga/customvarobject.hpp index 36f97dd93..b87d637a3 100644 --- a/lib/icinga/customvarobject.hpp +++ b/lib/icinga/customvarobject.hpp @@ -61,7 +61,7 @@ public: static boost::signals2::signal OnVarsChanged; - static void ValidateCustomAttributes(const String& location, const CustomVarObject::Ptr& object); + virtual void ValidateVarsRaw(const Dictionary::Ptr& value, const ValidationUtils& utils) override; Dictionary::Ptr GetVars(void) const; void SetVars(const Dictionary::Ptr& vars, const MessageOrigin& origin = MessageOrigin()); diff --git a/lib/icinga/dependency.cpp b/lib/icinga/dependency.cpp index 214b16dfb..d935a8f03 100644 --- a/lib/icinga/dependency.cpp +++ b/lib/icinga/dependency.cpp @@ -20,14 +20,12 @@ #include "icinga/dependency.hpp" #include "icinga/service.hpp" #include "base/logger.hpp" -#include "base/function.hpp" #include "base/exception.hpp" #include using namespace icinga; REGISTER_TYPE(Dependency); -REGISTER_SCRIPTFUNCTION(ValidateDependencyFilters, &Dependency::ValidateFilters); String DependencyNameComposer::MakeName(const String& shortName, const Object::Ptr& context) const { @@ -198,17 +196,15 @@ TimePeriod::Ptr Dependency::GetPeriod(void) const return TimePeriod::GetByName(GetPeriodRaw()); } -void Dependency::ValidateFilters(const String& location, const Dependency::Ptr& object) +void Dependency::ValidateStates(const Array::Ptr& value, const ValidationUtils& utils) { - int sfilter = FilterArrayToInt(object->GetStates(), 0); + ObjectImpl::ValidateStates(value, utils); - if (object->GetParentServiceName().IsEmpty() && (sfilter & ~(StateFilterUp | StateFilterDown)) != 0) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": State filter is invalid for host dependency.", object->GetDebugInfo())); - } + int sfilter = FilterArrayToInt(value, 0); - if (!object->GetParentServiceName().IsEmpty() && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": State filter is invalid for service dependency.", object->GetDebugInfo())); - } + if (GetParentServiceName().IsEmpty() && (sfilter & ~(StateFilterUp | StateFilterDown)) != 0) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("states"), "State filter is invalid for host dependency.")); + + if (!GetParentServiceName().IsEmpty() && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("states"), "State filter is invalid for service dependency.")); } diff --git a/lib/icinga/dependency.hpp b/lib/icinga/dependency.hpp index 6635cb302..2b38a32a3 100644 --- a/lib/icinga/dependency.hpp +++ b/lib/icinga/dependency.hpp @@ -51,7 +51,7 @@ public: static void RegisterApplyRuleHandler(void); - static void ValidateFilters(const String& location, const Dependency::Ptr& object); + virtual void ValidateStates(const Array::Ptr& value, const ValidationUtils& utils) override; static void EvaluateApplyRules(const intrusive_ptr& host); static void EvaluateApplyRules(const intrusive_ptr& service); diff --git a/lib/icinga/dependency.ti b/lib/icinga/dependency.ti index d89e046f1..7107e065a 100644 --- a/lib/icinga/dependency.ti +++ b/lib/icinga/dependency.ti @@ -36,13 +36,13 @@ class Dependency : CustomVarObject < DependencyNameComposer load_after Host; load_after Service; - [config] String child_host_name; + [config, required] name(Host) child_host_name; [config] String child_service_name; - [config] String parent_host_name; + [config, required] name(Host) parent_host_name; [config] String parent_service_name; - [config] String period (PeriodRaw); + [config] name(TimePeriod) period (PeriodRaw); [config] Array::Ptr states; int state_filter_real (StateFilter); @@ -57,4 +57,10 @@ class Dependency : CustomVarObject < DependencyNameComposer }; }; +validator Dependency { + Array states { + Number "*"; + }; +}; + } diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 8f6caec56..4e7083a20 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -22,7 +22,6 @@ #include "icinga/hostgroup.hpp" #include "icinga/pluginutility.hpp" #include "icinga/scheduleddowntime.hpp" -#include "base/exception.hpp" #include "base/objectlock.hpp" #include "base/convert.hpp" #include "base/utility.hpp" diff --git a/lib/icinga/host.ti b/lib/icinga/host.ti index bf570f86c..670f0b317 100644 --- a/lib/icinga/host.ti +++ b/lib/icinga/host.ti @@ -49,4 +49,10 @@ class Host : Checkable }; +validator Host { + Array groups { + name(HostGroup) "*"; + }; +}; + } diff --git a/lib/icinga/hostgroup.ti b/lib/icinga/hostgroup.ti index a0a8b7839..637f19a14 100644 --- a/lib/icinga/hostgroup.ti +++ b/lib/icinga/hostgroup.ti @@ -39,4 +39,10 @@ class HostGroup : CustomVarObject [config] String action_url; }; +validator HostGroup { + Array groups { + name(HostGroup) "*"; + }; +}; + } diff --git a/lib/icinga/icinga-type.conf b/lib/icinga/icinga-type.conf deleted file mode 100644 index 049eadc1d..000000000 --- a/lib/icinga/icinga-type.conf +++ /dev/null @@ -1,293 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -%type IcingaApplication { -} - -%type IcingaStatusWriter { - %attribute %string "status_path", - %attribute %number "update_interval" -} - -%type CustomVarObject { - %validator "ValidateCustomAttributes", - %attribute %dictionary "vars", -} - -%type Checkable %inherits CustomVarObject { - %validator "ValidateCheckableCheckInterval", - - %attribute %string "display_name", - - %require "check_command", - %attribute %name(CheckCommand) "check_command", - %attribute %number "max_check_attempts", - %attribute %name(TimePeriod) "check_period", - %attribute %number "check_interval", - %attribute %number "retry_interval", - - %attribute %number "enable_notifications", - %attribute %number "enable_active_checks", - %attribute %number "enable_passive_checks", - %attribute %number "enable_event_handler", - - %attribute %name(EventCommand) "event_command", - - %attribute %number "enable_flapping", - %attribute %number "flapping_threshold", - - %attribute %number "enable_perfdata", - - %attribute %number "volatile", - - %attribute %string "notes", - %attribute %string "notes_url", - %attribute %string "action_url", - %attribute %string "icon_image", - %attribute %string "icon_image_alt", - - %attribute %name(Endpoint) "command_endpoint", -} - -%type Host %inherits Checkable { - %attribute %string "display_name", - %attribute %array "groups" { - %attribute %name(HostGroup) "*" - }, - - %attribute %string "address", - %attribute %string "address6", -} - -%type HostGroup %inherits CustomVarObject { - %attribute %string "display_name" - - %attribute %array "groups" { - %attribute %name(HostGroup) "*" - }, - - %attribute %string "notes", - %attribute %string "notes_url", - %attribute %string "action_url", -} - -%type Service %inherits Checkable { - %require "host_name", - %attribute %name(Host) "host_name", - - %attribute %string "name", - - %attribute %array "groups" { - %attribute %name(ServiceGroup) "*" - }, -} - -%type ServiceGroup %inherits CustomVarObject { - %attribute %string "display_name" - - %attribute %array "groups" { - %attribute %name(ServiceGroup) "*" - }, - - %attribute %string "notes", - %attribute %string "notes_url", - %attribute %string "action_url", -} - -%type Notification %inherits CustomVarObject { - %validator "ValidateNotificationFilters" - %validator "ValidateNotificationUsers" - - %require "host_name", - %attribute %name(Host) "host_name", - %attribute %string "service_name", - - %attribute %string "name", - - %attribute %array "users" { - %attribute %name(User) "*" - }, - %attribute %array "user_groups" { - %attribute %name(UserGroup) "*" - }, - - %attribute %dictionary "times" { - %attribute %number "begin", - %attribute %number "end", - }, - - %require "command", - %attribute %name(NotificationCommand) "command", - - %attribute %number "interval", - %attribute %name(TimePeriod) "period", - - %attribute %array "types" { - %attribute %number "*" - }, - %attribute %array "states" { - %attribute %number "*" - }, - - %attribute %name(Endpoint) "command_endpoint", -} - -%type User %inherits CustomVarObject { - %validator "ValidateUserFilters" - - %attribute %string "display_name", - - %attribute %array "groups" { - %attribute %name(UserGroup) "*" - }, - - %attribute %number "enable_notifications", - %attribute %array "types" { - %attribute %number "*" - }, - %attribute %array "states" { - %attribute %number "*" - }, - %attribute %name(TimePeriod) "period", - - %attribute %string "email", - %attribute %string "pager", -} - -%type UserGroup %inherits CustomVarObject { - %attribute %string "display_name", - - %attribute %array "groups" { - %attribute %name(UserGroup) "*" - }, -} - -%type TimePeriod %inherits CustomVarObject { - %validator "ValidateTimePeriodRanges", - - %attribute %string "display_name", - - %require "update", - %attribute %function "update", - -/* %if (methods.update == "LegacyTimePeriod") { */ -// %require "ranges", - %attribute %dictionary "ranges" { - %attribute %string "*" - } -/* } */ -} - -%type Command %inherits CustomVarObject { - %validator "ValidateCommandAttributes", - %validator "ValidateCommandArguments", - %validator "ValidateEnvironmentVariables", - - %require "execute", - %attribute %function "execute", - -/* %if (methods.execute == "PluginNotification" || methods.execute == "PluginCheck" || methods.execute == "PluginEvent") { */ -// %require "command", - %attribute %string "command", - %attribute %function "command", - %attribute %array "command" { - %attribute %string "*" - %attribute %function "*", - }, - %attribute %dictionary "arguments" { - %attribute %string "*", - %attribute %dictionary "*" { - %attribute %string "key" - %attribute %string "value" - %attribute %function "value" - %attribute %string "description" - %attribute %number "required" - %attribute %number "skip_key" - %attribute %number "repeat_key" - %attribute %string "set_if" - %attribute %function "set_if" - %attribute %number "order" - } - }, - %attribute %dictionary "env" { - %attribute %string "*" - %attribute %function "*" - }, - %attribute %number "timeout" -/* } */ -} - -%type CheckCommand %inherits Command { - -} - -%type NotificationCommand %inherits Command { - -} - -%type EventCommand %inherits Command { - -} - -%type ScheduledDowntime %inherits CustomVarObject { - %validator "ValidateScheduledDowntimeRanges", - - %require "host_name", - %attribute %name(Host) "host_name", - %attribute %string "service_name", - - %attribute %string "name", - - %require "author", - %attribute %string "author", - - %require "comment", - %attribute %string "comment", - - %attribute %number "duration", - %attribute %number "fixed", - - %require "ranges", - %attribute %dictionary "ranges" { - %attribute %string "*" - }, -} - -%type Dependency %inherits CustomVarObject { - %validator "ValidateDependencyFilters" - - %require "parent_host_name", - %attribute %name(Host) "parent_host_name", - %attribute %string "parent_service_name", - - %require "child_host_name", - %attribute %name(Host) "child_host_name", - %attribute %string "child_service_name", - - %attribute %string "name", - - %attribute %name(TimePeriod) "period", - - %attribute %array "states" { - %attribute %number "*" - }, - %attribute %number "ignore_soft_states", - %attribute %number "disable_checks", - %attribute %number "disable_notifications" -} diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp index afce5c1e8..af2732d00 100644 --- a/lib/icinga/notification.cpp +++ b/lib/icinga/notification.cpp @@ -27,14 +27,11 @@ #include "base/exception.hpp" #include "base/initialize.hpp" #include "base/scriptglobal.hpp" -#include "base/function.hpp" #include using namespace icinga; REGISTER_TYPE(Notification); -REGISTER_SCRIPTFUNCTION(ValidateNotificationFilters, &Notification::ValidateFilters); -REGISTER_SCRIPTFUNCTION(ValidateNotificationUsers, &Notification::ValidateUsers); INITIALIZE_ONCE(&Notification::StaticInitialize); boost::signals2::signal Notification::OnNextNotificationChanged; @@ -256,8 +253,8 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe << "Not sending notifications for notification object '" << GetName() << "': before escalation range"; /* we need to adjust the next notification time - * to now + begin delaying the first notification - */ + * to now + begin delaying the first notification + */ double nextProposedNotification = now + times->Get("begin") + 1.0; if (GetNextNotification() > nextProposedNotification) SetNextNotification(nextProposedNotification); @@ -628,43 +625,46 @@ String Notification::NotificationHostStateToString(HostState state) } } -void Notification::ValidateUsers(const String& location, const Notification::Ptr& object) +void Notification::Validate(int types, const ValidationUtils& utils) { - Array::Ptr users = object->GetUsersRaw(); - Array::Ptr groups = object->GetUserGroupsRaw(); + ObjectImpl::Validate(types, utils); - if ((!users || users->GetLength() == 0) && (!groups || groups->GetLength() == 0)) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": No users/user_groups specified.", object->GetDebugInfo())); - } + if (!(types & FAConfig)) + return; + + Array::Ptr users = GetUsersRaw(); + Array::Ptr groups = GetUserGroupsRaw(); + + if ((!users || users->GetLength() == 0) && (!groups || groups->GetLength() == 0)) + BOOST_THROW_EXCEPTION(ValidationError(this, std::vector(), "Validation failed: No users/user_groups specified.")); } -void Notification::ValidateFilters(const String& location, const Notification::Ptr& object) +void Notification::ValidateStates(const Array::Ptr& value, const ValidationUtils& utils) { - int sfilter = FilterArrayToInt(object->GetStates(), 0); + ObjectImpl::ValidateStates(value, utils); - if (object->GetServiceName().IsEmpty() && (sfilter & ~(StateFilterUp | StateFilterDown)) != 0) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": State filter is invalid.", object->GetDebugInfo())); - } + int sfilter = FilterArrayToInt(value, 0); - if (!object->GetServiceName().IsEmpty() && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": State filter is invalid.", object->GetDebugInfo())); - } + if (GetServiceName().IsEmpty() && (sfilter & ~(StateFilterUp | StateFilterDown)) != 0) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("states"), "State filter is invalid.")); - int tfilter = FilterArrayToInt(object->GetTypes(), 0); + if (!GetServiceName().IsEmpty() && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("types"), "State filter is invalid.")); +} + +void Notification::ValidateTypes(const Array::Ptr& value, const ValidationUtils& utils) +{ + ObjectImpl::ValidateTypes(value, utils); + + int tfilter = FilterArrayToInt(value, 0); if ((tfilter & ~(1 << NotificationDowntimeStart | 1 << NotificationDowntimeEnd | 1 << NotificationDowntimeRemoved | 1 << NotificationCustom | 1 << NotificationAcknowledgement | 1 << NotificationProblem | 1 << NotificationRecovery | - 1 << NotificationFlappingStart | 1 << NotificationFlappingEnd)) != 0) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Type filter is invalid.", object->GetDebugInfo())); - } + 1 << NotificationFlappingStart | 1 << NotificationFlappingEnd)) != 0) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("types"), "Type filter is invalid.")); } Endpoint::Ptr Notification::GetCommandEndpoint(void) const { return Endpoint::GetByName(GetCommandEndpointRaw()); } - diff --git a/lib/icinga/notification.hpp b/lib/icinga/notification.hpp index 0a8f41770..62915f1f2 100644 --- a/lib/icinga/notification.hpp +++ b/lib/icinga/notification.hpp @@ -110,8 +110,10 @@ public: static void RegisterApplyRuleHandler(void); - static void ValidateUsers(const String& location, const Notification::Ptr& object); - static void ValidateFilters(const String& location, const Notification::Ptr& object); + virtual void Validate(int types, const ValidationUtils& utils) override; + + virtual void ValidateStates(const Array::Ptr& value, const ValidationUtils& utils) override; + virtual void ValidateTypes(const Array::Ptr& value, const ValidationUtils& utils) override; static void EvaluateApplyRules(const intrusive_ptr& host); static void EvaluateApplyRules(const intrusive_ptr& service); diff --git a/lib/icinga/notification.ti b/lib/icinga/notification.ti index dbb19a1d0..1f26d1b32 100644 --- a/lib/icinga/notification.ti +++ b/lib/icinga/notification.ti @@ -35,11 +35,11 @@ class Notification : CustomVarObject < NotificationNameComposer load_after Host; load_after Service; - [config, protected] String command (CommandRaw); + [config, protected, required] name(NotificationCommand) command (CommandRaw); [config] double interval { default {{{ return 1800; }}} }; - [config] String period (PeriodRaw); + [config] name(TimePeriod) period (PeriodRaw); [config, protected] Array::Ptr users (UsersRaw); [config, protected] Array::Ptr user_groups (UserGroupsRaw); [config] Dictionary::Ptr times; @@ -47,7 +47,7 @@ class Notification : CustomVarObject < NotificationNameComposer int type_filter_real (TypeFilter); [config] Array::Ptr states; int state_filter_real (StateFilter); - [config, protected] String host_name; + [config, protected, required] name(Host) host_name; [config, protected] String service_name; [state] Array::Ptr notified_users { @@ -59,7 +59,30 @@ class Notification : CustomVarObject < NotificationNameComposer [state, set_protected] Value notification_number; [state] double last_problem_notification; - [config] String command_endpoint (CommandEndpointRaw); + [config] name(Endpoint) command_endpoint (CommandEndpointRaw); +}; + +validator Notification { + Array users { + name(User) "*"; + }; + + Array user_groups { + name(UserGroup) "*"; + }; + + Dictionary times { + Number begin; + Number end; + }; + + Array types { + Number "*"; + }; + + Array states { + Number "*"; + }; }; } diff --git a/lib/icinga/scheduleddowntime.cpp b/lib/icinga/scheduleddowntime.cpp index 446e25281..4deacdd9a 100644 --- a/lib/icinga/scheduleddowntime.cpp +++ b/lib/icinga/scheduleddowntime.cpp @@ -34,7 +34,6 @@ using namespace icinga; REGISTER_TYPE(ScheduledDowntime); -REGISTER_SCRIPTFUNCTION(ValidateScheduledDowntimeRanges, &ScheduledDowntime::ValidateRanges); INITIALIZE_ONCE(&ScheduledDowntime::StaticInitialize); @@ -181,11 +180,9 @@ void ScheduledDowntime::CreateNextDowntime(void) downtime->SetConfigOwner(GetName()); } -void ScheduledDowntime::ValidateRanges(const String& location, const ScheduledDowntime::Ptr& object) +void ScheduledDowntime::ValidateRanges(const Dictionary::Ptr& value, const ValidationUtils& utils) { - Dictionary::Ptr ranges = object->GetRanges(); - - if (!ranges) + if (!value) return; /* create a fake time environment to validate the definitions */ @@ -193,22 +190,20 @@ void ScheduledDowntime::ValidateRanges(const String& location, const ScheduledDo tm reference = Utility::LocalTime(refts); Array::Ptr segments = new Array(); - ObjectLock olock(ranges); - BOOST_FOREACH(const Dictionary::Pair& kv, ranges) { + ObjectLock olock(value); + BOOST_FOREACH(const Dictionary::Pair& kv, value) { try { tm begin_tm, end_tm; int stride; LegacyTimePeriod::ParseTimeRange(kv.first, &begin_tm, &end_tm, &stride, &reference); } catch (std::exception&) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Invalid time specification.", object->GetDebugInfo())); + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("ranges"), "Invalid time specification: " + kv.first)); } try { LegacyTimePeriod::ProcessTimeRanges(kv.second, &reference, segments); } catch (std::exception&) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Invalid time range definition.", object->GetDebugInfo())); + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("ranges"), "Invalid time range definition: " + kv.first)); } } } diff --git a/lib/icinga/scheduleddowntime.hpp b/lib/icinga/scheduleddowntime.hpp index a4f5273d3..5eac7c6a8 100644 --- a/lib/icinga/scheduleddowntime.hpp +++ b/lib/icinga/scheduleddowntime.hpp @@ -53,7 +53,7 @@ public: static void EvaluateApplyRules(const intrusive_ptr& host); static void EvaluateApplyRules(const intrusive_ptr& service); - static void ValidateRanges(const String& location, const ScheduledDowntime::Ptr& object); + virtual void ValidateRanges(const Dictionary::Ptr& value, const ValidationUtils& utils) override; protected: virtual void OnAllConfigLoaded(void); diff --git a/lib/icinga/scheduleddowntime.ti b/lib/icinga/scheduleddowntime.ti index e2a68c579..06f84ef1b 100644 --- a/lib/icinga/scheduleddowntime.ti +++ b/lib/icinga/scheduleddowntime.ti @@ -35,18 +35,24 @@ class ScheduledDowntime : CustomVarObject < ScheduledDowntimeNameComposer load_after Host; load_after Service; - [config, protected] String host_name; + [config, protected, required] name(Host) host_name; [config, protected] String service_name; - [config] String author; - [config] String comment; + [config, required] String author; + [config, required] String comment; [config] double duration; [config] bool fixed { default {{{ return true; }}} }; - [config] Dictionary::Ptr ranges; + [config, required] Dictionary::Ptr ranges; +}; + +validator ScheduledDowntime { + Dictionary ranges { + String "*"; + }; }; } diff --git a/lib/icinga/service.ti b/lib/icinga/service.ti index 8f524006d..a516fac7e 100644 --- a/lib/icinga/service.ti +++ b/lib/icinga/service.ti @@ -45,7 +45,7 @@ class Service : Checkable < ServiceNameComposer return m_DisplayName; }}} }; - [config] String host_name; + [config, required] name(Host) host_name; [enum, no_storage] ServiceState "state" { get {{{ return GetStateRaw(); @@ -63,4 +63,10 @@ class Service : Checkable < ServiceNameComposer }; }; +validator Service { + Array groups { + name(ServiceGroup) "*"; + }; +}; + } diff --git a/lib/icinga/servicegroup.ti b/lib/icinga/servicegroup.ti index 9830c13fc..258864ba9 100644 --- a/lib/icinga/servicegroup.ti +++ b/lib/icinga/servicegroup.ti @@ -39,4 +39,10 @@ class ServiceGroup : CustomVarObject [config] String action_url; }; +validator ServiceGroup { + Array groups { + name(ServiceGroup) "*"; + }; +}; + } diff --git a/lib/icinga/timeperiod.cpp b/lib/icinga/timeperiod.cpp index 825e52e7c..07dcbe399 100644 --- a/lib/icinga/timeperiod.cpp +++ b/lib/icinga/timeperiod.cpp @@ -30,7 +30,6 @@ using namespace icinga; REGISTER_TYPE(TimePeriod); -REGISTER_SCRIPTFUNCTION(ValidateTimePeriodRanges, &TimePeriod::ValidateRanges); static Timer::Ptr l_UpdateTimer; @@ -306,11 +305,9 @@ void TimePeriod::Dump(void) Log(LogDebug, "TimePeriod", "---"); } -void TimePeriod::ValidateRanges(const String& location, const TimePeriod::Ptr& object) +void TimePeriod::ValidateRanges(const Dictionary::Ptr& value, const ValidationUtils& utils) { - Dictionary::Ptr ranges = object->GetRanges(); - - if (!ranges) + if (!value) return; /* create a fake time environment to validate the definitions */ @@ -318,22 +315,20 @@ void TimePeriod::ValidateRanges(const String& location, const TimePeriod::Ptr& o tm reference = Utility::LocalTime(refts); Array::Ptr segments = new Array(); - ObjectLock olock(ranges); - BOOST_FOREACH(const Dictionary::Pair& kv, ranges) { + ObjectLock olock(value); + BOOST_FOREACH(const Dictionary::Pair& kv, value) { try { tm begin_tm, end_tm; int stride; LegacyTimePeriod::ParseTimeRange(kv.first, &begin_tm, &end_tm, &stride, &reference); } catch (std::exception&) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Invalid time specification.", object->GetDebugInfo())); + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("ranges"), "Invalid time specification: " + kv.first)); } try { LegacyTimePeriod::ProcessTimeRanges(kv.second, &reference, segments); } catch (std::exception&) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Invalid time range definition.", object->GetDebugInfo())); + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("ranges"), "Invalid time range definition: " + kv.second)); } } } diff --git a/lib/icinga/timeperiod.hpp b/lib/icinga/timeperiod.hpp index e4482f299..843808155 100644 --- a/lib/icinga/timeperiod.hpp +++ b/lib/icinga/timeperiod.hpp @@ -48,7 +48,7 @@ public: bool IsInside(double ts) const; double FindNextTransition(double begin); - static void ValidateRanges(const String& location, const TimePeriod::Ptr& object); + virtual void ValidateRanges(const Dictionary::Ptr& value, const ValidationUtils& utils) override; private: void AddSegment(double s, double end); diff --git a/lib/icinga/timeperiod.ti b/lib/icinga/timeperiod.ti index 50a950632..c1db4b4d4 100644 --- a/lib/icinga/timeperiod.ti +++ b/lib/icinga/timeperiod.ti @@ -34,7 +34,7 @@ class TimePeriod : CustomVarObject }}} }; [config] Dictionary::Ptr ranges; - [config] Function::Ptr update; + [config, required] Function::Ptr update; [state] Value valid_begin; [state] Value valid_end; [state] Array::Ptr segments; @@ -43,4 +43,10 @@ class TimePeriod : CustomVarObject }; }; +validator TimePeriod { + Dictionary ranges { + String "*"; + }; +}; + } diff --git a/lib/icinga/user.cpp b/lib/icinga/user.cpp index a7e461638..cb68b6cf5 100644 --- a/lib/icinga/user.cpp +++ b/lib/icinga/user.cpp @@ -21,7 +21,6 @@ #include "icinga/usergroup.hpp" #include "icinga/notification.hpp" #include "icinga/usergroup.hpp" -#include "base/function.hpp" #include "base/objectlock.hpp" #include "base/exception.hpp" #include @@ -29,7 +28,6 @@ using namespace icinga; REGISTER_TYPE(User); -REGISTER_SCRIPTFUNCTION(ValidateUserFilters, &User::ValidateFilters); boost::signals2::signal User::OnEnableNotificationsChanged; @@ -101,22 +99,25 @@ TimePeriod::Ptr User::GetPeriod(void) const return TimePeriod::GetByName(GetPeriodRaw()); } -void User::ValidateFilters(const String& location, const User::Ptr& object) +void User::ValidateStates(const Array::Ptr& value, const ValidationUtils& utils) { - int sfilter = FilterArrayToInt(object->GetStates(), 0); + ObjectImpl::ValidateStates(value, utils); + + int sfilter = FilterArrayToInt(value, 0); if ((sfilter & ~(StateFilterUp | StateFilterDown | StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": State filter is invalid.", object->GetDebugInfo())); + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("states"), "State filter is invalid.")); } +} - int tfilter = FilterArrayToInt(object->GetTypes(), 0); +void User::ValidateTypes(const Array::Ptr& value, const ValidationUtils& utils) +{ + int tfilter = FilterArrayToInt(value, 0); if ((tfilter & ~(1 << NotificationDowntimeStart | 1 << NotificationDowntimeEnd | 1 << NotificationDowntimeRemoved | 1 << NotificationCustom | 1 << NotificationAcknowledgement | 1 << NotificationProblem | 1 << NotificationRecovery | 1 << NotificationFlappingStart | 1 << NotificationFlappingEnd)) != 0) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Type filter is invalid.", object->GetDebugInfo())); + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("types"), "Type filter is invalid.")); } } diff --git a/lib/icinga/user.hpp b/lib/icinga/user.hpp index 4b42c82df..f4e9137c6 100644 --- a/lib/icinga/user.hpp +++ b/lib/icinga/user.hpp @@ -44,7 +44,8 @@ public: /* Notifications */ TimePeriod::Ptr GetPeriod(void) const; - static void ValidateFilters(const String& location, const User::Ptr& attrs); + virtual void ValidateStates(const Array::Ptr& value, const ValidationUtils& utils) override; + virtual void ValidateTypes(const Array::Ptr& value, const ValidationUtils& utils) override; bool GetEnableNotifications(void) const; void SetEnableNotifications(bool enabled, const MessageOrigin& origin = MessageOrigin()); diff --git a/lib/icinga/user.ti b/lib/icinga/user.ti index 5685255bb..cf00e725a 100644 --- a/lib/icinga/user.ti +++ b/lib/icinga/user.ti @@ -36,7 +36,7 @@ class User : CustomVarObject [config] Array::Ptr groups { default {{{ return new Array(); }}} }; - [config] String period (PeriodRaw); + [config] name(TimePeriod) period (PeriodRaw); [config] Array::Ptr types; int type_filter_real (TypeFilter); [config] Array::Ptr states; @@ -53,4 +53,18 @@ class User : CustomVarObject [state] double last_notification; }; +validator User { + Array groups { + name(UserGroup) "*"; + }; + + Array types { + Number "*"; + }; + + Array states { + Number "*"; + }; +}; + } diff --git a/lib/icinga/usergroup.ti b/lib/icinga/usergroup.ti index bf3f3216c..7e45b9524 100644 --- a/lib/icinga/usergroup.ti +++ b/lib/icinga/usergroup.ti @@ -36,4 +36,10 @@ class UserGroup : CustomVarObject [config] Array::Ptr groups; }; +validator UserGroup { + Array groups { + name(UserGroup) "*"; + }; +}; + } diff --git a/lib/livestatus/CMakeLists.txt b/lib/livestatus/CMakeLists.txt index 07a4ce8ea..fb7033b72 100644 --- a/lib/livestatus/CMakeLists.txt +++ b/lib/livestatus/CMakeLists.txt @@ -17,8 +17,6 @@ mkclass_target(livestatuslistener.ti livestatuslistener.thpp) -mkembedconfig_target(livestatus-type.conf livestatus-type.cpp) - set(livestatus_SOURCES aggregator.cpp andfilter.cpp attributefilter.cpp avgaggregator.cpp column.cpp combinerfilter.cpp commandstable.cpp @@ -30,7 +28,7 @@ set(livestatus_SOURCES minaggregator.cpp negatefilter.cpp orfilter.cpp servicegroupstable.cpp servicestable.cpp statehisttable.cpp statustable.cpp stdaggregator.cpp sumaggregator.cpp table.cpp - timeperiodstable.cpp livestatus-type.cpp + timeperiodstable.cpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/livestatus/livestatus-type.conf b/lib/livestatus/livestatus-type.conf deleted file mode 100644 index 5acab91d6..000000000 --- a/lib/livestatus/livestatus-type.conf +++ /dev/null @@ -1,30 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -%type LivestatusListener { - %validator "ValidateSocketType", - - %attribute %string "socket_type", - - %attribute %string "socket_path", - %attribute %string "bind_host", - %attribute %string "bind_port", - - %attribute %string "compat_log_path", -} diff --git a/lib/livestatus/livestatuslistener.cpp b/lib/livestatus/livestatuslistener.cpp index 83e2948ad..800fa2713 100644 --- a/lib/livestatus/livestatuslistener.cpp +++ b/lib/livestatus/livestatuslistener.cpp @@ -35,7 +35,6 @@ using namespace icinga; REGISTER_TYPE(LivestatusListener); -REGISTER_SCRIPTFUNCTION(ValidateSocketType, &LivestatusListener::ValidateSocketType); static int l_ClientsConnected = 0; static int l_Connections = 0; @@ -213,12 +212,10 @@ void LivestatusListener::ClientHandler(const Socket::Ptr& client) } -void LivestatusListener::ValidateSocketType(const String& location, const LivestatusListener::Ptr& object) +void LivestatusListener::ValidateSocketType(const String& value, const ValidationUtils& utils) { - String socket_type = object->GetSocketType(); + ObjectImpl::ValidateSocketType(value, utils); - if (socket_type != "unix" && socket_type != "tcp") { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Socket type '" + socket_type + "' is invalid.", object->GetDebugInfo())); - } + if (value != "unix" && value != "tcp") + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("socket_type"), "Socket type '" + value + "' is invalid.")); } diff --git a/lib/livestatus/livestatuslistener.hpp b/lib/livestatus/livestatuslistener.hpp index 334e34b58..6886866a3 100644 --- a/lib/livestatus/livestatuslistener.hpp +++ b/lib/livestatus/livestatuslistener.hpp @@ -45,7 +45,7 @@ public: static int GetClientsConnected(void); static int GetConnections(void); - static void ValidateSocketType(const String& location, const LivestatusListener::Ptr& object); + virtual void ValidateSocketType(const String& value, const ValidationUtils& utils) override; protected: virtual void Start(void); diff --git a/lib/notification/CMakeLists.txt b/lib/notification/CMakeLists.txt index 84276ef12..dca75ba54 100644 --- a/lib/notification/CMakeLists.txt +++ b/lib/notification/CMakeLists.txt @@ -17,10 +17,8 @@ mkclass_target(notificationcomponent.ti notificationcomponent.thpp) -mkembedconfig_target(notification-type.conf notification-type.cpp) - set(notification_SOURCES - notificationcomponent.cpp notificationcomponent.thpp notification-type.cpp + notificationcomponent.cpp notificationcomponent.thpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/notification/notification-type.conf b/lib/notification/notification-type.conf deleted file mode 100644 index 626722097..000000000 --- a/lib/notification/notification-type.conf +++ /dev/null @@ -1,22 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -%type NotificationComponent { - %attribute %number "enable_ha" -} diff --git a/lib/perfdata/CMakeLists.txt b/lib/perfdata/CMakeLists.txt index 8ea16b4cf..b6c2cd918 100644 --- a/lib/perfdata/CMakeLists.txt +++ b/lib/perfdata/CMakeLists.txt @@ -20,10 +20,8 @@ mkclass_target(graphitewriter.ti graphitewriter.thpp) mkclass_target(opentsdbwriter.ti opentsdbwriter.thpp) mkclass_target(perfdatawriter.ti perfdatawriter.thpp) -mkembedconfig_target(perfdata-type.conf perfdata-type.cpp) - set(perfdata_SOURCES - gelfwriter.cpp gelfwriter.thpp graphitewriter.cpp graphitewriter.thpp opentsdbwriter.cpp opentsdbwriter.thpp perfdatawriter.cpp perfdatawriter.thpp perfdata-type.cpp + gelfwriter.cpp gelfwriter.thpp graphitewriter.cpp graphitewriter.thpp opentsdbwriter.cpp opentsdbwriter.thpp perfdatawriter.cpp perfdatawriter.thpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/perfdata/graphitewriter.cpp b/lib/perfdata/graphitewriter.cpp index cf6f42298..7304e6225 100644 --- a/lib/perfdata/graphitewriter.cpp +++ b/lib/perfdata/graphitewriter.cpp @@ -43,7 +43,6 @@ using namespace icinga; REGISTER_TYPE(GraphiteWriter); -REGISTER_SCRIPTFUNCTION(ValidateNameTemplates, &GraphiteWriter::ValidateNameTemplates); REGISTER_STATSFUNCTION(GraphiteWriterStats, &GraphiteWriter::StatsFunc); @@ -232,15 +231,18 @@ Value GraphiteWriter::EscapeMacroMetric(const Value& value) return EscapeMetric(value); } -void GraphiteWriter::ValidateNameTemplates(const String& location, const GraphiteWriter::Ptr& object) +void GraphiteWriter::ValidateHostNameTemplate(const String& value, const ValidationUtils& utils) { - if (!MacroProcessor::ValidateMacroString(object->GetHostNameTemplate())) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Closing $ not found in macro format string '" + object->GetHostNameTemplate() + "'.", object->GetDebugInfo())); - } + ObjectImpl::ValidateHostNameTemplate(value, utils); - if (!MacroProcessor::ValidateMacroString(object->GetServiceNameTemplate())) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Closing $ not found in macro format string '" + object->GetServiceNameTemplate() + "'.", object->GetDebugInfo())); - } + if (!MacroProcessor::ValidateMacroString(value)) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("host_name_template"), "Closing $ not found in macro format string '" + value + "'.")); +} + +void GraphiteWriter::ValidateServiceNameTemplate(const String& value, const ValidationUtils& utils) +{ + ObjectImpl::ValidateServiceNameTemplate(value, utils); + + if (!MacroProcessor::ValidateMacroString(value)) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("service_name_template"), "Closing $ not found in macro format string '" + value + "'.")); } diff --git a/lib/perfdata/graphitewriter.hpp b/lib/perfdata/graphitewriter.hpp index dc9392b4d..e20c88f22 100644 --- a/lib/perfdata/graphitewriter.hpp +++ b/lib/perfdata/graphitewriter.hpp @@ -43,7 +43,8 @@ public: static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata); - static void ValidateNameTemplates(const String& location, const GraphiteWriter::Ptr& object); + virtual void ValidateHostNameTemplate(const String& value, const ValidationUtils& utils) override; + virtual void ValidateServiceNameTemplate(const String& value, const ValidationUtils& utils) override; protected: virtual void Start(void); diff --git a/lib/perfdata/perfdata-type.conf b/lib/perfdata/perfdata-type.conf deleted file mode 100644 index a07cd29af..000000000 --- a/lib/perfdata/perfdata-type.conf +++ /dev/null @@ -1,50 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - -%type PerfdataWriter { - %validator "ValidateFormatTemplates", - - %attribute %string "host_perfdata_path", - %attribute %string "service_perfdata_path", - %attribute %string "host_temp_path", - %attribute %string "service_temp_path", - %attribute %string "host_format_template", - %attribute %string "service_format_template", - %attribute %number "rotation_interval" -} - -%type GraphiteWriter { - %validator "ValidateNameTemplates", - - %attribute %string "host", - %attribute %string "port", - %attribute %string "host_name_template", - %attribute %string "service_name_template" -} - -%type GelfWriter { - %attribute %string "host", - %attribute %string "port", - %attribute %string "source" -} - -%type OpenTsdbWriter { - %attribute %string "host", - %attribute %string "port", -} diff --git a/lib/perfdata/perfdatawriter.cpp b/lib/perfdata/perfdatawriter.cpp index a92b48446..368889fcb 100644 --- a/lib/perfdata/perfdatawriter.cpp +++ b/lib/perfdata/perfdatawriter.cpp @@ -34,7 +34,6 @@ using namespace icinga; REGISTER_TYPE(PerfdataWriter); -REGISTER_SCRIPTFUNCTION(ValidateFormatTemplates, &PerfdataWriter::ValidateFormatTemplates); REGISTER_STATSFUNCTION(PerfdataWriterStats, &PerfdataWriter::StatsFunc); @@ -140,15 +139,18 @@ void PerfdataWriter::RotationTimerHandler(void) RotateFile(m_HostOutputFile, GetHostTempPath(), GetHostPerfdataPath()); } -void PerfdataWriter::ValidateFormatTemplates(const String& location, const PerfdataWriter::Ptr& object) +void PerfdataWriter::ValidateHostFormatTemplate(const String& value, const ValidationUtils& utils) { - if (!MacroProcessor::ValidateMacroString(object->GetHostFormatTemplate())) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Closing $ not found in macro format string '" + object->GetHostFormatTemplate() + "'.", object->GetDebugInfo())); - } + ObjectImpl::ValidateHostFormatTemplate(value, utils); - if (!MacroProcessor::ValidateMacroString(object->GetServiceFormatTemplate())) { - BOOST_THROW_EXCEPTION(ScriptError("Validation failed for " + - location + ": Closing $ not found in macro format string '" + object->GetHostFormatTemplate() + "'.", object->GetDebugInfo())); - } + if (!MacroProcessor::ValidateMacroString(value)) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("host_format_template"), "Closing $ not found in macro format string '" + value + "'.")); +} + +void PerfdataWriter::ValidateServiceFormatTemplate(const String& value, const ValidationUtils& utils) +{ + ObjectImpl::ValidateServiceFormatTemplate(value, utils); + + if (!MacroProcessor::ValidateMacroString(value)) + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("service_format_template"), "Closing $ not found in macro format string '" + value + "'.")); } diff --git a/lib/perfdata/perfdatawriter.hpp b/lib/perfdata/perfdatawriter.hpp index 7edd334a9..aa58c7a1b 100644 --- a/lib/perfdata/perfdatawriter.hpp +++ b/lib/perfdata/perfdatawriter.hpp @@ -42,7 +42,8 @@ public: static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata); - static void ValidateFormatTemplates(const String& location, const PerfdataWriter::Ptr& object); + virtual void ValidateHostFormatTemplate(const String& value, const ValidationUtils& utils) override; + virtual void ValidateServiceFormatTemplate(const String& value, const ValidationUtils& utils) override; protected: virtual void Start(void); diff --git a/lib/remote/CMakeLists.txt b/lib/remote/CMakeLists.txt index 6e8194d6d..9965474aa 100644 --- a/lib/remote/CMakeLists.txt +++ b/lib/remote/CMakeLists.txt @@ -19,12 +19,10 @@ mkclass_target(apilistener.ti apilistener.thpp) mkclass_target(endpoint.ti endpoint.thpp) mkclass_target(zone.ti zone.thpp) -mkembedconfig_target(remote-type.conf remote-type.cpp) - set(remote_SOURCES apiclient.cpp apiclient-heartbeat.cpp apifunction.cpp apilistener.cpp apilistener-sync.cpp apilistener.thpp authority.cpp endpoint.cpp endpoint.thpp jsonrpc.cpp - messageorigin.cpp remote-type.cpp zone.cpp zone.thpp + messageorigin.cpp zone.cpp zone.thpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/remote/apilistener.ti b/lib/remote/apilistener.ti index c968eb33e..489511dd5 100644 --- a/lib/remote/apilistener.ti +++ b/lib/remote/apilistener.ti @@ -25,9 +25,9 @@ namespace icinga class ApiListener : DynamicObject { - [config] String cert_path; - [config] String key_path; - [config] String ca_path; + [config, required] String cert_path; + [config, required] String key_path; + [config, required] String ca_path; [config] String crl_path; [config] String bind_host; diff --git a/lib/remote/remote-type.conf b/lib/remote/remote-type.conf deleted file mode 100644 index 371a974ff..000000000 --- a/lib/remote/remote-type.conf +++ /dev/null @@ -1,56 +0,0 @@ -/****************************************************************************** - * Icinga 2 * - * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) * - * * - * 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. * - ******************************************************************************/ - - %type ApiListener { - %require "cert_path", - %attribute %string "cert_path", - - %require "key_path", - %attribute %string "key_path", - - %require "ca_path", - %attribute %string "ca_path", - - %attribute %string "crl_path", - - %attribute %string "bind_host", - %attribute %string "bind_port", - - %attribute %number "accept_config", - %attribute %number "accept_commands", - - %attribute %string "ticket_salt" -} - -%type Endpoint { - %attribute %string "host", - %attribute %string "port", - - %attribute %number "log_duration" -} - -%type Zone { - %attribute %name(Zone) "parent", - - %attribute %array "endpoints" { - %attribute %name(Endpoint) "*" - }, - - %attribute %number "global" -} diff --git a/lib/remote/zone.ti b/lib/remote/zone.ti index e21e21bd2..37df1c6e7 100644 --- a/lib/remote/zone.ti +++ b/lib/remote/zone.ti @@ -24,9 +24,15 @@ namespace icinga class Zone : DynamicObject { - [config] String parent (ParentRaw); + [config] name(Zone) parent (ParentRaw); [config] Array::Ptr endpoints (EndpointsRaw); [config] bool global; }; +validator Zone { + Array endpoints { + name(Endpoint) "*"; + }; +}; + } diff --git a/test/config-ops.cpp b/test/config-ops.cpp index 6468e3177..227abe4b1 100644 --- a/test/config-ops.cpp +++ b/test/config-ops.cpp @@ -18,6 +18,7 @@ ******************************************************************************/ #include "config/configcompiler.hpp" +#include "base/exception.hpp" #include using namespace icinga; diff --git a/test/livestatus.cpp b/test/livestatus.cpp index 9c79dff24..f50cfc8ce 100644 --- a/test/livestatus.cpp +++ b/test/livestatus.cpp @@ -18,13 +18,9 @@ ******************************************************************************/ #include "livestatus/livestatusquery.hpp" -#include "config/configtype.hpp" #include "config/configcompiler.hpp" +#include "config/configitem.hpp" #include "base/application.hpp" -#include "base/debug.hpp" -#include "base/objectlock.hpp" -#include "base/console.hpp" -#include "base/serializer.hpp" #include "base/stdiostream.hpp" #include "base/json.hpp" #include "base/loader.hpp" @@ -32,7 +28,6 @@ #include #include - using namespace icinga; struct GlobalConfigFixture { diff --git a/tools/mkclass/class_lexer.ll b/tools/mkclass/class_lexer.ll index 7c59cf62c..c7873c9d7 100644 --- a/tools/mkclass/class_lexer.ll +++ b/tools/mkclass/class_lexer.ll @@ -143,6 +143,9 @@ set_protected { yylval->num = FASetProtected; return T_FIELD_ATTRIBUTE; } protected { yylval->num = FAGetProtected | FASetProtected; return T_FIELD_ATTRIBUTE; } internal { yylval->num = FAInternal; return T_FIELD_ATTRIBUTE; } no_storage { yylval->num = FANoStorage; return T_FIELD_ATTRIBUTE; } +validator { return T_VALIDATOR; } +required { return T_REQUIRED; } +name { return T_NAME; } default { yylval->num = FTDefault; return T_FIELD_ACCESSOR_TYPE; } get { yylval->num = FTGet; return T_FIELD_ACCESSOR_TYPE; } set { yylval->num = FTSet; return T_FIELD_ACCESSOR_TYPE; } diff --git a/tools/mkclass/class_parser.yy b/tools/mkclass/class_parser.yy index 559a9b2c5..ab1f07d6c 100644 --- a/tools/mkclass/class_parser.yy +++ b/tools/mkclass/class_parser.yy @@ -45,11 +45,15 @@ using namespace icinga; %union { char *text; int num; + FieldType *type; Field *field; std::vector *fields; Klass *klass; FieldAccessor *fieldaccessor; std::vector *fieldaccessors; + Rule *rule; + std::vector *rules; + Validator *validator; } %token T_INCLUDE "include (T_INCLUDE)" @@ -57,6 +61,9 @@ using namespace icinga; %token T_CODE "code (T_CODE)" %token T_LOAD_AFTER "load_after (T_LOAD_AFTER)" %token T_NAMESPACE "namespace (T_NAMESPACE)" +%token T_VALIDATOR "validator (T_VALIDATOR)" +%token T_REQUIRED "required (T_REQUIRED)" +%token T_NAME "name (T_NAME)" %token T_STRING "string (T_STRING)" %token T_ANGLE_STRING "angle_string (T_ANGLE_STRING)" %token T_FIELD_ATTRIBUTE "field_attribute (T_FIELD_ATTRIBUTE)" @@ -77,17 +84,22 @@ using namespace icinga; %type angle_include %type code %type T_FIELD_ATTRIBUTE +%type field_attribute %type field_attributes %type field_attribute_list %type T_FIELD_ACCESSOR_TYPE %type T_CLASS_ATTRIBUTE %type class_attribute_list +%type field_type %type class_field %type class_fields %type class %type field_accessor_list %type field_accessors %type field_accessor +%type validator_rule +%type validator_rules +%type validator %{ @@ -110,6 +122,8 @@ void ClassCompiler::Compile(void) } catch (const std::exception& ex) { std::cerr << "Exception: " << ex.what(); } + + HandleMissingValidators(); } #define scanner (context->GetScanner()) @@ -137,6 +151,11 @@ statement: include context->HandleClass(*$1, yylloc); delete $1; } + | validator + { + context->HandleValidator(*$1, yylloc); + delete $1; + } | namespace | code { @@ -250,7 +269,23 @@ class_fields: /* empty */ } ; -class_field: field_attribute_list identifier identifier alternative_name_specifier field_accessor_list ';' +field_type: identifier + { + $$ = new FieldType(); + $$->IsName = false; + $$->TypeName = $1; + free($1); + } + | T_NAME '(' identifier ')' + { + $$ = new FieldType(); + $$->IsName = true; + $$->TypeName = $3; + free($3); + } + ; + +class_field: field_attribute_list field_type identifier alternative_name_specifier field_accessor_list ';' { Field *field = new Field(); @@ -259,8 +294,8 @@ class_field: field_attribute_list identifier identifier alternative_name_specifi if ((field->Attributes & (FAConfig | FAState)) == 0) field->Attributes |= FAEphemeral; - field->Type = $2; - std::free($2); + field->Type = *$2; + delete $2; field->Name = $3; std::free($3); @@ -321,15 +356,25 @@ field_attribute_list: /* empty */ } ; +field_attribute: T_FIELD_ATTRIBUTE + { + $$ = $1; + } + | T_REQUIRED + { + $$ = FARequired; + } + ; + field_attributes: /* empty */ { $$ = 0; } - | field_attributes ',' T_FIELD_ATTRIBUTE + | field_attributes ',' field_attribute { $$ = $1 | $3; } - | T_FIELD_ATTRIBUTE + | field_attribute { $$ = $1; } @@ -368,6 +413,84 @@ field_accessor: T_FIELD_ACCESSOR_TYPE T_STRING } ; +validator_rules: /* empty */ + { + $$ = new std::vector(); + } + | validator_rules validator_rule + { + $$->push_back(*$2); + delete $2; + } + ; + +validator_rule: T_NAME '(' T_IDENTIFIER ')' identifier ';' + { + $$ = new Rule(); + $$->Attributes = 0; + $$->IsName = true; + $$->Type = $3; + std::free($3); + $$->Pattern = $5; + std::free($5); + } + | T_IDENTIFIER identifier ';' + { + $$ = new Rule(); + $$->Attributes = 0; + $$->IsName = false; + $$->Type = $1; + std::free($1); + $$->Pattern = $2; + std::free($2); + } + | T_NAME '(' T_IDENTIFIER ')' identifier '{' validator_rules '}' ';' + { + $$ = new Rule(); + $$->Attributes = 0; + $$->IsName = true; + $$->Type = $3; + std::free($3); + $$->Pattern = $5; + std::free($5); + $$->Rules = *$7; + delete $7; + } + | T_IDENTIFIER identifier '{' validator_rules '}' ';' + { + $$ = new Rule(); + $$->Attributes = 0; + $$->IsName = false; + $$->Type = $1; + std::free($1); + $$->Pattern = $2; + std::free($2); + $$->Rules = *$4; + delete $4; + } + | T_REQUIRED identifier ';' + { + $$ = new Rule(); + $$->Attributes = RARequired; + $$->IsName = false; + $$->Type = ""; + $$->Pattern = $2; + std::free($2); + } + ; + +validator: T_VALIDATOR T_IDENTIFIER '{' validator_rules '}' ';' + { + $$ = new Validator(); + + $$->Name = $2; + std::free($2); + + $$->Rules = *$4; + delete $4; + } + ; + identifier: T_IDENTIFIER | T_STRING { diff --git a/tools/mkclass/classcompiler.cpp b/tools/mkclass/classcompiler.cpp index fa0c8d395..d700cfd52 100644 --- a/tools/mkclass/classcompiler.cpp +++ b/tools/mkclass/classcompiler.cpp @@ -19,9 +19,11 @@ #include "classcompiler.hpp" #include +#include #include #include #include +#include #include #include #ifndef _WIN32 @@ -77,6 +79,8 @@ void ClassCompiler::HandleNamespaceBegin(const std::string& name, const ClassDeb void ClassCompiler::HandleNamespaceEnd(const ClassDebugInfo&) { + HandleMissingValidators(); + std::cout << "}" << std::endl; } @@ -94,16 +98,16 @@ unsigned long ClassCompiler::SDBM(const std::string& str, size_t len = std::stri for (it = str.begin(); it != str.end(); it++) { if (current >= len) - break; + break; char c = *it; - hash = c + (hash << 6) + (hash << 16) - hash; + hash = c + (hash << 6) + (hash << 16) - hash; - current++; - } + current++; + } - return hash; + return hash; } static int TypePreference(const std::string& type) @@ -124,12 +128,12 @@ static int TypePreference(const std::string& type) static bool FieldLayoutCmp(const Field& a, const Field& b) { - return TypePreference(a.Type) < TypePreference(b.Type); + return TypePreference(a.Type.GetRealType()) < TypePreference(b.Type.GetRealType()); } static bool FieldTypeCmp(const Field& a, const Field& b) { - return a.Type < b.Type; + return a.Type.GetRealType() < b.Type.GetRealType(); } void ClassCompiler::OptimizeStructLayout(std::vector& fields) @@ -297,7 +301,10 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) size_t num = 0; for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { - std::string ftype = it->Type; + std::string ftype = it->Type.GetRealType(); + + if (ftype == "bool" || ftype == "int" || ftype == "double") + ftype = "Number"; if (ftype == "int" || ftype == "double") ftype = "Number"; @@ -310,8 +317,15 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) if (it->Attributes & FAEnum) ftype = "Number"; + std::string nameref; + + if (it->Type.IsName) + nameref = "\"" + it->Type.TypeName + "\""; + else + nameref = "NULL"; + std::cout << "\t\t\t" << "case " << num << ":" << std::endl - << "\t\t\t\t" << "return Field(" << num << ", \"" << ftype << "\", \"" << it->Name << "\", " << it->Attributes << ");" << std::endl; + << "\t\t\t\t" << "return Field(" << num << ", \"" << ftype << "\", \"" << it->Name << "\", " << nameref << ", " << it->Attributes << ");" << std::endl; num++; } @@ -362,13 +376,56 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) std::cout << "};" << std::endl << std::endl; + std::cout << std::endl; + /* ObjectImpl */ std::cout << "template<>" << std::endl << "class ObjectImpl<" << klass.Name << ">" << " : public " << (klass.Parent.empty() ? "Object" : klass.Parent) << std::endl << "{" << std::endl << "public:" << std::endl - << "\t" << "DECLARE_PTR_TYPEDEFS(ObjectImpl<" << klass.Name << ">);" << std::endl; + << "\t" << "DECLARE_PTR_TYPEDEFS(ObjectImpl<" << klass.Name << ">);" << std::endl << std::endl; + + /* Validate */ + std::cout << "\t" << "virtual void Validate(int types, const ValidationUtils& utils)" << std::endl + << "\t" << "{" << std::endl; + + if (!klass.Parent.empty()) + std::cout << "\t\t" << klass.Parent << "::Validate(types, utils);" << std::endl << std::endl; + + for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { + std::cout << "\t\t" << "if (" << (it->Attributes & (FAEphemeral|FAConfig|FAState)) << " & types)" << std::endl + << "\t\t\t" << "Validate" << it->GetFriendlyName() << "(Get" << it->GetFriendlyName() << "(), utils);" << std::endl; + } + + std::cout << "\t" << "}" << std::endl << std::endl; + + for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { + std::cout << "\t" << "inline void SimpleValidate" << it->GetFriendlyName() << "(" << it->Type.GetArgumentType() << " value, const ValidationUtils& utils)" << std::endl + << "\t" << "{" << std::endl; + + const Field& field = *it; + + if ((field.Attributes & (FARequired)) || field.Type.IsName) { + if (field.Attributes & FARequired) { + if (field.Type.GetRealType().find("::Ptr") != std::string::npos) + std::cout << "\t\t" << "if (!value)" << std::endl; + else + std::cout << "\t\t" << "if (value.IsEmpty())" << std::endl; + + std::cout << "\t\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of(\"" << field.Name << "\"), \"Attribute must not be empty.\"));" << std::endl << std::endl; + } + + if (field.Type.IsName) { + std::cout << "\t\t" << "String ref = value;" << std::endl + << "\t\t" << "if (!ref.IsEmpty() && !utils.ValidateName(\"" << field.Type.TypeName << "\", ref))" << std::endl + << "\t\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of(\"" << field.Name << "\"), \"Object '\" + ref + \"' of type '" << field.Type.TypeName + << "' does not exist.\"));" << std::endl; + } + } + + std::cout << "\t" << "}" << std::endl << std::endl; + } if (!klass.Fields.empty()) { /* constructor */ @@ -389,7 +446,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) if (!klass.Parent.empty()) std::cout << "\t\t" << "int real_id = id - TypeImpl<" << klass.Parent << ">::StaticGetFieldCount(); " << std::endl - << "\t\t" << "if (real_id < 0) { " << klass.Parent << "::SetField(id, value); return; }" << std::endl; + << "\t\t" << "if (real_id < 0) { " << klass.Parent << "::SetField(id, value); return; }" << std::endl; std::cout << "\t\t" << "switch ("; @@ -406,7 +463,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) << "\t\t\t\t" << "Set" << it->GetFriendlyName() << "("; if (it->Attributes & FAEnum) - std::cout << "static_cast<" << it->Type << ">(static_cast("; + std::cout << "static_cast<" << it->Type.GetRealType() << ">(static_cast("; std::cout << "value"; @@ -465,7 +522,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) prot = "public"; std::cout << prot << ":" << std::endl - << "\t" << "virtual " << it->Type << " Get" << it->GetFriendlyName() << "(void) const"; + << "\t" << "virtual " << it->Type.GetRealType() << " Get" << it->GetFriendlyName() << "(void) const"; if (it->PureGetAccessor) { std::cout << " = 0;" << std::endl; @@ -494,14 +551,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) prot = "public"; std::cout << prot << ":" << std::endl - << "\t" << "virtual void Set" << it->GetFriendlyName() << "("; - - if (it->Type == "bool" || it->Type == "double" || it->Type == "int") - std::cout << it->Type; - else - std::cout << "const " << it->Type << "&"; - - std::cout << " value)" << std::endl; + << "\t" << "virtual void Set" << it->GetFriendlyName() << "(" << it->Type.GetArgumentType() << " value)" << std::endl; if (it->PureSetAccessor) { std::cout << " = 0;" << std::endl; @@ -519,26 +569,32 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) /* default */ for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { - std::string prot; + std::string realType = it->Type.GetRealType(); std::cout << "private:" << std::endl - << "\t" << it->Type << " GetDefault" << it->GetFriendlyName() << "(void) const" << std::endl + << "\t" << realType << " GetDefault" << it->GetFriendlyName() << "(void) const" << std::endl << "\t" << "{" << std::endl; if (it->DefaultAccessor.empty()) - std::cout << "\t\t" << "return " << it->Type << "();" << std::endl; + std::cout << "\t\t" << "return " << realType << "();" << std::endl; else std::cout << it->DefaultAccessor << std::endl; std::cout << "\t" << "}" << std::endl; } + /* validators */ + for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { + std::cout << "protected:" << std::endl + << "\t" << "virtual void Validate" << it->GetFriendlyName() << "(" << it->Type.GetArgumentType() << " value, const ValidationUtils& utils);" << std::endl; + } + /* instance variables */ std::cout << "private:" << std::endl; for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { if (!(it->Attributes & FANoStorage)) - std::cout << "\t" << it->Type << " m_" << it->GetFriendlyName() << ";" << std::endl; + std::cout << "\t" << it->Type.GetRealType() << " m_" << it->GetFriendlyName() << ";" << std::endl; } } @@ -549,6 +605,286 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) std::cout << "\t" << "friend class " << klass.TypeBase << ";" << std::endl; std::cout << "};" << std::endl << std::endl; + + for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { + m_MissingValidators[std::make_pair(klass.Name, it->GetFriendlyName())] = *it; + } +} + +enum ValidatorType +{ + ValidatorField, + ValidatorArray, + ValidatorDictionary +}; + +static void CodeGenValidator(const std::string& name, const std::string& klass, const std::vector& rules, const std::string& field, const FieldType& fieldType, ValidatorType validatorType) +{ + std::cout << "inline void TIValidate" << name << "(const intrusive_ptr >& object, "; + + if (validatorType != ValidatorField) + std::cout << "const String& key, "; + + bool static_known_attribute = false; + + std::cout << fieldType.GetArgumentType() << " value, std::vector& location, const ValidationUtils& utils)" << std::endl + << "{" << std::endl; + + if (validatorType == ValidatorField) { + static_known_attribute = true; + + bool required = false; + + for (std::vector::size_type i = 0; i < rules.size(); i++) { + const Rule& rule = rules[i]; + + if ((rule.Attributes & RARequired) && rule.Pattern == field) { + required = true; + break; + } + } + + if (fieldType.GetRealType() != "int" && fieldType.GetRealType() != "double") { + if (fieldType.GetRealType() == "Value" || fieldType.GetRealType() == "String") + std::cout << "\t" << "if (value.IsEmpty())" << std::endl; + else + std::cout << "\t" << "if (!value)" << std::endl; + + if (required) + std::cout << "BOOST_THROW_EXCEPTION(ValidationError(this, location, \"This attribute must not be empty.\"));" << std::endl; + else + std::cout << "\t\t" << "return;" << std::endl; + + std::cout << std::endl; + } + } + + if (!static_known_attribute) + std::cout << "\t" << "bool known_attribute = false;" << std::endl; + + bool type_check = false; + + for (std::vector::size_type i = 0; i < rules.size(); i++) { + const Rule& rule = rules[i]; + + if (rule.Attributes & RARequired) + continue; + + if (validatorType == ValidatorField && rule.Pattern != field) + continue; + + std::cout << "\t" << "do {" << std::endl; + + if (validatorType != ValidatorField) { + if (rule.Pattern != "*") { + if (rule.Pattern.find_first_of("*?") != std::string::npos) + std::cout << "\t\t" << "if (!Utility::Match(\"" << rule.Pattern << "\", key))" << std::endl; + else + std::cout << "\t\t" << "if (key != \"" << rule.Pattern << "\")" << std::endl; + + std::cout << "\t\t\t" << "break;" << std::endl; + } else + static_known_attribute = true; + + if (!static_known_attribute) + std::cout << "\t\t" << "known_attribute = true;" << std::endl; + } + + if (rule.IsName) { + std::cout << "\t\t" << "if (value.IsScalar()) {" << std::endl + << "\t\t\t" << "if (utils.ValidateName(\"" << rule.Type << "\", value))" << std::endl + << "\t\t\t\t" << "return;" << std::endl + << "\t\t\t" << "else" << std::endl + << "\t\t\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(object, location, \"Object '\" + value + \"' of type '" << rule.Type << "' does not exist.\"));" << std::endl + << "\t\t" << "}" << std::endl; + } + + if (fieldType.GetRealType() == "Value") { + if (rule.Type == "String") + std::cout << "\t\t" << "if (value.IsScalar())" << std::endl + << "\t\t\t" << "return;" << std::endl; + else if (rule.Type == "Number") { + std::cout << "\t\t" << "try {" << std::endl + << "\t\t\t" << "Convert::ToDouble(value);" << std::endl + << "\t\t\t" << "return;" << std::endl + << "\t\t" << "} catch (...) { }" << std::endl; + } + } + + if (rule.Type == "Dictionary" || rule.Type == "Array" || rule.Type == "Function") { + if (fieldType.GetRealType() == "Value") { + std::cout << "\t\t" << "if (value.IsObjectType<" << rule.Type << ">()) {" << std::endl; + type_check = true; + } else if (fieldType.GetRealType() != rule.Type + "::Ptr") { + std::cout << "\t\t" << "if (dynamic_pointer_cast<" << rule.Type << ">(value)) {" << std::endl; + type_check = true; + } + + if (!rule.Rules.empty()) { + bool indent = false; + + if (rule.Type == "Dictionary") { + if (type_check) + std::cout << "\t\t\t" << "Dictionary::Ptr dict = value;" << std::endl; + else + std::cout << "\t\t" << "const Dictionary::Ptr& dict = value;" << std::endl; + + std::cout << (type_check ? "\t" : "") << "\t\t" << "ObjectLock olock(dict);" << std::endl + << (type_check ? "\t" : "") << "\t\t" << "BOOST_FOREACH(const Dictionary::Pair& kv, dict) {" << std::endl + << (type_check ? "\t" : "") << "\t\t\t" << "const String& akey = kv.first;" << std::endl + << (type_check ? "\t" : "") << "\t\t\t" << "const Value& avalue = kv.second;" << std::endl; + indent = true; + } else if (rule.Type == "Array") { + if (type_check) + std::cout << "\t\t\t" << "Array::Ptr arr = value;" << std::endl; + else + std::cout << "\t\t" << "const Array::Ptr& arr = value;" << std::endl; + + std::cout << (type_check ? "\t" : "") << "\t\t" << "Array::SizeType anum = 0;" << std::endl + << (type_check ? "\t" : "") << "\t\t" << "ObjectLock olock(arr);" << std::endl + << (type_check ? "\t" : "") << "\t\t" << "BOOST_FOREACH(const Value& avalue, arr) {" << std::endl + << (type_check ? "\t" : "") << "\t\t\t" << "String akey = Convert::ToString(anum);" << std::endl; + indent = true; + } else { + std::cout << (type_check ? "\t" : "") << "\t\t" << "String akey = \"\";" << std::endl + << (type_check ? "\t" : "") << "\t\t" << "const Value& avalue = value;" << std::endl; + } + + std::string subvalidator_prefix; + + if (validatorType == ValidatorField) + subvalidator_prefix = klass; + else + subvalidator_prefix = name; + + std::cout << (type_check ? "\t" : "") << (indent ? "\t" : "") << "\t\t" << "location.push_back(akey);" << std::endl + << (type_check ? "\t" : "") << (indent ? "\t" : "") << "\t\t" << "TIValidate" << subvalidator_prefix << "_" << i << "(object, akey, avalue, location, utils);" << std::endl; + + if (rule.Type == "Array") + std::cout << (type_check ? "\t" : "") << "\t\t\t" << "anum++;" << std::endl; + + if (rule.Type == "Dictionary" || rule.Type == "Array") + std::cout << (type_check ? "\t" : "") << "\t\t" << "}" << std::endl; + + for (std::vector::size_type i = 0; i < rule.Rules.size(); i++) { + const Rule& srule = rule.Rules[i]; + + if ((srule.Attributes & RARequired) == 0) + continue; + + if (rule.Type == "Dictionary") { + std::cout << (type_check ? "\t" : "") << "\t\t" << "if (dict.Get(\"" << srule.Pattern << "\").IsEmpty())" << std::endl + << (type_check ? "\t" : "") << "\t\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(this, location, \"Required dictionary item '" << srule.Pattern << "' is not set.\"));" << std::endl; + } else if (rule.Type == "Array") { + int index = -1; + std::stringstream idxbuf; + idxbuf << srule.Pattern; + idxbuf >> index; + + if (index == -1) { + std::cerr << "Invalid index for 'required' keyword: " << srule.Pattern; + std::exit(1); + } + + std::cout << (type_check ? "\t" : "") << "\t\t" << "if (arr.GetLength() < " << (index + 1) << ")" << std::endl + << (type_check ? "\t" : "") << "\t\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(this, location, \"Required index '" << index << "' is not set.\"));" << std::endl; + } + } + + std::cout << (type_check ? "\t" : "") << (indent ? "\t" : "") << "\t\t" << "location.pop_back();" << std::endl; + } + + std::cout << (type_check ? "\t" : "") << "\t\t" << "return;" << std::endl; + + if (fieldType.GetRealType() == "Value" || fieldType.GetRealType() != rule.Type + "::Ptr") + std::cout << "\t\t" << "}" << std::endl; + } + + std::cout << "\t" << "} while (0);" << std::endl << std::endl; + } + + if (type_check || validatorType != ValidatorField) { + if (!static_known_attribute) + std::cout << "\t" << "if (!known_attribute)" << std::endl + << "\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(object, location, \"Invalid attribute: \" + key));" << std::endl + << "\t" << "else" << std::endl; + + std::cout << (!static_known_attribute ? "\t" : "") << "\t" << "BOOST_THROW_EXCEPTION(ValidationError(object, boost::assign::list_of("; + + if (validatorType == ValidatorField) + std::cout << "\"" << field << "\""; + else + std::cout << "key"; + + std::cout << "), \"Invalid type.\"));" << std::endl; + } + + std::cout << "}" << std::endl << std::endl; +} + +static void CodeGenValidatorSubrules(const std::string& name, const std::string& klass, const std::vector& rules) +{ + for (std::vector::size_type i = 0; i < rules.size(); i++) { + const Rule& rule = rules[i]; + + if (rule.Attributes & RARequired) + continue; + + if (!rule.Rules.empty()) { + ValidatorType subtype; + + if (rule.Type == "Array") + subtype = ValidatorArray; + else if (rule.Type == "Dictionary") + subtype = ValidatorDictionary; + else { + std::cerr << "Invalid sub-validator type: " << rule.Type << std::endl; + std::exit(EXIT_FAILURE); + } + + std::ostringstream namebuf; + namebuf << name << "_" << i; + + CodeGenValidatorSubrules(namebuf.str(), klass, rule.Rules); + + FieldType ftype; + ftype.IsName = false; + ftype.TypeName = "Value"; + CodeGenValidator(namebuf.str(), klass, rule.Rules, rule.Pattern, ftype, subtype); + } + } +} + +void ClassCompiler::HandleValidator(const Validator& validator, const ClassDebugInfo&) +{ + CodeGenValidatorSubrules(validator.Name, validator.Name, validator.Rules); + + for (std::map, Field>::const_iterator it = m_MissingValidators.begin(); it != m_MissingValidators.end(); it++) + CodeGenValidator(it->first.first + it->first.second, it->first.first, validator.Rules, it->second.Name, it->second.Type, ValidatorField); + + for (std::map, Field>::const_iterator it = m_MissingValidators.begin(); it != m_MissingValidators.end(); it++) { + std::cout << "inline void ObjectImpl<" << it->first.first << ">::Validate" << it->first.second << "(" << it->second.Type.GetArgumentType() << " value, const ValidationUtils& utils)" << std::endl + << "{" << std::endl + << "\t" << "SimpleValidate" << it->first.second << "(value, utils);" << std::endl + << "\t" << "std::vector location;" << std::endl + << "\t" << "location.push_back(\"" << it->second.Name << "\");" << std::endl + << "\t" << "TIValidate" << it->first.first << it->first.second << "(this, value, location, utils);" << std::endl + << "}" << std::endl << std::endl; + } + + m_MissingValidators.clear(); +} + +void ClassCompiler::HandleMissingValidators(void) +{ + for (std::map, Field>::const_iterator it = m_MissingValidators.begin(); it != m_MissingValidators.end(); it++) { + std::cout << "inline void ObjectImpl<" << it->first.first << ">::Validate" << it->first.second << "(" << it->second.Type.GetArgumentType() << " value, const ValidationUtils& utils)" << std::endl + << "{" << std::endl + << "\t" << "SimpleValidate" << it->first.second << "(value, utils);" << std::endl + << "}" << std::endl << std::endl; + } + + m_MissingValidators.clear(); } void ClassCompiler::CompileFile(const std::string& path) @@ -562,7 +898,7 @@ void ClassCompiler::CompileFile(const std::string& path) return CompileStream(path, &stream); } -std::string ClassCompiler::BaseName(const std::string& path) +std::string ClassCompiler::BaseName(const std::string& path) { char *dir = strdup(path.c_str()); std::string result; @@ -610,7 +946,12 @@ void ClassCompiler::CompileStream(const std::string& path, std::istream *stream) << "#include \"base/value.hpp\"" << std::endl << "#include \"base/array.hpp\"" << std::endl << "#include \"base/dictionary.hpp\"" << std::endl + << "#include \"base/convert.hpp\"" << std::endl + << "#include \"base/exception.hpp\"" << std::endl + << "#include \"base/objectlock.hpp\"" << std::endl << "#include \"base/utility.hpp\"" << std::endl << std::endl + << "#include " << std::endl + << "#include " << std::endl << "#ifdef _MSC_VER" << std::endl << "#pragma warning( push )" << std::endl << "#pragma warning( disable : 4244 )" << std::endl diff --git a/tools/mkclass/classcompiler.hpp b/tools/mkclass/classcompiler.hpp index bb53c745a..697f32f49 100644 --- a/tools/mkclass/classcompiler.hpp +++ b/tools/mkclass/classcompiler.hpp @@ -24,6 +24,7 @@ #include #include #include +#include namespace icinga { @@ -66,13 +67,38 @@ enum FieldAttribute FASetProtected = 32, FAInternal = 64, FANoStorage = 128, - FALoadDependency = 256 + FALoadDependency = 256, + FARequired = 512 +}; + +struct FieldType +{ + bool IsName; + std::string TypeName; + + inline std::string GetRealType(void) const + { + if (IsName) + return "String"; + else + return TypeName; + } + + inline std::string GetArgumentType(void) const + { + std::string realType = GetRealType(); + + if (realType == "bool" || realType == "double" || realType == "int") + return realType; + else + return "const " + realType + "&"; + } }; struct Field { int Attributes; - std::string Type; + FieldType Type; std::string Name; std::string AlternativeName; std::string GetAccessor; @@ -85,7 +111,7 @@ struct Field : Attributes(0), PureGetAccessor(false), PureSetAccessor(false) { } - std::string GetFriendlyName(void) const + inline std::string GetFriendlyName(void) const { if (!AlternativeName.empty()) return AlternativeName; @@ -130,6 +156,27 @@ struct Klass std::vector LoadDependencies; }; +enum RuleAttribute +{ + RARequired = 1 +}; + +struct Rule +{ + int Attributes; + bool IsName; + std::string Type; + std::string Pattern; + + std::vector Rules; +}; + +struct Validator +{ + std::string Name; + std::vector Rules; +}; + class ClassCompiler { public: @@ -150,9 +197,11 @@ public: void HandleInclude(const std::string& path, const ClassDebugInfo& locp); void HandleAngleInclude(const std::string& path, const ClassDebugInfo& locp); void HandleClass(const Klass& klass, const ClassDebugInfo& locp); + void HandleValidator(const Validator& validator, const ClassDebugInfo& locp); void HandleNamespaceBegin(const std::string& name, const ClassDebugInfo& locp); void HandleNamespaceEnd(const ClassDebugInfo& locp); void HandleCode(const std::string& code, const ClassDebugInfo& locp); + void HandleMissingValidators(void); static void CompileFile(const std::string& path); static void CompileStream(const std::string& path, std::istream *stream); @@ -164,6 +213,8 @@ private: std::istream *m_Input; void *m_Scanner; + std::map, Field> m_MissingValidators; + static unsigned long SDBM(const std::string& str, size_t len); static std::string BaseName(const std::string& path); static std::string FileNameToGuardName(const std::string& path);