diff --git a/doc/19-language-reference.md b/doc/19-language-reference.md index 746704f22..a63b16e65 100644 --- a/doc/19-language-reference.md +++ b/doc/19-language-reference.md @@ -517,6 +517,29 @@ recursively included. The file names need to match the pattern given in the second parameter. When no pattern is specified the default pattern "*.conf" is used. +## Zone Includes + +The `include_zones` recursively includes all subdirectories for the +given path. + +In addition to that it sets the `zone` attribute for all objects created +in these subdirectories to the name of the subdirectory. + +Example: + + include_zones "etc", "zones.d", "*.conf" + include_zones "puppet", "puppet-zones" + +The first parameter specifies a tag name for this directive. Each `include_zones` +invocation should use a unique tag name. When copying the zones' configuration +files Icinga uses the tag name as the name for the destination directory in +`/var/lib/icinga2/api/config`. + +The second parameter specifies the directory which contains the subdirectories. + +The file names need to match the pattern given in the third parameter. +When no pattern is specified the default pattern "*.conf" is used. + ## Library directive The `library` directive can be used to manually load additional diff --git a/doc/4-configuring-icinga-2.md b/doc/4-configuring-icinga-2.md index f5686365e..a85d83e13 100644 --- a/doc/4-configuring-icinga-2.md +++ b/doc/4-configuring-icinga-2.md @@ -136,6 +136,16 @@ and their generated configuration described in You can put your own configuration files in the [conf.d](4-configuring-icinga-2.md#conf-d) directory. This directive makes sure that all of your own configuration files are included. + /** + * The zones.d directory contains configuration files for satellite + * instances. + */ + include_zones "etc", "zones.d" + +Configuration files for satellite instances are managed in 'zones'. This directive ensures +that all configuration files in the `zones.d` directory are included and that the `zones` +attribute for objects defined in this directory is set appropriately. + ### constants.conf The `constants.conf` configuration file can be used to define global constants. diff --git a/etc/icinga2/icinga2.conf b/etc/icinga2/icinga2.conf index 1d7335465..825ffe19b 100644 --- a/etc/icinga2/icinga2.conf +++ b/etc/icinga2/icinga2.conf @@ -49,3 +49,8 @@ include_recursive "repository.d" */ include_recursive "conf.d" +/** + * The zones.d directory contains configuration files for satellite + * instances. + */ +include_zones "etc", "zones.d" diff --git a/lib/cli/daemonutility.cpp b/lib/cli/daemonutility.cpp index deffb3caa..c09de0346 100644 --- a/lib/cli/daemonutility.cpp +++ b/lib/cli/daemonutility.cpp @@ -85,13 +85,6 @@ bool DaemonUtility::ValidateConfigFiles(const std::vector& configs, * unfortunately moving it there is somewhat non-trivial. */ success = true; - String zonesEtcDir = Application::GetZonesDir(); - if (!zonesEtcDir.IsEmpty() && Utility::PathExists(zonesEtcDir)) - Utility::Glob(zonesEtcDir + "/*", boost::bind(&IncludeZoneDirRecursive, _1, boost::ref(success)), GlobDirectory); - - if (!success) - return false; - String zonesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones"; if (Utility::PathExists(zonesVarDir)) Utility::Glob(zonesVarDir + "/*", boost::bind(&IncludeNonLocalZone, _1, boost::ref(success)), GlobDirectory); diff --git a/lib/config/config_lexer.ll b/lib/config/config_lexer.ll index dc0a7eed5..9455eb1b4 100644 --- a/lib/config/config_lexer.ll +++ b/lib/config/config_lexer.ll @@ -171,6 +171,7 @@ object return T_OBJECT; template return T_TEMPLATE; include return T_INCLUDE; include_recursive return T_INCLUDE_RECURSIVE; +include_zones return T_INCLUDE_ZONES; library return T_LIBRARY; null return T_NULL; true { yylval->boolean = 1; return T_BOOLEAN; } diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 1abcb16a4..2e63f6f56 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -149,6 +149,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig %token T_TEMPLATE "template (T_TEMPLATE)" %token T_INCLUDE "include (T_INCLUDE)" %token T_INCLUDE_RECURSIVE "include_recursive (T_INCLUDE_RECURSIVE)" +%token T_INCLUDE_ZONES "include_zones (T_INCLUDE_ZONES)" %token T_LIBRARY "library (T_LIBRARY)" %token T_INHERITS "inherits (T_INHERITS)" %token T_APPLY "apply (T_APPLY)" @@ -197,7 +198,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig %type object_declaration %right T_FOLLOWS -%right T_INCLUDE T_INCLUDE_RECURSIVE T_OBJECT T_TEMPLATE T_APPLY T_IMPORT T_ASSIGN T_IGNORE T_WHERE +%right T_INCLUDE T_INCLUDE_RECURSIVE T_INCLUDE_ZONES T_OBJECT T_TEMPLATE T_APPLY T_IMPORT T_ASSIGN T_IGNORE T_WHERE %right T_FUNCTION T_FOR %left T_SET T_SET_ADD T_SET_SUBTRACT T_SET_MULTIPLY T_SET_DIVIDE T_SET_MODULO T_SET_XOR T_SET_BINARY_AND T_SET_BINARY_OR %left T_LOGICAL_OR @@ -464,6 +465,14 @@ lterm: library free($2); free($4); } + | T_INCLUDE_ZONES T_STRING ',' T_STRING + { + $$ = context->HandleIncludeZones($2, $4, "*.conf", @$); + } + | T_INCLUDE_ZONES T_STRING ',' T_STRING ',' T_STRING + { + $$ = context->HandleIncludeZones($2, $4, $6, @$); + } | T_IMPORT rterm { $$ = new ImportExpression($2, @$); diff --git a/lib/config/configcompiler.cpp b/lib/config/configcompiler.cpp index 8fc48c64f..80c8850d9 100644 --- a/lib/config/configcompiler.cpp +++ b/lib/config/configcompiler.cpp @@ -30,6 +30,7 @@ using namespace icinga; std::vector ConfigCompiler::m_IncludeSearchDirs; +std::map > ConfigCompiler::m_ZoneDirs; /** * Constructor for the ConfigCompiler class. @@ -165,6 +166,47 @@ Expression *ConfigCompiler::HandleIncludeRecursive(const String& path, const Str return new DictExpression(expressions); } +void ConfigCompiler::HandleIncludeZone(const String& tag, const String& path, const String& pattern, std::vector& expressions) +{ + String zoneName = Utility::BaseName(path); + + String ppath; + + if (path.GetLength() > 0 && path[0] == '/') + ppath = path; + else + ppath = Utility::DirName(GetPath()) + "/" + path; + + ZoneFragment zf; + zf.Tag = tag; + zf.Path = ppath; + m_ZoneDirs[zoneName].push_back(zf); + + Utility::GlobRecursive(ppath, pattern, boost::bind(&ConfigCompiler::CollectIncludes, boost::ref(expressions), _1, zoneName), GlobFile); +} + +/** + * Handles zone includes. + * + * @param tag The tag name. + * @param path The directory path. + * @param pattern The file pattern. + * @param debuginfo Debug information. + */ +Expression *ConfigCompiler::HandleIncludeZones(const String& tag, const String& path, const String& pattern, const DebugInfo&) +{ + String ppath; + + if (path.GetLength() > 0 && path[0] == '/') + ppath = path; + else + ppath = Utility::DirName(GetPath()) + "/" + path; + + std::vector expressions; + Utility::Glob(ppath + "/*", boost::bind(&ConfigCompiler::HandleIncludeZone, this, tag, _1, pattern, boost::ref(expressions)), GlobDirectory); + return new DictExpression(expressions); +} + /** * Handles the library directive. * @@ -272,3 +314,12 @@ void ConfigCompiler::AddIncludeSearchDir(const String& dir) m_IncludeSearchDirs.push_back(dir); } +std::vector ConfigCompiler::GetZoneDirs(const String& zone) +{ + std::map >::const_iterator it; + it = m_ZoneDirs.find(zone); + if (it == m_ZoneDirs.end()) + return std::vector(); + else + return it->second; +} diff --git a/lib/config/configcompiler.hpp b/lib/config/configcompiler.hpp index f0a429b29..e4626758b 100644 --- a/lib/config/configcompiler.hpp +++ b/lib/config/configcompiler.hpp @@ -64,6 +64,12 @@ struct EItemInfo CompilerDebugInfo DebugInfo; }; +struct ZoneFragment +{ + String Tag; + String Path; +}; + /** * The configuration compiler can be used to compile a configuration file * into a number of configuration items. @@ -94,11 +100,14 @@ public: /* internally used methods */ Expression *HandleInclude(const String& include, bool search, const DebugInfo& debuginfo = DebugInfo()); Expression *HandleIncludeRecursive(const String& path, const String& pattern, const DebugInfo& debuginfo = DebugInfo()); + Expression *HandleIncludeZones(const String& tag, const String& path, const String& pattern, const DebugInfo& debuginfo = DebugInfo()); void HandleLibrary(const String& library); size_t ReadInput(char *buffer, size_t max_bytes); void *GetScanner(void) const; + static std::vector GetZoneDirs(const String& zone); + private: boost::promise > m_Promise; @@ -109,12 +118,15 @@ private: void *m_Scanner; static std::vector m_IncludeSearchDirs; + static std::map > m_ZoneDirs; void InitializeScanner(void); void DestroyScanner(void); void CompileHelper(void); + void HandleIncludeZone(const String& tag, const String& path, const String& pattern, std::vector& expressions); + public: bool m_Eof; int m_OpenBraces; diff --git a/lib/remote/apilistener-sync.cpp b/lib/remote/apilistener-sync.cpp index 21002109a..7bca5731c 100644 --- a/lib/remote/apilistener-sync.cpp +++ b/lib/remote/apilistener-sync.cpp @@ -19,6 +19,7 @@ #include "remote/apilistener.hpp" #include "remote/apifunction.hpp" +#include "config/configcompiler.hpp" #include "base/dynamictype.hpp" #include "base/logger.hpp" #include "base/convert.hpp" @@ -121,11 +122,20 @@ bool ApiListener::UpdateConfigDir(const Dictionary::Ptr& oldConfig, const Dictio void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const { - String newDir = Application::GetZonesDir() + "/" + zone->GetName(); + Dictionary::Ptr newConfig = new Dictionary(); + BOOST_FOREACH(const ZoneFragment& zf, ConfigCompiler::GetZoneDirs(zone->GetName())) { + Dictionary::Ptr newConfigPart = LoadConfigDir(zf.Path); + + ObjectLock olock(newConfigPart); + BOOST_FOREACH(const Dictionary::Pair& kv, newConfigPart) { + newConfig->Set(zf.Tag + "/" + kv.first, kv.second); + } + } + String oldDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones/" + zone->GetName(); Log(LogInformation, "ApiListener") - << "Copying zone configuration files from '" << newDir << "' to '" << oldDir << "'."; + << "Copying zone configuration files for zone '" << zone->GetName() << "' to '" << oldDir << "'."; if (!Utility::MkDir(oldDir, 0700)) { Log(LogCritical, "ApiListener") @@ -137,7 +147,6 @@ void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const << boost::errinfo_file_name(oldDir)); } - Dictionary::Ptr newConfig = LoadConfigDir(newDir); Dictionary::Ptr oldConfig = LoadConfigDir(oldDir); UpdateConfigDir(oldConfig, newConfig, oldDir, true);