org.helios.jzab.agent.net.active.ActiveAgent.java Source code

Java tutorial

Introduction

Here is the source code for org.helios.jzab.agent.net.active.ActiveAgent.java

Source

/**
 * Helios, OpenSource Monitoring
 * Brought to you by the Helios Development Group
 *
 * Copyright 2007, Helios Development Group and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 
 *
 */
package org.helios.jzab.agent.net.active;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.management.AttributeChangeNotification;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectName;

import org.helios.jzab.agent.SystemClock;
import org.helios.jzab.agent.internal.jmx.ScheduledThreadPoolFactory;
import org.helios.jzab.agent.internal.jmx.TaskScheduler;
import org.helios.jzab.agent.internal.jmx.ThreadPoolFactory;
import org.helios.jzab.agent.logging.LoggerManager;
import org.helios.jzab.agent.net.active.ActiveHost.ActiveHostCheck;
import org.helios.jzab.agent.net.active.collection.ActiveCollectionStream;
import org.helios.jzab.agent.net.active.collection.ActiveCollectionStreamType;
import org.helios.jzab.agent.net.active.collection.CommandThreadPolicy;
import org.helios.jzab.agent.net.active.collection.IResultCollector;
import org.helios.jzab.agent.net.active.schedule.ActiveScheduleBucket;
import org.helios.jzab.agent.net.routing.JSONResponseHandler;
import org.helios.jzab.util.JMXHelper;
import org.helios.jzab.util.XMLHelper;
import org.jboss.netty.channel.Channel;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;

/**
 * <p>Title: ActiveAgent</p>
 * <p>Description: Service that coordinates the configuration and execution of active host checks</p> 
 * <p>Company: Helios Development Group LLC</p>
 * @author Whitehead (nwhitehead AT heliosdev DOT org)
 * <p><code>org.helios.jzab.agent.net.active.ActiveAgent</code></p>
 */
public class ActiveAgent implements ActiveAgentMXBean, NotificationListener {
    /** Instance logger */
    protected final Logger log = LoggerFactory.getLogger(getClass());

    /** The task scheduler */
    protected final TaskScheduler scheduler;
    /** The asynch task executor */
    protected final ThreadPoolExecutor executor;

    /** The collection threading policy */
    protected CommandThreadPolicy commandThreadPolicy;
    /** The result collation type policy */
    protected ActiveCollectionStreamType collectionStreamType;

    /** The master schedule bucket for this agent */
    protected final ActiveScheduleBucket<ActiveServer, ActiveAgent> scheduleBucket;

    /** The agent level refresh period in seconds */
    protected long agentRefreshPeriod;
    /** The agent level collection timeout in seconds */
    protected long agentCollectionTimeout;

    /** A map of the configured active servers keyed by <code>address:port</code> */
    protected final Map<String, ActiveServer> activeServers = new ConcurrentHashMap<String, ActiveServer>();

    /** The active agent singleton instance */
    private static volatile ActiveAgent instance = null;
    /** The active agent singleton instance ctor lock */
    private static final Object lock = new Object();

    /** Indicates if the active agent has been started */
    protected final AtomicBoolean started = new AtomicBoolean(false);
    /** Sets of active servers with scheduled checks keyed by the delay of the checks */
    protected final Map<Long, Set<ActiveServer>> serverSchedules = new ConcurrentHashMap<Long, Set<ActiveServer>>();

    /** The ActiveAgent JMX ObjectName */
    public static final ObjectName OBJECT_NAME = JMXHelper
            .objectName("org.helios.jzab.agent.active:service=ActiveAgent");
    /** The configuration node name */
    public static final String NODE = "active-agent";

    /** The default collection threading policy */
    public static final CommandThreadPolicy DEFAULT_COLLECTION_THREADING_POLICY = CommandThreadPolicy.HOST;
    /** The collection threading policy attribute name */
    public static final String COLLECTION_THREADING_POLICY_ATTR = "threading-policy";
    /** The default collation policy */
    public static final ActiveCollectionStreamType DEFAULT_COLLATION_TYPE = ActiveCollectionStreamType.DISK;
    /** The collation policy attribute name */
    public static final String COLLATION_TYPE_ATTR = "collation-type";
    /** The default collection timeout in seconds */
    public static final long DEFAULT_COLLECTION_TIMEOUT = 5;
    /** The collection timeout attribute name */
    public static final String COLLECTION_TIMEOUT_ATTR = "collection-timeout";

    /** The default agent refresh period on which the agent attempts to refresh marching orders for all monitored servers, which is 3600 or 1 hour */
    public static final long DEFAULT_AGENT_REFRESH = 60 * 60;
    /** The agent refresh attribute name */
    public static final String AGENT_REFRESH_ATTR = "refresh";

    /**
     * Initializes the ActiveAgent singleton instance
     * @param configNode The configuration node
     * @return the ActiveAgent singleton instance
     */
    public static ActiveAgent getInstance(Node configNode) {
        if (instance == null) {
            synchronized (lock) {
                if (instance == null) {
                    instance = new ActiveAgent(configNode);
                }
            }
        }
        return instance;
    }

    /**
     * Acquires the ActiveAgent singleton instance
     * @return the ActiveAgent singleton instance
     */
    public static ActiveAgent getInstance() {
        if (instance == null) {
            throw new IllegalStateException("The ActiveAgent has not been initialized", new Throwable());
        }
        return instance;
    }

    /**
     * Returns the active checks scheduled for the passed delay
     * @param delay The delay to get checks for
     * @return the active checks scheduled 
     */
    public Set<ActiveHostCheck> getChecksForDelay(long delay) {
        Set<ActiveHostCheck> set = new HashSet<ActiveHostCheck>();
        for (ActiveServer server : scheduleBucket.get(delay)) {
            set.addAll(server.getChecksForDelay(delay));
        }
        return set;
    }

    /**
     * Returns the active servers that have hosts with active checks scheduled for the passed delay
     * @param delay The active check collection delay
     * @return A set of ActiveServers.
     */
    public Set<ActiveServer> getServersForDelay(long delay) {
        return Collections.unmodifiableSet(scheduleBucket.get(delay));
    }

    /**
     * Executes all the checks in all this agent's servers for the passed delay
     * @param delay The delay window
     * @param collector The result collection stream
     */
    public void executeChecks(long delay, IResultCollector collector) {
        for (ActiveServer server : activeServers.values()) {
            server.executeChecks(delay, collector);
        }
    }

    /**
     * Returns the number of active zabbix servers configured
     * @return the number of active zabbix servers configured
     */
    public int getActiveServerCount() {
        return activeServers.size();
    }

    /**
     * Creates a new ActiveAgent
     * @param configNode The configuration node
     */
    private ActiveAgent(Node configNode) {
        if (configNode == null)
            throw new IllegalArgumentException("The passed configuration node was null", new Throwable());
        log.info("Configuring ActiveAgent");
        String nodeName = configNode.getNodeName();
        if (!NODE.equals(nodeName)) {
            throw new RuntimeException(
                    "Configuration Node expected to have node name [" + NODE + "] but was [" + nodeName + "]",
                    new Throwable());
        }
        agentRefreshPeriod = XMLHelper.getAttributeByName(configNode, AGENT_REFRESH_ATTR, DEFAULT_AGENT_REFRESH);
        agentCollectionTimeout = XMLHelper.getAttributeByName(configNode, COLLECTION_TIMEOUT_ATTR,
                DEFAULT_COLLECTION_TIMEOUT);
        String schedulerName = null, executorName = null;
        try {
            schedulerName = XMLHelper.getAttributeByName(
                    XMLHelper.getChildNodeByName(configNode, "scheduler-pool", false), "name", "Scheduler");
            scheduler = ScheduledThreadPoolFactory.getInstance(schedulerName);
        } catch (Exception e) {
            throw new RuntimeException("ActiveAgent failed to get scheduler named [" + schedulerName + "]", e);
        }
        try {
            executorName = XMLHelper.getAttributeByName(
                    XMLHelper.getChildNodeByName(configNode, "task-pool", false), "name", "TaskExecutor");
            executor = ThreadPoolFactory.getInstance(executorName);
        } catch (Exception e) {
            throw new RuntimeException("ActiveAgent failed to get task executor named [" + executorName + "]", e);
        }

        commandThreadPolicy = CommandThreadPolicy.forName(XMLHelper.getAttributeByName(configNode,
                COLLECTION_THREADING_POLICY_ATTR, DEFAULT_COLLECTION_THREADING_POLICY.name()));
        collectionStreamType = ActiveCollectionStreamType.forName(
                XMLHelper.getAttributeByName(configNode, COLLATION_TYPE_ATTR, DEFAULT_COLLATION_TYPE.name()));

        scheduleBucket = new ActiveScheduleBucket<ActiveServer, ActiveAgent>(ActiveServer.class) {
            /**
             * {@inheritDoc}
             * @see org.helios.jzab.agent.net.active.schedule.ActiveScheduleBucket#fireStartScheduledEvent(long)
             */
            @Override
            public void fireStartScheduledEvent(final long delay) {
                super.fireStartScheduledEvent(delay);
                scheduler.scheduleAtFixedRate("Delayed Active Checks [" + delay + "]", new Runnable() {
                    @Override
                    public void run() {
                        ActiveCollectionStream.execute(collectionStreamType, commandThreadPolicy, delay,
                                agentCollectionTimeout);
                    }
                }, delay, delay, TimeUnit.SECONDS);
            }
        };

        Node servers = XMLHelper.getChildNodeByName(configNode, "servers", false);
        if (servers == null) {
            log.warn("ActiveAgent had no configured servers to be active for....");
            return;
        }
        int serverCount = 0;
        for (Node serverNode : XMLHelper.getChildNodesByName(servers, "server", false)) {
            String address = XMLHelper.getAttributeByName(serverNode, "address", null);
            if (address == null || address.trim().isEmpty()) {
                log.warn("Empty active agent server name. Node was [{}]", XMLHelper.getStringFromNode(serverNode));
                continue;
            }
            int port = XMLHelper.getAttributeByName(serverNode, "port", 10051);
            long refresh = XMLHelper.getAttributeByName(serverNode, "refresh", agentRefreshPeriod);
            String key = address + ":" + port;
            if (activeServers.containsKey(key)) {
                log.warn("Duplicate active server definition [{}]", key);
                continue;
            }
            activeServers.put(key, new ActiveServer(this, address, port, refresh, executor, scheduleBucket,
                    XMLHelper.getChildNodeByName(serverNode, "hosts", false)));
            serverCount++;
        }
        if (serverCount == 0) {
            log.warn("ActiveAgent had no configured servers to be active for....");
        }
        JMXHelper.registerMBean(JMXHelper.getHeliosMBeanServer(),
                JMXHelper.objectName("org.helios.jzab.agent.active:service=ActiveAgent"), this);
    }

    /**
     *  Asynchronously starts the active agent
     */
    public void start() {
        if (started.get()) {
            log.warn("ActiveAgent already started");
            return;
        }
        executor.execute(new Runnable() {
            @Override
            public void run() {
                initializeServers();
                started.set(true);
            }
        });

    }

    /**
     * Initializes this agent's active servers
     */
    protected void initializeServers() {
        log.debug("Initializing Servers");
        for (ActiveServer server : activeServers.values()) {
            long currentTime = SystemClock.currentTimeMillis();
            if (server.requiresRefresh(currentTime)) {
                for (ActiveHost ah : server) {
                    ah.addNotificationListener(this, null, ah);
                    requestActiveChecks(server, ah, true);
                }
                //server.refreshActiveChecks();
                //executeInitialCheck(server);
            }
        }
    }

    /**
     * Issues a request for an Active Check summary from the zabbix server.
     * The response for this request will be roited back to the matching instance of the {@link ActiveHost}
     * and handled in {@link ActiveHost#upsertActiveChecks(org.json.JSONArray)}
     * @param server The active server to get the active checks from 
     * @param host The active host to get the active checks for
     * @param force If true, will force the request, even if the host is up to date
     */
    public void requestActiveChecks(ActiveServer server, ActiveHost host, boolean force) {
        if (!host.isRequiresRefresh() && !force) {
            log.debug(
                    "ActiveCheck request cancelled. Host [{}] for server [{}] is up to date and no force requested",
                    host.getHostName(), server.getId());
        } else {
            final Channel channel = ActiveClient.getInstance().newChannel(server.getAddress(), server.getPort());
            try {
                log.debug("[Active Check] Acquired channel [{}]", channel);
                channel.write(new JSONObject()
                        .put(JSONResponseHandler.KEY_REQUEST, JSONResponseHandler.VALUE_ACTIVE_CHECK_REQUEST)
                        .put(JSONResponseHandler.KEY_HOST, host.getHostName()));
            } catch (Exception e) {
                log.error("Failed to execute ActiveCheck request for server [{}]. Error was [{}]", server,
                        e.getMessage());
                log.debug("Failed to execute ActiveCheck request for server [{}]", server, e);
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * Issues a request for an Active Check summary from the zabbix server.
     * The response for this request will be roited back to the matching instance of the {@link ActiveHost}
     * and handled in {@link ActiveHost#upsertActiveChecks(org.json.JSONArray)}
     * @param serverId The ID of zabbix server to get the active checks from 
     * @param hostName THe host name to get the active checks for
     * @param force If true, will force the request, even if the host is up to date
     */
    @Override
    public void requestActiveChecks(String serverId, String hostName, boolean force) {
        if (serverId == null)
            throw new IllegalArgumentException("The passed serverId was null", new Throwable());
        if (hostName == null)
            throw new IllegalArgumentException("The passed hostName was null", new Throwable());
        ActiveServer server = this.activeServers.get(serverId);
        if (server == null) {
            log.warn("Unable to request active checks. No server with id [{}] was found", serverId);
            throw new RuntimeException("No server with id [" + serverId + "] was found", new Throwable());
        }
        ActiveHost activeHost = server.getActiveHost(hostName);
        if (activeHost == null) {
            log.warn("Unable to request active checks. No host named [{}] was found for server with id [{}]",
                    hostName, serverId);
            throw new RuntimeException("No server with id [" + serverId + "] was found", new Throwable());
        }
        requestActiveChecks(server, activeHost, force);
    }

    /**
     * Executes a check of all an active hosts checks and submits asynchronously
     * @param serverId The id of the server managing the host to execute checks for
     * @param hostName The name of the active host to execute checks for
     */
    @Override
    public void executeChecks(String serverId, String hostName) {
        if (serverId == null)
            throw new IllegalArgumentException("The passed serverId was null", new Throwable());
        if (hostName == null)
            throw new IllegalArgumentException("The passed hostName was null", new Throwable());
        ActiveServer server = activeServers.get(serverId);
        if (server == null) {
            log.warn("Unable to execute active checks. No server with id [{}] was found", serverId);
            throw new RuntimeException("No server with id [" + serverId + "] was found", new Throwable());
        }
        ActiveHost activeHost = server.getActiveHost(hostName);
        if (activeHost == null) {
            log.warn("Unable to execute active checks. No host named [{}] was found for server with id [{}]",
                    hostName, serverId);
            throw new RuntimeException("No server with id [" + serverId + "] was found", new Throwable());
        }
        executeChecks(activeHost);
    }

    /**
     * Executes a check of all an active hosts checks and submits asynchronously
     * @param activeHost The active host to execute checks for
     */
    protected void executeChecks(final ActiveHost activeHost) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    ActiveCollectionStream.execute(collectionStreamType, activeHost, ActiveClient.getInstance()
                            .newChannel(activeHost.getServer().getAddress(), activeHost.getServer().getPort()));
                } catch (Exception e) {
                    log.error("Collection Failure", e);
                } finally {
                }
            }
        });
    }

    /**
     * Returns a map of the number of servers registered for checks for each delay
     * @return a map of the number of servers registered for checks for each delay
     */
    @Override
    public Map<Long, Integer> getScheduleCounts() {
        Map<Long, Integer> map = new HashMap<Long, Integer>(scheduleBucket.size());
        for (Map.Entry<Long, Set<ActiveServer>> entry : scheduleBucket.entrySet()) {
            map.put(entry.getKey(), entry.getValue().size());
        }
        return map;
    }

    /**
     * {@inheritDoc}
     * @see org.helios.jzab.agent.net.active.ActiveAgentMXBean#getLevel()
     */
    @Override
    public String getLevel() {
        return LoggerManager.getInstance().getLoggerLevelManager().getLoggerLevel(getClass().getName());
    }

    /**
     * {@inheritDoc}
     * @see org.helios.jzab.agent.net.active.ActiveAgentMXBean#setLevel(java.lang.String)
     */
    @Override
    public void setLevel(String level) {
        LoggerManager.getInstance().getLoggerLevelManager().setLoggerLevel(getClass().getName(), level);
    }

    /**
     * Returns the command thread policy
     * @return the commandThreadPolicy
     */
    @Override
    public String getCommandThreadPolicy() {
        return commandThreadPolicy.name();
    }

    /**
     * Sets the command thread policy
     * @param commandThreadPolicyName the commandThreadPolicy to set
     */
    @Override
    public void setCommandThreadPolicy(String commandThreadPolicyName) {
        this.commandThreadPolicy = CommandThreadPolicy.forName(commandThreadPolicyName);
    }

    /**
     * Returns the collation type name
     * @return the collation type name
     */
    @Override
    public String getCollationType() {
        return collectionStreamType.name();
    }

    /**
     * Sets the collation type name
     * @param collationTypeName the collation type name to set
     */
    @Override
    public void setCollationType(String collationTypeName) {
        this.collectionStreamType = ActiveCollectionStreamType.forName(collationTypeName);
    }

    /**
     * {@inheritDoc}
     * @see javax.management.NotificationListener#handleNotification(javax.management.Notification, java.lang.Object)
     */
    @Override
    public void handleNotification(Notification notif, Object handback) {
        if (notif instanceof AttributeChangeNotification) {
            AttributeChangeNotification acn = (AttributeChangeNotification) notif;
            log.debug("Handling Attribute Change Notification [{}]", acn);

            if (handback instanceof ActiveHost) {
                ActiveHost host = (ActiveHost) handback;

                if (ActiveHostState.ACTIVE.name().equals(acn.getNewValue())) {
                    executeChecks(host);
                    return;
                }

                JSONObject results = new JSONObject();
                JSONArray array = new JSONArray();

                for (ActiveHostCheck check : host.getDiscoveryChecks()) {
                    try {
                        JSONObject[] checkResults = (JSONObject[]) check.discover();
                        for (JSONObject disc : checkResults) {
                            array.put(disc);
                        }
                    } catch (Exception e) {
                    }
                }
                try {
                    results.put("data", array);
                } catch (Exception e) {
                    log.error("JSON Error", e);
                }
                try {
                    log.debug("Sending Discovery Check Results\n[{}]", results);
                    JSONObject result = ActiveClient.getInstance().requestResponse(results, JSONObject.class,
                            host.getServer(), 2, TimeUnit.SECONDS);
                    log.info("Discovery Check Result \n[{}]", result);
                } catch (Exception e) {
                    log.error("Failed to execute discovery check submission for [{}]", host, e);
                }
            }
        }
    }

    /**
     * Returns the agent level refresh period in seconds
     * @return the agent level refresh period in seconds
     */
    @Override
    public long getAgentRefreshPeriod() {
        return agentRefreshPeriod;
    }

    /**
     * Sets the agent level refresh period in seconds
     * @param agentRefreshPeriod the agent level refresh period in seconds
     */
    @Override
    public void setAgentRefreshPeriod(long agentRefreshPeriod) {
        this.agentRefreshPeriod = agentRefreshPeriod;
    }

    /**
     * Returns the agent level collection timeout in seconds
     * @return the agent level collection timeout in seconds
     */
    @Override
    public long getAgentCollectionTimeout() {
        return agentCollectionTimeout;
    }

    /**
     * Sets the agent level collection timeout in seconds
     * @param agentCollectionTimeout the agent level collection timeout in seconds
     */
    @Override
    public void setAgentCollectionTimeout(long agentCollectionTimeout) {
        this.agentCollectionTimeout = agentCollectionTimeout;
    }

}

/*
 <active-agent refresh="10">
<scheduler-pool name="Scheduler" />
<servers>   
   <server address="10.230.12.145" port="10051" refresh="20">
      <hosts>
         <host name="NE-WK-NWHI-01 Active" refresh="120" />
      </hosts>
   </server>
</servers>
 </active-agent>
     
 "response":"failed",
  "info":"host [NE-WK-NWHI-01 ActiveX] not found"}
      
{
   "response":"success",
   "info":"Processed 47 Failed 0 Total 47 Seconds spent 0.000708"}
       
{
   "response":"success",
   "info":"Processed 0 Failed 14 Total 14 Seconds spent 0.000368"}     
    
 */