]> granicus.if.org Git - icinga2/commitdiff
Implement reflection support for the API
authorGunnar Beutner <gunnar@beutner.name>
Wed, 26 Aug 2015 08:58:59 +0000 (10:58 +0200)
committerGunnar Beutner <gunnar@beutner.name>
Wed, 26 Aug 2015 08:58:59 +0000 (10:58 +0200)
fixes #9076

lib/remote/CMakeLists.txt
lib/remote/actionshandler.cpp
lib/remote/apiaction.cpp
lib/remote/deleteobjecthandler.cpp
lib/remote/filterutility.cpp
lib/remote/filterutility.hpp
lib/remote/modifyobjecthandler.cpp
lib/remote/statusqueryhandler.cpp
lib/remote/typequeryhandler.cpp [new file with mode: 0644]
lib/remote/typequeryhandler.hpp [new file with mode: 0644]

index 4b28dd4fd8af78327d874991c97079ead9a59ba8..0d35f445c4a30d4bbb80b38b8214570ef2bb303b 100644 (file)
@@ -29,7 +29,8 @@ set(remote_SOURCES
   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 modifyobjecthandler.cpp statusqueryhandler.cpp url.cpp zone.cpp zone.thpp
+  messageorigin.cpp modifyobjecthandler.cpp statusqueryhandler.cpp typequeryhandler.cpp
+  url.cpp zone.cpp zone.thpp
 )
 
 if(ICINGA2_UNITY_BUILD)
index 636a4ac81d554b2755a9bd8eba6c73d88d319011..eaabd7c371325bab343056ddc310e0d1f739d05f 100644 (file)
@@ -52,15 +52,10 @@ bool ActionsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& reques
        Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
 
        const std::vector<String>& types = action->GetTypes();
-       std::vector<ConfigObject::Ptr> objs;
+       std::vector<Value> objs;
 
        if (!types.empty()) {
-               BOOST_FOREACH(const String& typeName, types) {
-                       Type::Ptr type = Type::GetByName(typeName);
-                       ASSERT(type);
-                       qd.Types.insert(type);
-               }
-
+               qd.Types = std::set<String>(types.begin(), types.end());
 
                objs = FilterUtility::GetFilterTargets(qd, params);
        } else
index ba4c4dcffcf3b6b7e43179c46293da93949879d7..ea755452a087b0af0b3860dcc8551f917161b18c 100644 (file)
@@ -23,7 +23,7 @@
 using namespace icinga;
 
 ApiAction::ApiAction(const std::vector<String>& types, const Callback& action)
-: m_Types(types), m_Callback(action)
+    : m_Types(types), m_Callback(action)
 { }
 
 Value ApiAction::Invoke(const ConfigObject::Ptr& target, const Dictionary::Ptr& params)
index 854224e28b0163b7ac4d6e159af1fda0ab95b7f3..792734ab7e0b8df6b4ed2bff61f1f6a24eb84b56 100644 (file)
@@ -46,7 +46,7 @@ bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
                return false;
 
        QueryDescription qd;
-       qd.Types.insert(type);
+       qd.Types.insert(type->GetName());
 
        Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
 
@@ -58,7 +58,7 @@ bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
                params->Set(attr, request.RequestUrl->GetPath()[2]);
        }
 
-       std::vector<ConfigObject::Ptr> objs = FilterUtility::GetFilterTargets(qd, params);
+       std::vector<Value> objs = FilterUtility::GetFilterTargets(qd, params);
 
        bool cascade = HttpUtility::GetLastParameter(params, "cascade");
 
index 1939dcf4049587d346cf4b60cfc2a02c6dfa6f38..3c4f6f2a0fffb0f16f3540c2c3c828b8056ddec0 100644 (file)
@@ -48,31 +48,66 @@ Type::Ptr FilterUtility::TypeFromPluralName(const String& pluralName)
        return Type::Ptr();
 }
 
-ConfigObject::Ptr FilterUtility::GetObjectByTypeAndName(const String& type, const String& name)
+void ConfigObjectTargetProvider::FindTargets(const String& type, const boost::function<void (const Value&)>& addTarget) const
 {
        ConfigType::Ptr dtype = ConfigType::GetByName(type);
        ASSERT(dtype);
 
-       return dtype->GetObject(name);
+       BOOST_FOREACH(const ConfigObject::Ptr& object, dtype->GetObjects()) {
+               addTarget(object);
+       }
 }
 
-std::vector<ConfigObject::Ptr> FilterUtility::GetFilterTargets(const QueryDescription& qd, const Dictionary::Ptr& query)
+Value ConfigObjectTargetProvider::GetTargetByName(const String& type, const String& name) const
 {
-       std::vector<ConfigObject::Ptr> result;
+       ConfigObject::Ptr obj = ConfigObject::GetObject(type, name);
+
+       if (!obj)
+               BOOST_THROW_EXCEPTION(std::invalid_argument("Object does not exist."));
+
+       return obj;
+}
+
+bool ConfigObjectTargetProvider::IsValidType(const String& type) const
+{
+       return ConfigType::GetByName(type) != ConfigType::Ptr();
+}
 
-       BOOST_FOREACH(const Type::Ptr& type, qd.Types) {
-               String attr = type->GetName();
+String ConfigObjectTargetProvider::GetPluralName(const String& type) const
+{
+       return Type::GetByName(type)->GetPluralName();
+}
+
+static void FilteredAddTarget(ScriptFrame& frame, const String& varName, Expression *ufilter, std::vector<Value>& result, const Value& target)
+{
+       frame.Locals->Set(varName, target);
+
+       if (Convert::ToBool(ufilter->Evaluate(frame)))
+               result.push_back(target);
+}
+
+std::vector<Value> FilterUtility::GetFilterTargets(const QueryDescription& qd, const Dictionary::Ptr& query)
+{
+       std::vector<Value> result;
+
+       TargetProvider::Ptr provider;
+
+       if (qd.Provider)
+               provider = qd.Provider;
+       else
+               provider = new ConfigObjectTargetProvider();
+
+       BOOST_FOREACH(const String& type, qd.Types) {
+               String attr = type;
                boost::algorithm::to_lower(attr);
 
-               if (query->Contains(attr)) {
-                       String name = HttpUtility::GetLastParameter(query, attr);
-                       ConfigObject::Ptr obj = GetObjectByTypeAndName(type->GetName(), name);
-                       if (!obj)
-                               BOOST_THROW_EXCEPTION(std::invalid_argument("Object does not exist."));
-                       result.push_back(obj);
-               }
+               if (attr == "type")
+                       attr = "name";
+
+               if (query->Contains(attr))
+                       result.push_back(provider->GetTargetByName(type, query->Get(attr)));
 
-               attr = type->GetPluralName();
+               attr = provider->GetPluralName(type);
                boost::algorithm::to_lower(attr);
 
                if (query->Contains(attr)) {
@@ -80,10 +115,7 @@ std::vector<ConfigObject::Ptr> FilterUtility::GetFilterTargets(const QueryDescri
                        if (names) {
                                ObjectLock olock(names);
                                BOOST_FOREACH(const String& name, names) {
-                                       ConfigObject::Ptr obj = GetObjectByTypeAndName(type->GetName(), name);
-                                       if (!obj)
-                                               BOOST_THROW_EXCEPTION(std::invalid_argument("Object does not exist."));
-                                       result.push_back(obj);
+                                       result.push_back(provider->GetTargetByName(type, name));
                                }
                        }
                }
@@ -103,17 +135,12 @@ std::vector<ConfigObject::Ptr> FilterUtility::GetFilterTargets(const QueryDescri
 
                Log(LogInformation, "FilterUtility", filter);
 
-               Type::Ptr utype = Type::GetByName(type);
-
-               if (!utype)
+               if (!provider->IsValidType(type))
                        BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid type specified."));
 
-               if (qd.Types.find(utype) == qd.Types.end())
+               if (qd.Types.find(type) == qd.Types.end())
                        BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid type specified for this query."));
 
-               ConfigType::Ptr dtype = ConfigType::GetByName(type);
-               ASSERT(dtype);
-
                Expression *ufilter = ConfigCompiler::CompileText("<API query>", filter);
                ScriptFrame frame;
                frame.Sandboxed = true;
@@ -126,16 +153,11 @@ std::vector<ConfigObject::Ptr> FilterUtility::GetFilterTargets(const QueryDescri
                        }
                }
 
-               String varName = utype->GetName();
+               String varName = type;
                boost::algorithm::to_lower(varName);
 
                try {
-                       BOOST_FOREACH(const ConfigObject::Ptr& object, dtype->GetObjects()) {
-                               frame.Locals->Set(varName, object);
-
-                               if (Convert::ToBool(ufilter->Evaluate(frame)))
-                                       result.push_back(object);
-                       }
+                       provider->FindTargets(type, boost::bind(&FilteredAddTarget, boost::ref(frame), varName, ufilter, boost::ref(result), _1));
                } catch (const std::exception& ex) {
                        delete ufilter;
                        throw;
index 70e0c3e00546c4a7a77483d6d170ab9ee7d3cacd..176110597301dea40af14564de9407c5eea5ef14 100644 (file)
 namespace icinga
 {
 
+class TargetProvider : public Object
+{
+public:
+       DECLARE_PTR_TYPEDEFS(TargetProvider);
+
+       virtual void FindTargets(const String& type, const boost::function<void (const Value&)>& addTarget) const = 0;
+       virtual Value GetTargetByName(const String& type, const String& name) const = 0;
+       virtual bool IsValidType(const String& type) const = 0;
+       virtual String GetPluralName(const String& type) const = 0;
+};
+
+class ConfigObjectTargetProvider : public TargetProvider
+{
+public:
+       DECLARE_PTR_TYPEDEFS(ConfigObjectTargetProvider);
+
+       virtual void FindTargets(const String& type, const boost::function<void (const Value&)>& addTarget) const override;
+       virtual Value GetTargetByName(const String& type, const String& name) const override;
+       virtual bool IsValidType(const String& type) const override;
+       virtual String GetPluralName(const String& type) const override;
+};
+
 struct QueryDescription
 {
-       std::set<Type::Ptr> Types;
+       std::set<String> Types;
+       TargetProvider::Ptr Provider;
 };
 
 /**
@@ -42,8 +65,7 @@ class I2_REMOTE_API FilterUtility
 {
 public:
        static Type::Ptr TypeFromPluralName(const String& pluralName);
-       static ConfigObject::Ptr GetObjectByTypeAndName(const String& type, const String& name);
-       static std::vector<ConfigObject::Ptr> GetFilterTargets(const QueryDescription& qd, const Dictionary::Ptr& query);
+       static std::vector<Value> GetFilterTargets(const QueryDescription& qd, const Dictionary::Ptr& query);
 };
 
 }
index f912c284890243cfa2fa70c10b646def4683e609..1c3b020639f1185859555cbd7481b63bb6bad955 100644 (file)
@@ -44,7 +44,7 @@ bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
                return false;
 
        QueryDescription qd;
-       qd.Types.insert(type);
+       qd.Types.insert(type->GetName());
 
        Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
 
@@ -56,7 +56,7 @@ bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
                params->Set(attr, request.RequestUrl->GetPath()[2]);
        }
 
-       std::vector<ConfigObject::Ptr> objs = FilterUtility::GetFilterTargets(qd, params);
+       std::vector<Value> objs = FilterUtility::GetFilterTargets(qd, params);
 
        Dictionary::Ptr attrs = params->Get("attrs");
 
index 71df3a21a474ef68f21398f225b8dc1ae31bfa28..e94b0124892245c02dc8aaaf4501a22846dc2fe4 100644 (file)
@@ -44,7 +44,7 @@ bool StatusQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
                return false;
 
        QueryDescription qd;
-       qd.Types.insert(type);
+       qd.Types.insert(type->GetName());
 
        std::vector<String> joinTypes;
        joinTypes.push_back(type->GetName());
@@ -59,7 +59,7 @@ bool StatusQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
                params->Set(attr, request.RequestUrl->GetPath()[2]);
        }
 
-       std::vector<ConfigObject::Ptr> objs = FilterUtility::GetFilterTargets(qd, params);
+       std::vector<Value> objs = FilterUtility::GetFilterTargets(qd, params);
 
        Array::Ptr results = new Array();
 
diff --git a/lib/remote/typequeryhandler.cpp b/lib/remote/typequeryhandler.cpp
new file mode 100644 (file)
index 0000000..79d4388
--- /dev/null
@@ -0,0 +1,160 @@
+/******************************************************************************
+ * 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/typequeryhandler.hpp"
+#include "remote/httputility.hpp"
+#include "remote/filterutility.hpp"
+#include "base/configtype.hpp"
+#include "base/scriptglobal.hpp"
+#include "base/logger.hpp"
+#include <boost/algorithm/string.hpp>
+#include <set>
+
+using namespace icinga;
+
+REGISTER_URLHANDLER("/v1/types", TypeQueryHandler);
+
+class TypeTargetProvider : public TargetProvider
+{
+public:
+       DECLARE_PTR_TYPEDEFS(TypeTargetProvider);
+
+       virtual void FindTargets(const String& type, const boost::function<void (const Value&)>& addTarget) const override
+       {
+               std::vector<Type::Ptr> targets;
+
+               {
+                       Dictionary::Ptr globals = ScriptGlobal::GetGlobals();
+                       ObjectLock olock(globals);
+                       BOOST_FOREACH(const Dictionary::Pair& kv, globals) {
+                               if (kv.second.IsObjectType<Type>())
+                                       targets.push_back(kv.second);
+                       }
+               }
+
+               BOOST_FOREACH(const Type::Ptr& target, targets) {
+                       addTarget(target);
+               }
+       }
+
+       virtual Value GetTargetByName(const String& type, const String& name) const override
+       {
+               Type::Ptr ptype = Type::GetByName(name);
+
+               if (!ptype)
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("Type does not exist."));
+
+               return ptype;
+       }
+
+       virtual bool IsValidType(const String& type) const override
+       {
+               return type == "Type";
+       }
+
+       virtual String GetPluralName(const String& type) const override
+       {
+               return "types";
+       }
+};
+
+bool TypeQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
+{
+       if (request.RequestMethod != "GET")
+               return false;
+
+       if (request.RequestUrl->GetPath().size() < 2)
+               return false;
+
+       QueryDescription qd;
+       qd.Types.insert("Type");
+       qd.Provider = new TypeTargetProvider();
+
+       Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
+
+       if (params->Contains("type"))
+               params->Set("name", params->Get("type"));
+
+       params->Set("type", "Type");
+
+       if (request.RequestUrl->GetPath().size() >= 3)
+               params->Set("name", request.RequestUrl->GetPath()[2]);
+
+       std::vector<Value> objs = FilterUtility::GetFilterTargets(qd, params);
+
+       Array::Ptr results = new Array();
+
+       BOOST_FOREACH(const Type::Ptr& obj, objs) {
+               Dictionary::Ptr result1 = new Dictionary();
+               results->Add(result1);
+
+               Dictionary::Ptr resultAttrs = new Dictionary();
+               result1->Set("name", obj->GetName());
+               if (obj->GetBaseType())
+                       result1->Set("base", obj->GetBaseType()->GetName());
+               result1->Set("abstract", obj->IsAbstract());
+               result1->Set("fields", resultAttrs);
+
+               Dictionary::Ptr prototype = dynamic_pointer_cast<Dictionary>(obj->GetPrototype());
+               Array::Ptr prototypeKeys = new Array();
+               result1->Set("prototype_keys", prototypeKeys);
+
+               if (prototype) {
+                       ObjectLock olock(prototype);
+                       BOOST_FOREACH(const Dictionary::Pair& kv, prototype) {
+                               prototypeKeys->Add(kv.first);
+                       }
+               }
+
+               int baseFieldCount = 0;
+
+               if (obj->GetBaseType())
+                       baseFieldCount = obj->GetBaseType()->GetFieldCount();
+
+               for (int fid = baseFieldCount; fid < obj->GetFieldCount(); fid++) {
+                       Field field = obj->GetFieldInfo(fid);
+
+                       Dictionary::Ptr fieldInfo = new Dictionary();
+                       resultAttrs->Set(field.Name, fieldInfo);
+
+                       fieldInfo->Set("id", fid);
+                       fieldInfo->Set("type", field.TypeName);
+                       if (field.RefTypeName)
+                               fieldInfo->Set("ref_type", field.RefTypeName);
+                       fieldInfo->Set("array_rank", field.ArrayRank);
+
+                       Dictionary::Ptr attributeInfo = new Dictionary();
+                       fieldInfo->Set("attributes", attributeInfo);
+
+                       attributeInfo->Set("config", static_cast<bool>(field.Attributes & FAConfig));
+                       attributeInfo->Set("state", static_cast<bool>(field.Attributes & FAState));
+                       attributeInfo->Set("required", static_cast<bool>(field.Attributes & FARequired));
+
+               }
+       }
+
+       Dictionary::Ptr result = new Dictionary();
+       result->Set("results", results);
+
+       response.SetStatus(200, "OK");
+       HttpUtility::SendJsonBody(response, result);
+
+       return true;
+}
+
diff --git a/lib/remote/typequeryhandler.hpp b/lib/remote/typequeryhandler.hpp
new file mode 100644 (file)
index 0000000..20d2041
--- /dev/null
@@ -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 TYPEQUERYHANDLER_H
+#define TYPEQUERYHANDLER_H
+
+#include "remote/httphandler.hpp"
+
+namespace icinga
+{
+
+class I2_REMOTE_API TypeQueryHandler : public HttpHandler
+{
+public:
+       DECLARE_PTR_TYPEDEFS(TypeQueryHandler);
+
+       virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override;
+};
+
+}
+
+#endif /* TYPEQUERYHANDLER_H */