package codeine.nodes; import java.net.InetAddress; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import org.apache.log4j.Logger; import codeine.PeerStatusChangedUpdater; import codeine.RunMonitors; import codeine.SnoozeKeeper; import codeine.api.NodeInfo; import codeine.collectors.CollectorsRunner; import codeine.collectors.CollectorsRunnerFactory; import codeine.configuration.IConfigurationManager; import codeine.configuration.PathHelper; import codeine.executer.PeriodicExecuter; import codeine.executer.Task; import codeine.jsons.nodes.NodesManager; import codeine.jsons.peer_status.PeerStatus; import codeine.jsons.project.ProjectJson; import codeine.mail.MailSender; import codeine.mail.NotificationDeliverToDatabase; import codeine.model.Constants; import codeine.utils.network.InetUtils; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; public class NodesRunner implements Task{ private static final Logger log = Logger.getLogger(NodesRunner.class); private static final long NODE_MONITOR_INTERVAL = TimeUnit.SECONDS.toMillis(29); public static final long NODE_RUNNER_INTERVAL = TimeUnit.HOURS.toMillis(1); @Inject private IConfigurationManager configurationManager; @Inject private PathHelper pathHelper; @Inject private PeerStatus peerStatus; @Inject private MailSender mailSender; @Inject private NotificationDeliverToDatabase notificationDeliverToMongo; @Inject private NodesManager nodesManager; @Inject private SnoozeKeeper snoozeKeeper; private Map<String, Map<NodeInfo, PeriodicExecuter>> executers = Maps.newHashMap(); @Inject private PeerStatusChangedUpdater mongoPeerStatusUpdater; @Inject private CollectorsRunnerFactory collectorsRunnerFactory; @Override public synchronized void run() { InetAddress localHost = InetUtils.getLocalHost(); log.info("NodeRunner is starting on host " + localHost.getHostName() + " " + localHost.getCanonicalHostName()); log.info("NodeRunner is starting " + this + " with executers " + executers); Set<String> removedProjects = Sets.newHashSet(executers.keySet()); for (ProjectJson project : getProjects()) { removedProjects.remove(project.name()); try { boolean hasNodes = startStopExecutorsForProject(project); if (!hasNodes) { cleanupProject(project.name()); } } catch (Exception e) { log.error("failed startStopExecutorsForProject for project " + project.name(), e); } } for (String project : removedProjects) { try { stopNodes(project, executers.get(project)); cleanupProject(project); log.info("removed project " + project); } catch (Exception e) { log.error("failed to stop nodes for project " + project, e); } } } /** * assuming nodes already stopped */ private void cleanupProject(String project) { log.info("cleanupProject " + project); executers.remove(project); peerStatus.removeProject(project); } private void stop(PeriodicExecuter e) { log.info("stopping 1executor " + e.name()); e.stopWhenPossible(); } private boolean startStopExecutorsForProject(ProjectJson project) { Map<NodeInfo, PeriodicExecuter> currentNodes = getCurrentNodes(project); log.info("project: " + project.name() + " currentProjectExecutors: " + currentNodes.keySet()); SelectedNodes selectedNodes; try { selectedNodes = new NodesSelector(currentNodes, getNodes(project)).selectStartStop(); } catch (Exception e) { log.error("failed to select nodes for project " + project.name() + " will leave old nodes " + currentNodes, e); return !currentNodes.isEmpty(); } log.info("selectedNodes: " + selectedNodes); stopNodes(project.name(), selectedNodes.nodesToStop()); Map<NodeInfo, PeriodicExecuter> newProjectExecutors = selectedNodes.existingProjectExecutors(); for (NodeInfo nodeJson : selectedNodes.nodesToStart()) { log.info("start exec1 monitoring node " + nodeJson + " in project " + project.name()); try { PeriodicExecuter e = startExecuter(project, nodeJson); newProjectExecutors.put(nodeJson, e); } catch (Exception e1) { log.error("failed to start executor for node " + nodeJson + " in project " + project.name(), e1); } } executers.put(project.name(), newProjectExecutors); log.info("project: " + project.name() + " newProjectExecutors: " + newProjectExecutors.keySet()); return !executers.get(project.name()).isEmpty(); } private void stopNodes(String project, Map<NodeInfo, PeriodicExecuter> map) { for (Entry<NodeInfo, PeriodicExecuter> e : map.entrySet()) { log.info("stop exec1 monitoring node " + e.getKey() + " in project " + project); peerStatus.removeNode(project, e.getKey().name()); stop(e.getValue()); } } private Map<NodeInfo, PeriodicExecuter> getCurrentNodes(ProjectJson project) { Map<NodeInfo, PeriodicExecuter> currentNodes = executers.get(project.name()); if (null == currentNodes) { currentNodes = Maps.newHashMap(); executers.put(project.name(), currentNodes); } return currentNodes; } private PeriodicExecuter startExecuter(ProjectJson project, NodeInfo nodeJson) { log.info("Starting monitor thread for project " + project.name() + " node " + nodeJson); Task task; RunMonitors monitorsTask = new RunMonitors(configurationManager, project.name(), peerStatus, mailSender, pathHelper, nodeJson, notificationDeliverToMongo, mongoPeerStatusUpdater, snoozeKeeper); if (Constants.RUNNING_COLLECTORS_IN_PEER) { CollectorsRunner collectorsTask = collectorsRunnerFactory.create(project.name(), nodeJson); collectorsTask.init(); task = collectorsTask; } else { task = monitorsTask; } PeriodicExecuter periodicExecuter = new PeriodicExecuter(NODE_MONITOR_INTERVAL, task, "RunMonitors_" + project.name() + "_" + nodeJson.name()); log.info("starting 1executor " + periodicExecuter.name()); periodicExecuter.runInThread(); return periodicExecuter; } private List<ProjectJson> getProjects() { return configurationManager.getConfiguredProjects(); } private List<NodeInfo> getNodes(ProjectJson project) { try { return nodesManager.nodesOf(project).nodes(); } catch (Exception e) { log.warn("failed to get nodes for project " + project.name(), e); } return Lists.newArrayList(); } }