From 9b6ebbc69b11f7bbeac5ff022e5dad288e1c0b96 Mon Sep 17 00:00:00 2001 From: Jean-Marcel Flach Date: Thu, 30 Jul 2015 17:50:17 +0200 Subject: [PATCH] Implement basic actions framework refs #9080 --- lib/icinga/CMakeLists.txt | 2 +- lib/icinga/apiactions.cpp | 48 ++++++++++++++++++ lib/icinga/apiactions.hpp | 41 ++++++++++++++++ lib/remote/CMakeLists.txt | 4 +- lib/remote/actionshandler.cpp | 83 +++++++++++++++++++++++++++++++ lib/remote/actionshandler.hpp | 38 +++++++++++++++ lib/remote/apiaction.cpp | 57 ++++++++++++++++++++++ lib/remote/apiaction.hpp | 92 +++++++++++++++++++++++++++++++++++ lib/remote/apifunction.hpp | 11 ----- lib/remote/httputility.cpp | 4 +- lib/remote/httputility.hpp | 2 +- 11 files changed, 365 insertions(+), 17 deletions(-) create mode 100644 lib/icinga/apiactions.cpp create mode 100644 lib/icinga/apiactions.hpp create mode 100644 lib/remote/actionshandler.cpp create mode 100644 lib/remote/actionshandler.hpp create mode 100644 lib/remote/apiaction.cpp create mode 100644 lib/remote/apiaction.hpp diff --git a/lib/icinga/CMakeLists.txt b/lib/icinga/CMakeLists.txt index 65801a1a2..57a8a3e28 100644 --- a/lib/icinga/CMakeLists.txt +++ b/lib/icinga/CMakeLists.txt @@ -41,7 +41,7 @@ mkclass_target(user.ti user.tcpp user.thpp) mkembedconfig_target(icinga-app.conf icinga-app.cpp) set(icinga_SOURCES - api.cpp apievents.cpp checkable.cpp checkable.thpp checkable-dependency.cpp checkable-downtime.cpp checkable-event.cpp + api.cpp apiactions.cpp apievents.cpp checkable.cpp checkable.thpp checkable-dependency.cpp checkable-downtime.cpp checkable-event.cpp checkable-flapping.cpp checkcommand.cpp checkcommand.thpp checkresult.cpp checkresult.thpp cib.cpp command.cpp command.thpp comment.cpp comment.thpp compatutility.cpp dependency.cpp dependency.thpp dependency-apply.cpp downtime.cpp downtime.thpp eventcommand.cpp eventcommand.thpp diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp new file mode 100644 index 000000000..608c7b200 --- /dev/null +++ b/lib/icinga/apiactions.cpp @@ -0,0 +1,48 @@ +/****************************************************************************** + * 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 "icinga/apiactions.hpp" +#include "icinga/service.hpp" +#include "remote/apiaction.hpp" +#include "remote/httputility.hpp" +#include "base/utility.hpp" +#include "base/convert.hpp" + +using namespace icinga; + +REGISTER_APIACTION(reschedule_check, "Service;Host", &ApiActions::RescheduleCheck); + +Dictionary::Ptr ApiActions::RescheduleCheck(const DynamicObject::Ptr& object, const Dictionary::Ptr& params) +{ + Checkable::Ptr checkable = static_pointer_cast(object); + if (Convert::ToBool(HttpUtility::GetLastParameter(params, "force"))) + checkable->SetForceNextCheck(true); + + double nextCheck; + if (params->Contains("next_check")) + nextCheck = HttpUtility::GetLastParameter(params, "next_check"); + else + nextCheck = Utility::GetTime(); + + checkable->SetNextCheck(nextCheck); + Dictionary::Ptr result = new Dictionary(); + result->Set("code", 200); + result->Set("status", "yay"); + return result; +} diff --git a/lib/icinga/apiactions.hpp b/lib/icinga/apiactions.hpp new file mode 100644 index 000000000..567186ccc --- /dev/null +++ b/lib/icinga/apiactions.hpp @@ -0,0 +1,41 @@ +/****************************************************************************** + * 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 APIACTIONS_H +#define APIACTIONS_H + +#include "icinga/i2-icinga.hpp" +#include "base/dynamicobject.hpp" +#include "base/dictionary.hpp" + +namespace icinga +{ + +/** + * @ingroup icinga + */ +class I2_ICINGA_API ApiActions +{ +public: + static Dictionary::Ptr RescheduleCheck(const DynamicObject::Ptr& object, const Dictionary::Ptr& params); +}; + +} + +#endif /* APIACTIONS_H */ diff --git a/lib/remote/CMakeLists.txt b/lib/remote/CMakeLists.txt index f5aed5aa9..07a9953f2 100644 --- a/lib/remote/CMakeLists.txt +++ b/lib/remote/CMakeLists.txt @@ -21,14 +21,14 @@ mkclass_target(endpoint.ti endpoint.tcpp endpoint.thpp) mkclass_target(zone.ti zone.tcpp zone.thpp) set(remote_SOURCES + actionshandler.cpp apiaction.cpp apifunction.cpp apilistener.cpp apilistener.thpp apilistener-sync.cpp apiuser.cpp apiuser.thpp authority.cpp base64.cpp configfileshandler.cpp configmoduleshandler.cpp configmoduleutility.cpp configstageshandler.cpp endpoint.cpp endpoint.thpp filterutility.cpp httpchunkedencoding.cpp httpconnection.cpp httphandler.cpp httprequest.cpp httpresponse.cpp httputility.cpp jsonrpc.cpp jsonrpcconnection.cpp jsonrpcconnection-heartbeat.cpp - messageorigin.cpp statusqueryhandler.cpp zone.cpp zone.thpp - url.cpp + messageorigin.cpp statusqueryhandler.cpp url.cpp zone.cpp zone.thpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp new file mode 100644 index 000000000..f9291d492 --- /dev/null +++ b/lib/remote/actionshandler.cpp @@ -0,0 +1,83 @@ +/****************************************************************************** + * 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/actionshandler.hpp" +#include "remote/httputility.hpp" +#include "remote/filterutility.hpp" +#include "remote/apiaction.hpp" +#include "base/exception.hpp" +#include "base/serializer.hpp" +#include +#include + +using namespace icinga; + +REGISTER_URLHANDLER("/v1/actions", ActionsHandler); + +bool ActionsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +{ + if (request.RequestUrl->GetPath().size() < 3) + return false; + + if (request.RequestMethod != "POST") { + response.SetStatus(400, "Bad request"); + return true; + } + + String actionName = request.RequestUrl->GetPath()[2]; + + ApiAction::Ptr action = ApiAction::GetByName(actionName); + + if (!action) + return false; + + QueryDescription qd; + + BOOST_FOREACH(const String& typeName, action->GetTypes()) { + Type::Ptr type = Type::GetByName(typeName); + ASSERT(type); + qd.Types.insert(type); + } + + Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); + + std::vector objs = FilterUtility::GetFilterTargets(qd, params); + + Array::Ptr results = new Array(); + + BOOST_FOREACH(const DynamicObject::Ptr& obj, objs) { + try { + results->Add(action->Invoke(obj, params)); + } catch (const std::exception& ex) { + Dictionary::Ptr fail = new Dictionary(); + fail->Set("code", 501); + fail->Set("status", "Error: " + DiagnosticInformation(ex)); + results->Add(fail); + } + } + + Dictionary::Ptr result = new Dictionary(); + result->Set("results", results); + + response.SetStatus(200, "OK"); + HttpUtility::SendJsonBody(response, result); + + return true; +} + diff --git a/lib/remote/actionshandler.hpp b/lib/remote/actionshandler.hpp new file mode 100644 index 000000000..e9445bb13 --- /dev/null +++ b/lib/remote/actionshandler.hpp @@ -0,0 +1,38 @@ +/****************************************************************************** + * 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 ACTIONSHANDLER_H +#define ACTIONSHANDLER_H + +#include "remote/httphandler.hpp" + +namespace icinga +{ + +class I2_REMOTE_API ActionsHandler : public HttpHandler +{ +public: + DECLARE_PTR_TYPEDEFS(ActionsHandler); + + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response); +}; + +} + +#endif /* ACTIONSHANDLER_H */ diff --git a/lib/remote/apiaction.cpp b/lib/remote/apiaction.cpp new file mode 100644 index 000000000..3c999d3e7 --- /dev/null +++ b/lib/remote/apiaction.cpp @@ -0,0 +1,57 @@ +/****************************************************************************** + * 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/apiaction.hpp" +#include "base/singleton.hpp" + +using namespace icinga; + +ApiAction::ApiAction(const std::vector& types, const Callback& action) +: m_Types(types), m_Callback(action) +{ } + +Value ApiAction::Invoke(const DynamicObject::Ptr& target, const Dictionary::Ptr& params) +{ + return m_Callback(target, params); +} + +const std::vector& ApiAction::GetTypes(void) const +{ + return m_Types; +} + +ApiAction::Ptr ApiAction::GetByName(const String& name) +{ + return ApiActionRegistry::GetInstance()->GetItem(name); +} + +void ApiAction::Register(const String& name, const ApiAction::Ptr& action) +{ + ApiActionRegistry::GetInstance()->Register(name, action); +} + +void ApiAction::Unregister(const String& name) +{ + ApiActionRegistry::GetInstance()->Unregister(name); +} + +ApiActionRegistry *ApiActionRegistry::GetInstance(void) +{ + return Singleton::GetInstance(); +} diff --git a/lib/remote/apiaction.hpp b/lib/remote/apiaction.hpp new file mode 100644 index 000000000..ff7e92432 --- /dev/null +++ b/lib/remote/apiaction.hpp @@ -0,0 +1,92 @@ +/****************************************************************************** + * 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 APIACTION_H +#define APIACTION_H + +#include "remote/i2-remote.hpp" +#include "base/registry.hpp" +#include "base/value.hpp" +#include "base/dictionary.hpp" +#include "base/dynamicobject.hpp" +#include +#include +#include +#include +#include + +namespace icinga +{ + +/** + * An API action. + * + * @ingroup remote + */ +class I2_REMOTE_API ApiAction : public Object +{ +public: + DECLARE_PTR_TYPEDEFS(ApiAction); + + typedef boost::function Callback; + + ApiAction(const std::vector& registerTypes, const Callback& function); + + Value Invoke(const DynamicObject::Ptr& target, const Dictionary::Ptr& params); + + const std::vector& GetTypes(void) const; + + static ApiAction::Ptr GetByName(const String& name); + static void Register(const String& name, const ApiAction::Ptr& action); + static void Unregister(const String& name); + +private: + std::vector m_Types; + Callback m_Callback; +}; + +/** + * A registry for API actions. + * + * @ingroup remote + */ +class I2_REMOTE_API ApiActionRegistry : public Registry +{ +public: + static ApiActionRegistry *GetInstance(void); +}; + +#define REGISTER_APIACTION(name, types, callback) \ + namespace { namespace UNIQUE_NAME(apia) { namespace apia ## name { \ + void RegisterAction(void) \ + { \ + String registerName = #name; \ + boost::algorithm::replace_all(registerName, "_", "-"); \ + std::vector registerTypes; \ + String typeNames = types; \ + boost::algorithm::split(registerTypes, typeNames, boost::is_any_of(";")); \ + ApiAction::Ptr action = new ApiAction(registerTypes, callback); \ + ApiActionRegistry::GetInstance()->Register(registerName, action); \ + } \ + INITIALIZE_ONCE(RegisterAction); \ + } } } + +} + +#endif /* APIACTION_H */ diff --git a/lib/remote/apifunction.hpp b/lib/remote/apifunction.hpp index 07d5b6a34..1c604d6f9 100644 --- a/lib/remote/apifunction.hpp +++ b/lib/remote/apifunction.hpp @@ -66,17 +66,6 @@ public: static ApiFunctionRegistry *GetInstance(void); }; -/** - * Helper class for registering ApiFunction implementation classes. - * - * @ingroup base - */ -class I2_REMOTE_API RegisterApiFunctionHelper -{ -public: - RegisterApiFunctionHelper(const String& name, const ApiFunction::Callback& function); -}; - #define REGISTER_APIFUNCTION(name, ns, callback) \ namespace { namespace UNIQUE_NAME(apif) { namespace apif ## name { \ void RegisterFunction(void) \ diff --git a/lib/remote/httputility.cpp b/lib/remote/httputility.cpp index fd9b33fca..50e388463 100644 --- a/lib/remote/httputility.cpp +++ b/lib/remote/httputility.cpp @@ -56,7 +56,7 @@ void HttpUtility::SendJsonBody(HttpResponse& response, const Value& val) response.WriteBody(body.CStr(), body.GetLength()); } -String HttpUtility::GetLastParameter(const Dictionary::Ptr& params, const String& key) +Value HttpUtility::GetLastParameter(const Dictionary::Ptr& params, const String& key) { Value varr = params->Get(key); @@ -66,7 +66,7 @@ String HttpUtility::GetLastParameter(const Dictionary::Ptr& params, const String Array::Ptr arr = varr; if (arr->GetLength() == 0) - return String(); + return Empty; else return arr->Get(arr->GetLength() - 1); } diff --git a/lib/remote/httputility.hpp b/lib/remote/httputility.hpp index 7553642e5..64f84fa9b 100644 --- a/lib/remote/httputility.hpp +++ b/lib/remote/httputility.hpp @@ -38,7 +38,7 @@ class I2_REMOTE_API HttpUtility public: static Dictionary::Ptr FetchRequestParameters(HttpRequest& request); static void SendJsonBody(HttpResponse& response, const Value& val); - static String GetLastParameter(const Dictionary::Ptr& params, const String& key); + static Value GetLastParameter(const Dictionary::Ptr& params, const String& key); }; }