]> granicus.if.org Git - icinga2/commitdiff
Implement support for hosts in the agent component.
authorGunnar Beutner <gunnar@beutner.name>
Tue, 15 Apr 2014 11:11:14 +0000 (13:11 +0200)
committerGunnar Beutner <gunnar@beutner.name>
Tue, 15 Apr 2014 11:38:01 +0000 (13:38 +0200)
Refs #4865

components/agent/agentchecktask.cpp
components/agent/agentlistener.cpp
components/agent/agentlistener.h
contrib/make-agent-config.py
lib/icinga/host.cpp
tools/icinga2-discover-agent.cmake
tools/icinga2-list-agents.cmake

index 6b6e3a289da8b848e7cc19f0e63f6ce916dc80d2..58ee20214337a9e35316f4d47c0956c8f77d22fe 100644 (file)
@@ -84,24 +84,41 @@ void AgentCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu
        resolvers.push_back(std::make_pair("command", checkable->GetCheckCommand()));
        resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
 
+       String agent_identity = MacroProcessor::ResolveMacros("$agent_identity$", resolvers, checkable->GetLastCheckResult());
        String agent_host = MacroProcessor::ResolveMacros("$agent_host$", resolvers, checkable->GetLastCheckResult());
-       String agent_port = MacroProcessor::ResolveMacros("$agent_port$", resolvers, checkable->GetLastCheckResult());
+       String agent_service = MacroProcessor::ResolveMacros("$agent_service$", resolvers, checkable->GetLastCheckResult());
 
-       if (agent_host.IsEmpty() || agent_port.IsEmpty()) {
-               Log(LogWarning, "agent", "'agent_host' and 'agent_port' must be set for agent checks.");
+       if (agent_identity.IsEmpty() || agent_host.IsEmpty()) {
+               Log(LogWarning, "agent", "'agent_name' and 'agent_host' must be set for agent checks.");
                return;
        }
-       
-       std::pair<String, String> key = std::make_pair(agent_host, agent_port);
+
+       String agent_peer_host = MacroProcessor::ResolveMacros("$agent_peer_host$", resolvers, checkable->GetLastCheckResult());
+       String agent_peer_port = MacroProcessor::ResolveMacros("$agent_peer_port$", resolvers, checkable->GetLastCheckResult());
        
        double now = Utility::GetTime();
        
+       BOOST_FOREACH(const AgentListener::Ptr& al, DynamicType::GetObjects<AgentListener>()) {
+               double seen = al->GetAgentSeen(agent_identity);
+
+               if (seen < now - 300)
+                       continue;
+
+               CheckResult::Ptr cr = al->GetCheckResult(agent_identity, agent_host, agent_service);
+
+               if (cr) {
+                       checkable->ProcessCheckResult(cr);
+                       return;
+               }
+       }
+       
        {
                boost::mutex::scoped_lock lock(l_Mutex);
                l_PendingChecks[checkable] = now;
        }
-       
+
        BOOST_FOREACH(const AgentListener::Ptr& al, DynamicType::GetObjects<AgentListener>()) {
-               al->AddConnection(agent_host, agent_port);
+               if (!agent_peer_host.IsEmpty() && !agent_peer_port.IsEmpty())
+                       al->AddConnection(agent_peer_host, agent_peer_port);
        }
 }
index cf05ef42a7d841e327e8fec902661ce96b3bbc6f..0da7c91000c4dbbec11dddb9e957404e6a49cc57 100644 (file)
@@ -40,6 +40,8 @@ void AgentListener::Start(void)
 {
        DynamicObject::Start();
 
+       m_Results = make_shared<Dictionary>();
+
        /* set up SSL context */
        shared_ptr<X509> cert = GetX509Certificate(GetCertPath());
        SetIdentity(GetCertificateCN(cert));
@@ -58,6 +60,7 @@ void AgentListener::Start(void)
        m_AgentTimer->OnTimerExpired.connect(boost::bind(&AgentListener::AgentTimerHandler, this));
        m_AgentTimer->SetInterval(GetUpstreamInterval());
        m_AgentTimer->Start();
+       m_AgentTimer->Reschedule(0);
 }
 
 shared_ptr<SSL_CTX> AgentListener::GetSSLContext(void) const
@@ -181,21 +184,28 @@ void AgentListener::MessageHandler(const TlsStream::Ptr& sender, const String& i
 
        if (identity == GetUpstreamName()) {
                if (method == "get_crs") {
-                       Dictionary::Ptr services = make_shared<Dictionary>();
+                       Dictionary::Ptr hosts = make_shared<Dictionary>();
+
+                       BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjects<Host>()) {
+                               Dictionary::Ptr hostInfo = make_shared<Dictionary>();
 
-                       Host::Ptr host = Host::GetByName("localhost");
+                               hostInfo->Set("cr", Serialize(host->GetLastCheckResult()));
+
+                               Dictionary::Ptr services = make_shared<Dictionary>();
 
-                       if (!host)
-                               Log(LogWarning, "agent", "Agent doesn't have any services for 'localhost'.");
-                       else {
                                BOOST_FOREACH(const Service::Ptr& service, host->GetServices()) {
-                                       services->Set(service->GetShortName(), Serialize(service->GetLastCheckResult()));
+                                       Dictionary::Ptr serviceInfo = make_shared<Dictionary>();
+                                       serviceInfo->Set("cr", Serialize(service->GetLastCheckResult()));
+                                       services->Set(service->GetShortName(), serviceInfo);
                                }
+
+                               hostInfo->Set("services", services);
+
+                               hosts->Set(host->GetName(), hostInfo);
                        }
 
                        Dictionary::Ptr params = make_shared<Dictionary>();
-                       params->Set("services", services);
-                       params->Set("host", Serialize(host->GetLastCheckResult()));
+                       params->Set("hosts", hosts);
 
                        Dictionary::Ptr request = make_shared<Dictionary>();
                        request->Set("method", "push_crs");
@@ -206,14 +216,18 @@ void AgentListener::MessageHandler(const TlsStream::Ptr& sender, const String& i
        }
 
        if (method == "push_crs") {
-               Dictionary::Ptr params = message->Get("params");
+               Value paramsv = message->Get("params");
 
-               if (!params)
+               if (paramsv.IsEmpty() || !paramsv.IsObjectType<Dictionary>())
                        return;
 
+               Dictionary::Ptr params = paramsv;
+
+               params->Set("seen", Utility::GetTime());
+
                Dictionary::Ptr inventoryDescr = make_shared<Dictionary>();
                inventoryDescr->Set("identity", identity);
-               inventoryDescr->Set("crs", params);
+               inventoryDescr->Set("params", params);
 
                String inventoryFile = GetInventoryDir() + SHA256(identity);
                String inventoryTempFile = inventoryFile + ".tmp";
@@ -233,47 +247,69 @@ void AgentListener::MessageHandler(const TlsStream::Ptr& sender, const String& i
                            << boost::errinfo_file_name(inventoryTempFile));
                }
 
-               Host::Ptr host = Host::GetByName(identity);
+               m_Results->Set(identity, params);
+       }
+}
 
-               if (!host) {
-                       Log(LogWarning, "agent", "Ignoring check results for host '" + identity + "'.");
-                       return;
-               }
+double  AgentListener::GetAgentSeen(const String& agentIdentity)
+{
+       Dictionary::Ptr agentparams = m_Results->Get(agentIdentity);
 
-               Value hostcr = Deserialize(params->Get("host"), true);
+       if (!agentparams)
+               return 0;
 
-               if (!hostcr.IsObjectType<CheckResult>()) {
-                       Log(LogWarning, "agent", "Ignoring invalid check result for host '" + identity + "'.");
-               } else {
-                       CheckResult::Ptr cr = hostcr;
-                       host->ProcessCheckResult(cr);
-               }
+       return agentparams->Get("seen");
+}
+
+CheckResult::Ptr AgentListener::GetCheckResult(const String& agentIdentity, const String& hostName, const String& serviceName)
+{
+       Dictionary::Ptr agentparams = m_Results->Get(agentIdentity);
 
-               Dictionary::Ptr services = params->Get("services");
+       if (!agentparams)
+               return CheckResult::Ptr();
 
-               if (!services)
-                       return;
+       Value hostsv = agentparams->Get("hosts");
 
-               Dictionary::Pair kv;
+       if (hostsv.IsEmpty() || !hostsv.IsObjectType<Dictionary>())
+               return CheckResult::Ptr();
 
-               BOOST_FOREACH(kv, services) {
-                       Service::Ptr service = host->GetServiceByShortName(kv.first);
+       Dictionary::Ptr hosts = hostsv;
 
-                       if (!service) {
-                               Log(LogWarning, "agent", "Ignoring check result for service '" + kv.first + "' on host '" + identity + "'.");
-                               continue;
-                       }
+       Value hostv = hosts->Get(hostName);
 
-                       Value servicecr = Deserialize(kv.second, true);
+       if (hostv.IsEmpty() || !hostv.IsObjectType<Dictionary>())
+               return CheckResult::Ptr();
 
-                       if (!servicecr.IsObjectType<CheckResult>()) {
-                               Log(LogWarning, "agent", "Ignoring invalid check result for service '" + kv.first + "' on host '" + identity + "'.");
-                               continue;
-                       }
+       Dictionary::Ptr host = hostv;
 
-                       CheckResult::Ptr cr = servicecr;
-                       service->ProcessCheckResult(cr);
-               }
+       if (serviceName.IsEmpty()) {
+               Value hostcrv = Deserialize(host->Get("cr"));
+
+               if (hostcrv.IsEmpty() || !hostcrv.IsObjectType<CheckResult>())
+                       return CheckResult::Ptr();
+
+               return hostcrv;
+       } else {
+               Value servicesv = host->Get("services");
+
+               if (servicesv.IsEmpty() || !servicesv.IsObjectType<Dictionary>())
+                       return CheckResult::Ptr();
+
+               Dictionary::Ptr services = servicesv;
+
+               Value servicev = services->Get(serviceName);
+
+               if (servicev.IsEmpty() || !servicev.IsObjectType<Dictionary>())
+                       return CheckResult::Ptr();
+
+               Dictionary::Ptr service = servicev;
+
+               Value servicecrv = Deserialize(service->Get("cr"));
+
+               if (servicecrv.IsEmpty() || !servicecrv.IsObjectType<CheckResult>())
+                       return CheckResult::Ptr();
+
+               return servicecrv;
        }
 }
 
index d1d862daacdfeb0b4f0ce33aaf39e0e41c081b8a..6967e3ae8c827e202c9c243368104afd27ba3030 100644 (file)
@@ -46,11 +46,16 @@ public:
 
        shared_ptr<SSL_CTX> GetSSLContext(void) const;
 
+       double GetAgentSeen(const String& agentIdentity);
+       CheckResult::Ptr GetCheckResult(const String& agentIdentity, const String& hostName, const String& serviceName);
+
 private:
        shared_ptr<SSL_CTX> m_SSLContext;
        std::set<TcpSocket::Ptr> m_Servers;
        Timer::Ptr m_Timer;
 
+       Dictionary::Ptr m_Results;
+
        Timer::Ptr m_AgentTimer;
        void AgentTimerHandler(void);
 
index 407bf61358676093b52f324108af9d984ca9b69d..a6994575cdd0fb99692bc202fb21ce1cd917881a 100755 (executable)
 #
 # template Host "agent-host" {
 #   check_command = "agent"
-#   vars.agent_host = "$address$"
-#   vars.agent_port = 7000
+#   vars.agent_host = "$host.name$"
+#   vars.agent_service = ""
+#   vars.agent_peer_host = "$address$"
+#   vars.agent_peer_port = 7000
 # }
 #
 # template Service "agent-service" {
 #   check_command = "agent"
+#   vars.agent_service = "$service.name$"
 #}
 
 import subprocess, json
@@ -33,20 +36,30 @@ import subprocess, json
 inventory_json = subprocess.check_output(["icinga2-list-agents", "--batch"])
 inventory = json.loads(inventory_json)
 
-for host, hostinfo in inventory.items():
-    print "object Host \"%s\" {" % (host)
-    print "  import \"agent-host\""
+for agent, agent_info in inventory.items():
+    for host, host_info in agent_info["hosts"].items():
+        if host == "localhost":
+            host_name = agent
+        else:
+            host_name = host
 
-    if "peer" in hostinfo:
-        print "  vars.agent_host = \"%s\"" % (hostinfo["peer"]["agent_host"])
-        print "  vars.agent_port = \"%s\"" % (hostinfo["peer"]["agent_port"])
+        print "object Host \"%s\" {" % (host_name)
+        print "  import \"agent-host\""
+        print "  vars.agent_identity = \"%s\"" % (agent)
 
-    print "}"
-    print ""
+        if host != host_name:
+            print "  vars.agent_host = \"%s\"" % (host)
+
+        if "peer" in agent_info:
+            print "  vars.agent_peer_host = \"%s\"" % (agent_info["peer"]["agent_host"])
+            print "  vars.agent_peer_port = \"%s\"" % (agent_info["peer"]["agent_port"])
 
-    for service in hostinfo["services"]:
-        print "object Service \"%s\" {" % (service)
-        print "  import \"agent-service\""
-        print "  host_name = \"%s\"" % (host)
         print "}"
         print ""
+
+        for service in host_info["services"]:
+            print "object Service \"%s\" {" % (service)
+            print "  import \"agent-service\""
+            print "  host_name = \"%s\"" % (host)
+            print "}"
+            print ""
index 45697bc47299cdcad896e77ec047711684951dc4..9b49bcb46c87b7419448b0067f0d9d044adc9768 100644 (file)
@@ -147,29 +147,21 @@ HostState Host::CalculateState(ServiceState state)
 
 HostState Host::GetState(void) const
 {
-       ASSERT(!OwnsLock());
-
        return CalculateState(GetStateRaw());
 }
 
 HostState Host::GetLastState(void) const
 {
-       ASSERT(!OwnsLock());
-
        return CalculateState(GetLastStateRaw());
 }
 
 HostState Host::GetLastHardState(void) const
 {
-       ASSERT(!OwnsLock());
-
        return CalculateState(GetLastHardStateRaw());
 }
 
 double Host::GetLastStateUp(void) const
 {
-       ASSERT(!OwnsLock());
-
        if (GetLastStateOK() > GetLastStateWarning())
                return GetLastStateOK();
        else
@@ -178,8 +170,6 @@ double Host::GetLastStateUp(void) const
 
 double Host::GetLastStateDown(void) const
 {
-       ASSERT(!OwnsLock());
-
        return GetLastStateCritical();
 }
 
index 197172a6f9e8e288033f0f1c0f49be565c8e50ac..7ff76fd4ef9c05b52264ad5ec3e1e46b90eb2eb9 100644 (file)
@@ -109,7 +109,7 @@ 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, sys, json, os, hashlib
+import socket, ssl, sys, json, os, hashlib, time
 
 def warning(*objs):
     print(*objs, file=sys.stderr)
@@ -180,11 +180,12 @@ if method != "push_crs":
     warning("Agent did not return any check results. Make sure you're using the master certificate.")
     sys.exit(1)
 
-params = response['params']
+params = response["params"]
+params["seen"] = time.time()
 
 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 }
+inventory_info = { "identity": cn, "params": params }
 json.dump(inventory_info, fp)
 fp.close()
 
index d1e5f1b98aa894304910bb1be44d88d3b6dcfb28..30e626093e45357dc6f15d3b7727acb8450473c9 100644 (file)
@@ -17,6 +17,7 @@
 # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
 import sys, os, json
+from datetime import datetime
 
 inventory_dir = "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/lib/icinga2/agent/inventory/"
 
@@ -32,7 +33,11 @@ for root, dirs, files in os.walk(inventory_dir):
         fp.close()
 
         inventory[inventory_info["identity"]] = {}
-        inventory[inventory_info["identity"]]["services"] = inventory_info["crs"]["services"].keys()
+        inventory[inventory_info["identity"]]["seen"] = inventory_info["params"]["seen"]
+        inventory[inventory_info["identity"]]["hosts"] = {}
+
+        for host, host_info in inventory_info["params"]["hosts"].items():
+            inventory[inventory_info["identity"]]["hosts"][host] = { "services": host_info["services"].keys() }
 
         try:
             fp = open(root + file + ".peer", "r")
@@ -46,16 +51,19 @@ for root, dirs, files in os.walk(inventory_dir):
 if len(sys.argv) > 1 and sys.argv[1] == "--batch":
     json.dump(inventory, sys.stdout)
 else:
-    for host, host_info in inventory.items():
-        if "peer" in host_info:
-            peer_info = host_info["peer"]
-            peer_addr = " (%s:%s)" % (peer_info["agent_host"], peer_info["agent_port"])
+    for agent, agent_info in inventory.items():
+        if "peer" in agent_info:
+            peer_info = agent_info["peer"]
+            peer_addr = "peer address: %s:%s, " % (peer_info["agent_host"], peer_info["agent_port"])
         else:
-            peer_addr = ""
+            peer_addr = "no peer address"
+
+        print "* %s (%slast seen: %s)" % (agent, peer_addr, datetime.fromtimestamp(agent_info["seen"]))
 
-        print "* %s%s" % (host, peer_addr)
+        for host, host_info in agent_info["hosts"].items():
+            print "    * %s" % (host)
 
-        for service in host_info["services"]:
-            print "    * %s" % (service)
+            for service in host_info["services"]:
+                print "        * %s" % (service)
 
 sys.exit(0)