/******************************************************************************* * * Copyright (c) 2004-2011 Oracle Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Kohsuke Kawaguchi, Seiji Sogabe, Winston Prakash * * *******************************************************************************/ package hudson.model; import hudson.Extension; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.export.ExportedBean; import org.kohsuke.stapler.export.Exported; import java.io.IOException; import java.util.List; import org.eclipse.hudson.graph.ColorPalette; import org.eclipse.hudson.graph.MultiStageTimeSeries; import org.eclipse.hudson.graph.MultiStageTimeSeries.TimeScale; import org.eclipse.hudson.graph.MultiStageTimeSeries.TrendChart; /** * Utilization statistics for a node or a set of nodes. * * <h2>Implementation Note</h2> <p> Instances of this class is not capable of * updating the statistics itself — instead, it's done by the * {@link LoadStatisticsUpdater} timer. This is more efficient (as it allows us * a single pass to update all stats), but it's not clear to me if the loss of * autonomy is worth it. * * @author Kohsuke Kawaguchi * @see Label#loadStatistics * @see Hudson#overallLoad */ @ExportedBean public abstract class LoadStatistics { /** * Number of busy executors and how it changes over time. */ @Exported public final MultiStageTimeSeries busyExecutors; /** * Number of total executors and how it changes over time. */ @Exported public final MultiStageTimeSeries totalExecutors; /** * Number of {@link Queue.BuildableItem}s that can run on any node in this * node set but blocked. */ @Exported public final MultiStageTimeSeries queueLength; protected LoadStatistics(int initialTotalExecutors, int initialBusyExecutors) { this.totalExecutors = new MultiStageTimeSeries( Messages._LoadStatistics_Legends_TotalExecutors(), ColorPalette.BLUE, initialTotalExecutors, DECAY); this.busyExecutors = new MultiStageTimeSeries( Messages._LoadStatistics_Legends_BusyExecutors(), ColorPalette.RED, initialBusyExecutors, DECAY); this.queueLength = new MultiStageTimeSeries( Messages._LoadStatistics_Legends_QueueLength(), ColorPalette.GREY, 0, DECAY); } public float getLatestIdleExecutors(TimeScale timeScale) { return totalExecutors.pick(timeScale).getLatest() - busyExecutors.pick(timeScale).getLatest(); } /** * Computes the # of idle executors right now and obtains the snapshot * value. */ public abstract int computeIdleExecutors(); /** * Computes the # of total executors right now and obtains the snapshot * value. */ public abstract int computeTotalExecutors(); /** * Computes the # of queue length right now and obtains the snapshot value. */ public abstract int computeQueueLength(); /** * Creates {@link CategoryDataset} which then becomes the basis of the load * statistics graph. */ public TrendChart createTrendChart(TimeScale timeScale) { return MultiStageTimeSeries.createTrendChart(timeScale, totalExecutors, busyExecutors, queueLength); } /** * Generates the load statistics graph. */ public TrendChart doGraph(@QueryParameter String type) throws IOException { return createTrendChart(TimeScale.parse(type)); } public Api getApi() { return new Api(this); } /** * With 0.90 decay ratio for every 10sec, half reduction is about 1 min. */ public static final float DECAY = Float.parseFloat(System.getProperty(LoadStatistics.class.getName() + ".decay", "0.9")); /** * Load statistics clock cycle in milliseconds. Specify a small value for * quickly debugging this feature and node provisioning through cloud. */ public static int CLOCK = Integer.getInteger(LoadStatistics.class.getName() + ".clock", 10 * 1000); /** * Periodically update the load statistics average. */ @Extension public static class LoadStatisticsUpdater extends PeriodicWork { public long getRecurrencePeriod() { return CLOCK; } protected void doRun() { Hudson h = Hudson.getInstance(); List<hudson.model.Queue.BuildableItem> bis = h.getQueue().getBuildableItems(); // update statistics on slaves for (Label l : h.getLabels()) { l.loadStatistics.totalExecutors.update(l.getTotalExecutors()); l.loadStatistics.busyExecutors.update(l.getBusyExecutors()); int q = 0; for (hudson.model.Queue.BuildableItem bi : bis) { if (bi.task.getAssignedLabel() == l) { q++; } } l.loadStatistics.queueLength.update(q); } // update statistics of the entire system ComputerSet cs = new ComputerSet(); h.overallLoad.totalExecutors.update(cs.getTotalExecutors()); h.overallLoad.busyExecutors.update(cs.getBusyExecutors()); int q = 0; for (hudson.model.Queue.BuildableItem bi : bis) { if (bi.task.getAssignedLabel() == null) { q++; } } h.overallLoad.queueLength.update(q); h.overallLoad.totalQueueLength.update(bis.size()); } } }