icinga2/lib/cli/daemonutility.cpp

200 lines
7.1 KiB
C++

/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cli/daemonutility.hpp"
#include "base/utility.hpp"
#include "base/logger.hpp"
#include "base/application.hpp"
#include "base/scriptglobal.hpp"
#include "config/configcompiler.hpp"
#include "config/configcompilercontext.hpp"
#include "config/configitembuilder.hpp"
using namespace icinga;
static bool ExecuteExpression(Expression *expression)
{
if (!expression)
return false;
try {
ScriptFrame frame(true);
expression->Evaluate(frame);
} catch (const std::exception& ex) {
Log(LogCritical, "config", DiagnosticInformation(ex));
return false;
}
return true;
}
static void IncludeZoneDirRecursive(const String& path, const String& package, bool& success)
{
String zoneName = Utility::BaseName(path);
/* register this zone path for cluster config sync */
ConfigCompiler::RegisterZoneDir("_etc", path, zoneName);
std::vector<std::unique_ptr<Expression> > expressions;
Utility::GlobRecursive(path, "*.conf", std::bind(&ConfigCompiler::CollectIncludes, std::ref(expressions), _1, zoneName, package), GlobFile);
DictExpression expr(std::move(expressions));
if (!ExecuteExpression(&expr))
success = false;
}
static void IncludeNonLocalZone(const String& zonePath, const String& package, bool& success)
{
/* Note: This include function must not call RegisterZoneDir().
* We do not need to copy it for cluster config sync. */
String zoneName = Utility::BaseName(zonePath);
/* Check whether this node already has an authoritative config version
* from zones.d in etc or api package directory, or a local marker file)
*/
if (ConfigCompiler::HasZoneConfigAuthority(zoneName) || Utility::PathExists(zonePath + "/.authoritative")) {
Log(LogNotice, "config")
<< "Ignoring non local config include for zone '" << zoneName << "': We already have an authoritative copy included.";
return;
}
std::vector<std::unique_ptr<Expression> > expressions;
Utility::GlobRecursive(zonePath, "*.conf", std::bind(&ConfigCompiler::CollectIncludes, std::ref(expressions), _1, zoneName, package), GlobFile);
DictExpression expr(std::move(expressions));
if (!ExecuteExpression(&expr))
success = false;
}
static void IncludePackage(const String& packagePath, bool& success)
{
/* Note: Package includes will register their zones
* for config sync inside their generated config. */
String packageName = Utility::BaseName(packagePath);
if (Utility::PathExists(packagePath + "/include.conf")) {
std::unique_ptr<Expression> expr = ConfigCompiler::CompileFile(packagePath + "/include.conf",
String(), packageName);
if (!ExecuteExpression(&*expr))
success = false;
}
}
bool DaemonUtility::ValidateConfigFiles(const std::vector<std::string>& configs, const String& objectsFile)
{
bool success;
if (!objectsFile.IsEmpty())
ConfigCompilerContext::GetInstance()->OpenObjectsFile(objectsFile);
if (!configs.empty()) {
for (const String& configPath : configs) {
try {
std::unique_ptr<Expression> expression = ConfigCompiler::CompileFile(configPath, String(), "_etc");
success = ExecuteExpression(&*expression);
if (!success)
return false;
} catch (const std::exception& ex) {
Log(LogCritical, "cli", "Could not compile config files: " + DiagnosticInformation(ex, false));
Application::Exit(1);
}
}
}
/* Load cluster config files from /etc/icinga2/zones.d.
* This should probably be in libremote but
* unfortunately moving it there is somewhat non-trivial. */
success = true;
String zonesEtcDir = Configuration::ZonesDir;
if (!zonesEtcDir.IsEmpty() && Utility::PathExists(zonesEtcDir))
Utility::Glob(zonesEtcDir + "/*", std::bind(&IncludeZoneDirRecursive, _1, "_etc", std::ref(success)), GlobDirectory);
if (!success)
return false;
/* Load package config files - they may contain additional zones which
* are authoritative on this node and are checked in HasZoneConfigAuthority(). */
String packagesVarDir = Configuration::DataDir + "/api/packages";
if (Utility::PathExists(packagesVarDir))
Utility::Glob(packagesVarDir + "/*", std::bind(&IncludePackage, _1, std::ref(success)), GlobDirectory);
if (!success)
return false;
/* Load cluster synchronized configuration files */
String zonesVarDir = Configuration::DataDir + "/api/zones";
if (Utility::PathExists(zonesVarDir))
Utility::Glob(zonesVarDir + "/*", std::bind(&IncludeNonLocalZone, _1, "_cluster", std::ref(success)), GlobDirectory);
if (!success)
return false;
Namespace::Ptr systemNS = ScriptGlobal::Get("System");
VERIFY(systemNS);
/* This is initialized inside the IcingaApplication class. */
Value vAppType;
VERIFY(systemNS->Get("ApplicationType", &vAppType));
Type::Ptr appType = Type::GetByName(vAppType);
if (ConfigItem::GetItems(appType).empty()) {
ConfigItemBuilder builder;
builder.SetType(appType);
builder.SetName("app");
builder.AddExpression(new ImportDefaultTemplatesExpression());
ConfigItem::Ptr item = builder.Compile();
item->Register();
}
return true;
}
bool DaemonUtility::LoadConfigFiles(const std::vector<std::string>& configs,
std::vector<ConfigItem::Ptr>& newItems,
const String& objectsFile, const String& varsfile)
{
ActivationScope ascope;
if (!DaemonUtility::ValidateConfigFiles(configs, objectsFile)) {
ConfigCompilerContext::GetInstance()->CancelObjectsFile();
return false;
}
WorkQueue upq(25000, Configuration::Concurrency);
upq.SetName("DaemonUtility::LoadConfigFiles");
bool result = ConfigItem::CommitItems(ascope.GetContext(), upq, newItems);
if (!result) {
ConfigCompilerContext::GetInstance()->CancelObjectsFile();
return false;
}
ConfigCompilerContext::GetInstance()->FinishObjectsFile();
try {
ScriptGlobal::WriteToFile(varsfile);
} catch (const std::exception& ex) {
Log(LogCritical, "cli", "Could not write vars file: " + DiagnosticInformation(ex, false));
Application::Exit(1);
}
return true;
}