package nl.ipo.cds.nagios;
import java.io.File;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import nl.ipo.cds.nagios.ast.HostStatusNode;
import nl.ipo.cds.nagios.ast.ServiceStatusNode;
import nl.ipo.cds.nagios.config.NagiosStatusConfiguration;
import nl.ipo.cds.nagios.harvester.Harvester;
import nl.ipo.cds.nagios.harvester.HarvesterListener;
public class NagiosStatus implements NagiosStatusService {
private static final Log log = LogFactory.getLog (NagiosStatus.class);
private final static String DEFAULT_FILENAME = "/var/cache/nagios3/status.dat";
private static class Pair<A,B> {
public final A a;
public final B b;
public Pair (final A a, final B b) {
this.a = a;
this.b = b;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((a == null) ? 0 : a.hashCode());
result = prime * result + ((b == null) ? 0 : b.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Pair<?,?> other = (Pair<?,?>) obj;
if (a == null) {
if (other.a != null)
return false;
} else if (!a.equals(other.a))
return false;
if (b == null) {
if (other.b != null)
return false;
} else if (!b.equals(other.b))
return false;
return true;
}
}
private NagiosStatusConfiguration configuration;
private Harvester harvester;
private Map<String, HostStatusNode> hostStatus = new ConcurrentHashMap<String, HostStatusNode> ();
private Map<Pair<String, String>, ServiceStatusNode> serviceStatus = new ConcurrentHashMap<NagiosStatus.Pair<String,String>, ServiceStatusNode> ();
public NagiosStatus (final String filename, final Collection<String> hosts, final Collection<String> services) {
configuration = new NagiosStatusConfiguration ();
configuration.setLocation (new File (filename));
configuration.setHosts (new HashSet<String> (hosts));
configuration.setServices (new HashSet<String> (services));
harvester = new Harvester (configuration);
}
public void run () {
while (true) {
final Set<String> oldHosts = new HashSet<String> (hostStatus.keySet ());
final Set<Pair<String, String>> oldServices = new HashSet<NagiosStatus.Pair<String,String>> (serviceStatus.keySet ());
harvester.harvest(new HarvesterListener() {
@Override
public void putStatus(HostStatusNode status) {
final String key = status.getHostName ();
hostStatus.put (key, status);
oldHosts.remove (key);
}
@Override
public void putStatus(ServiceStatusNode status) {
final Pair<String, String> key = new Pair<String, String> (status.getHostName (), status.getServiceDescription ());
serviceStatus.put(key, status);
oldServices.remove (key);
}
});
for (final String key: oldHosts) {
hostStatus.remove (key);
}
for (final Pair<String, String> key: oldServices) {
serviceStatus.remove (key);
}
log.debug (String.format ("Host status: %d, service status: %d", hostStatus.size (), serviceStatus.size ()));
try {
Thread.sleep (10000);
} catch (InterruptedException e) {
log.error ("Harvester interrupted", e);
}
}
}
public static Options createOptions () {
final Options options = new Options ();
options
.addOption ("f", "file", true, "Status filename")
.addOption ("H", "hosts", true, "Hostnames, comma separated")
.addOption ("s", "services", true, "Services, comma separated");
return options;
}
public static void printUsage (final String applicationName, final Options options, final OutputStream out) {
final PrintWriter writer = new PrintWriter (out);
final HelpFormatter usageFormatter = new HelpFormatter ();
usageFormatter.printUsage (writer, 80, applicationName, options);
writer.close ();
}
private static List<String> parseList (final String value) {
final List<String> values = new ArrayList<String> ();
for (final String s: value.split(",")) {
values.add (s.trim ());
}
return values;
}
public static void main (final String[] args) {
final CommandLineParser cmdLineParser = new GnuParser ();
final Options options = createOptions ();
final String filename;
final List<String> hosts;
final List<String> services;
try {
final CommandLine commandLine = cmdLineParser.parse (options, args);
filename = commandLine.getOptionValue ("f", DEFAULT_FILENAME);
hosts = parseList (commandLine.getOptionValue ("H", ""));
services = parseList (commandLine.getOptionValue ("s", ""));
} catch (ParseException e) {
log.error ("Encountered an exception while parsing using GnuParser", e);
System.exit (1);
return;
}
// Add the commandline arguments as beans to the root context:
final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory ();
final BeanDefinition hostsBeanDefinition = BeanDefinitionBuilder
.rootBeanDefinition(ArrayList.class)
.addConstructorArg (hosts)
.getBeanDefinition ();
final BeanDefinition servicesBeanDefinition = BeanDefinitionBuilder
.rootBeanDefinition(ArrayList.class)
.addConstructorArg (services)
.getBeanDefinition ();
final BeanDefinition filenameBeanDefinition = BeanDefinitionBuilder
.rootBeanDefinition(String.class)
.addConstructorArg (filename)
.getBeanDefinition ();
beanFactory.registerBeanDefinition ("hosts", hostsBeanDefinition);
beanFactory.registerBeanDefinition ("services", servicesBeanDefinition);
beanFactory.registerBeanDefinition ("filename", filenameBeanDefinition);
final GenericApplicationContext argumentsContext = new GenericApplicationContext (beanFactory);
argumentsContext.refresh ();
// Create the application context:
final ApplicationContext ctx = new ClassPathXmlApplicationContext (new String[] { "nl/ipo/cds/nagios/nagios-status-server.xml" }, argumentsContext);
final NagiosStatus nagiosStatus = (NagiosStatus)ctx.getBean ("nagiosStatusService");
// Run the parser in the background:
new Thread (new Runnable() {
public void run() {
nagiosStatus.run ();
}
}).start ();
}
@Override
public Collection<HostStatusNode> getHostStatus() {
return null;
}
@Override
public Collection<ServiceStatusNode> getServiceStatus() {
return null;
}
@Override
public HostStatusNode getHostStatus(String hostName) {
return hostStatus.get (hostName);
}
@Override
public ServiceStatusNode getServiceStatus(String hostName,
String serviceDescription) {
return serviceStatus.get(new Pair<String, String> (hostName, serviceDescription));
}
@Override
public Collection<String> getAvailableHosts() {
return new ArrayList<String> (hostStatus.keySet ());
}
@Override
public Collection<String> getAvailableServices() {
final Set<String> services = new HashSet<String> ();
for (final Pair<String, String> pair: serviceStatus.keySet ()) {
services.add (pair.b);
}
return services;
}
}