]> granicus.if.org Git - icinga2/commitdiff
Ensure that runtime config objects are persisted on disk
authorGunnar Beutner <gunnar@beutner.name>
Tue, 18 Aug 2015 12:21:55 +0000 (14:21 +0200)
committerGunnar Beutner <gunnar@beutner.name>
Tue, 18 Aug 2015 12:21:55 +0000 (14:21 +0200)
refs #9101
fixes #9926

lib/remote/CMakeLists.txt
lib/remote/configmoduleutility.cpp
lib/remote/configmoduleutility.hpp
lib/remote/configobjectutility.cpp [new file with mode: 0644]
lib/remote/configobjectutility.hpp [new file with mode: 0644]
lib/remote/createobjecthandler.cpp
lib/remote/deleteobjecthandler.cpp

index 1e3b3cbafc2a83dc1a7ab27f11777f0151e07281..4b28dd4fd8af78327d874991c97079ead9a59ba8 100644 (file)
@@ -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
index 2f0eb0e25f2165932da6219e29fcdf61666bdf58..15b1571285542b70489d9bbde44fb0c1b0bd23cb 100644 (file)
@@ -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) {
index 99f446894fe37aaf168230b523358f57593b7f87..5370f562db2d3247c841aec0d77eafeccbada41d 100644 (file)
@@ -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);
diff --git a/lib/remote/configobjectutility.cpp b/lib/remote/configobjectutility.cpp
new file mode 100644 (file)
index 0000000..b4e097f
--- /dev/null
@@ -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;
+}
+       
\ No newline at end of file
diff --git a/lib/remote/configobjectutility.hpp b/lib/remote/configobjectutility.hpp
new file mode 100644 (file)
index 0000000..4760540
--- /dev/null
@@ -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 */
index 4569357814692d95ace1606e1d393c22d91994dd..28fdc4385aa5cd15e0fd81f7291ad024c70bb73b 100644 (file)
  ******************************************************************************/
 
 #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);
index 92b6aa079381e65cd507c51cb00fc144951ad3c8..232e0de8ab6b8c746816de7b5773468ce183a59d 100644 (file)
@@ -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));
                }
        }