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 2005 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.io.*;
32 import java.util.*;
33 import java.util.logging.*;
34 import java.text.DecimalFormat;
35 import java.util.concurrent.atomic.*;
36 
37 import com.sun.solaris.service.locality.*;
38 import com.sun.solaris.service.logging.*;
39 import com.sun.solaris.service.pools.*;
40 import com.sun.solaris.service.exception.*;
41 
42 /*
43  * poold overview
44  * ----- --------
45  *
46  * poold manipulates system resources in accordance with administrator
47  * specified constraints and objectives. The "goal" of the application
48  * is to maximise the efficiency of available resources within these
49  * parameters.
50  *
51  * Constraints are specified as follows:
52  *
53  * On a resource set:
54  *
55  *	- min Is the minimum amount of resource a set should
56  *	receive. poold will never elect to move resource so that a set
57  *	falls below its minimum value. It is possible for a set to
58  *	fall below its minimum as a consequence of administrative
59  *	intervention, in which case poold will endeavour to bring a
60  *	set back to its minimum level at the earliest opportunity.
61  *
62  *	- max Is the maximum amount of resource a set should
63  *	recieve. poold will never elect to move resource so that a set
64  *	rises above its maximum value. It is possible for a set to
65  *	rise above its maximum as a consequence of administrative
66  *	intervention, in which case poold will endeavour to bring a
67  *	set back to its maximum level at the earliest opportunity.
68  *
69  * On a resource component:
70  *
71  *	- cpu.pinned Is an indication that a CPU should be ignored by
72  *	poold for purposes of reallocation. A pinned CPU will never be
73  *	moved by poold from one set to another.
74  *
75  * In addition to constraints, an administrator may also specify
76  * objectives. Currently three types of objectives are supported:
77  *
78  *	- system.wt-load Is an objective set across an entire
79  *	configuration. It attempts to ensure that resource is shared
80  *	in accordance with current consumption. Those partitions which
81  *	are most heavily utilized are give more resource in an attempt
82  *	to lower their utilization levels.
83  *
84  *	- set.locality Is a locality objective which attempts to
85  *	minimize or maximize resource locality for a set.
86  *
87  *	- set.utilization Is a utilization based objective which an
88  *	administrator may use to explicitly dictate the utilization
89  *	levels which should be achieved for a set.
90  *
91  * When executing, poold uses information about the current pools
92  * configuration; monitored resource utilization and specified
93  * constraints and objectives to determine if a move of resources is
94  * likely to lead to an increase in objective satisfaction.
95  *
96  * Candidate moves are generated by observing resource constraints.
97  * These moves are evaluated and scored in terms of their contribution
98  * to objective satisfaction (note: objectives are weighted according
99  * to their importance) and ranked accordingly. If a move can be
100  * identified that delivers an improvement, the move is made. Data is
101  * collected about past moves and recorded as "Decision History",
102  * before the move is made this data is consulted and if the move is
103  * expected not to yield an improvement, it may be cancelled. This
104  * refinement is designed to improve the quality of decision making by
105  * reflecting upon the past performance of decisions as well as the
106  * contribution to objective satisfaction.
107  *
108  * poold structure
109  * ----- ---------
110  *
111  * The common package name begins with:
112  *
113  * com.sun.solaris
114  *
115  * The software is divided into two main functional areas:
116  *
117  *	service
118  *
119  *	These packages collaborate to provide services to poold
120  *	(typically, they use JNI to access exising
121  *	functionality). They are not designed to be extended. For more
122  *	details on these classes examine the source files in each
123  *	directory.
124  *
125  *	exception	Stack trace formatter
126  *	kstat		Interface to Solaris kstat facility
127  *	locality	Interface to Solaris lgrp facility
128  *	logging		Interface to Solaris syslog facility
129  *	pools		Interface to Solaris libpool facility
130  *	timer		High resolution timestamps
131  *
132  *	domain:
133  *
134  *	These package reflect problem domain concepts and are
135  *	responsible for application logic related to these
136  *	concepts.
137  *
138  *	pools		Dynamic Resource Pools specific functionality.
139  *
140  * This code block will continue to explain in more detail how poold
141  * is organized.
142  *
143  * poold provides the following basic facilities:
144  *
145  * Monitoring:
146  *
147  * Basic statistic access is provided by the
148  * com.sun.solaris.service.kstat package. The following interfaces and
149  * classes collaborate to provide higher level statistic and
150  * monitoring facilities to the application:
151  *
152  *	INTERFACES
153  *
154  *	AggregateStatistic
155  *	Monitor
156  *	Statistic
157  *	StatisticListener
158  *
159  *	CLASSES
160  *
161  *	AbstractStatistic
162  *	DoubleStatistic
163  *	LongStatistic
164  *	ResourceMonitor
165  *	StatisticEvent
166  *	StatisticList
167  *	StatisticOperations
168  *	SystemMonitor
169  *	UnsignedInt64Statistic
170  *
171  * Logging:
172  *
173  * Logging services are provided by the com.sun.solaris.logging
174  * package. In addition, the following class implements Poold's
175  * specific logging requirements.
176  *
177  *	CLASSES
178  *
179  *	Poold
180  *
181  * Optimization:
182  *
183  * lgrp service are provided by the com.sun.solaris.service.lgrp
184  * package. pools services are provided by the
185  * com.sun.solaris.service.pools package. In addition, optimization is
186  * implemented in the following Interfaces and Classes:
187  *
188  *	INTERFACES
189  *
190  *	Objective
191  *	Solver
192  *	WorkloadDependentObjective
193  *
194  *	CLASSES
195  *
196  *	AbstractObjective
197  *	ComponentMove
198  *	DecisionHistory
199  *	Expression
200  *	KExpression
201  *	KVEpression
202  *	KVOpExpression
203  *	LocalityObjective
204  *	Move
205  *	Poold
206  *	QuantityMove
207  *	SystemSolver
208  *	UtilizationObjective
209  *	WeightedLoadObjective
210  *
211  * Configuration:
212  *
213  * pools services are provided by the com.sun.solaris.service.pools
214  * package, this is used to read poold configuration details from a
215  * libpool configuration. In addition, configuration is implemented in
216  * the following Classes:
217  *
218  *	CLASSES
219  *
220  *	AbstractObjective
221  *	Expression
222  *	Poold
223  *	SystemSolver
224  *
225  * (NB: Some classes were mentioned in multiple categories where there
226  * responsbilities overlap. Inner classes are not listed as their
227  * responsibilities can be clearly inferred from their context.)
228  *
229  * For more details on any of the packages, classes or interfaces
230  * mentioned above, look at the documentation associated with each
231  * class.
232  */
233 
234 /**
235  * The <code>Poold</code> class implements a dynamic resource
236  * allocation system for Solaris.
237  *
238  * <code>Poold</code> is a monitoring daemon, designed to evaluate
239  * user specified objectives, monitor workload behaviour and
240  * dynamically assign resources in order to satisfy the evaluated
241  * objectives. For more details see:
242  *
243  * <a href="http://sac.eng.sun.com/PSARC/2002/287/">PSARC/2002/287</a>
244  */
245 final class Poold
246 {
247 	/**
248 	 * The configuration which is manipulated.
249 	 */
250 	private Configuration conf;
251 
252 	/**
253 	 * The monitoring interface.
254 	 */
255 	private Monitor monitor;
256 
257 	/**
258 	 * The interface to higher level resource managers.
259 	 */
260 	private DRM drm;
261 
262 	/**
263 	 * The interface to the configuration solver.
264 	 */
265 	private Solver solver;
266 
267 	/**
268 	 * Default path to the logging properties file.
269 	 */
270 	public static final String POOLD_PROPERTIES_PATH =
271 	    "/usr/lib/pool/poold.properties";
272 
273 	/**
274 	 * Logger for records which aren't produced in the Monitoring,
275 	 * Configuration, or Optimization states.  This logger is the
276 	 * parent to the loggers used in those states.
277 	 */
278 	public static final Logger BASE_LOG = Logger.getLogger(
279 	    "com.sun.solaris.domain.pools.poold");
280 
281 	/**
282 	 * Logger for records produced in the Configuration state.
283 	 */
284 	public static final Logger CONF_LOG = Logger.getLogger(
285 	    "com.sun.solaris.domain.pools.poold.Configuration");
286 
287 	/**
288 	 * Logger for records produced in the Monitoring state.
289 	 */
290 	public static final Logger MON_LOG = Logger.getLogger(
291 	    "com.sun.solaris.domain.pools.poold.Monitoring");
292 
293 	/**
294 	 * Logger for records produced in the Optimization state.
295 	 */
296 	public static final Logger OPT_LOG = Logger.getLogger(
297 	    "com.sun.solaris.domain.pools.poold.Optimization");
298 
299 	/**
300 	 * Singleton instance of Poold.
301 	 */
302 	private static Poold instance;
303 
304 	/**
305 	 * The main sampling and solving thread.
306 	 */
307 	private Thread mainThread;
308 
309 	/**
310 	 * Process exit code indicating a failure.
311 	 */
312 	private static final int E_PO_FAILURE = 2;
313 
314 	/**
315 	 * Keep track of whether initialize() has been invoked, to
316 	 * output the "starting" message on the first.
317 	 */
318 	private AtomicBoolean firstInitialization = new AtomicBoolean(true);
319 
320 	/**
321 	 * Flags whether poold should run or exit.
322 	 */
323 	private AtomicBoolean shouldRun = new AtomicBoolean(true);
324 
325 	private static class logHelper {
326 		/**
327 		 * Default logfile location
328 		 */
329 		public static final String DEF_LOG_LOC = "/var/log/pool/poold";
330 
331 		/**
332 		 * Log location indicating <code>syslog</code>, as
333 		 * opposed to a file, should be used.
334 		 */
335 		public static final String SYSLOG_LOG_LOC = "SYSLOG";
336 
337 		/**
338 		 * Default Log severity (if not overridden)
339 		 */
340 		public static final Severity DEF_SEVERITY = Severity.INFO;
341 
342 		/**
343 		 * Name of configuration property, log location.
344 		 */
345 		public static final String PROPERTY_NAME_LOG_LOC =
346 		    "system.poold.log-location";
347 
348 		/**
349 		 * Name of configuration property, log level.
350 		 */
351 		public static final String PROPERTY_NAME_LOG_LEVEL =
352 		    "system.poold.log-level";
353 
354 		/**
355 		 * Location of logfile -- an absolute filename, or
356 		 * "SYSLOG".
357 		 */
358 		private static String location;
359 
360 		/**
361 		 * Logfile handler, responsible for taking log messages
362 		 * and exporting them.
363 		 */
364 		private static Handler handler;
365 
366 		/**
367 		 * Logfile severity, log messages below this severity are
368 		 * ignored.
369 		 */
370 		private static Severity severity;
371 
372 		/**
373 		 * Flag recording whether preinitialization has occurred.
374 		 */
375 		private static boolean preinitialized = false;
376 
377 		/**
378 		 * Flag recording whether the logging Severity has been
379 		 * overridden with the -l command-line option, which
380 		 * means the console is the only thing being logged to,
381 		 * and the configuration's logging properties are
382 		 * ignored.
383 		 */
384 		private static boolean usingConsole;
385 
386 		/**
387 		 * Indicates whether logging semantics should be changed
388 		 * to facilitate debugging.
389 		 */
390 		private static final boolean loggingDebugging = false;
391 
392 		/**
393 		 * Do the pre-initialization initialization:  install
394 		 * loggers for reporting errors during initialization.
395 		 *
396 		 * @param consoleSeverity If non-null, indicates that
397 		 * the configuration property-controlled logging behavior
398 		 * is to be overridden (the <code>-l</code> option was
399 		 * specified), and messages are to be logged only to the
400 		 * console, with (at most) the given maximum severity.
401 		 */
preinitialize(Severity consoleSeverity)402 		private static void preinitialize(Severity consoleSeverity) {
403 			if (preinitialized)
404 				return;
405 
406 			/*
407 			 * Read logging properties affecting the
408 			 * FileHandler and ConsoleHandler from
409 			 * <code>poold.properties</code>.
410 			 */
411 			Properties props = new Properties();
412 			ByteArrayOutputStream bos = new ByteArrayOutputStream();
413 			try {
414 				props.load(
415 				    new FileInputStream(POOLD_PROPERTIES_PATH));
416 				props.store(bos, "");
417 				LogManager.getLogManager().readConfiguration(
418 				    new ByteArrayInputStream(
419 				    bos.toByteArray()));
420 			} catch (IOException ioe) {
421 				Poold.MON_LOG.log(Severity.WARNING, "could not "
422 				    + "read logging properties from "
423 				    + "poold.properties: " + ioe);
424 			}
425 
426 			if (consoleSeverity == null || loggingDebugging) {
427 				/*
428 				 * Log messages to /var/log/pool/poold,
429 				 * the default location, until the pools
430 				 * configuration properties are read and
431 				 * applied, which may change the logging
432 				 * file and severity.
433 				 *
434 				 * Under normal circumstances, it's
435 				 * expected that NO INFO-level messages
436 				 * will be emitted until that time; this
437 				 * is only a measure to ensure that
438 				 * unanticipated errors are reported in
439 				 * some log.
440 				 */
441 				location = SYSLOG_LOG_LOC;
442 				handler = SyslogHandler.getInstance("poold",
443 				    Facility.DAEMON);
444 				severity = DEF_SEVERITY;
445 				handler.setLevel(severity);
446 				BASE_LOG.addHandler(handler);
447 			}
448 
449 			if (consoleSeverity != null) {
450 				/*
451 				 * If -l is specified, log to the
452 				 * console.  Unless loggingDebug is
453 				 * true, this will also mean that the
454 				 * logging properties are ignored.
455 				 *
456 				 * Determine if the user has specified
457 				 * the use of a ConsoleHandler through
458 				 * poold.properties.
459 				 */
460 				Logger root = Logger.getLogger("");
461 				Handler[] handler = root.getHandlers();
462 				ConsoleHandler ch = null;
463 				for (int i = 0; i < handler.length && ch ==
464 				    null; i++)
465 					if (handler[i]
466 					    instanceof ConsoleHandler)
467 						ch = (ConsoleHandler)handler[i];
468 				/*
469 				 * If none was previously, install a
470 				 * ConsoleHandler.
471 				 */
472 				if (ch == null) {
473 					ch = new ConsoleHandler();
474 
475 					ch.setFormatter(
476 					    new SysloglikeFormatter());
477 					ch.setLevel(consoleSeverity);
478 					root.addHandler(ch);
479 				}
480 				severity = consoleSeverity;
481 				BASE_LOG.log(Severity.DEBUG,
482 				    "logging with level " + severity);
483 
484 				/**
485 				 * Allow logging properties to be
486 				 * effective if loggingDebugging is not
487 				 * set.
488 				 */
489 				if (!loggingDebugging)
490 					usingConsole = true;
491 			}
492 			preinitialized = true;
493 		}
494 
495 		/**
496 		 * Configure loggers based on the logging-related
497 		 * configuration properties.  Outputs a description of
498 		 * any changes to the configuration logger.
499 		 *
500 		 * @throws ConfigurationException if there is an error
501 		 * applying libpool configuration properties to
502 		 * <code>poold</code>
503 		 */
initializeWithConfiguration( Configuration conf)504 		public static void initializeWithConfiguration(
505 		    Configuration conf) throws ConfigurationException
506 		{
507 			String newLogLocation;
508 			Severity newLogSeverity;
509 			String newLogSeverityName = null;
510 
511 			/*
512 			 * Set the log location as specified by the
513 			 * configuration's system properties.
514 			 */
515 			try {
516 				newLogLocation = conf.getStringProperty(
517 				    PROPERTY_NAME_LOG_LOC);
518 			} catch (PoolsException e) {
519 				newLogLocation = DEF_LOG_LOC;
520 			}
521 
522 			try {
523 				newLogSeverityName = conf.getStringProperty(
524 				    PROPERTY_NAME_LOG_LEVEL);
525 				newLogSeverity = Severity.getSeverityWithName(
526 				    newLogSeverityName);
527 				assert(newLogSeverity != null);
528 			} catch (PoolsException e) {
529 				newLogSeverity = DEF_SEVERITY;
530 			} catch (IllegalArgumentException e) {
531 				throw(ConfigurationException)
532 				    (new ConfigurationException(
533 				    "invalid " + PROPERTY_NAME_LOG_LEVEL +
534 				    "value: " + newLogSeverityName)
535 				    .initCause(e));
536 			}
537 
538 			Handler newLogHandler = null;
539 
540 			/*
541 			 * (Re)install the logger for the poold class
542 			 * hierarchy.  This means that only poold
543 			 * messages are controlled by the pools
544 			 * configuration properties/command-line
545 			 * options.
546 			 */
547 
548 			/*
549 			 * The logfile is always re-opened, in case the
550 			 * cause for reinitialization is due to SIGHUP
551 			 * following a log rotation.
552 			 */
553 			if (handler != null) {
554 				BASE_LOG.removeHandler(handler);
555 				handler.close();
556 				handler = null;
557 			}
558 
559 			if (newLogLocation.toUpperCase().equals(
560 			    SYSLOG_LOG_LOC.toUpperCase()))
561 				newLogHandler =
562 				    SyslogHandler.getInstance("poold",
563 				    Facility.DAEMON);
564 			else {
565 				if (!newLogLocation.startsWith("/"))
566 					throw
567 					    new ConfigurationException(
568 						PROPERTY_NAME_LOG_LOC +
569 						" value is not an" +
570 						" absolute path");
571 				try {
572 					newLogHandler =
573 					    new FileHandler(
574 					    newLogLocation, 0, 1, true);
575 					newLogHandler.setFormatter(
576 					    new SysloglikeFormatter());
577 				} catch (java.io.IOException ioe) {
578 					Poold.utility.die(
579 					    Poold.CONF_LOG,
580 					    new PooldException(
581 					    newLogLocation +
582 					    ": can't write")
583 					    .initCause(ioe), false);
584 				}
585 			}
586 
587 			if (!severity.equals(newLogSeverity) ||
588 			    !location.equals(newLogLocation))
589 				CONF_LOG.log(Severity.DEBUG,
590 				    "logging with level " + severity);
591 
592 			severity = newLogSeverity;
593 			handler = newLogHandler;
594 			location = newLogLocation;
595 
596 			handler.setLevel(severity);
597 			BASE_LOG.addHandler(handler);
598 		}
599 
600 		/**
601 		 * Configure the loggers based on the pool's logging
602 		 * properties, or, if the -l option was specified on the
603 		 * command line, continue to use the console.
604 		 */
initialize(Configuration conf)605 		public static void initialize(Configuration conf)
606 		    throws ConfigurationException
607 		{
608 			if (usingConsole)
609 				return;
610 			else
611 				initializeWithConfiguration(conf);
612 		}
613 
614 		/**
615 		 * Return the current logging level.
616 		 */
getSeverity()617 		public static Severity getSeverity()
618 		{
619 			return (severity);
620 		}
621 
close()622 		public static void close()
623 		{
624 			if (handler != null) {
625 				BASE_LOG.removeHandler(handler);
626 				handler.close();
627 			}
628 		}
629 	}
630 
631 	/**
632 	 * Constructor
633 	 *
634 	 * Only one poold instance should be running per system.
635 	 *
636 	 * @param consoleSeverity If non-null, indicates that the
637 	 * configuration property-controlled logging behavior is to be
638 	 * overridden (the <code>-l</code> option was specified), and
639 	 * messages are to be logged only to the console, with (at most)
640 	 * the given maximum severity.
641 	 */
Poold(Severity consoleSeverity)642 	private Poold(Severity consoleSeverity)
643 	{
644 		/*
645 		 * Establish loggers for recording errors during
646 		 * initialization.  Under normal circumstances, no
647 		 * messages will be emitted; this is only a measure to
648 		 * make sure that unanticipated errors are reported in
649 		 * some log, or the console, if the -l option is used.
650 		 */
651 		logHelper.preinitialize(consoleSeverity);
652 
653 		/*
654 		 * Try opening the configuration read-write in hopes the
655 		 * ability will be possessed henceforth.
656 		 */
657 		try {
658 			conf = new Configuration(PoolInternal.
659 			    pool_dynamic_location(), PoolInternal.PO_RDWR);
660 			conf.close();
661 		} catch (PoolsException pe) {
662 			Poold.utility.die(CONF_LOG, new PooldException(
663 			    "cannot open dynamic pools configuration " +
664 			     "read-write (" + pe.getMessage() + ")")
665 			     .initCause(pe), false);
666 		}
667 
668 		try {
669 			conf = new Configuration(PoolInternal.
670 			    pool_dynamic_location(), PoolInternal.PO_RDONLY);
671 		} catch (PoolsException pe) {
672 			Poold.utility.die(CONF_LOG, pe);
673 		}
674 
675 		/*
676 		 * Create the required sub-components:
677 		 * - a monitoring object
678 		 * - a DRM implementer
679 		 * - a solver
680 		 */
681 		monitor = new SystemMonitor();
682 		drm = new LogDRM();
683 		solver = new SystemSolver(monitor);
684 	}
685 
686 	/**
687 	 * Returns a reference to the singleton <code>Poold</code>,
688 	 * constructing one if necessary.
689 	 *
690 	 * @param consoleSeverity If non-null, indicates that the
691 	 * configuration property-controlled logging behavior is to be
692 	 * overridden (the <code>-l</code> option was specified), and
693 	 * messages are to be logged only to the console, with (at most)
694 	 * the given maximum severity.
695 	 * @throws IllegalArgumentException if the given console
696 	 * severity doesn't match that of an existing instance.
697 	 */
getInstanceWithConsoleLogging( Severity consoleSeverity)698 	public static Poold getInstanceWithConsoleLogging(
699 	    Severity consoleSeverity)
700 	{
701 		if (instance == null)
702 			return (instance = new Poold(consoleSeverity));
703 		else
704 			if (logHelper.usingConsole == false &&
705 			    consoleSeverity == null || consoleSeverity !=
706 			    null && logHelper.getSeverity().equals(
707 			    consoleSeverity))
708 				return (instance);
709 			else
710 				throw new IllegalArgumentException();
711 	}
712 
713 	/**
714 	 * Initializes <code>Poold</code> for operation at startup or
715 	 * in response to a detected libpool configuration change.
716 	 */
initialize()717 	private void initialize()
718 	{
719 		try {
720 			logHelper.initialize(conf);
721 			if (firstInitialization.get())
722 				CONF_LOG.log(Severity.INFO, "starting");
723 			/*
724 			 * When a system is extremely busy, it may
725 			 * prove difficult to initialize poold. Just
726 			 * keep trying until we succeed.
727 			 */
728 			boolean busy = true;
729 			while (busy && shouldRun.get()) {
730 				busy = false;
731 				try {
732 					monitor.initialize(conf);
733 					CONF_LOG.log(Severity.DEBUG,
734 					    "configuring solver...");
735 					solver.initialize(conf);
736 					CONF_LOG.log(Severity.INFO,
737 					    "configuration complete");
738 				} catch (PoolsException pe) {
739 					CONF_LOG.log(Severity.INFO,
740 					    "The system is too busy to " +
741 					    "initialize, attempting " +
742 					    "initialization again");
743 					/*
744 					 * pause for a while before
745 					 * re-attempting the
746 					 * initialization.
747 					 */
748 					try {
749 						Thread.sleep(50);
750 					} catch (InterruptedException ie) {
751 						/*
752 						 * Safe to ignore this
753 						 * exception as we
754 						 * will simply try
755 						 * again sooner.
756 						 */
757 					}
758 					busy = true;
759 				} catch (StaleMonitorException sme) {
760 					CONF_LOG.log(Severity.INFO,
761 					    "The system is too busy to " +
762 					    "initialize, attempting " +
763 					    "initialization again");
764 					/*
765 					 * pause for a while before
766 					 * re-attempting the
767 					 * initialization.
768 					 */
769 					try {
770 						Thread.sleep(50);
771 					} catch (InterruptedException ie) {
772 						/*
773 						 * Safe to ignore this
774 						 * exception as we
775 						 * will simply try
776 						 * again sooner.
777 						 */
778 					}
779 					busy = true;
780 				}
781 			}
782 			if (firstInitialization.get())
783 				firstInitialization.set(false);
784 		} catch (ConfigurationException ce) {
785 			Poold.utility.die(CONF_LOG, ce);
786 		}
787 	}
788 
789 	/**
790 	 * Execute <code>Poold</code> indefinitely. This method is
791 	 * invoked after <code>Poold</code> completes
792 	 * configuration. It will continue to execute until
793 	 * <code>Poold</code> is terminated.
794 	 *
795 	 * @throws Exception If an there is an error in execution.
796 	 */
execute()797 	private void execute() throws Exception
798 	{
799 		int changed = 0;
800 		boolean confRequired = false;
801 
802 		while (shouldRun.get()) {
803 			try {
804 				changed = conf.update();
805 				assert(!confRequired || confRequired &&
806 				    changed != 0);
807 				if (changed != 0) {
808 					CONF_LOG.log(Severity.DEBUG,
809 					    "configuration change detected");
810 					if (!confRequired)
811 						CONF_LOG.log(Severity.INFO,
812 						    "configuration changed " +
813 						    "externally");
814 					CONF_LOG.log(Severity.INFO,
815 					    "reconfiguring...");
816 				}
817 				confRequired = false;
818 			} catch (PoolsException pe) {
819 				Poold.utility.die(CONF_LOG, pe);
820 			}
821 			if (changed != 0)
822 				initialize();
823 
824 			boolean gotNext = false;
825 			while (shouldRun.get() && !gotNext) {
826 				try {
827 					monitor.getNext();
828 					gotNext = true;
829 
830 					/*
831 					 * All workload-dependent
832 					 * objectives must now be
833 					 * checked for violations.  The
834 					 * solver holds all objectives
835 					 * and it makes the decision
836 					 * about whether a
837 					 * reconfiguration is required.
838 					 */
839 					if (solver.examine(monitor)) {
840 						MON_LOG.log(Severity.INFO,
841 						    "reconfiguration required");
842 						confRequired = solver.solve();
843 					} else {
844 						MON_LOG.log(Severity.INFO,
845 						    "all evaluated objectives"
846 						    + " satisfied");
847 					}
848 				} catch (StaleMonitorException e) {
849 					/*
850 					 * Later, assert that every
851 					 * cause of the
852 					 * StaleMonitorException is
853 					 * handled by the above
854 					 * conf.update().
855 					 */
856 					confRequired = true;
857 				} catch (InterruptedException ie) {
858 					Poold.MON_LOG.log(Severity.INFO,
859 					    "interrupted");
860 					break;
861 				}
862 			}
863 			if (!shouldRun.get())
864 				break;
865 			System.runFinalization();
866 		}
867 		Poold.BASE_LOG.log(Severity.NOTICE, "exiting");
868 	}
869 
870 	/**
871 	 * Cleanup any resources when the application terminates.
872 	 */
cleanup()873 	private void cleanup()
874 	{
875 		conf.close();
876 		logHelper.close();
877                 instance = null;
878 	}
879 
880 	/**
881 	 * Invoke <code>Poold</code>. This main function is provided
882 	 * so that <code>poold</code> can be executed.  Execution will
883 	 * continue indefinitely unless there is an error, in which case
884 	 * when execute() terminates.
885 	 */
run()886 	public void run()
887 	{
888 		mainThread = Thread.currentThread();
889 
890 		Runtime.getRuntime().addShutdownHook(new Thread() {
891 			public void run() {
892 				try {
893 					cleanup();
894 				} catch (Throwable t) {
895 				}
896 			}
897 		});
898 
899 		try {
900 			initialize();
901 			execute();
902 		} catch (Throwable t) {
903 			Poold.utility.die(BASE_LOG, t);
904 		}
905 	}
906 
907 	/**
908 	 * Stops <code>Poold</code>.  Sets a flag indicating that run()
909 	 * should break out of the monitor/solve loop and clean up.
910 	 */
shutdown()911 	public void shutdown() {
912 		/*
913 		 * Flag that the main thread should break out of the
914 		 * sample/solve loop as soon as possible.
915 		 */
916 		shouldRun.set(false);
917 
918 		/*
919 		 * Interrupt the main thread, which will cause the
920 		 * monitor to break out of its sleep if it's waiting for
921 		 * the sample time to arrive, and cause the sample/solve
922 		 * loop condition to be evaluated sooner.  But make some
923 		 * effort not to cause an InterruptedIOException if
924 		 * we're not even through initialization yet; we'll get
925 		 * around to shutting down soon enough.
926 		 */
927 		if (!firstInitialization.get() && mainThread != null)
928 			mainThread.interrupt();
929 	}
930 
main(String args[])931 	public static void main(String args[]) throws IllegalArgumentException
932 	{
933 		Severity severity = null;
934 
935 		if (args.length > 0) {
936 			if (args[0].compareTo("-l") == 0 && args.length == 2) {
937 				severity = (Severity) Severity.parse(args[1]);
938 			} else
939 				throw new IllegalArgumentException(
940 				    "usage: poold [-l level]");
941 		}
942 		Poold p = getInstanceWithConsoleLogging(severity);
943 		p.run();
944 	}
945 
946 	/**
947 	 * The <code>utility</code> class provides various
948 	 * <code>Poold</code> related static utility methods that
949 	 * don't naturally reside on any class.
950 	 */
951 	static class utility {
952 		/**
953 		 * Outputs a near-final message corresponding
954 		 * to an exception (or other throwable) to the named
955 		 * logger before causing the VM to exit.  The message
956 		 * will have ERROR severity unless an instance of
957 		 * PoolsException is given, in which case the message
958 		 * will adopt the PoolsException's severity.  Similarly,
959 		 * if the PoolsException specifies an exit code, it will
960 		 * be used; otherwise the default of
961 		 * <code>E_PO_FAILURE</code> will be used.
962 		 *
963 		 * @param logger Logger used to log the message
964 		 * @param t The cause.  A stack trace will be affixed to
965 		 * the message.
966 		 */
die(Logger logger, Throwable t)967 		public static void die(Logger logger, Throwable t)
968 		{
969 			die(logger, t, true);
970 		}
971 
972 		/**
973 		 * Outputs a near-final message corresponding
974 		 * to an exception (or other throwable) to the named
975 		 * logger before causing the VM to exit.  The message
976 		 * will have ERROR severity unless an instance of
977 		 * PoolsException is given, in which case the message
978 		 * will adopt the PoolsException's severity.  Similarly,
979 		 * if the PoolsException specifies an exit code, it will
980 		 * be used; otherwise the default of
981 		 * <code>E_PO_FAILURE</code> will be used.
982 		 *
983 		 * @param logger Logger used to log the message
984 		 * @param t The cause.
985 		 * @param showStackTrace If true, a stack trace will be
986 		 * affixed to the message.
987 		 */
die(Logger logger, Throwable t, boolean showStackTrace)988 		public static void die(Logger logger, Throwable t,
989 		    boolean showStackTrace)
990 		{
991 			try {
992 				Severity severity;
993 				/*
994 				 * Configure the message's exception and
995 				 * severity.
996 				 */
997 				LogRecord record;
998 				if (t instanceof PooldException)
999 					record = new LogRecord(
1000 					    ((PooldException)t).getSeverity(),
1001 					    t.getMessage());
1002 				else
1003 					record = new LogRecord(Severity.ERR,
1004 					    t.getMessage());
1005 				if (record.getMessage() == null)
1006 					record.setMessage("exception " +
1007 					    t.getClass().getName());
1008 				if (showStackTrace)
1009 					record.setThrown(t);
1010 
1011 				record.setLoggerName(logger.getName());
1012 				logger.log(record);
1013 
1014 				if (logHelper.handler != null)
1015 					logHelper.handler.flush();
1016 				if (t instanceof PooldException)
1017 					System.exit(((PooldException)t)
1018 					    .getExitCode());
1019 				else
1020 					System.exit(E_PO_FAILURE);
1021 			} catch (Exception e) {
1022 				SuccinctStackTraceFormatter.printStackTrace(e);
1023 				System.exit(-1);
1024 			}
1025 		}
1026 
1027 		/**
1028 		 * Outputs a warning-level message to the named logger.
1029 		 *
1030 		 * @param logger Logger used to log the message
1031 		 * @param t The cause.
1032 		 * @param showStackTrace If true, a stack trace will be
1033 		 * affixed to the message.
1034 		 */
warn(Logger logger, Throwable t, boolean showStackTrace)1035 		public static void warn(Logger logger, Throwable t,
1036 		    boolean showStackTrace)
1037 		{
1038 			try {
1039 				Severity severity;
1040 				/*
1041 				 * Configure the message's exception and
1042 				 * severity.
1043 				 */
1044 				LogRecord record;
1045 				if (t instanceof PooldException)
1046 					record = new LogRecord(
1047 					    ((PooldException)t).getSeverity(),
1048 					    t.getMessage());
1049 				else
1050 					record = new LogRecord(Severity.WARNING,
1051 					    t.getMessage());
1052 				if (record.getMessage() == null)
1053 					record.setMessage("exception " +
1054 					    t.getClass().getName());
1055 				if (showStackTrace)
1056 					record.setThrown(t);
1057 
1058 				record.setLoggerName(logger.getName());
1059 				logger.log(record);
1060 
1061 				if (logHelper.handler != null)
1062 					logHelper.handler.flush();
1063 			} catch (Exception e) {
1064 				SuccinctStackTraceFormatter.printStackTrace(e);
1065 				System.exit(-1);
1066 			}
1067 		}
1068 	}
1069 }
1070 
1071 class ConfigurationException extends Exception
1072 {
ConfigurationException(String message)1073 	public ConfigurationException(String message)
1074 	{
1075 		super(message);
1076 	}
1077 }
1078 
1079 class IllegalOFValueException extends RuntimeException
1080 {
IllegalOFValueException(String message)1081 	public IllegalOFValueException(String message)
1082 	{
1083 		super(message);
1084 	}
1085 }
1086 
1087 class PooldException extends Exception {
1088 	/**
1089 	 * The exit code which the virtual machine should exit at if
1090 	 * this exception cannot be handled.
1091 	 */
1092 	private int exitCode;
1093 
1094 	/**
1095 	 * The severity of this message.  See <code>Severity</code>.
1096 	 */
1097 	private Severity severity;
1098 
1099 	/**
1100 	 * Constructs a message with default exit code and
1101 	 * <code>System.ERR</code> severity.
1102 	 */
PooldException(String message)1103 	public PooldException(String message)
1104 	{
1105 		this(message, 1, Severity.ERR);
1106 	}
1107 
1108 	/**
1109 	 * Constructs a message with given exit code and
1110 	 * <code>System.ERR</code> severity.
1111 	 */
PooldException(String message, int exitCode)1112 	public PooldException(String message, int exitCode)
1113 	{
1114 		this(message, exitCode, Severity.ERR);
1115 	}
1116 
1117 	/**
1118 	 * Constructs a message with a given exit code and severity.
1119 	 */
PooldException(String message, int exitCode, Severity severity)1120 	public PooldException(String message, int exitCode, Severity severity)
1121 	{
1122 		super(message);
1123 		this.exitCode = exitCode;
1124 		this.severity = severity;
1125 	}
1126 
1127 	/**
1128 	 * The exit code which the virtual machine should exit at if
1129 	 * this exception cannot be handled.
1130 	 */
getExitCode()1131 	public int getExitCode()
1132 	{
1133 		return (exitCode);
1134 	}
1135 
getSeverity()1136 	public Severity getSeverity()
1137 	{
1138 		return (severity);
1139 	}
1140 }
1141