mirror of
https://github.com/Icinga/icinga2.git
synced 2025-07-26 23:24:09 +02:00
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
|
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.
|
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
|
### <a id="constants-conf"></a> constants.conf
|
||||||
|
|
||||||
The `constants.conf` configuration file can be used to define global constants.
|
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".
|
* directory. Each of these files must have the file extension ".conf".
|
||||||
*/
|
*/
|
||||||
include_recursive "conf.d"
|
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;
|
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)
|
void Utility::CopyFile(const String& source, const String& target)
|
||||||
{
|
{
|
||||||
std::ifstream ifs(source.CStr(), std::ios::binary);
|
std::ifstream ifs(source.CStr(), std::ios::binary);
|
||||||
|
@ -123,6 +123,7 @@ public:
|
|||||||
|
|
||||||
static bool PathExists(const String& path);
|
static bool PathExists(const String& path);
|
||||||
|
|
||||||
|
static void RemoveDirRecursive(const String& path);
|
||||||
static void CopyFile(const String& source, const String& target);
|
static void CopyFile(const String& source, const String& target);
|
||||||
|
|
||||||
static Value LoadJsonFile(const String& path);
|
static Value LoadJsonFile(const String& path);
|
||||||
@ -130,10 +131,10 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Utility(void);
|
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<String> m_ThreadName;
|
||||||
static boost::thread_specific_ptr<unsigned int> m_RandSeed;
|
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. */
|
* unfortunately moving it there is somewhat non-trivial. */
|
||||||
success = true;
|
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";
|
String zonesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones";
|
||||||
if (Utility::PathExists(zonesVarDir))
|
if (Utility::PathExists(zonesVarDir))
|
||||||
Utility::Glob(zonesVarDir + "/*", boost::bind(&IncludeNonLocalZone, _1, boost::ref(success)), GlobDirectory);
|
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)
|
if (!success)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -22,9 +22,11 @@ mkclass_target(zone.ti zone.tcpp zone.thpp)
|
|||||||
|
|
||||||
set(remote_SOURCES
|
set(remote_SOURCES
|
||||||
apifunction.cpp apilistener.cpp apilistener.thpp apilistener-sync.cpp
|
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
|
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
|
messageorigin.cpp zone.cpp zone.thpp
|
||||||
url.cpp
|
url.cpp
|
||||||
)
|
)
|
||||||
|
96
lib/remote/configfileshandler.cpp
Normal file
96
lib/remote/configfileshandler.cpp
Normal file
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
42
lib/remote/configfileshandler.hpp
Normal file
42
lib/remote/configfileshandler.hpp
Normal 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 */
|
149
lib/remote/configmoduleshandler.cpp
Normal file
149
lib/remote/configmoduleshandler.cpp
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
|
45
lib/remote/configmoduleshandler.hpp
Normal file
45
lib/remote/configmoduleshandler.hpp
Normal file
@ -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 */
|
297
lib/remote/configmoduleutility.cpp
Normal file
297
lib/remote/configmoduleutility.cpp
Normal file
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
72
lib/remote/configmoduleutility.hpp
Normal file
72
lib/remote/configmoduleutility.hpp
Normal file
@ -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 */
|
184
lib/remote/configstageshandler.cpp
Normal file
184
lib/remote/configstageshandler.cpp
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
|
45
lib/remote/configstageshandler.hpp
Normal file
45
lib/remote/configstageshandler.hpp
Normal file
@ -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>";
|
String msg = "<h1>Unauthorized</h1>";
|
||||||
response.WriteBody(msg.CStr(), msg.GetLength());
|
response.WriteBody(msg.CStr(), msg.GetLength());
|
||||||
} else {
|
} 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();
|
response.Finish();
|
||||||
|
59
lib/remote/httputility.cpp
Normal file
59
lib/remote/httputility.cpp
Normal file
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
45
lib/remote/httputility.hpp
Normal file
45
lib/remote/httputility.hpp
Normal file
@ -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…
x
Reference in New Issue
Block a user