]> granicus.if.org Git - icinga2/commitdiff
Implement support for auto-discovering services for passive agents.
authorGunnar Beutner <gunnar.beutner@netways.de>
Sun, 13 Apr 2014 07:22:27 +0000 (09:22 +0200)
committerGunnar Beutner <gunnar.beutner@netways.de>
Sun, 13 Apr 2014 07:22:27 +0000 (09:22 +0200)
Fixes #6002

components/agent/CMakeLists.txt
components/agent/agentlistener.cpp
components/agent/agentlistener.h
debian/icinga2-common.install
icinga2.spec
tools/CMakeLists.txt
tools/icinga2-discover-agent.cmake
tools/icinga2-forget-agent.cmake [new file with mode: 0644]
tools/icinga2-list-agents.cmake [new file with mode: 0644]

index 6a956e94392823d4eec7d67967975e0483f2a3ae..67396c1ec10499f04cb2c457a5bfe599f83de34e 100644 (file)
@@ -34,3 +34,6 @@ set_target_properties (
 
 install(TARGETS agent RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2)
 
+#install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/agent\")")
+install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/agent/inventory\")")
+
index 469a9b95cb5963db3e98bf945a8a3cbc43c20a5c..ed216cc4421a38dfc43e2c5799be0948efd94152 100644 (file)
@@ -27,6 +27,7 @@
 #include "base/networkstream.h"
 #include "base/application.h"
 #include "base/context.h"
+#include <fstream>
 
 using namespace icinga;
 
@@ -52,7 +53,7 @@ void AgentListener::Start(void)
        /* create the primary JSON-RPC listener */
        if (!GetBindPort().IsEmpty())
                AddListener(GetBindPort());
-               
+
        m_AgentTimer = make_shared<Timer>();
        m_AgentTimer->OnTimerExpired.connect(boost::bind(&AgentListener::AgentTimerHandler, this));
        m_AgentTimer->SetInterval(GetUpstreamInterval());
@@ -64,6 +65,11 @@ shared_ptr<SSL_CTX> AgentListener::GetSSLContext(void) const
        return m_SSLContext;
 }
 
+String AgentListener::GetInventoryDir(void)
+{
+       return Application::GetLocalStateDir() + "/lib/icinga2/agent/inventory/";
+}
+
 /**
  * Creates a new JSON-RPC listener on the specified port.
  *
@@ -170,15 +176,15 @@ void AgentListener::NewClientHandler(const Socket::Ptr& client, TlsRole role)
 void AgentListener::MessageHandler(const TlsStream::Ptr& sender, const String& identity, const Dictionary::Ptr& message)
 {
        CONTEXT("Processing agent message of type '" + message->Get("method") + "'");
-       
+
        String method = message->Get("method");
-       
+
        if (identity == GetUpstreamName()) {
                if (method == "get_crs") {
                        Dictionary::Ptr services = make_shared<Dictionary>();
 
                        Host::Ptr host = Host::GetByName("localhost");
-                       
+
                        if (!host)
                                Log(LogWarning, "agent", "Agent doesn't have any services for 'localhost'.");
                        else {
@@ -186,34 +192,34 @@ void AgentListener::MessageHandler(const TlsStream::Ptr& sender, const String& i
                                        services->Set(service->GetShortName(), Serialize(service->GetLastCheckResult()));
                                }
                        }
-                       
+
                        Dictionary::Ptr params = make_shared<Dictionary>();
                        params->Set("services", services);
                        params->Set("host", Serialize(host->GetLastCheckResult()));
-                       
+
                        Dictionary::Ptr request = make_shared<Dictionary>();
                        request->Set("method", "push_crs");
                        request->Set("params", params);
-                       
+
                        JsonRpc::SendMessage(sender, request);
                }
        }
-       
+
        if (method == "push_crs") {
                Host::Ptr host = Host::GetByName(identity);
-               
+
                if (!host) {
                        Log(LogWarning, "agent", "Ignoring check results for host '" + identity + "'.");
                        return;
                }
-               
+
                Dictionary::Ptr params = message->Get("params");
-               
+
                if (!params)
                        return;
 
                Value hostcr = Deserialize(params->Get("host"), true);
-               
+
                if (!hostcr.IsObjectType<CheckResult>()) {
                        Log(LogWarning, "agent", "Ignoring invalid check result for host '" + identity + "'.");
                } else {
@@ -222,30 +228,52 @@ void AgentListener::MessageHandler(const TlsStream::Ptr& sender, const String& i
                }
 
                Dictionary::Ptr services = params->Get("services");
-               
+
                if (!services)
                        return;
-               
+
                Dictionary::Pair kv;
-               
+
                BOOST_FOREACH(kv, services) {
                        Service::Ptr service = host->GetServiceByShortName(kv.first);
-                       
+
                        if (!service) {
                                Log(LogWarning, "agent", "Ignoring check result for service '" + kv.first + "' on host '" + identity + "'.");
                                continue;
                        }
-                       
+
                        Value servicecr = Deserialize(kv.second, true);
-                       
+
                        if (!servicecr.IsObjectType<CheckResult>()) {
                                Log(LogWarning, "agent", "Ignoring invalid check result for service '" + kv.first + "' on host '" + identity + "'.");
                                continue;
                        }
-                       
+
                        CheckResult::Ptr cr = servicecr;
                        service->ProcessCheckResult(cr);
                }
+
+               Dictionary::Ptr inventoryDescr = make_shared<Dictionary>();
+               inventoryDescr->Set("identity", identity);
+               inventoryDescr->Set("crs", params);
+
+               String inventoryFile = GetInventoryDir() + SHA256(identity);
+               String inventoryTempFile = inventoryFile + ".tmp";
+
+               std::ofstream fp(inventoryTempFile.CStr(), std::ofstream::out | std::ostream::trunc);
+               fp << JsonSerialize(inventoryDescr);
+               fp.close();
+
+#ifdef _WIN32
+               _unlink(inventoryFile.CStr());
+#endif /* _WIN32 */
+
+               if (rename(inventoryTempFile.CStr(), inventoryFile.CStr()) < 0) {
+                       BOOST_THROW_EXCEPTION(posix_error()
+                           << boost::errinfo_api_function("rename")
+                           << boost::errinfo_errno(errno)
+                           << boost::errinfo_file_name(inventoryTempFile));
+               }
        }
 }
 
@@ -256,6 +284,6 @@ void AgentListener::AgentTimerHandler(void)
 
        if (host.IsEmpty() || port.IsEmpty())
                return;
-       
+
        AddConnection(host, port);
 }
index 6025b62ea8350fe0f75ac76ae40ea7e006951251..d1d862daacdfeb0b4f0ce33aaf39e0e41c081b8a 100644 (file)
@@ -61,7 +61,9 @@ private:
        void ListenerThreadProc(const Socket::Ptr& server);
 
        void MessageHandler(const TlsStream::Ptr& sender, const String& identity, const Dictionary::Ptr& message);
-       
+
+       static String GetInventoryDir(void);
+
        friend class AgentCheckTask;
 };
 
index f7cd060c8bd6eb5881ebffedf0502e5ab602b180..3843570b2b32c562aa89142379146ee782d4ebf5 100644 (file)
@@ -5,4 +5,6 @@ usr/bin/icinga2-migrate-config
 usr/sbin/icinga2-*-feature
 usr/sbin/icinga2-setup-agent
 usr/sbin/icinga2-discover-agent
+usr/sbin/icinga2-forget-agent
+usr/sbin/icinga2-list-agents
 usr/share/icinga2
index e50aa84f2e36eae8adc564bd23a2dfd83377fe35..1cab802aca9f2a58bceffe8262e0e01921b3b31b 100644 (file)
@@ -426,6 +426,8 @@ exit 0
 %{_sbindir}/%{name}-disable-feature
 %{_sbindir}/%{name}-setup-agent
 %{_sbindir}/%{name}-discover-agent
+%{_sbindir}/%{name}-forget-agent
+%{_sbindir}/%{name}-list-agents
 %exclude %{_libdir}/%{name}/libdb_ido_mysql*
 %exclude %{_libdir}/%{name}/libdb_ido_pgsql*
 %{_libdir}/%{name}
index a8f57613c1f6442d8653d28ac5dd01d869bb27b9..fac3f5a178118fd35fd27a339f53a5a7dcb90cf9 100644 (file)
@@ -21,9 +21,12 @@ add_subdirectory(mkembedconfig)
 if(UNIX OR CYGWIN)
   configure_file(icinga2-enable-feature.cmake ${CMAKE_CURRENT_BINARY_DIR}/icinga2-enable-feature @ONLY)
   configure_file(icinga2-discover-agent.cmake ${CMAKE_CURRENT_BINARY_DIR}/icinga2-discover-agent @ONLY)
+  configure_file(icinga2-forget-agent.cmake ${CMAKE_CURRENT_BINARY_DIR}/icinga2-forget-agent @ONLY)
+  configure_file(icinga2-list-agents.cmake ${CMAKE_CURRENT_BINARY_DIR}/icinga2-list-agents @ONLY)
 
   install(
     FILES ${CMAKE_CURRENT_BINARY_DIR}/icinga2-enable-feature ${CMAKE_CURRENT_BINARY_DIR}/icinga2-discover-agent
+          ${CMAKE_CURRENT_BINARY_DIR}/icinga2-forget-agent ${CMAKE_CURRENT_BINARY_DIR}/icinga2-list-agents
     DESTINATION ${CMAKE_INSTALL_SBINDIR}
     PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
   )
index 62625085a25255cdcc6b117e9502099407991884..af776188720eef31ef44ea8f9f60636ffbda3fa8 100644 (file)
@@ -109,17 +109,20 @@ class NetstringParser(object):
 # along with this program; if not, write to the Free Software Foundation
 # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
-import socket, ssl, pprint, sys, json, os
+import socket, ssl, sys, json, os, hashlib
 
 def warning(*objs):
     print(*objs, file=sys.stderr)
 
-if len(sys.argv) < 3:
-    warning("Syntax: %s <host> <port>" % (sys.argv[0]))
+if len(sys.argv) < 2:
+    warning("Syntax: %s <host> [<port>]" % (sys.argv[0]))
     sys.exit(1)
 
 host = sys.argv[1]
-port = int(sys.argv[2])
+if len(sys.argv) > 2:
+    port = int(sys.argv[2])
+else:
+    port = 8483
 
 agentpki = "@CMAKE_INSTALL_FULL_SYSCONFDIR@/icinga2/pki/agent"
 keyfile = agentpki + "/agent.key"
@@ -164,6 +167,8 @@ while True:
         break
     nsp.feed(data)
 
+ssl_sock.close()
+
 if len(nsp.results) != 1:
     warning("Agent returned invalid response: ", repr(nsp.results))
     sys.exit(1)
@@ -177,14 +182,11 @@ if method != "push_crs":
 
 params = response['params']
 
-for service in params['services']:
-    print(\
-"""apply Service "%s" {
-  import "agent-service"
+inventory_file = "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/lib/icinga2/agent/inventory/" + hashlib.sha256(cn).hexdigest()
+fp = open(inventory_file, "w")
+inventory_info = { "identity": cn, "crs": params }
+json.dump(inventory_info, fp)
+fp.close()
 
-  assign where host.name == "%s"
-}
-""" % (service, cn))
-
-# note that closing the SSLSocket will also close the underlying socket
-ssl_sock.close()
+print("Inventory information has been updated for agent '%s'." % (cn))
+sys.exit(0)
diff --git a/tools/icinga2-forget-agent.cmake b/tools/icinga2-forget-agent.cmake
new file mode 100644 (file)
index 0000000..8cabe88
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+# Icinga 2
+# Copyright (C) 2012-2014 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.
+
+from __future__ import print_function
+import sys, os, hashlib
+
+def warning(*objs):
+    print(*objs, file=sys.stderr)
+
+if len(sys.argv) < 2:
+    warning("Syntax: %s <identity>" % (sys.argv[0]))
+    sys.exit(1)
+
+cn = sys.argv[1]
+
+inventory_file = "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/lib/icinga2/agent/inventory/" + hashlib.sha256(cn).hexdigest()
+
+if not os.path.isfile(inventory_file):
+    warning("There's no inventory file for agent '%s'.")
+    sys.exit(0)
+
+os.unlink(inventory_file)
+
+print("Inventory information has been removed for agent '%s'." % (cn))
+sys.exit(0)
diff --git a/tools/icinga2-list-agents.cmake b/tools/icinga2-list-agents.cmake
new file mode 100644 (file)
index 0000000..6f289ac
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+# Icinga 2
+# Copyright (C) 2012-2014 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.
+
+from __future__ import print_function
+import sys, os, json
+
+def warning(*objs):
+    print(*objs, file=sys.stderr)
+
+inventory_dir = "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/lib/icinga2/agent/inventory/"
+
+inventory = {}
+
+for root, dirs, files in os.walk(inventory_dir):
+    for file in files:
+        fp = open(root + file, "r")
+        inventory_info = json.load(fp)
+        inventory[inventory_info["identity"]] = inventory_info["crs"]["services"].keys()
+
+json.dump(inventory, sys.stdout)
+sys.exit(0)