1
0
mirror of https://github.com/Icinga/icinga2.git synced 2025-04-08 17:05:25 +02:00

Ensure that runtime config objects are persisted on disk

refs 
fixes 
This commit is contained in:
Gunnar Beutner 2015-08-18 14:21:55 +02:00
parent d8cab2f0e8
commit 0cbcb75e79
7 changed files with 273 additions and 108 deletions

@ -23,8 +23,9 @@ mkclass_target(zone.ti zone.tcpp zone.thpp)
set(remote_SOURCES
actionshandler.cpp apiaction.cpp
apifunction.cpp apilistener.cpp apilistener.thpp apilistener-sync.cpp
apiuser.cpp apiuser.thpp authority.cpp base64.cpp createobjecthandler.cpp deleteobjecthandler.cpp
configfileshandler.cpp configmoduleshandler.cpp configmoduleutility.cpp configstageshandler.cpp
apiuser.cpp apiuser.thpp authority.cpp base64.cpp
configfileshandler.cpp configmoduleshandler.cpp configmoduleutility.cpp configobjectutility.cpp
configstageshandler.cpp createobjecthandler.cpp deleteobjecthandler.cpp
endpoint.cpp endpoint.thpp filterutility.cpp
httpchunkedencoding.cpp httpconnection.cpp httphandler.cpp httprequest.cpp httpresponse.cpp
httputility.cpp jsonrpc.cpp jsonrpcconnection.cpp jsonrpcconnection-heartbeat.cpp

@ -70,6 +70,11 @@ void ConfigModuleUtility::CollectDirNames(const String& path, std::vector<String
dirs.push_back(name);
}
bool ConfigModuleUtility::ModuleExists(const String& name)
{
return Utility::PathExists(GetModuleDir() + "/" + name);
}
String ConfigModuleUtility::CreateStage(const String& moduleName, const Dictionary::Ptr& files)
{
String stageName = Utility::NewUniqueID();
@ -87,23 +92,26 @@ String ConfigModuleUtility::CreateStage(const String& moduleName, const Dictiona
WriteStageConfig(moduleName, stageName);
bool foundDotDot = false;
ObjectLock olock(files);
BOOST_FOREACH(const Dictionary::Pair& kv, files) {
if (ContainsDotDot(kv.first)) {
foundDotDot = true;
break;
if (files) {
ObjectLock olock(files);
BOOST_FOREACH(const Dictionary::Pair& kv, files) {
if (ContainsDotDot(kv.first)) {
foundDotDot = true;
break;
}
String filePath = path + "/" + kv.first;
Log(LogInformation, "ConfigModuleUtility")
<< "Updating configuration file: " << filePath;
//pass the directory and generate a dir tree, if not existing already
Utility::MkDirP(Utility::DirName(filePath), 0750);
std::ofstream fp(filePath.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc);
fp << kv.second;
fp.close();
}
String filePath = path + "/" + kv.first;
Log(LogInformation, "ConfigModuleUtility")
<< "Updating configuration file: " << filePath;
//pass the directory and generate a dir tree, if not existing already
Utility::MkDirP(Utility::DirName(filePath), 0750);
std::ofstream fp(filePath.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc);
fp << kv.second;
fp.close();
}
if (foundDotDot) {

@ -44,8 +44,9 @@ public:
static void CreateModule(const String& name);
static void DeleteModule(const String& name);
static std::vector<String> GetModules(void);
static bool ModuleExists(const String& name);
static String CreateStage(const String& moduleName, const Dictionary::Ptr& files);
static String CreateStage(const String& moduleName, const Dictionary::Ptr& files = Dictionary::Ptr());
static void DeleteStage(const String& moduleName, const String& stageName);
static std::vector<String> GetStages(const String& moduleName);
static String GetActiveStage(const String& moduleName);

@ -0,0 +1,174 @@
/******************************************************************************
* 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 "remote/configobjectutility.hpp"
#include "remote/configmoduleutility.hpp"
#include "config/configitembuilder.hpp"
#include "config/configitem.hpp"
#include "config/configwriter.hpp"
#include "base/exception.hpp"
#include "base/serializer.hpp"
#include <boost/algorithm/string/case_conv.hpp>
using namespace icinga;
String ConfigObjectUtility::GetConfigDir(void)
{
return ConfigModuleUtility::GetModuleDir() + "/_api/" +
ConfigModuleUtility::GetActiveStage("_api");
}
String ConfigObjectUtility::EscapeName(const String& name)
{
return Utility::EscapeString(name, "<>:\"/\\|?*", true);
}
bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& fullName,
const Array::Ptr& templates, const Dictionary::Ptr& attrs, const Array::Ptr& errors)
{
NameComposer *nc = dynamic_cast<NameComposer *>(type.get());
Dictionary::Ptr nameParts;
String name;
if (nc) {
nameParts = nc->ParseName(fullName);
name = nameParts->Get("name");
} else
name = fullName;
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder();
builder->SetType(type->GetName());
builder->SetName(name);
builder->SetScope(ScriptGlobal::GetGlobals());
builder->SetModule("_api");
if (templates) {
ObjectLock olock(templates);
BOOST_FOREACH(const String& tmpl, templates) {
ImportExpression *expr = new ImportExpression(MakeLiteral(tmpl));
builder->AddExpression(expr);
}
}
if (nameParts) {
ObjectLock olock(nameParts);
BOOST_FOREACH(const Dictionary::Pair& kv, nameParts) {
SetExpression *expr = new SetExpression(MakeIndexer(ScopeThis, kv.first), OpSetLiteral, MakeLiteral(kv.second));
builder->AddExpression(expr);
}
}
if (attrs) {
ObjectLock olock(attrs);
BOOST_FOREACH(const Dictionary::Pair& kv, attrs) {
SetExpression *expr = new SetExpression(MakeIndexer(ScopeThis, kv.first), OpSetLiteral, MakeLiteral(kv.second));
builder->AddExpression(expr);
}
}
try {
ConfigItem::Ptr item = builder->Compile();
item->Register();
WorkQueue upq;
if (!ConfigItem::CommitItems(upq) || !ConfigItem::ActivateItems(upq, false)) {
if (errors) {
BOOST_FOREACH(const boost::exception_ptr& ex, upq.GetExceptions()) {
errors->Add(DiagnosticInformation(ex));
}
}
return false;
}
} catch (const std::exception& ex) {
if (errors)
errors->Add(DiagnosticInformation(ex));
return false;
}
if (!ConfigModuleUtility::ModuleExists("_api")) {
ConfigModuleUtility::CreateModule("_api");
String stage = ConfigModuleUtility::CreateStage("_api");
ConfigModuleUtility::ActivateStage("_api", stage);
}
String typeDir = type->GetPluralName();
boost::algorithm::to_lower(typeDir);
String path = GetConfigDir() + "/conf.d/" + typeDir;
Utility::MkDirP(path, 0700);
path += "/" + EscapeName(fullName) + ".conf";
ConfigWriter::Ptr cw = new ConfigWriter(path);
cw->EmitConfigItem(type->GetName(), name, false, templates, attrs);
cw->EmitRaw("\n");
return true;
}
bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, const Array::Ptr& errors)
{
if (object->GetModule() != "_api") {
if (errors)
errors->Add("Object cannot be deleted because it was not created using the API.");
return false;
}
Type::Ptr type = object->GetReflectionType();
ConfigItem::Ptr item = ConfigItem::GetObject(type->GetName(), object->GetName());
try {
object->Deactivate();
if (item)
item->Unregister();
else
object->Unregister();
} catch (const std::exception& ex) {
if (errors)
errors->Add(DiagnosticInformation(ex));
return false;
}
String typeDir = type->GetPluralName();
boost::algorithm::to_lower(typeDir);
String path = GetConfigDir() + "/conf.d/" + typeDir +
"/" + EscapeName(object->GetName()) + ".conf";
if (Utility::PathExists(path)) {
if (unlink(path.CStr()) < 0) {
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("unlink")
<< boost::errinfo_errno(errno)
<< boost::errinfo_file_name(path));
}
}
return true;
}

@ -0,0 +1,55 @@
/******************************************************************************
* 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 CONFIGOBJECTUTILITY_H
#define CONFIGOBJECTUTILITY_H
#include "remote/i2-remote.hpp"
#include "base/array.hpp"
#include "base/configobject.hpp"
#include "base/dictionary.hpp"
#include "base/type.hpp"
namespace icinga
{
/**
* Helper functions.
*
* @ingroup remote
*/
class I2_REMOTE_API ConfigObjectUtility
{
public:
static String GetConfigDir(void);
static bool CreateObject(const Type::Ptr& type, const String& fullName,
const Array::Ptr& templates, const Dictionary::Ptr& attrs,
const Array::Ptr& errors);
static bool DeleteObject(const ConfigObject::Ptr& object, const Array::Ptr& errors);
private:
static String EscapeName(const String& name);
};
}
#endif /* CONFIGOBJECTUTILITY_H */

@ -18,13 +18,10 @@
******************************************************************************/
#include "remote/createobjecthandler.hpp"
#include "remote/configobjectutility.hpp"
#include "remote/httputility.hpp"
#include "remote/filterutility.hpp"
#include "remote/apiaction.hpp"
#include "config/configitembuilder.hpp"
#include "config/configitem.hpp"
#include "base/exception.hpp"
#include "base/serializer.hpp"
#include <boost/algorithm/string.hpp>
#include <set>
@ -46,81 +43,22 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
return false;
String name = request.RequestUrl->GetPath()[2];
NameComposer *nc = dynamic_cast<NameComposer *>(type.get());
Dictionary::Ptr nameParts;
if (nc) {
nameParts = nc->ParseName(name);
name = nameParts->Get("name");
}
Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder();
builder->SetType(type->GetName());
builder->SetName(name);
builder->SetScope(ScriptGlobal::GetGlobals());
builder->SetModule("_api");
Array::Ptr templates = params->Get("templates");
if (templates) {
ObjectLock olock(templates);
BOOST_FOREACH(const String& tmpl, templates) {
ImportExpression *expr = new ImportExpression(MakeLiteral(tmpl));
builder->AddExpression(expr);
}
}
if (nameParts) {
ObjectLock olock(nameParts);
BOOST_FOREACH(const Dictionary::Pair& kv, nameParts) {
SetExpression *expr = new SetExpression(MakeIndexer(ScopeThis, kv.first), OpSetLiteral, MakeLiteral(kv.second));
builder->AddExpression(expr);
}
}
Dictionary::Ptr attrs = params->Get("attrs");
if (attrs) {
ObjectLock olock(attrs);
BOOST_FOREACH(const Dictionary::Pair& kv, attrs) {
SetExpression *expr = new SetExpression(MakeIndexer(ScopeThis, kv.first), OpSetLiteral, MakeLiteral(kv.second));
builder->AddExpression(expr);
}
}
Dictionary::Ptr result1 = new Dictionary();
int code;
String status;
try {
ConfigItem::Ptr item = builder->Compile();
item->Register();
WorkQueue upq;
if (!ConfigItem::CommitItems(upq) || !ConfigItem::ActivateItems(upq, false)) {
code = 500;
status = "Object could not be created.";
Array::Ptr errors = new Array();
BOOST_FOREACH(const boost::exception_ptr& ex, upq.GetExceptions()) {
errors->Add(DiagnosticInformation(ex));
}
result1->Set("errors", errors);
} else {
code = 200;
status = "Object created";
}
} catch (const std::exception& ex) {
Array::Ptr errors = new Array();
if (!ConfigObjectUtility::CreateObject(type, name, templates, attrs, errors)) {
result1->Set("errors", errors);
code = 500;
status = "Object could not be created.";
Array::Ptr errors = new Array();
errors->Add(DiagnosticInformation(ex));
result1->Set("errors", errors);
} else {
code = 200;
status = "Object was created.";
}
result1->Set("code", code);

@ -18,6 +18,7 @@
******************************************************************************/
#include "remote/deleteobjecthandler.hpp"
#include "remote/configobjectutility.hpp"
#include "remote/httputility.hpp"
#include "remote/filterutility.hpp"
#include "remote/apiaction.hpp"
@ -67,27 +68,14 @@ bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
result1->Set("name", obj->GetName());
results->Add(result1);
if (obj->GetModule() != "_api") {
result1->Set("code", 500);
result1->Set("status", "Object cannot be deleted because it was not created using the API.");
continue;
}
ConfigItem::Ptr item = ConfigItem::GetObject(type->GetName(), obj->GetName());
try {
obj->Deactivate();
if (item)
item->Unregister();
else
obj->Unregister();
Array::Ptr errors = new Array();
if (!ConfigObjectUtility::DeleteObject(obj, errors)) {
result1->Set("code", 500);
result1->Set("status", "Object could not be deleted.");
} else {
result1->Set("code", 200);
result1->Set("status", "Object was deleted.");
} catch (const std::exception& ex) {
result1->Set("code", 500);
result1->Set("status", "Object could not be deleted: " + DiagnosticInformation(ex));
}
}