Implement support for hosts in the agent component.

Refs #4865
This commit is contained in:
Gunnar Beutner 2014-04-15 13:11:14 +02:00
parent 9defdd74d9
commit 060a802d15
7 changed files with 154 additions and 84 deletions

View 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("command", checkable->GetCheckCommand()));
resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); 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_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()) { if (agent_identity.IsEmpty() || agent_host.IsEmpty()) {
Log(LogWarning, "agent", "'agent_host' and 'agent_port' must be set for agent checks."); Log(LogWarning, "agent", "'agent_name' and 'agent_host' must be set for agent checks.");
return; 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(); 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); boost::mutex::scoped_lock lock(l_Mutex);
l_PendingChecks[checkable] = now; l_PendingChecks[checkable] = now;
} }
BOOST_FOREACH(const AgentListener::Ptr& al, DynamicType::GetObjects<AgentListener>()) { 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);
} }
} }

View File

@ -40,6 +40,8 @@ void AgentListener::Start(void)
{ {
DynamicObject::Start(); DynamicObject::Start();
m_Results = make_shared<Dictionary>();
/* set up SSL context */ /* set up SSL context */
shared_ptr<X509> cert = GetX509Certificate(GetCertPath()); shared_ptr<X509> cert = GetX509Certificate(GetCertPath());
SetIdentity(GetCertificateCN(cert)); SetIdentity(GetCertificateCN(cert));
@ -58,6 +60,7 @@ void AgentListener::Start(void)
m_AgentTimer->OnTimerExpired.connect(boost::bind(&AgentListener::AgentTimerHandler, this)); m_AgentTimer->OnTimerExpired.connect(boost::bind(&AgentListener::AgentTimerHandler, this));
m_AgentTimer->SetInterval(GetUpstreamInterval()); m_AgentTimer->SetInterval(GetUpstreamInterval());
m_AgentTimer->Start(); m_AgentTimer->Start();
m_AgentTimer->Reschedule(0);
} }
shared_ptr<SSL_CTX> AgentListener::GetSSLContext(void) const 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 (identity == GetUpstreamName()) {
if (method == "get_crs") { if (method == "get_crs") {
Dictionary::Ptr services = make_shared<Dictionary>(); Dictionary::Ptr hosts = make_shared<Dictionary>();
Host::Ptr host = Host::GetByName("localhost"); BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjects<Host>()) {
Dictionary::Ptr hostInfo = make_shared<Dictionary>();
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()) { 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>(); Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("services", services); params->Set("hosts", hosts);
params->Set("host", Serialize(host->GetLastCheckResult()));
Dictionary::Ptr request = make_shared<Dictionary>(); Dictionary::Ptr request = make_shared<Dictionary>();
request->Set("method", "push_crs"); request->Set("method", "push_crs");
@ -206,14 +216,18 @@ void AgentListener::MessageHandler(const TlsStream::Ptr& sender, const String& i
} }
if (method == "push_crs") { if (method == "push_crs") {
Dictionary::Ptr params = message->Get("params"); Value paramsv = message->Get("params");
if (!params) if (paramsv.IsEmpty() || !paramsv.IsObjectType<Dictionary>())
return; return;
Dictionary::Ptr params = paramsv;
params->Set("seen", Utility::GetTime());
Dictionary::Ptr inventoryDescr = make_shared<Dictionary>(); Dictionary::Ptr inventoryDescr = make_shared<Dictionary>();
inventoryDescr->Set("identity", identity); inventoryDescr->Set("identity", identity);
inventoryDescr->Set("crs", params); inventoryDescr->Set("params", params);
String inventoryFile = GetInventoryDir() + SHA256(identity); String inventoryFile = GetInventoryDir() + SHA256(identity);
String inventoryTempFile = inventoryFile + ".tmp"; String inventoryTempFile = inventoryFile + ".tmp";
@ -233,47 +247,69 @@ void AgentListener::MessageHandler(const TlsStream::Ptr& sender, const String& i
<< boost::errinfo_file_name(inventoryTempFile)); << boost::errinfo_file_name(inventoryTempFile));
} }
Host::Ptr host = Host::GetByName(identity); m_Results->Set(identity, params);
}
}
if (!host) { double AgentListener::GetAgentSeen(const String& agentIdentity)
Log(LogWarning, "agent", "Ignoring check results for host '" + identity + "'."); {
return; Dictionary::Ptr agentparams = m_Results->Get(agentIdentity);
}
Value hostcr = Deserialize(params->Get("host"), true); if (!agentparams)
return 0;
if (!hostcr.IsObjectType<CheckResult>()) { return agentparams->Get("seen");
Log(LogWarning, "agent", "Ignoring invalid check result for host '" + identity + "'."); }
} else {
CheckResult::Ptr cr = hostcr;
host->ProcessCheckResult(cr);
}
Dictionary::Ptr services = params->Get("services"); CheckResult::Ptr AgentListener::GetCheckResult(const String& agentIdentity, const String& hostName, const String& serviceName)
{
Dictionary::Ptr agentparams = m_Results->Get(agentIdentity);
if (!services) if (!agentparams)
return; return CheckResult::Ptr();
Dictionary::Pair kv; Value hostsv = agentparams->Get("hosts");
BOOST_FOREACH(kv, services) { if (hostsv.IsEmpty() || !hostsv.IsObjectType<Dictionary>())
Service::Ptr service = host->GetServiceByShortName(kv.first); return CheckResult::Ptr();
if (!service) { Dictionary::Ptr hosts = hostsv;
Log(LogWarning, "agent", "Ignoring check result for service '" + kv.first + "' on host '" + identity + "'.");
continue;
}
Value servicecr = Deserialize(kv.second, true); Value hostv = hosts->Get(hostName);
if (!servicecr.IsObjectType<CheckResult>()) { if (hostv.IsEmpty() || !hostv.IsObjectType<Dictionary>())
Log(LogWarning, "agent", "Ignoring invalid check result for service '" + kv.first + "' on host '" + identity + "'."); return CheckResult::Ptr();
continue;
}
CheckResult::Ptr cr = servicecr; Dictionary::Ptr host = hostv;
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;
} }
} }

View File

@ -46,11 +46,16 @@ public:
shared_ptr<SSL_CTX> GetSSLContext(void) const; 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: private:
shared_ptr<SSL_CTX> m_SSLContext; shared_ptr<SSL_CTX> m_SSLContext;
std::set<TcpSocket::Ptr> m_Servers; std::set<TcpSocket::Ptr> m_Servers;
Timer::Ptr m_Timer; Timer::Ptr m_Timer;
Dictionary::Ptr m_Results;
Timer::Ptr m_AgentTimer; Timer::Ptr m_AgentTimer;
void AgentTimerHandler(void); void AgentTimerHandler(void);

View File

@ -20,12 +20,15 @@
# #
# template Host "agent-host" { # template Host "agent-host" {
# check_command = "agent" # check_command = "agent"
# vars.agent_host = "$address$" # vars.agent_host = "$host.name$"
# vars.agent_port = 7000 # vars.agent_service = ""
# vars.agent_peer_host = "$address$"
# vars.agent_peer_port = 7000
# } # }
# #
# template Service "agent-service" { # template Service "agent-service" {
# check_command = "agent" # check_command = "agent"
# vars.agent_service = "$service.name$"
#} #}
import subprocess, json import subprocess, json
@ -33,20 +36,30 @@ import subprocess, json
inventory_json = subprocess.check_output(["icinga2-list-agents", "--batch"]) inventory_json = subprocess.check_output(["icinga2-list-agents", "--batch"])
inventory = json.loads(inventory_json) inventory = json.loads(inventory_json)
for host, hostinfo in inventory.items(): for agent, agent_info in inventory.items():
print "object Host \"%s\" {" % (host) for host, host_info in agent_info["hosts"].items():
print " import \"agent-host\"" if host == "localhost":
host_name = agent
else:
host_name = host
if "peer" in hostinfo: print "object Host \"%s\" {" % (host_name)
print " vars.agent_host = \"%s\"" % (hostinfo["peer"]["agent_host"]) print " import \"agent-host\""
print " vars.agent_port = \"%s\"" % (hostinfo["peer"]["agent_port"]) print " vars.agent_identity = \"%s\"" % (agent)
print "}" if host != host_name:
print "" 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 "}"
print "" print ""
for service in host_info["services"]:
print "object Service \"%s\" {" % (service)
print " import \"agent-service\""
print " host_name = \"%s\"" % (host)
print "}"
print ""

View File

@ -147,29 +147,21 @@ HostState Host::CalculateState(ServiceState state)
HostState Host::GetState(void) const HostState Host::GetState(void) const
{ {
ASSERT(!OwnsLock());
return CalculateState(GetStateRaw()); return CalculateState(GetStateRaw());
} }
HostState Host::GetLastState(void) const HostState Host::GetLastState(void) const
{ {
ASSERT(!OwnsLock());
return CalculateState(GetLastStateRaw()); return CalculateState(GetLastStateRaw());
} }
HostState Host::GetLastHardState(void) const HostState Host::GetLastHardState(void) const
{ {
ASSERT(!OwnsLock());
return CalculateState(GetLastHardStateRaw()); return CalculateState(GetLastHardStateRaw());
} }
double Host::GetLastStateUp(void) const double Host::GetLastStateUp(void) const
{ {
ASSERT(!OwnsLock());
if (GetLastStateOK() > GetLastStateWarning()) if (GetLastStateOK() > GetLastStateWarning())
return GetLastStateOK(); return GetLastStateOK();
else else
@ -178,8 +170,6 @@ double Host::GetLastStateUp(void) const
double Host::GetLastStateDown(void) const double Host::GetLastStateDown(void) const
{ {
ASSERT(!OwnsLock());
return GetLastStateCritical(); return GetLastStateCritical();
} }

View File

@ -109,7 +109,7 @@ class NetstringParser(object):
# along with this program; if not, write to the Free Software Foundation # along with this program; if not, write to the Free Software Foundation
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. # 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): def warning(*objs):
print(*objs, file=sys.stderr) 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.") warning("Agent did not return any check results. Make sure you're using the master certificate.")
sys.exit(1) 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() inventory_file = "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/lib/icinga2/agent/inventory/" + hashlib.sha256(cn).hexdigest()
fp = open(inventory_file, "w") fp = open(inventory_file, "w")
inventory_info = { "identity": cn, "crs": params } inventory_info = { "identity": cn, "params": params }
json.dump(inventory_info, fp) json.dump(inventory_info, fp)
fp.close() fp.close()

View File

@ -17,6 +17,7 @@
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
import sys, os, json import sys, os, json
from datetime import datetime
inventory_dir = "@CMAKE_INSTALL_FULL_LOCALSTATEDIR@/lib/icinga2/agent/inventory/" 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() fp.close()
inventory[inventory_info["identity"]] = {} 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: try:
fp = open(root + file + ".peer", "r") 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": if len(sys.argv) > 1 and sys.argv[1] == "--batch":
json.dump(inventory, sys.stdout) json.dump(inventory, sys.stdout)
else: else:
for host, host_info in inventory.items(): for agent, agent_info in inventory.items():
if "peer" in host_info: if "peer" in agent_info:
peer_info = host_info["peer"] peer_info = agent_info["peer"]
peer_addr = " (%s:%s)" % (peer_info["agent_host"], peer_info["agent_port"]) peer_addr = "peer address: %s:%s, " % (peer_info["agent_host"], peer_info["agent_port"])
else: else:
peer_addr = "" peer_addr = "no peer address"
print "* %s%s" % (host, peer_addr) print "* %s (%slast seen: %s)" % (agent, peer_addr, datetime.fromtimestamp(agent_info["seen"]))
for service in host_info["services"]: for host, host_info in agent_info["hosts"].items():
print " * %s" % (service) print " * %s" % (host)
for service in host_info["services"]:
print " * %s" % (service)
sys.exit(0) sys.exit(0)