mirror of https://github.com/Icinga/icinga2.git
Implement config file management for the API
refs #9083 fixes #9102 fixes #9103 fixes #9104 fixes #9705
This commit is contained in:
parent
43ff15cf86
commit
fca7a33aac
|
@ -136,16 +136,6 @@ 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.
|
||||
|
||||
### <a id="constants-conf"></a> constants.conf
|
||||
|
||||
The `constants.conf` configuration file can be used to define global constants.
|
||||
|
|
|
@ -48,9 +48,3 @@ include_recursive "repository.d"
|
|||
* directory. Each of these files must have the file extension ".conf".
|
||||
*/
|
||||
include_recursive "conf.d"
|
||||
|
||||
/**
|
||||
* The zones.d directory contains configuration files for satellite
|
||||
* instances.
|
||||
*/
|
||||
include_zones "etc", "zones.d"
|
||||
|
|
|
@ -581,6 +581,39 @@ bool Utility::MkDirP(const String& path, int flags)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void Utility::RemoveDirRecursive(const String& path)
|
||||
{
|
||||
std::vector<String> paths;
|
||||
Utility::GlobRecursive(path, "*", boost::bind(&Utility::CollectPaths, _1, boost::ref(paths)), GlobFile | GlobDirectory);
|
||||
|
||||
/* This relies on the fact that GlobRecursive lists the parent directory
|
||||
first before recursing into subdirectories. */
|
||||
std::reverse(paths.begin(), paths.end());
|
||||
|
||||
BOOST_FOREACH(const String& path, paths) {
|
||||
if (remove(path.CStr()) < 0)
|
||||
BOOST_THROW_EXCEPTION(posix_error()
|
||||
<< boost::errinfo_api_function("remove")
|
||||
<< boost::errinfo_errno(errno)
|
||||
<< boost::errinfo_file_name(path));
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
if (rmdir(path.CStr()) < 0)
|
||||
#else /* _WIN32 */
|
||||
if (_rmdir(path.CStr()) < 0)
|
||||
#endif /* _WIN32 */
|
||||
BOOST_THROW_EXCEPTION(posix_error()
|
||||
<< boost::errinfo_api_function("rmdir")
|
||||
<< boost::errinfo_errno(errno)
|
||||
<< boost::errinfo_file_name(path));
|
||||
}
|
||||
|
||||
void Utility::CollectPaths(const String& path, std::vector<String>& paths)
|
||||
{
|
||||
paths.push_back(path);
|
||||
}
|
||||
|
||||
void Utility::CopyFile(const String& source, const String& target)
|
||||
{
|
||||
std::ifstream ifs(source.CStr(), std::ios::binary);
|
||||
|
|
|
@ -123,6 +123,7 @@ public:
|
|||
|
||||
static bool PathExists(const String& path);
|
||||
|
||||
static void RemoveDirRecursive(const String& path);
|
||||
static void CopyFile(const String& source, const String& target);
|
||||
|
||||
static Value LoadJsonFile(const String& path);
|
||||
|
@ -130,10 +131,10 @@ public:
|
|||
|
||||
private:
|
||||
Utility(void);
|
||||
static void CollectPaths(const String& path, std::vector<String>& paths);
|
||||
|
||||
static boost::thread_specific_ptr<String> m_ThreadName;
|
||||
static boost::thread_specific_ptr<unsigned int> m_RandSeed;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -85,10 +85,29 @@ bool DaemonUtility::ValidateConfigFiles(const std::vector<std::string>& 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);
|
||||
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
String modulesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/modules";
|
||||
if (Utility::PathExists(modulesVarDir)) {
|
||||
std::vector<Expression *> expressions;
|
||||
Utility::Glob(modulesVarDir + "/*/include.conf", boost::bind(&ConfigCompiler::CollectIncludes, boost::ref(expressions), _1, ""), GlobFile);
|
||||
DictExpression expr(expressions);
|
||||
if (!ExecuteExpression(&expr))
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -22,9 +22,11 @@ mkclass_target(zone.ti zone.tcpp zone.thpp)
|
|||
|
||||
set(remote_SOURCES
|
||||
apifunction.cpp apilistener.cpp apilistener.thpp apilistener-sync.cpp
|
||||
apiuser.cpp apiuser.thpp authority.cpp base64.cpp endpoint.cpp endpoint.thpp
|
||||
apiuser.cpp apiuser.thpp authority.cpp base64.cpp configfileshandler.cpp
|
||||
configmoduleshandler.cpp configmoduleutility.cpp configstageshandler.cpp
|
||||
endpoint.cpp endpoint.thpp
|
||||
httpchunkedencoding.cpp httpconnection.cpp httpdemohandler.cpp httphandler.cpp httprequest.cpp httpresponse.cpp
|
||||
jsonrpc.cpp jsonrpcconnection.cpp jsonrpcconnection-heartbeat.cpp
|
||||
httputility.cpp jsonrpc.cpp jsonrpcconnection.cpp jsonrpcconnection-heartbeat.cpp
|
||||
messageorigin.cpp zone.cpp zone.thpp
|
||||
url.cpp
|
||||
)
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/******************************************************************************
|
||||
* 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/configfileshandler.hpp"
|
||||
#include "remote/configmoduleutility.hpp"
|
||||
#include "remote/httputility.hpp"
|
||||
#include "base/exception.hpp"
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <fstream>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
REGISTER_URLHANDLER("/v1/config/files", ConfigFilesHandler);
|
||||
|
||||
void ConfigFilesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
if (request.RequestMethod == "GET")
|
||||
HandleGet(user, request, response);
|
||||
else
|
||||
response.SetStatus(400, "Bad request");
|
||||
}
|
||||
|
||||
bool ConfigFilesHandler::CanAlsoHandleUrl(const Url::Ptr& url) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConfigFilesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
|
||||
|
||||
const std::vector<String>& urlPath = request.RequestUrl->GetPath();
|
||||
|
||||
if (urlPath.size() >= 4)
|
||||
params->Set("module", urlPath[3]);
|
||||
|
||||
if (urlPath.size() >= 5)
|
||||
params->Set("stage", urlPath[4]);
|
||||
|
||||
if (urlPath.size() >= 6) {
|
||||
std::vector<String> tmpPath(urlPath.begin() + 5, urlPath.end());
|
||||
params->Set("path", boost::algorithm::join(tmpPath, "/"));
|
||||
}
|
||||
|
||||
String moduleName = params->Get("module");
|
||||
String stageName = params->Get("stage");
|
||||
|
||||
if (!ConfigModuleUtility::ValidateName(moduleName) || !ConfigModuleUtility::ValidateName(stageName)) {
|
||||
response.SetStatus(403, "Forbidden");
|
||||
return;
|
||||
}
|
||||
|
||||
String relativePath = params->Get("path");
|
||||
|
||||
if (ConfigModuleUtility::ContainsDotDot(relativePath)) {
|
||||
response.SetStatus(403, "Forbidden");
|
||||
return;
|
||||
}
|
||||
|
||||
String path = ConfigModuleUtility::GetModuleDir() + "/" + moduleName + "/" + stageName + "/" + relativePath;
|
||||
|
||||
if (!Utility::PathExists(path)) {
|
||||
response.SetStatus(404, "File not found");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
std::ifstream fp(path.CStr(), std::ifstream::in | std::ifstream::binary);
|
||||
fp.exceptions(std::ifstream::badbit);
|
||||
|
||||
String content((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
|
||||
response.SetStatus(200, "OK");
|
||||
response.AddHeader("Content-Type", "application/octet-stream");
|
||||
response.WriteBody(content.CStr(), content.GetLength());
|
||||
} catch (const std::exception& ex) {
|
||||
response.SetStatus(503, "Could not read file");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/******************************************************************************
|
||||
* 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 CONFIGFILESHANDLER_H
|
||||
#define CONFIGFILESHANDLER_H
|
||||
|
||||
#include "remote/httphandler.hpp"
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
class I2_REMOTE_API ConfigFilesHandler : public HttpHandler
|
||||
{
|
||||
public:
|
||||
DECLARE_PTR_TYPEDEFS(ConfigFilesHandler);
|
||||
|
||||
virtual bool CanAlsoHandleUrl(const Url::Ptr& url) const;
|
||||
virtual void HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
|
||||
|
||||
private:
|
||||
void HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* CONFIGFILESHANDLER_H */
|
|
@ -0,0 +1,149 @@
|
|||
/******************************************************************************
|
||||
* 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/configmoduleshandler.hpp"
|
||||
#include "remote/configmoduleutility.hpp"
|
||||
#include "remote/httputility.hpp"
|
||||
#include "base/exception.hpp"
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
REGISTER_URLHANDLER("/v1/config/modules", ConfigModulesHandler);
|
||||
|
||||
void ConfigModulesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
if (request.RequestMethod == "GET")
|
||||
HandleGet(user, request, response);
|
||||
else if (request.RequestMethod == "POST")
|
||||
HandlePost(user, request, response);
|
||||
else if (request.RequestMethod == "DELETE")
|
||||
HandleDelete(user, request, response);
|
||||
else
|
||||
response.SetStatus(400, "Bad request");
|
||||
}
|
||||
|
||||
bool ConfigModulesHandler::CanAlsoHandleUrl(const Url::Ptr& url) const
|
||||
{
|
||||
if (url->GetPath().size() > 4)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConfigModulesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
std::vector<String> modules = ConfigModuleUtility::GetModules();
|
||||
|
||||
Array::Ptr results = new Array();
|
||||
|
||||
BOOST_FOREACH(const String& module, modules) {
|
||||
Dictionary::Ptr moduleInfo = new Dictionary();
|
||||
moduleInfo->Set("name", module);
|
||||
moduleInfo->Set("stages", Array::FromVector(ConfigModuleUtility::GetStages(module)));
|
||||
moduleInfo->Set("active-stage", ConfigModuleUtility::GetActiveStage(module));
|
||||
results->Add(moduleInfo);
|
||||
}
|
||||
|
||||
Dictionary::Ptr result = new Dictionary();
|
||||
result->Set("results", results);
|
||||
|
||||
response.SetStatus(200, "OK");
|
||||
HttpUtility::SendJsonBody(response, result);
|
||||
}
|
||||
|
||||
void ConfigModulesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
|
||||
|
||||
if (request.RequestUrl->GetPath().size() >= 4)
|
||||
params->Set("module", request.RequestUrl->GetPath()[3]);
|
||||
|
||||
String moduleName = params->Get("module");
|
||||
|
||||
if (!ConfigModuleUtility::ValidateName(moduleName)) {
|
||||
response.SetStatus(403, "Forbidden");
|
||||
return;
|
||||
}
|
||||
|
||||
int code = 200;
|
||||
String status = "Created module.";
|
||||
|
||||
try {
|
||||
ConfigModuleUtility::CreateModule(moduleName);
|
||||
} catch (const std::exception& ex) {
|
||||
code = 501;
|
||||
status = "Error: " + DiagnosticInformation(ex);
|
||||
}
|
||||
|
||||
Dictionary::Ptr result1 = new Dictionary();
|
||||
|
||||
result1->Set("module", moduleName);
|
||||
result1->Set("code", code);
|
||||
result1->Set("status", status);
|
||||
|
||||
Array::Ptr results = new Array();
|
||||
results->Add(result1);
|
||||
|
||||
Dictionary::Ptr result = new Dictionary();
|
||||
result->Set("results", results);
|
||||
|
||||
response.SetStatus(code, (code == 200) ? "OK" : "Error");
|
||||
HttpUtility::SendJsonBody(response, result);
|
||||
}
|
||||
|
||||
void ConfigModulesHandler::HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
|
||||
|
||||
if (request.RequestUrl->GetPath().size() >= 4)
|
||||
params->Set("module", request.RequestUrl->GetPath()[3]);
|
||||
|
||||
String moduleName = params->Get("module");
|
||||
|
||||
if (!ConfigModuleUtility::ValidateName(moduleName)) {
|
||||
response.SetStatus(403, "Forbidden");
|
||||
return;
|
||||
}
|
||||
|
||||
int code = 200;
|
||||
String status = "Deleted module.";
|
||||
|
||||
try {
|
||||
ConfigModuleUtility::DeleteModule(moduleName);
|
||||
} catch (const std::exception& ex) {
|
||||
code = 501;
|
||||
status = "Error: " + DiagnosticInformation(ex);
|
||||
}
|
||||
|
||||
Dictionary::Ptr result1 = new Dictionary();
|
||||
|
||||
result1->Set("module", moduleName);
|
||||
result1->Set("code", code);
|
||||
result1->Set("status", status);
|
||||
|
||||
Array::Ptr results = new Array();
|
||||
results->Add(result1);
|
||||
|
||||
Dictionary::Ptr result = new Dictionary();
|
||||
result->Set("results", results);
|
||||
|
||||
response.SetStatus(code, (code == 200) ? "OK" : "Error");
|
||||
HttpUtility::SendJsonBody(response, result);
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/******************************************************************************
|
||||
* 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 CONFIGMODULESHANDLER_H
|
||||
#define CONFIGMODULESHANDLER_H
|
||||
|
||||
#include "remote/httphandler.hpp"
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
class I2_REMOTE_API ConfigModulesHandler : public HttpHandler
|
||||
{
|
||||
public:
|
||||
DECLARE_PTR_TYPEDEFS(ConfigModulesHandler);
|
||||
|
||||
virtual bool CanAlsoHandleUrl(const Url::Ptr& url) const;
|
||||
virtual void HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
|
||||
|
||||
private:
|
||||
void HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
|
||||
void HandlePost(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
|
||||
void HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* CONFIGMODULESHANDLER_H */
|
|
@ -0,0 +1,297 @@
|
|||
/******************************************************************************
|
||||
* 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/configmoduleutility.hpp"
|
||||
#include "base/application.hpp"
|
||||
#include "base/exception.hpp"
|
||||
#include "base/scriptglobal.hpp"
|
||||
#include "base/utility.hpp"
|
||||
#include "boost/foreach.hpp"
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
String ConfigModuleUtility::GetModuleDir(void)
|
||||
{
|
||||
return Application::GetLocalStateDir() + "/lib/icinga2/api/modules";
|
||||
}
|
||||
|
||||
void ConfigModuleUtility::CreateModule(const String& name)
|
||||
{
|
||||
String path = GetModuleDir() + "/" + name;
|
||||
|
||||
if (Utility::PathExists(path))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Module already exists."));
|
||||
|
||||
Utility::MkDirP(path, 0700);
|
||||
WriteModuleConfig(name);
|
||||
}
|
||||
|
||||
void ConfigModuleUtility::DeleteModule(const String& name)
|
||||
{
|
||||
String path = GetModuleDir() + "/" + name;
|
||||
|
||||
if (!Utility::PathExists(path))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Module does not exist."));
|
||||
|
||||
Utility::RemoveDirRecursive(path);
|
||||
Application::RequestRestart();
|
||||
}
|
||||
|
||||
std::vector<String> ConfigModuleUtility::GetModules(void)
|
||||
{
|
||||
std::vector<String> modules;
|
||||
Utility::Glob(GetModuleDir() + "/*", boost::bind(&ConfigModuleUtility::CollectDirNames, _1, boost::ref(modules)), GlobDirectory);
|
||||
return modules;
|
||||
}
|
||||
|
||||
void ConfigModuleUtility::CollectDirNames(const String& path, std::vector<String>& dirs)
|
||||
{
|
||||
String name = Utility::BaseName(path);
|
||||
dirs.push_back(name);
|
||||
}
|
||||
|
||||
String ConfigModuleUtility::CreateStage(const String& moduleName, const Dictionary::Ptr& files)
|
||||
{
|
||||
String stageName = Utility::NewUniqueID();
|
||||
|
||||
String path = GetModuleDir() + "/" + moduleName;
|
||||
|
||||
if (!Utility::PathExists(path))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Module does not exist."));
|
||||
|
||||
path += "/" + stageName;
|
||||
|
||||
Utility::MkDirP(path, 0700);
|
||||
WriteStageConfig(moduleName, stageName);
|
||||
|
||||
ObjectLock olock(files);
|
||||
BOOST_FOREACH(const Dictionary::Pair& kv, files) {
|
||||
if (ContainsDotDot(kv.first))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Path must not contain '..'."));
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
return stageName;
|
||||
}
|
||||
|
||||
void ConfigModuleUtility::WriteModuleConfig(const String& moduleName)
|
||||
{
|
||||
String stageName = GetActiveStage(moduleName);
|
||||
|
||||
String includePath = GetModuleDir() + "/" + moduleName + "/include.conf";
|
||||
std::ofstream fpInclude(includePath.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc);
|
||||
fpInclude << "include \"*/include.conf\"\n";
|
||||
fpInclude.close();
|
||||
|
||||
String activePath = GetModuleDir() + "/" + moduleName + "/active.conf";
|
||||
std::ofstream fpActive(activePath.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc);
|
||||
fpActive << "if (!globals.contains(\"ActiveStages\")) {\n"
|
||||
<< " globals.ActiveStages = {}\n"
|
||||
<< "}\n"
|
||||
<< "\n"
|
||||
<< "if (globals.contains(\"ActiveStageOverride\")) {\n"
|
||||
<< " var arr = ActiveStageOverride.split(\":\")\n"
|
||||
<< " if (arr[0] == \"" << moduleName << "\") {\n"
|
||||
<< " if (arr.len() < 2) {\n"
|
||||
<< " log(LogCritical, \"Config\", \"Invalid value for ActiveStageOverride\")\n"
|
||||
<< " } else {\n"
|
||||
<< " ActiveStages[\"" << moduleName << "\"] = arr[1]\n"
|
||||
<< " }\n"
|
||||
<< " }\n"
|
||||
<< "}\n"
|
||||
<< "\n"
|
||||
<< "if (!ActiveStages.contains(\"" << moduleName << "\")) {\n"
|
||||
<< " ActiveStages[\"" << moduleName << "\"] = \"" << stageName << "\"\n"
|
||||
<< "}\n";
|
||||
fpActive.close();
|
||||
}
|
||||
|
||||
void ConfigModuleUtility::WriteStageConfig(const String& moduleName, const String& stageName)
|
||||
{
|
||||
String path = GetModuleDir() + "/" + moduleName + "/" + stageName + "/include.conf";
|
||||
std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc);
|
||||
fp << "include \"../active.conf\"\n"
|
||||
<< "if (ActiveStages[\"" << moduleName << "\"] == \"" << stageName << "\") {\n"
|
||||
<< " include_recursive \"conf.d\"\n"
|
||||
<< " include_zones \"" << moduleName << "\", \"zones.d\"\n"
|
||||
<< "}\n";
|
||||
fp.close();
|
||||
}
|
||||
|
||||
void ConfigModuleUtility::ActivateStage(const String& moduleName, const String& stageName)
|
||||
{
|
||||
String activeStagePath = GetModuleDir() + "/" + moduleName + "/active-stage";
|
||||
std::ofstream fpActiveStage(activeStagePath.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc);
|
||||
fpActiveStage << stageName;
|
||||
fpActiveStage.close();
|
||||
|
||||
WriteModuleConfig(moduleName);
|
||||
}
|
||||
|
||||
void ConfigModuleUtility::TryActivateStageCallback(const ProcessResult& pr, const String& moduleName, const String& stageName)
|
||||
{
|
||||
String logFile = GetModuleDir() + "/" + moduleName + "/" + stageName + "/startup.log";
|
||||
std::ofstream fpLog(logFile.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc);
|
||||
fpLog << pr.Output;
|
||||
fpLog.close();
|
||||
|
||||
String statusFile = GetModuleDir() + "/" + moduleName + "/" + stageName + "/status";
|
||||
std::ofstream fpStatus(statusFile.CStr(), std::ofstream::out | std::ostream::binary | std::ostream::trunc);
|
||||
fpStatus << pr.ExitStatus;
|
||||
fpStatus.close();
|
||||
|
||||
/* validation went fine, activate stage and reload */
|
||||
if (pr.ExitStatus == 0) {
|
||||
ActivateStage(moduleName, stageName);
|
||||
Application::RequestRestart();
|
||||
} else {
|
||||
Log(LogCritical, "ConfigModuleUtility")
|
||||
<< "Config validation failed for module '"
|
||||
<< moduleName << "' and stage '" << stageName << "'.";
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigModuleUtility::AsyncTryActivateStage(const String& moduleName, const String& stageName)
|
||||
{
|
||||
// prepare arguments
|
||||
Array::Ptr args = new Array();
|
||||
args->Add(Application::GetExePath("icinga2"));
|
||||
args->Add("daemon");
|
||||
args->Add("--validate");
|
||||
args->Add("--define");
|
||||
args->Add("ActiveStageOverride=" + moduleName + ":" + stageName);
|
||||
|
||||
Process::Ptr process = new Process(Process::PrepareCommand(args));
|
||||
process->SetTimeout(300);
|
||||
process->Run(boost::bind(&TryActivateStageCallback, _1, moduleName, stageName));
|
||||
}
|
||||
|
||||
void ConfigModuleUtility::DeleteStage(const String& moduleName, const String& stageName)
|
||||
{
|
||||
String path = GetModuleDir() + "/" + moduleName + "/" + stageName;
|
||||
|
||||
if (!Utility::PathExists(path))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Stage does not exist."));
|
||||
|
||||
if (GetActiveStage(moduleName) == stageName)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Active stage cannot be deleted."));
|
||||
|
||||
Utility::RemoveDirRecursive(path);
|
||||
}
|
||||
|
||||
std::vector<String> ConfigModuleUtility::GetStages(const String& moduleName)
|
||||
{
|
||||
std::vector<String> stages;
|
||||
Utility::Glob(GetModuleDir() + "/" + moduleName + "/*", boost::bind(&ConfigModuleUtility::CollectDirNames, _1, boost::ref(stages)), GlobDirectory);
|
||||
return stages;
|
||||
}
|
||||
|
||||
String ConfigModuleUtility::GetActiveStage(const String& moduleName)
|
||||
{
|
||||
String path = GetModuleDir() + "/" + moduleName + "/active-stage";
|
||||
|
||||
std::ifstream fp;
|
||||
fp.open(path.CStr());
|
||||
|
||||
String stage;
|
||||
std::getline(fp, stage.GetData());
|
||||
stage.Trim();
|
||||
|
||||
fp.close();
|
||||
|
||||
if (fp.fail())
|
||||
return "";
|
||||
|
||||
return stage;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::pair<String, bool> > ConfigModuleUtility::GetFiles(const String& moduleName, const String& stageName)
|
||||
{
|
||||
std::vector<std::pair<String, bool> > paths;
|
||||
Utility::GlobRecursive(GetModuleDir() + "/" + moduleName + "/" + stageName, "*", boost::bind(&ConfigModuleUtility::CollectPaths, _1, boost::ref(paths)), GlobDirectory | GlobFile);
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
void ConfigModuleUtility::CollectPaths(const String& path, std::vector<std::pair<String, bool> >& paths)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
struct stat statbuf;
|
||||
int rc = lstat(path.CStr(), &statbuf);
|
||||
if (rc < 0)
|
||||
BOOST_THROW_EXCEPTION(posix_error()
|
||||
<< boost::errinfo_api_function("lstat")
|
||||
<< boost::errinfo_errno(errno)
|
||||
<< boost::errinfo_file_name(path));
|
||||
#else /* _WIN32 */
|
||||
struct _stat statbuf;
|
||||
int rc = _stat(path.CStr(), &statbuf);
|
||||
if (rc < 0)
|
||||
BOOST_THROW_EXCEPTION(posix_error()
|
||||
<< boost::errinfo_api_function("_stat")
|
||||
<< boost::errinfo_errno(errno)
|
||||
<< boost::errinfo_file_name(path));
|
||||
#endif /* _WIN32 */
|
||||
|
||||
paths.push_back(std::make_pair(path, S_ISDIR(statbuf.st_mode)));
|
||||
}
|
||||
|
||||
bool ConfigModuleUtility::ContainsDotDot(const String& path)
|
||||
{
|
||||
std::vector<String> tokens;
|
||||
boost::algorithm::split(tokens, path, boost::is_any_of("/\\"));
|
||||
|
||||
BOOST_FOREACH(const String& part, tokens) {
|
||||
if (part == "..")
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConfigModuleUtility::ValidateName(const String& name)
|
||||
{
|
||||
if (name.IsEmpty())
|
||||
return false;
|
||||
|
||||
/* check for path injection */
|
||||
if (ContainsDotDot(name))
|
||||
return false;
|
||||
|
||||
boost::regex expr("^[^a-zA-Z0-9_\\-]*$", boost::regex::icase);
|
||||
boost::smatch what;
|
||||
return (!boost::regex_search(name.GetData(), what, expr));
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/******************************************************************************
|
||||
* 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 CONFIGMODULEUTILITY_H
|
||||
#define CONFIGMODULEUTILITY_H
|
||||
|
||||
#include "remote/i2-remote.hpp"
|
||||
#include "base/application.hpp"
|
||||
#include "base/dictionary.hpp"
|
||||
#include "base/process.hpp"
|
||||
#include "base/string.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
/**
|
||||
* Helper functions.
|
||||
*
|
||||
* @ingroup remote
|
||||
*/
|
||||
class I2_REMOTE_API ConfigModuleUtility
|
||||
{
|
||||
|
||||
public:
|
||||
static String GetModuleDir(void);
|
||||
|
||||
static void CreateModule(const String& name);
|
||||
static void DeleteModule(const String& name);
|
||||
static std::vector<String> GetModules(void);
|
||||
|
||||
static String CreateStage(const String& moduleName, const Dictionary::Ptr& files);
|
||||
static void DeleteStage(const String& moduleName, const String& stageName);
|
||||
static std::vector<String> GetStages(const String& moduleName);
|
||||
static String GetActiveStage(const String& moduleName);
|
||||
static void ActivateStage(const String& moduleName, const String& stageName);
|
||||
static void AsyncTryActivateStage(const String& moduleName, const String& stageName);
|
||||
|
||||
static std::vector<std::pair<String, bool> > GetFiles(const String& moduleName, const String& stageName);
|
||||
|
||||
static bool ContainsDotDot(const String& path);
|
||||
static bool ValidateName(const String& name);
|
||||
|
||||
private:
|
||||
static void CollectDirNames(const String& path, std::vector<String>& dirs);
|
||||
static void CollectPaths(const String& path, std::vector<std::pair<String, bool> >& paths);
|
||||
|
||||
static void WriteModuleConfig(const String& moduleName);
|
||||
static void WriteStageConfig(const String& moduleName, const String& stageName);
|
||||
|
||||
static void TryActivateStageCallback(const ProcessResult& pr, const String& moduleName, const String& stageName);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* CONFIGMODULEUTILITY_H */
|
|
@ -0,0 +1,184 @@
|
|||
/******************************************************************************
|
||||
* 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/configstageshandler.hpp"
|
||||
#include "remote/configmoduleutility.hpp"
|
||||
#include "remote/httputility.hpp"
|
||||
#include "base/application.hpp"
|
||||
#include "base/exception.hpp"
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
REGISTER_URLHANDLER("/v1/config/stages", ConfigStagesHandler);
|
||||
|
||||
void ConfigStagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
if (request.RequestMethod == "GET")
|
||||
HandleGet(user, request, response);
|
||||
else if (request.RequestMethod == "POST")
|
||||
HandlePost(user, request, response);
|
||||
else if (request.RequestMethod == "DELETE")
|
||||
HandleDelete(user, request, response);
|
||||
else
|
||||
response.SetStatus(400, "Bad request");
|
||||
}
|
||||
|
||||
bool ConfigStagesHandler::CanAlsoHandleUrl(const Url::Ptr& url) const
|
||||
{
|
||||
if (url->GetPath().size() > 5)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConfigStagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
|
||||
|
||||
if (request.RequestUrl->GetPath().size() >= 4)
|
||||
params->Set("module", request.RequestUrl->GetPath()[3]);
|
||||
|
||||
if (request.RequestUrl->GetPath().size() >= 5)
|
||||
params->Set("stage", request.RequestUrl->GetPath()[4]);
|
||||
|
||||
String moduleName = params->Get("module");
|
||||
String stageName = params->Get("stage");
|
||||
|
||||
if (!ConfigModuleUtility::ValidateName(moduleName) || !ConfigModuleUtility::ValidateName(stageName)) {
|
||||
response.SetStatus(403, "Forbidden");
|
||||
return;
|
||||
}
|
||||
|
||||
Array::Ptr results = new Array();
|
||||
|
||||
std::vector<std::pair<String, bool> > paths = ConfigModuleUtility::GetFiles(moduleName, stageName);
|
||||
|
||||
String prefixPath = ConfigModuleUtility::GetModuleDir() + "/" + moduleName + "/" + stageName + "/";
|
||||
|
||||
typedef std::pair<String, bool> kv_pair;
|
||||
BOOST_FOREACH(const kv_pair& kv, paths) {
|
||||
Dictionary::Ptr stageInfo = new Dictionary();
|
||||
stageInfo->Set("type", (kv.second ? "directory" : "file"));
|
||||
stageInfo->Set("name", kv.first.SubStr(prefixPath.GetLength()));
|
||||
results->Add(stageInfo);
|
||||
}
|
||||
|
||||
Dictionary::Ptr result = new Dictionary();
|
||||
result->Set("results", results);
|
||||
|
||||
response.SetStatus(200, "OK");
|
||||
HttpUtility::SendJsonBody(response, result);
|
||||
}
|
||||
|
||||
void ConfigStagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
|
||||
|
||||
if (request.RequestUrl->GetPath().size() >= 4)
|
||||
params->Set("module", request.RequestUrl->GetPath()[3]);
|
||||
|
||||
String moduleName = params->Get("module");
|
||||
|
||||
if (!ConfigModuleUtility::ValidateName(moduleName)) {
|
||||
response.SetStatus(403, "Forbidden");
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary::Ptr files = params->Get("files");
|
||||
|
||||
int code = 200;
|
||||
String status = "Created stage.";
|
||||
String stageName;
|
||||
|
||||
try {
|
||||
if (!files)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("Parameter 'files' must be specified."));
|
||||
|
||||
stageName = ConfigModuleUtility::CreateStage(moduleName, files);
|
||||
|
||||
/* validate the config. on success, activate stage and reload */
|
||||
ConfigModuleUtility::AsyncTryActivateStage(moduleName, stageName);
|
||||
} catch (const std::exception& ex) {
|
||||
code = 501;
|
||||
status = "Error: " + DiagnosticInformation(ex);
|
||||
}
|
||||
|
||||
Dictionary::Ptr result1 = new Dictionary();
|
||||
|
||||
result1->Set("module", moduleName);
|
||||
result1->Set("stage", stageName);
|
||||
result1->Set("code", code);
|
||||
result1->Set("status", status);
|
||||
|
||||
Array::Ptr results = new Array();
|
||||
results->Add(result1);
|
||||
|
||||
Dictionary::Ptr result = new Dictionary();
|
||||
result->Set("results", results);
|
||||
|
||||
response.SetStatus(code, (code == 200) ? "OK" : "Error");
|
||||
HttpUtility::SendJsonBody(response, result);
|
||||
}
|
||||
|
||||
void ConfigStagesHandler::HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
|
||||
|
||||
if (request.RequestUrl->GetPath().size() >= 4)
|
||||
params->Set("module", request.RequestUrl->GetPath()[3]);
|
||||
|
||||
if (request.RequestUrl->GetPath().size() >= 5)
|
||||
params->Set("stage", request.RequestUrl->GetPath()[4]);
|
||||
|
||||
String moduleName = params->Get("module");
|
||||
String stageName = params->Get("stage");
|
||||
|
||||
if (!ConfigModuleUtility::ValidateName(moduleName) || !ConfigModuleUtility::ValidateName(stageName)) {
|
||||
response.SetStatus(403, "Forbidden");
|
||||
return;
|
||||
}
|
||||
|
||||
int code = 200;
|
||||
String status = "Deleted stage.";
|
||||
|
||||
try {
|
||||
ConfigModuleUtility::DeleteStage(moduleName, stageName);
|
||||
} catch (const std::exception& ex) {
|
||||
code = 501;
|
||||
status = "Error: " + DiagnosticInformation(ex);
|
||||
}
|
||||
|
||||
Dictionary::Ptr result1 = new Dictionary();
|
||||
|
||||
result1->Set("module", moduleName);
|
||||
result1->Set("stage", stageName);
|
||||
result1->Set("code", code);
|
||||
result1->Set("status", status);
|
||||
|
||||
Array::Ptr results = new Array();
|
||||
results->Add(result1);
|
||||
|
||||
Dictionary::Ptr result = new Dictionary();
|
||||
result->Set("results", results);
|
||||
|
||||
response.SetStatus(code, (code == 200) ? "OK" : "Error");
|
||||
HttpUtility::SendJsonBody(response, result);
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/******************************************************************************
|
||||
* 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 CONFIGSTAGESHANDLER_H
|
||||
#define CONFIGSTAGESHANDLER_H
|
||||
|
||||
#include "remote/httphandler.hpp"
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
class I2_REMOTE_API ConfigStagesHandler : public HttpHandler
|
||||
{
|
||||
public:
|
||||
DECLARE_PTR_TYPEDEFS(ConfigStagesHandler);
|
||||
|
||||
virtual bool CanAlsoHandleUrl(const Url::Ptr& url) const;
|
||||
virtual void HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
|
||||
|
||||
private:
|
||||
void HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
|
||||
void HandlePost(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
|
||||
void HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* CONFIGSTAGESHANDLER_H */
|
|
@ -154,7 +154,14 @@ void HttpConnection::ProcessMessageAsync(HttpRequest& request)
|
|||
String msg = "<h1>Unauthorized</h1>";
|
||||
response.WriteBody(msg.CStr(), msg.GetLength());
|
||||
} else {
|
||||
HttpHandler::ProcessRequest(user, request, response);
|
||||
try {
|
||||
HttpHandler::ProcessRequest(user, request, response);
|
||||
} catch (const std::exception& ex) {
|
||||
response.SetStatus(503, "Unhandled exception");
|
||||
response.AddHeader("Content-Type", "text/plain");
|
||||
String errorInfo = DiagnosticInformation(ex);
|
||||
response.WriteBody(errorInfo.CStr(), errorInfo.GetLength());
|
||||
}
|
||||
}
|
||||
|
||||
response.Finish();
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/******************************************************************************
|
||||
* 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/httputility.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
Dictionary::Ptr HttpUtility::FetchRequestParameters(HttpRequest& request)
|
||||
{
|
||||
Dictionary::Ptr result;
|
||||
|
||||
String body;
|
||||
char buffer[1024];
|
||||
size_t count;
|
||||
|
||||
while ((count = request.ReadBody(buffer, sizeof(buffer))) > 0)
|
||||
body += String(buffer, buffer + count);
|
||||
|
||||
if (!body.IsEmpty())
|
||||
result = JsonDecode(body);
|
||||
|
||||
if (!result)
|
||||
result = new Dictionary();
|
||||
|
||||
typedef std::pair<String, Value> kv_pair;
|
||||
BOOST_FOREACH(const kv_pair& kv, request.RequestUrl->GetQuery()) {
|
||||
result->Set(kv.first, kv.second);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void HttpUtility::SendJsonBody(HttpResponse& response, const Value& val)
|
||||
{
|
||||
response.AddHeader("Content-Type", "application/json");
|
||||
|
||||
String body = JsonEncode(val);
|
||||
response.WriteBody(body.CStr(), body.GetLength());
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/******************************************************************************
|
||||
* 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 HTTPUTILITY_H
|
||||
#define HTTPUTILITY_H
|
||||
|
||||
#include "remote/httprequest.hpp"
|
||||
#include "remote/httpresponse.hpp"
|
||||
#include "base/dictionary.hpp"
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
/**
|
||||
* Helper functions.
|
||||
*
|
||||
* @ingroup remote
|
||||
*/
|
||||
class I2_REMOTE_API HttpUtility
|
||||
{
|
||||
|
||||
public:
|
||||
static Dictionary::Ptr FetchRequestParameters(HttpRequest& request);
|
||||
static void SendJsonBody(HttpResponse& response, const Value& val);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* HTTPUTILITY_H */
|
Loading…
Reference in New Issue