xref: /illumos-gate/usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/SystemMonitor.java (revision 1edba515a3484e0f74b638b203d462b3112ac84d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * ident	"%Z%%M%	%I%	%E% SMI"
27  */
28 
29 package com.sun.solaris.domain.pools;
30 
31 import java.math.BigInteger;
32 import java.text.DecimalFormat;
33 import java.util.*;
34 import java.util.logging.*;
35 
36 import com.sun.solaris.service.kstat.*;
37 import com.sun.solaris.service.logging.Severity;
38 import com.sun.solaris.domain.pools.*;
39 import com.sun.solaris.service.pools.*;
40 import com.sun.solaris.service.timer.*;
41 
42 /**
43  * Regularly samples the objective-related utilization statistics of
44  * the resource in the pool with the given configuration.
45  */
46 class SystemMonitor implements Monitor
47 {
48 	/**
49 	 * The pool configuration with resources to be monitored.
50 	 */
51 	private Configuration conf;
52 
53 	/**
54 	 * The map of monitored resources.The map is keyed by
55 	 * resource, with the values being the monitorable instances.
56 	 */
57 	private Map monitored;
58 
59 	/**
60 	 * Default sample interval (milliseconds).
61 	 */
62 	public static final int DEF_SAMPLE_INT = 15000;
63 
64 	/**
65 	 * Sample interval (milliseconds, default 15000).
66 	 */
67 	private int interval;
68 
69 	/**
70 	 * Sample interval property name.
71 	 */
72 	static final String SAMPLE_INTERVAL_PROP_NAME =
73 	    "system.poold.monitor-interval";
74 
75 	/**
76 	 * Kstat interface for raw data
77 	 */
78 	private KstatCtl kc;
79 
80 	/**
81 	 * Raw statistics which are sampled each interval.
82 	 */
83 	private final String stats[] = { "idle", "kernel", "wait", "user" };
84 
85 	/**
86 	 * Timer regulating sampling frequency.
87 	 */
88 	private RecurringEventTimer timer;
89 
90 	/**
91 	 * The number of samples taken.
92 	 */
93 	private int sampleCount = 0;
94 
95 	/**
96 	 * Time the last sample was taken.
97 	 */
98 	private Date lastSampleTime = null;
99 
100 	/**
101 	 * Constructs a new monitor which is not associated with a
102 	 * specific configuration.
103 	 */
104 	public SystemMonitor()
105 	{
106 		this(null);
107 	}
108 
109 	/**
110 	 * Constructs a new Monitor for monitoring the resources in the
111 	 * given configuration using the given statistic source.
112 	 *
113 	 * @param conf The configuration which is monitored.
114 	 */
115 	public SystemMonitor(Configuration conf)
116 	{
117 		this.conf = conf;
118 		monitored = new HashMap();
119 		kc = new KstatCtl();
120 	}
121 
122 	/**
123 	 * Initialize the monitor with the configuration which is to
124 	 * be monitored.
125 	 *
126 	 * @param conf The configuration which is monitored.
127 	 *
128 	 * @throws PoolsException if manipulating the configuration
129 	 * fails.
130 	 * @throws StaleMonitorException if the resource monitors
131 	 * cannot be set.
132 	 */
133 	public void initialize(Configuration conf) throws PoolsException,
134 	    StaleMonitorException
135 	{
136 		Poold.CONF_LOG.log(Severity.DEBUG, "initializing");
137 		this.conf = conf;
138 		try {
139 			kc.chainUpdate();
140 		} catch (KstatChainUpdateException kcue) {
141 			Poold.utility.die(Poold.CONF_LOG, kcue);
142 		}
143 
144 		try {
145 			interval = (int)conf.getLongProperty(
146 			    SAMPLE_INTERVAL_PROP_NAME);
147 		} catch (PoolsException pe) {
148 			interval = DEF_SAMPLE_INT;
149 		}
150 		timer = new SimpleRecurringEventTimer(interval);
151 
152 		setResourceMonitors("pset");
153 	}
154 
155 	/**
156 	 * Add all resources of the supplied type in the monitored
157 	 * configuration to the set of monitored resources. Remove
158 	 * redundant monitors for resources which no longer exist.
159 	 * Don't monitor resource sets which are empty.
160 	 *
161 	 * @param type The type of the resources to be added
162 	 */
163 	private void setResourceMonitors(String type) throws PoolsException,
164 	    StaleMonitorException
165 	{
166 		Value val = new Value("type", type);
167 
168 		List valueList = new LinkedList();
169 		valueList.add(val);
170 		Iterator resIt = conf.getResources(valueList).iterator();
171 		val.close();
172 		HashSet oldKeys = new HashSet(monitored.keySet());
173 		while (resIt.hasNext()) {
174 			Resource res = (Resource)resIt.next();
175 			ResourceMonitor mon = null;
176 			boolean activeComponents = false;
177 
178 			List compList = res.getComponents(null);
179 			Iterator compIt = compList.iterator();
180 			while (compIt.hasNext()) {
181 				Component comp = (Component) compIt.next();
182 				String status = comp.getStringProperty(
183 				    "cpu.status");
184 				if (status.compareTo("off-line") != 0 &&
185 			    	    status.compareTo("powered-off") != 0) {
186 					activeComponents = true;
187 					break;
188 				}
189 			}
190 			if (activeComponents == false)
191 				continue;
192 
193 			if (monitored.containsKey(res)) {
194 				mon = get(res);
195 				for (int i = 0; i < stats.length; i++)
196 					mon.resetData(stats[i]);
197 			} else {
198 				mon = new ResourceMonitor(res, 50);
199 				for (int i = 0; i < stats.length; i++) {
200 					StatisticList sl = new StatisticList(
201 					    stats[i], 2);
202 					mon.put(stats[i], sl);
203 				}
204 				mon.put("utilization", new StatisticList(
205 				    "utilization", mon.getMaxSampleSize(),
206 				    true));
207 				monitored.put(res, mon);
208 			}
209 			mon.initialize();
210 			oldKeys.remove(res);
211 		}
212 		monitored.keySet().removeAll(oldKeys);
213 	}
214 
215 	/**
216 	 * Get the next sample. Before obtaining the sample, the call
217 	 * will block for the interval which was specified when this
218 	 * monitor was initialized.
219 	 *
220 	 * @throws StaleMonitorException if a statistic cannot be obtained.
221 	 * @throws PoolsException if there is an error manipulating the
222 	 * pools configuration.
223 	 * @throws InterruptedException if the thread is interrupted
224 	 * while waiting for the sampling time to arrive.  The caller
225 	 * may wish to check other conditions before possibly
226 	 * reinitiating the sample.
227 	 */
228 	public void getNext() throws StaleMonitorException, PoolsException,
229 	    InterruptedException
230 	{
231 		Poold.MON_LOG.log(Severity.DEBUG, "waiting sampling interval");
232 
233 		Date start = lastSampleTime;
234 		if (start == null)
235 			start = new Date();
236 
237 		timer.waitUntilNextFiring();
238 		Date end = new Date();
239 
240 		Poold.MON_LOG.log(Severity.DEBUG, "sampling");
241 		Iterator itMon = monitored.values().iterator();
242 		while (itMon.hasNext()) {
243 			ResourceMonitor mon = (ResourceMonitor) itMon.next();
244 			List compList = mon.getComponents();
245 			Iterator itComp = compList.iterator();
246 			BigInteger statsv[] = new BigInteger[4];
247 			while (itComp.hasNext()) {
248 				Component cpu = (Component) itComp.next();
249 				Kstat kstat = kc.lookup("cpu",
250 				    (int) cpu.getLongProperty("cpu.sys_id"),
251 				    "sys");
252 				if (kstat == null)
253 					throw new StaleMonitorException();
254 				UnsignedInt64 value;
255 				try {
256 					kstat.read();
257 					for (int i = 0; i < stats.length; i++) {
258 						value = (UnsignedInt64) kstat.
259 						    getValue("cpu_ticks_" +
260 						    stats[i]);
261 						if (value == null)
262 							throw new
263 							StaleMonitorException();
264 						if (statsv[i] == null)
265 							statsv[i] = value;
266 						else
267 							statsv[i] = statsv[i].
268 							    add(value);
269 					}
270 				} catch (KstatException ke) {
271 					StaleMonitorException sme =
272 					    new StaleMonitorException();
273 					sme.initCause(ke);
274 					Poold.MON_LOG.log(Severity.DEBUG,
275 					    "configuration necessary due to "
276 					    + ke);
277 					throw(sme);
278 				}
279 			}
280 			if (compList.isEmpty() == false) {
281 				for (int i = 0; i < stats.length; i++) {
282 					StatisticList sl;
283 					sl = (StatisticList) mon.get(stats[i]);
284 					sl.add(new UnsignedInt64Statistic(
285 					    new UnsignedInt64(statsv[i].
286 					    divide(new BigInteger(
287 					    Integer.toString(
288 					    compList.size())))),
289 					    start, end));
290 				}
291 			}
292 			mon.updateDerived();
293 		}
294 
295 		sampleCount++;
296 		lastSampleTime = end;
297 	}
298 
299 	/**
300 	 * Return the number of samples taken.  This is the number of
301 	 * successful calls to <code>getNext()</code>.
302 	 */
303 	public int getSampleCount()
304 	{
305 		return (sampleCount);
306 	}
307 
308 	/**
309 	 * Return the utilization of the supplied resource. The
310 	 * utilization is represented as a percentage between 0
311 	 * and 100.
312 	 *
313 	 * @param res A reference to a configuration resource.
314 	 */
315 	public double getUtilization(Resource res) throws StaleMonitorException
316 	{
317 		ResourceMonitor mon = get(res);
318 		DoubleStatistic util = null;
319 		try {
320 			util = (DoubleStatistic)mon.getDerivedStatistic(
321 			    "utilization");
322 		} catch (NoSuchElementException nsee) {
323 			util = new DoubleStatistic(new Double(0));
324 		}
325 		Poold.MON_LOG.log(Severity.DEBUG,
326 		    res + " utilization " + util.toString());
327 		return (((Double)util.getValue()).doubleValue());
328 	}
329 
330 	/**
331 	 * Return true if the system contains enough sampled data for
332 	 * monitoring to be worthwhile.
333 	 */
334 	public boolean isValid()
335 	{
336 		Iterator itMon = monitored.values().iterator();
337 		while (itMon.hasNext()) {
338 			ResourceMonitor mon = (ResourceMonitor) itMon.next();
339 
340 			Iterator itSL = mon.values().iterator();
341 			while (itSL.hasNext()) {
342 				StatisticList sl = (StatisticList) itSL.next();
343 				if (sl.getName().compareTo("utilization") ==
344 				    0) {
345 					if (sl.isValid() == false)
346 						return (false);
347 				}
348 			}
349 		}
350 		return (true);
351 	}
352 
353 	/**
354 	 * Return the ResourceMonitor for the supplied Resource
355 	 *
356 	 * @throws StaleMonitorException if the ResourceMonitor cannot be found.
357 	*/
358 	public ResourceMonitor get(Resource res) throws StaleMonitorException
359 	{
360 		ResourceMonitor rm = (ResourceMonitor)monitored.get(res);
361 
362 		if (rm == null)
363 			throw new StaleMonitorException();
364 		return (rm);
365 	}
366 }
367