]> granicus.if.org Git - icinga2/commitdiff
Implement support for filters
authorGunnar Beutner <gunnar@beutner.name>
Tue, 28 Jul 2015 11:57:59 +0000 (13:57 +0200)
committerGunnar Beutner <gunnar@beutner.name>
Tue, 28 Jul 2015 11:57:59 +0000 (13:57 +0200)
fixes #9077

14 files changed:
lib/remote/CMakeLists.txt
lib/remote/configfileshandler.cpp
lib/remote/configfileshandler.hpp
lib/remote/configmoduleshandler.cpp
lib/remote/configmoduleshandler.hpp
lib/remote/configstageshandler.cpp
lib/remote/configstageshandler.hpp
lib/remote/filterutility.cpp [new file with mode: 0644]
lib/remote/filterutility.hpp [moved from lib/remote/httpdemohandler.cpp with 61% similarity]
lib/remote/httpconnection.cpp
lib/remote/httphandler.cpp
lib/remote/httphandler.hpp
lib/remote/statusqueryhandler.cpp [new file with mode: 0644]
lib/remote/statusqueryhandler.hpp [moved from lib/remote/httpdemohandler.hpp with 85% similarity]

index 6d3827f02bf12d999fe2b0cf923410989256995b..f5aed5aa91bb1848135e6fcd556ab3fbe32f22c2 100644 (file)
@@ -24,10 +24,10 @@ set(remote_SOURCES
   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
-  httpchunkedencoding.cpp  httpconnection.cpp httpdemohandler.cpp httphandler.cpp httprequest.cpp httpresponse.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 zone.cpp zone.thpp
+  messageorigin.cpp statusqueryhandler.cpp zone.cpp zone.thpp
   url.cpp
 )
 
index a98ec276e05e58f14af05b55f241451033f6377f..5d11cd0902d81478e27e0df43cce963ea168ad95 100644 (file)
@@ -28,16 +28,13 @@ using namespace icinga;
 
 REGISTER_URLHANDLER("/v1/config/files", ConfigFilesHandler);
 
-void ConfigFilesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
+bool 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;
 }
 
index 4a906643935143482951bd430e7d4e5ef5acd2a3..9471bbb0d95611a9832907a0a94fde053526fa7e 100644 (file)
@@ -30,8 +30,7 @@ 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);
+       virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
 
 private:
        void HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
index 736d97964f228c7ad72852cc70a465de82caeb4d..738f84f85c50ab462cbbcf9a7f1b920c1d08530b 100644 (file)
@@ -26,8 +26,11 @@ using namespace icinga;
 
 REGISTER_URLHANDLER("/v1/config/modules", ConfigModulesHandler);
 
-void ConfigModulesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
+bool ConfigModulesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
 {
+       if (request.RequestUrl->GetPath().size() > 4)
+               return false;
+
        if (request.RequestMethod == "GET")
                HandleGet(user, request, response);
        else if (request.RequestMethod == "POST")
@@ -36,14 +39,8 @@ void ConfigModulesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest&
                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;
+       return true;
 }
 
 void ConfigModulesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
index 8e670b62455c18ed0da641dcbea964d2c6616cfe..048305490f2108fced18c46c4d803bcccea94882 100644 (file)
@@ -30,8 +30,7 @@ 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);
+       virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
 
 private:
        void HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
index 24b12ed6977720e4915f66da0daebc00a1b1799e..ca7d26039591bdaa6720b41d4a88ad732b911767 100644 (file)
@@ -28,8 +28,11 @@ using namespace icinga;
 
 REGISTER_URLHANDLER("/v1/config/stages", ConfigStagesHandler);
 
-void ConfigStagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
+bool ConfigStagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
 {
+       if (request.RequestUrl->GetPath().size() > 5)
+               return false;
+
        if (request.RequestMethod == "GET")
                HandleGet(user, request, response);
        else if (request.RequestMethod == "POST")
@@ -38,14 +41,8 @@ void ConfigStagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
                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;
+       return true;
 }
 
 void ConfigStagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
index ccdb3b7b8fe5c6d5e0efe44c4ab71a746f639607..044bd078532501a9f93b866987ebf705375672c8 100644 (file)
@@ -30,8 +30,7 @@ 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);
+       virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
 
 private:
        void HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
diff --git a/lib/remote/filterutility.cpp b/lib/remote/filterutility.cpp
new file mode 100644 (file)
index 0000000..5da7938
--- /dev/null
@@ -0,0 +1,139 @@
+/******************************************************************************
+ * 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/filterutility.hpp"
+#include "config/configcompiler.hpp"
+#include "config/expression.hpp"
+#include "base/json.hpp"
+#include "base/dynamictype.hpp"
+#include <boost/foreach.hpp>
+#include <boost/algorithm/string.hpp>
+
+using namespace icinga;
+
+Type::Ptr FilterUtility::TypeFromPluralName(const String& pluralName)
+{
+       String uname = pluralName;
+       boost::algorithm::to_lower(uname);
+
+       BOOST_FOREACH(const DynamicType::Ptr& dtype, DynamicType::GetTypes()) {
+               Type::Ptr type = Type::GetByName(dtype->GetName());
+               ASSERT(type);
+
+               String pname = GetPluralName(type);
+               boost::algorithm::to_lower(pname);
+
+               if (uname == pname)
+                       return type;
+       }
+
+       return Type::Ptr();
+}
+
+String FilterUtility::GetPluralName(const Type::Ptr& type)
+{
+       String name = type->GetName();
+
+       if (name[name.GetLength() - 1] == 'y')
+               return name.SubStr(0, name.GetLength() - 1) + "ies";
+       else
+               return name + "s";
+}
+
+DynamicObject::Ptr FilterUtility::GetObjectByTypeAndName(const String& type, const String& name)
+{
+       DynamicType::Ptr dtype = DynamicType::GetByName(type);
+       ASSERT(dtype);
+
+       return dtype->GetObject(name);
+}
+
+std::vector<DynamicObject::Ptr> FilterUtility::GetFilterTargets(const QueryDescription& qd, const Dictionary::Ptr& query)
+{
+       std::vector<DynamicObject::Ptr> result;
+
+       BOOST_FOREACH(const Type::Ptr& type, qd.Types) {
+               String attr = type->GetName();
+               boost::algorithm::to_lower(attr);
+
+               if (query->Contains(attr)) {
+                       String name = query->Get(attr);
+                       DynamicObject::Ptr obj = GetObjectByTypeAndName(type->GetName(), name);
+                       if (!obj)
+                               BOOST_THROW_EXCEPTION(std::invalid_argument("Object does not exist."));
+                       result.push_back(obj);
+               }
+
+               attr = GetPluralName(type);
+               boost::algorithm::to_lower(attr);
+
+               if (query->Contains(attr)) {
+                       Array::Ptr names = query->Get(attr);
+                       ObjectLock olock(names);
+                       BOOST_FOREACH(const String& name, names) {
+                               DynamicObject::Ptr obj = GetObjectByTypeAndName(type->GetName(), name);
+                               if (!obj)
+                                       BOOST_THROW_EXCEPTION(std::invalid_argument("Object does not exist."));
+                               result.push_back(obj);
+                       }
+               }
+       }
+
+       if (query->Contains("filter")) {
+               if (!query->Contains("type"))
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("Type must be specified when using a filter."));
+
+               String filter = query->Get("filter");
+               String type = query->Get("type");
+
+               Type::Ptr utype = Type::GetByName(type);
+
+               if (!utype)
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid type specified."));
+
+               if (qd.Types.find(utype) == qd.Types.end())
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid type specified for this query."));
+
+               DynamicType::Ptr dtype = DynamicType::GetByName(type);
+               ASSERT(dtype);
+
+               Expression *ufilter = ConfigCompiler::CompileText("<API query>", filter, false);
+               ScriptFrame frame;
+               frame.Sandboxed = true;
+
+               String varName = utype->GetName();
+               boost::algorithm::to_lower(varName);
+
+               try {
+                       BOOST_FOREACH(const DynamicObject::Ptr& object, dtype->GetObjects()) {
+                               frame.Locals->Set(varName, object);
+
+                               if (Convert::ToBool(ufilter->Evaluate(frame)))
+                                       result.push_back(object);
+                       }
+               } catch (const std::exception& ex) {
+                       delete ufilter;
+                       throw;
+               }
+
+               delete ufilter;
+       }
+
+       return result;
+}
similarity index 61%
rename from lib/remote/httpdemohandler.cpp
rename to lib/remote/filterutility.hpp
index 86e37d2fefd7d43353f4c7bae67f4585ba0ec9a6..b7c28821d3d38f92e81447fda1026db352cf0e66 100644 (file)
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#include "remote/httpdemohandler.hpp"
+#ifndef FILTERUTILITY_H
+#define FILTERUTILITY_H
 
-using namespace icinga;
+#include "remote/i2-remote.hpp"
+#include "base/dictionary.hpp"
+#include "base/dynamicobject.hpp"
+#include <set>
 
-REGISTER_URLHANDLER("/demo", HttpDemoHandler);
+namespace icinga
+{
+
+struct QueryDescription
+{
+       std::set<Type::Ptr> Types;
+};
 
-void HttpDemoHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
+/**
+ * Filter utilities.
+ *
+ * @ingroup remote
+ */
+class I2_REMOTE_API FilterUtility
 {
-       if (request.RequestMethod == "GET") {
-               String form = "<h1>Hallo " + user->GetName() + "</h1><form action=\"/demo\" method=\"post\"><input type=\"text\" name=\"msg\"><input type=\"submit\"></form>";
-               response.SetStatus(200, "OK");
-               response.AddHeader("Content-Type", "text/html");
-               response.WriteBody(form.CStr(), form.GetLength());
-       } else if (request.RequestMethod == "POST") {
-               response.SetStatus(200, "OK");
-               String msg = "You sent: ";
+public:
+       static String GetPluralName(const Type::Ptr& type);
+       static Type::Ptr TypeFromPluralName(const String& pluralName);
+       static DynamicObject::Ptr GetObjectByTypeAndName(const String& type, const String& name);
+       static std::vector<DynamicObject::Ptr> GetFilterTargets(const QueryDescription& qd, const Dictionary::Ptr& query);
+};
 
-               char buffer[512];
-               size_t count;
-               while ((count = request.ReadBody(buffer, sizeof(buffer))) > 0)
-                       msg += String(buffer, buffer + count);
-               response.WriteBody(msg.CStr(), msg.GetLength());
-       } else {
-               response.SetStatus(400, "Bad request");
-       }
 }
 
+#endif /* FILTERUTILITY_H */
index b1056c3e106561c758df4145b324bde1a12fe9c9..7c29c10f38491ce6a989e37d9165709ac3e0981d 100644 (file)
@@ -90,7 +90,7 @@ bool HttpConnection::ProcessMessage(void)
        } catch (const std::exception& ex) {
                HttpResponse response(m_Stream, m_CurrentRequest);
                response.SetStatus(400, "Bad request");
-               String msg = "<h1>Bad request</h1>";
+               String msg = "<h1>Bad request</h1><p>" + DiagnosticInformation(ex) + "</p>";
                response.WriteBody(msg.CStr(), msg.GetLength());
                response.Finish();
 
index 9716805d764dc38435c51efcd9ee99a1678aa617..8e42df13c8d6ccbf35b3a71d049df6bdd35179da 100644 (file)
@@ -48,48 +48,54 @@ void HttpHandler::Register(const Url::Ptr& url, const HttpHandler::Ptr& handler)
                node = sub_node;
        }
 
-       node->Set("handler", handler);
-}
+       Array::Ptr handlers = node->Get("handlers");
 
-bool HttpHandler::CanAlsoHandleUrl(const Url::Ptr& url) const
-{
-       return false;
+       if (!handlers) {
+               handlers = new Array();
+               node->Set("handlers", handlers);
+       }
+
+       handlers->Add(handler);
 }
 
 void HttpHandler::ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
 {
        Dictionary::Ptr node = m_UrlTree;
-       HttpHandler::Ptr current_handler, handler;
-       bool exact_match = true;
+       std::vector<HttpHandler::Ptr> handlers;
 
        BOOST_FOREACH(const String& elem, request.RequestUrl->GetPath()) {
-               current_handler = node->Get("handler");
-               if (current_handler)
-                       handler = current_handler;
+               Array::Ptr current_handlers = node->Get("handlers");
+
+               if (current_handlers) {
+                       ObjectLock olock(current_handlers);
+                       BOOST_FOREACH(const HttpHandler::Ptr current_handler, current_handlers) {
+                               handlers.push_back(current_handler);
+                       }
+               }
 
                Dictionary::Ptr children = node->Get("children");
 
                if (!children) {
-                       exact_match = false;
                        node.reset();
                        break;
                }
 
                node = children->Get(elem);
 
-               if (!node) {
-                       exact_match = false;
+               if (!node)
                        break;
-               }
        }
 
-       if (node) {
-               current_handler = node->Get("handler");
-               if (current_handler)
-                       handler = current_handler;
-       }
+       std::reverse(handlers.begin(), handlers.end());
 
-       if (!handler || (!exact_match && !handler->CanAlsoHandleUrl(request.RequestUrl))) {
+       bool processed = false;
+       BOOST_FOREACH(const HttpHandler::Ptr& handler, handlers) {
+               if (handler->HandleRequest(user, request, response)) {
+                       processed = true;
+                       break;
+               }
+       }
+       if (!processed) {
                response.SetStatus(404, "Not found");
                response.AddHeader("Content-Type", "text/html");
                String msg = "<h1>Not found</h1>";
@@ -97,6 +103,4 @@ void HttpHandler::ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request,
                response.Finish();
                return;
        }
-
-       handler->HandleRequest(user, request, response);
 }
index b689a95f5a4d33f136cc35268b1bb61d3572d914..afeeff29abb9bfb0aa649679d65af8128fdb0839 100644 (file)
@@ -40,8 +40,7 @@ class I2_REMOTE_API HttpHandler : public Object
 public:
        DECLARE_PTR_TYPEDEFS(HttpHandler);
 
-       virtual bool CanAlsoHandleUrl(const Url::Ptr& url) const;
-       virtual void HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) = 0;
+       virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) = 0;
 
        static void Register(const Url::Ptr& url, const HttpHandler::Ptr& handler);
        static void ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
diff --git a/lib/remote/statusqueryhandler.cpp b/lib/remote/statusqueryhandler.cpp
new file mode 100644 (file)
index 0000000..9df8f70
--- /dev/null
@@ -0,0 +1,68 @@
+/******************************************************************************
+ * 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/statusqueryhandler.hpp"
+#include "remote/httputility.hpp"
+#include "remote/filterutility.hpp"
+#include "base/serializer.hpp"
+#include <boost/algorithm/string.hpp>
+
+using namespace icinga;
+
+REGISTER_URLHANDLER("/", StatusQueryHandler);
+
+bool StatusQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
+{
+       if (request.RequestUrl->GetPath().empty())
+               return false;
+
+       Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[0]);
+
+       if (!type)
+               return false;
+
+       QueryDescription qd;
+       qd.Types.insert(type);
+
+       Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
+
+       if (request.RequestUrl->GetPath().size() > 1) {
+               String attr = type->GetName();
+               boost::algorithm::to_lower(attr);
+               params->Set(attr, request.RequestUrl->GetPath()[1]);
+       }
+
+       std::vector<DynamicObject::Ptr> objs = FilterUtility::GetFilterTargets(qd, params);
+
+       Array::Ptr results = new Array();
+
+       BOOST_FOREACH(const DynamicObject::Ptr& obj, objs) {
+               Value result1 = Serialize(obj, FAConfig | FAState);
+               results->Add(result1);
+       }
+
+       Dictionary::Ptr result = new Dictionary();
+       result->Set("results", results);
+
+       response.SetStatus(200, "OK");
+       HttpUtility::SendJsonBody(response, result);
+
+       return true;
+}
+
similarity index 85%
rename from lib/remote/httpdemohandler.hpp
rename to lib/remote/statusqueryhandler.hpp
index dbece031d5f9c3b2359964fa4744e0dc5f88446e..64cf2bd0c087404a3b896b8e0a1530a2fd939e3d 100644 (file)
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#ifndef HTTPDEMOHANDLER_H
-#define HTTPDEMOHANDLER_H
+#ifndef STATUSQUERYHANDLER_H
+#define STATUSQUERYHANDLER_H
 
 #include "remote/httphandler.hpp"
 
 namespace icinga
 {
 
-class I2_REMOTE_API HttpDemoHandler : public HttpHandler
+class I2_REMOTE_API StatusQueryHandler : public HttpHandler
 {
 public:
-       DECLARE_PTR_TYPEDEFS(HttpDemoHandler);
+       DECLARE_PTR_TYPEDEFS(StatusQueryHandler);
 
-       virtual void HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
+       virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response);
 };
 
 }
 
-#endif /* HTTPDEMOHANDLER_H */
+#endif /* STATUSQUERYHANDLER_H */