From: Gunnar Beutner Date: Tue, 18 Aug 2015 12:21:55 +0000 (+0200) Subject: Ensure that runtime config objects are persisted on disk X-Git-Tag: v2.4.0~399 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0cbcb75e792bbbadf5dc1b947714dce56b000104;p=icinga2 Ensure that runtime config objects are persisted on disk refs #9101 fixes #9926 --- diff --git a/lib/remote/CMakeLists.txt b/lib/remote/CMakeLists.txt index 1e3b3cbaf..4b28dd4fd 100644 --- a/lib/remote/CMakeLists.txt +++ b/lib/remote/CMakeLists.txt @@ -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 diff --git a/lib/remote/configmoduleutility.cpp b/lib/remote/configmoduleutility.cpp index 2f0eb0e25..15b157128 100644 --- a/lib/remote/configmoduleutility.cpp +++ b/lib/remote/configmoduleutility.cpp @@ -70,6 +70,11 @@ void ConfigModuleUtility::CollectDirNames(const String& path, std::vector 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 GetStages(const String& moduleName); static String GetActiveStage(const String& moduleName); diff --git a/lib/remote/configobjectutility.cpp b/lib/remote/configobjectutility.cpp new file mode 100644 index 000000000..b4e097fc9 --- /dev/null +++ b/lib/remote/configobjectutility.cpp @@ -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 + +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(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; +} + \ No newline at end of file diff --git a/lib/remote/configobjectutility.hpp b/lib/remote/configobjectutility.hpp new file mode 100644 index 000000000..476054052 --- /dev/null +++ b/lib/remote/configobjectutility.hpp @@ -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 */ diff --git a/lib/remote/createobjecthandler.cpp b/lib/remote/createobjecthandler.cpp index 456935781..28fdc4385 100644 --- a/lib/remote/createobjecthandler.cpp +++ b/lib/remote/createobjecthandler.cpp @@ -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 #include @@ -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(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); diff --git a/lib/remote/deleteobjecthandler.cpp b/lib/remote/deleteobjecthandler.cpp index 92b6aa079..232e0de8a 100644 --- a/lib/remote/deleteobjecthandler.cpp +++ b/lib/remote/deleteobjecthandler.cpp @@ -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)); } }