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.io.*;
32 import java.nio.*;
33 import java.nio.channels.*;
34 import java.util.*;
35 import java.util.regex.*;
36 import java.text.DecimalFormat;
37 
38 import com.sun.solaris.service.locality.*;
39 import com.sun.solaris.service.logging.*;
40 import com.sun.solaris.service.pools.*;
41 import com.sun.solaris.service.exception.*;
42 
43 /**
44  * The <code>SystemSolver</code> class implements a dynamic resource
45  * allocation solver. The Solver takes a configuration and "solves"
46  * the resource allocation problem.
47  *
48  * This class operates upon a configuration which is suppplied during
49  * initialization. Its responsibilities include:
50  * <ul>
51  * <li><p>
52  * Maintaining decision history
53  * <li><p>
54  * Maintaining information about the locality domain
55  * <li><p>
56  * Maintaining a map of all elements and their associated set of objectives
57  * <li><p>
58  * Identifying objective expressions with a configuration
59  * </ul>
60  */
61 class SystemSolver implements Solver {
62 	/**
63 	 * The default location of this history file.
64 	 */
65 	public static final String DH_FILE_DEF_PATH =
66 	    "/var/adm/pool/history";
67 
68 	/**
69 	 * The property overriding the default decision history path.
70 	 */
71 	public static final String DH_FILE_PROP_NAME =
72 	    "system.poold.history-file";
73 
74 	/**
75 	 * The LocalityDomain extracted from the configuration.
76 	 */
77 	private LocalityDomain ldom;
78 
79 	/**
80 	 * The objective map used to link all elements with their set
81 	 * of objectives.
82 	 */
83 	private Map objMap;
84 
85 	/**
86 	 * The pattern used to identify objective expressions.
87 	 */
88 	private static final Pattern p0 = Pattern.compile(";");
89 
90 	/**
91 	 * The configuration for which this solver is "solving".
92 	 */
93 	private Configuration conf;
94 
95 	/**
96 	 * Monitor providing statistics for this solver.
97 	 */
98 	private final Monitor monitor;
99 
100 	/**
101 	 * The decision history maintainer.
102 	 */
103 	private DecisionHistory dh;
104 
105 	/**
106 	 * The path to the decision history file.
107 	 */
108 	private String dhPath;
109 
110 	/**
111 	 * The number of CPUs on the system.
112 	 */
113 	private int cpuCount;
114 
115 	/**
116 	 * The number of locality triggered examinations made.
117 	 */
118 	private int examineCount;
119 
120 	/**
121 	 * Constructs a solver which initialises a decision history
122 	 * maintainer.  The decision history is be used during operation
123 	 * to veto historically-poor decisions output from the objective
124 	 * function.
125 	 */
SystemSolver(Monitor monitor)126 	SystemSolver(Monitor monitor)
127 	{
128 		/*
129 		 * Create a HashMap to store all objectives.
130 		 */
131 		objMap = new HashMap();
132 		this.monitor = monitor;
133 	}
134 
135 	/**
136 	 * Initialize the solver for operation upon the supplied
137 	 * configuration. Possible objective expressions set upon
138 	 * target elements are identified and stored in an objective
139 	 * map which associates elements with the set of their
140 	 * objectives.
141 	 *
142 	 * @param conf The configuration to be manipulated.
143 	 * @throws PoolsException If the initialization fails.
144 	 */
initialize(Configuration conf)145 	public void initialize(Configuration conf) throws PoolsException
146 	{
147 		String oString = null;
148 		String exps[];
149 
150 		this.conf = conf;
151 		/*
152 		 * Count the CPUs in the system, this is used to
153 		 * control locality objective processing in the
154 		 * examine() method.
155 		 */
156 		cpuCount = conf.getComponents(null).size();
157 		examineCount = 0;
158 		/*
159 		 * Remove any old objectives
160 		 */
161 		objMap.clear();
162 
163 		/*
164 		 * Extract any configuration objectives
165 		 */
166 		try {
167 			oString = conf.getStringProperty(
168 			    "system.poold.objectives");
169 			if (oString.length() > 0) {
170 				Set oSet = new HashSet();
171 				objMap.put(conf, oSet);
172 				Poold.CONF_LOG.log(Severity.DEBUG,
173 				    "adding configuration objective " +
174 				    oString);
175 				exps = p0.split(oString);
176 				for (int i = 0; i < exps.length; i++) {
177 					try {
178 						Expression exp =
179 						    Expression.valueOf(
180 						    exps[i]);
181 						addObjective(oSet, "system",
182 						    exp);
183 					} catch (IllegalArgumentException iae) {
184 						Poold.utility.warn(
185 						    Poold.CONF_LOG, iae, false);
186 					}
187 				}
188 			}
189 		} catch (PoolsException pe) {
190 			/*
191 			 * Ignore as this means there is no objective
192 			 * property
193 			 */
194 		}
195 		/*
196 		 * Now extract all pset objectives
197 		 */
198 		Value typeProp = new Value("type", "pset");
199 		List valueList = new LinkedList();
200 		valueList.add(typeProp);
201 		Iterator resIt = conf.getResources(valueList).iterator();
202 		typeProp.close();
203 		while (resIt.hasNext()) {
204 			Resource resource = (Resource)resIt.next();
205 
206 			try {
207 				oString = resource.getStringProperty(
208 				    "pset.poold.objectives");
209 				if (oString.length() > 0) {
210 					Set oSet = new HashSet();
211 					objMap.put(resource, oSet);
212 					Poold.CONF_LOG.log(Severity.DEBUG,
213 					    "adding " +
214 					    resource.getStringProperty(
215 					    "pset.name") +
216 					    " objective \"" + oString + "\"");
217 					exps = p0.split(oString);
218 					for (int i = 0; i < exps.length; i++) {
219 						Expression exp = null;
220 						try {
221 							exp = Expression.
222 							    valueOf(exps[i]);
223 							addObjective(oSet,
224 							    "pset", exp);
225 						} catch
226 						    (IllegalArgumentException e)
227 						{
228 							Poold.utility.warn(
229 							    Poold.CONF_LOG, e,
230 							    false);
231 						}
232 					}
233 				}
234 			} catch (PoolsException pe) {
235 				continue;
236 			}
237 		}
238 		Poold.CONF_LOG.log(Severity.DEBUG, "objective map: " +
239 		    objMap.toString());
240 
241 		/*
242 		 * Capture the LocalityDomain details.
243 		 */
244 		if (ldom != null) {
245 			ldom.close();
246 		}
247 		try {
248 			ldom = new LocalityDomain(LocalityDomain.LGRP_VIEW_OS);
249 		} catch (Exception e) {
250 			Poold.utility.die(Poold.OPT_LOG, e);
251 		}
252 
253 		/*
254 		 * Load or create the decision history.
255 		 */
256 		String newDhPath;
257 		try {
258 			newDhPath = conf.getStringProperty(
259 			    DH_FILE_PROP_NAME);
260 		} catch (PoolsException pe) {
261 			newDhPath = DH_FILE_DEF_PATH;
262 		}
263 
264 		if (!newDhPath.equals(dhPath)) {
265 			try {
266 				dh = DecisionHistory.loadFromFile(newDhPath);
267 				Poold.CONF_LOG.log(Severity.DEBUG,
268 				    "loaded history file " + newDhPath);
269 			} catch (Exception e) {
270 				if (!(e instanceof FileNotFoundException)) {
271 					Poold.CONF_LOG.log(Severity.WARNING,
272 					    newDhPath +
273 					    ": contents unusable; ignoring");
274 					Poold.CONF_LOG.log(Severity.DEBUG,
275 					    newDhPath + ": contents unusable",
276 					    e);
277 				}
278 				/*
279 				 * Use current DecisionHistory instead,
280 				 * if any.
281 				 */
282 				if (dh == null)
283 					dh = new DecisionHistory();
284 			}
285 
286 			/*
287 			 * Try using the new path.
288 			 */
289 			try {
290 				dh.syncToFile(newDhPath);
291 			} catch (Exception e) {
292 				Poold.utility.die(Poold.CONF_LOG,
293 				    new PooldException(newDhPath +
294 				    ": couldn't synchronize history file")
295 				    .initCause(e), false);
296 			}
297 			dhPath = newDhPath;
298 		}
299 	}
300 
301 	/**
302 	 * Determine if the given resource has non-workload-dependent
303 	 * objectives.
304 	 * @param elem The element to examine.
305 	 */
hasNonWorkloadDependentObjectives(Element elem)306 	private boolean hasNonWorkloadDependentObjectives(Element elem)
307 	{
308 		Set elemObj = (Set)objMap.get(elem);
309 
310 		/*
311 		 * This code relies upon the fact that an element with
312 		 * no objectives will not have an empty set, but rather
313 		 * no value in the objMap.
314 		 */
315 
316 		if (elemObj == null)
317 			return (false);
318 
319 		Iterator elemObjIt = elemObj.iterator();
320 		while (elemObjIt.hasNext())
321 			if (elemObjIt.next() instanceof
322 			    WorkloadDependentObjective)
323 				return (false);
324 
325 		return (true);
326 	}
327 
328 	/**
329 	 * Determine if the given resource has workload-dependent
330 	 * objectives.
331 	 * @param elem The element to examine.
332 	 */
hasWorkloadDependentObjectives(Element elem)333 	private boolean hasWorkloadDependentObjectives(Element elem)
334 	{
335 		Set elemObj = (Set)objMap.get(elem);
336 
337 		/*
338 		 * This code relies upon the fact that an element with
339 		 * no objectives will not have an empty set, but rather
340 		 * no value in the objMap.
341 		 */
342 
343 		if (elemObj == null)
344 			return (false);
345 
346 		Iterator elemObjIt = elemObj.iterator();
347 		while (elemObjIt.hasNext())
348 			if (elemObjIt.next() instanceof
349 			    WorkloadDependentObjective)
350 				return (true);
351 
352 		return (false);
353 	}
354 
355 	/**
356 	 * Return true if the monitored configuration should be
357 	 * reconfigured. All workload-dependent objectives are
358 	 * examined to determine if the configuration is still
359 	 * satisfying all specified objectives on all elements. If any
360 	 * objectives are failing, then this method will return true.
361 	 *
362 	 * @param mon The monitoring object used to assess objective
363 	 * compliance.
364 	 */
examine(Monitor mon)365 	public boolean examine(Monitor mon) throws PoolsException,
366 	    StaleMonitorException
367 	{
368 		/*
369 		 * Take advantage of the guaranteed-valid monitor data
370 		 * for measuring the improvement of any past decisions.
371 		 */
372 		dh.expireAndMeasureImprovements(mon);
373 
374 		Iterator objIt = objMap.keySet().iterator();
375 		boolean ret = false;
376 		boolean hasLocalityObjectives = false;
377 		boolean isMonitorValid = true;
378 
379 		/*
380 		 * All objectives are examined, even though failure
381 		 * could be detected earlier. This is because logging
382 		 * of all objectives is required and the information
383 		 * about failure can be stored and used during
384 		 * solving.
385 		 */
386 		while (objIt.hasNext()) {
387 			Element elem = (Element) objIt.next();
388 			Set elemObj = (Set) objMap.get(elem);
389 			Iterator elemObjIt = elemObj.iterator();
390 			while (elemObjIt.hasNext()) {
391 				Objective obj = (Objective) elemObjIt.next();
392 				Poold.OPT_LOG.log(Severity.DEBUG,
393 				    "checking objective " + obj);
394 				if (obj instanceof WorkloadDependentObjective) {
395 					if (isValid()) {
396 						/*
397 						 * If any objectives
398 						 * are violated, then
399 						 * we must
400 						 * reconfigure, so
401 						 * check them all in
402 						 * turn.
403 						 */
404 					ret = ((WorkloadDependentObjective)
405 					    obj).examine(conf, this, elem) ||
406 					    ret;
407 					} else
408 						isMonitorValid = false;
409 				}
410 			}
411 
412 			/*
413 			 * Check if this is the first element, seen in
414 			 * this pass, that has locality objectives.
415 			 */
416 			if (!hasLocalityObjectives &&
417 			    hasNonWorkloadDependentObjectives(elem))
418 				hasLocalityObjectives = true;
419 		}
420 		if (isMonitorValid == false) {
421 			Poold.MON_LOG.log(Severity.INFO,
422 			    "not evaluating workload-dependent objectives " +
423 			    "until sufficient statistics are collected");
424 		}
425 		/*
426 		 * If we don't have locality objectives, we don't force
427 		 * the reexamination.  This is controlled by
428 		 * hasLocalityObjectives.
429 		 *
430 		 * So that we don't continually trigger
431 		 * reconfiguration examinations for locality
432 		 * objectives we stop forcing recalculations when we
433 		 * reach cpuCount / 2. This should be enough moves to
434 		 * get good locality for those configurations which
435 		 * have no WorkloadDependentObjectives.
436 		 */
437 		return (ret || (hasLocalityObjectives && (examineCount++ <
438 		    cpuCount / 2)));
439 	}
440 
441 	/**
442 	 * Reallocate resources in a configuration to achieve user
443 	 * specified objectives. Return true if the configuration has
444 	 * been updated, false otherwise.
445 	 *
446 	 * This method should only be invoked if a previous
447 	 * examination of the configuration which is monitored
448 	 * indicates that objectives are failing. The monitored
449 	 * configuration is re-opened for editing, locking that
450 	 * configuration for the duration of this operation.
451 	 *
452 	 * @throws Exception If the solve fails.
453 	 */
solve()454 	public boolean solve() throws Exception
455 	{
456 		boolean madeMove = false;
457 		/*
458 		 * All solving operations must be done in an
459 		 * "editable" context, so create a new modifiable
460 		 * configuration which is at the same location as the
461 		 * monitored configuration
462 		 */
463 		Configuration rwConf = new Configuration(conf.getLocation(),
464 		    PoolInternal.PO_RDWR);
465 
466 		try {
467 			/*
468 			 * Build a resource set importance map for use
469 			 * when propagating pool importance to each
470 			 * possible solution. Use the same logic as
471 			 * libpool and let a resource take the highest
472 			 * importance value from all pools associated
473 			 * with the set.
474 			 */
475 			Map resImp = new HashMap();
476 			List poolList = rwConf.getPools(null);
477 			Iterator itPool = poolList.iterator();
478 			while (itPool.hasNext()) {
479 				Pool pool = (Pool) itPool.next();
480 				long newImp = pool.
481 				    getLongProperty("pool.importance");
482 				List resList = pool.getResources(null);
483 				Iterator itRes = resList.iterator();
484 				while (itRes.hasNext()) {
485 					Resource res = (Resource) itRes.next();
486 					if (resImp.containsKey(res)) {
487 						Long imp = (Long) resImp.
488 						    get(res);
489 						if (newImp > imp.longValue())
490 							resImp.put(res,
491 							    new Long(newImp));
492 					} else
493 						resImp.put(res,
494 						    new Long(newImp));
495 				}
496 			}
497 			/*
498 			 * Consider all possible alternative
499 			 * configurations.  This list is generated as a
500 			 * series of moves.  Administrative constraints
501 			 * are applied to the moves to prune the list of
502 			 * possible configurations.
503 			 */
504 			Value val = new Value("type", "pset");
505 			List valueList = new LinkedList();
506 			valueList.add(val);
507 
508 
509 			List resList = rwConf.getResources(valueList);
510 			val.close();
511 			List donors = getDonors(resList);
512 			List receivers = getRecipients(resList);
513 			Poold.OPT_LOG.log(Severity.DEBUG, "donors: " +
514 			    donors);
515 			Poold.OPT_LOG.log(Severity.DEBUG, "receivers: " +
516 			    receivers);
517 			Iterator itDonor = donors.iterator();
518 			List moves = new ArrayList();
519 			while (itDonor.hasNext()) {
520 				Resource donor = (Resource) itDonor.next();
521 				List processors = getProcessors(donor);
522 				Poold.OPT_LOG.log(Severity.DEBUG,
523 				    "donor processors: " + processors);
524 				Iterator itProcessor = processors.iterator();
525 				while (itProcessor.hasNext()) {
526 					Component cpu = (Component) itProcessor.
527 					    next();
528 					Iterator itReceiver = receivers.
529 					    iterator();
530 					while (itReceiver.hasNext()) {
531 						Resource receiver =
532 						    (Resource) itReceiver.
533 						    next();
534 						/*
535 						 * Can't move to yourself
536 						 */
537 						if (receiver == donor)
538 							continue;
539 						moves.add(new ComponentMove(
540 						      donor, receiver, cpu));
541 					}
542 				}
543 			}
544 			Poold.OPT_LOG.log(Severity.DEBUG,
545 			    "potential moves: " + moves);
546 			/*
547 			 * Now that we have our alternative configurations,
548 			 * score each configuration by applying all objectives
549 			 * to each configuration. Hold the scores in the
550 			 * score set.
551 			 */
552 			HashSet scores = new HashSet();
553 			Iterator itMoves = moves.iterator();
554 			while (itMoves.hasNext()) {
555 				double totalContrib = 0;
556 				Move move = (Move) itMoves.next();
557 				Iterator objIt = objMap.keySet().iterator();
558 				while (objIt.hasNext()) {
559 					Element elem = (Element) objIt.next();
560 					Set elemObj = (Set) objMap.get(elem);
561 					Iterator elemObjIt = elemObj.iterator();
562 					while (elemObjIt.hasNext()) {
563 						Objective obj =
564 						    (Objective)elemObjIt.next();
565 						if (obj instanceof
566 						    LocalityObjective)
567 							((LocalityObjective)obj)
568 							    .prepare(ldom,
569 							    (Resource)elem);
570 						/*
571 						 * If the monitor is
572 						 * invalid, do not
573 						 * process
574 						 * WorkloadDependentObjectives
575 						 * since they have an
576 						 * implicit dependency
577 						 * on the monitor
578 						 * data.
579 						 */
580 						if (obj instanceof
581 						    WorkloadDependentObjective)
582 							if (!isValid())
583 								continue;
584 						double contrib = obj.calculate(
585 						    rwConf, move, elem);
586 						if (contrib < -1 || contrib > 1)
587 							throw new
588 							IllegalOFValueException(
589 						        "x: " + contrib +
590 							" is invalid, legal " +
591 							"range is -1 <= x <= " +
592 							"1");
593 						/*
594 						 * Modify the basic
595 						 * score by the
596 						 * importance of the
597 						 * objective and (if
598 						 * appropriate) the
599 						 * importance of an
600 						 * associated pool.
601 						 */
602 						if (resImp.containsKey(elem)) {
603 							contrib *= ((Long)
604 							    resImp.get(elem)).
605 							    longValue();
606 						}
607 
608 						totalContrib += contrib *
609 						    obj.getExpression().
610 						    getImportance();
611 					}
612 				}
613 				Poold.OPT_LOG.log(Severity.DEBUG,
614 				    "scored move (" + move + ") " +
615 				    totalContrib);
616 				scores.add(new ScoreMove(move, totalContrib));
617 			}
618 			if (scores.size() != 0) {
619 				/*
620 				 * Try to find a move to apply which
621 				 * yields a positive contribution.
622 				 */
623 				Object scoresArray[] = scores.toArray();
624 				Arrays.sort(scoresArray,
625 				    Collections.reverseOrder());
626 				if ((madeMove = processMoves(rwConf,
627 				    scoresArray, false)) == false)
628 					madeMove = processMoves(rwConf,
629 					    scoresArray, true);
630 			} else
631 				Poold.OPT_LOG.log(Severity.INFO,
632 				    "no moves found");
633 			rwConf.close();
634 			Poold.OPT_LOG.log(Severity.DEBUG,
635 			    "synchronizing decision history");
636 			dh.syncToFile(dhPath);
637 		} catch (Exception ex) {
638 			rwConf.close();
639 			throw ex;
640 		}
641 		return (madeMove);
642 	}
643 
644 	/*
645 	 * Process the supplied array of scored moves, trying to find
646 	 * a move to apply. Return true if a move could be applied,
647 	 * false otherwise.
648 	 *
649 	 * @param conf The configuration to be modified.
650 	 * @param scores The areay of scored moves to be tried.
651 	 * @param ignoreDH Ignore Decision History details.
652 	 */
processMoves(Configuration rwConf, Object scores[], boolean ignoreDH)653 	private boolean processMoves(Configuration rwConf, Object scores[],
654 	    boolean ignoreDH) throws PoolsException, StaleMonitorException
655 	{
656 		boolean madeMove = false;
657 
658 		for (int i = 0; i < scores.length; i++) {
659 			ScoreMove move = (ScoreMove) scores[i];
660 			if (move.getScore() <= 0) {
661 				if (ignoreDH)
662 					Poold.OPT_LOG.log(Severity.INFO,
663 					    move + " not applied as " +
664 					    "benefit not significant");
665 				break;
666 			}
667 			if ((madeMove = applyMove(rwConf, move, ignoreDH)) ==
668 			    true)
669 				break;
670 		}
671 		return (madeMove);
672 	}
673 
674 	/*
675 	 * Attempt to apply the supplied move to the
676 	 * configuration. Return true if the move could be applied,
677 	 * false otherwise.
678 	 *
679 	 * @param conf The configuration to be modified.
680 	 * @param move The move to be applied.
681 	 * @param ignoreDH Ignore Decision History details.
682 	 */
applyMove(Configuration conf, ScoreMove move, boolean ignoreDH)683 	private boolean applyMove(Configuration conf, ScoreMove move,
684 	    boolean ignoreDH)
685 	    throws PoolsException, StaleMonitorException
686 	{
687 		boolean madeMove = false;
688 		boolean wdpInvolved = false;
689 		double utilization = 0.0;
690 
691 		Poold.OPT_LOG.log(Severity.DEBUG, "selected " + move);
692 		if (hasWorkloadDependentObjectives(move.getMove().getTo()) ||
693 		    hasWorkloadDependentObjectives(conf)) {
694 			Poold.OPT_LOG.log(Severity.DEBUG,
695 			    "Attempting to retrieve utilization for:" + move
696 			    .getMove().getTo());
697 			utilization = monitor.getUtilization(move
698 			    .getMove().getTo());
699 			wdpInvolved = true;
700 		}
701 
702 		/*
703 		 * Unless a move can be vetoed (i.e. decision history
704 		 * is effective and there are is a workload-dependent
705 		 * involved), the move should alwways be applied.
706 		 */
707 		if (ignoreDH || !wdpInvolved || !dh.veto(move.getMove(),
708 		    utilization)) {
709 			Poold.OPT_LOG.log(Severity.INFO,
710 			    "applying move " + move.getMove());
711 			move.getMove().apply();
712 			ResourceMonitor mon = getMonitor().get(move.getMove().
713 			    getFrom());
714 			mon.resetData("utilization");
715 			mon = getMonitor().get(move.getMove().getTo());
716 			mon.resetData("utilization");
717 			try {
718 				Poold.OPT_LOG.log(Severity.DEBUG,
719 				    "committing configuration");
720 				conf.commit(0);
721 				try {
722 					if (move.getMove() instanceof
723 					    ComponentMove)
724 						if (wdpInvolved)
725 							dh.recordProcessorMove(
726 							    (ComponentMove)move
727 							    .getMove(),
728 							    utilization,
729 							    monitor
730 							    .getSampleCount());
731 						else
732 							Poold.OPT_LOG.log(
733 							    Severity.DEBUG,
734 							    "decision not " +
735 							    "recorded due to " +
736 							    "lack of workload-"
737 							    + "dependent " +
738 							    "objectives");
739 				} catch (Exception e) {
740 					Poold.OPT_LOG.log(Severity.INFO,
741 					    "couldn't update " +
742 					    "decision history (" +
743 					    e.toString() + ")");
744 				}
745 				madeMove = true;
746 			} catch (PoolsException pe) {
747 				conf.rollback();
748 				Poold.OPT_LOG.log(Severity.INFO,
749 				    "move failed, possibly due to a " +
750 				    "bound process in a 1-processor " +
751 				    "set");
752 			}
753 		} else {
754 			/*
755 			 * Move was vetoed.
756 			 */
757 			if (!ignoreDH && wdpInvolved)
758 				Poold.OPT_LOG.log(Severity.INFO,
759 				    move.getMove() + " not applied due to " +
760 				    "poor past results");
761 		}
762 		return (madeMove);
763 	}
764 
765 	/**
766 	 * Add an objective based on the supplied expression to the
767 	 * supplied set of objectives.
768 	 *
769 	 * @param oSet Set of objectives to be extended
770 	 * @param type Type of element to which the expression is applied
771 	 * @param exp Expression to be used in the objective
772 	 * @throws IllegalArgumentException If a duplicate objective
773 	 * is identified or an invalid expression is supplied for this
774 	 * type of element
775 	 */
addObjective(Set oSet, String type, Expression exp)776 	private void addObjective(Set oSet, String type, Expression exp)
777 	    throws IllegalArgumentException
778 	{
779 		Objective o = AbstractObjective.getInstance(type, exp);
780 		Poold.CONF_LOG.log(Severity.DEBUG, "parsed objective " + o);
781 		/*
782 		 * Check the set of objectives and find contradictions.
783 		 */
784 		Iterator itObjs = oSet.iterator();
785 		while (itObjs.hasNext()) {
786 			Objective other = (Objective) itObjs.next();
787 			if (o.getExpression().contradicts(
788 			    other.getExpression()))
789 				throw new IllegalArgumentException(
790 				    "contradictory objectives:" + other +
791 				    ", " + o);
792 		}
793 
794 		if (oSet.add(o) != true)
795 			throw new IllegalArgumentException(
796 			    "duplicate objective:" + o);
797 	}
798 
799 	/**
800 	 * Return a list of resource sets prepared to receive
801 	 * resources
802 	 *
803 	 * The list consists of all resource setss (of the supplied
804 	 * type) whose size is < their max constraint.
805 	 *
806 	 * @param resList The list of all resource sets from which
807 	 * recipients will be chosen
808 	 * @throws PoolsException if there is an error manipulation
809 	 * the pool resources
810 	 */
getRecipients(List resList)811 	private List getRecipients(List resList) throws PoolsException
812 	{
813 		List recipientList = new ArrayList();
814 		long size, max;
815 		for (int i = 0; i < resList.size(); i++) {
816 			Resource res;
817 			res = (Resource)resList.get(i);
818 			String type = res.getStringProperty("type");
819 			size = res.getLongProperty(type+".size");
820 			max = res.getLongProperty(type+".max");
821 			if (size < max)
822 				recipientList.add(res);
823 		}
824 		return (recipientList);
825 	}
826 
827 	/**
828 	 * Return a list of resource sets prepared to donate resources
829 	 *
830 	 * The list consists of all resource sets (of the supplied
831 	 * type) whose size (minus the number of pinned resources
832 	 * where applicable) is > their min constraint.
833 	 *
834 	 * @param resList The list of all resource sets from which
835 	 * recipients will be chosen
836 	 * @throws PoolsException if there is an error manipulation
837 	 * the pool resources
838 	 */
getDonors(List resList)839 	private List getDonors(List resList) throws PoolsException
840 	{
841 		List donorList = new ArrayList();
842 		long size, min;
843 		for (int i = 0; i < resList.size(); i++) {
844 			Value bValue;
845 			Resource res;
846 			List pinned;
847 			ArrayList valueList = new ArrayList();
848 
849 			res = (Resource)resList.get(i);
850 			String type = res.getStringProperty("type");
851 			size = res.getLongProperty(type+".size");
852 			bValue = new Value("cpu.pinned", true);
853 			valueList.add(bValue);
854 			pinned = res.getComponents(valueList);
855 			bValue.close();
856 			min = res.getLongProperty(type+".min");
857 			if (pinned.size() > min)
858 				size -= pinned.size() - min;
859 			if (size > min)
860 				donorList.add(res);
861 		}
862 		return (donorList);
863 	}
864 
865 	/**
866 	 * Return a list of Processors for the supplied resource.
867 	 *
868 	 * The list consists of all Processors (excluding the pinned
869 	 * Processors) in the set.
870 	 *
871 	 * @param set The resource for which Processors should be found
872 	 * @throws PoolsException if there is an error manipulation
873 	 * the pool resources
874 	 */
getProcessors(Resource set)875 	private List getProcessors(Resource set) throws PoolsException
876 	{
877 		Iterator it = set.getComponents(null).iterator();
878 		List ret = new ArrayList();
879 
880 		while (it.hasNext()) {
881 			Component cpu = (Component) it.next();
882 			try
883 			{
884 				if (cpu.getBoolProperty("cpu.pinned") == false)
885 					ret.add(cpu);
886 			} catch (PoolsException pe)
887 			{
888 				ret.add(cpu);
889 			}
890 		}
891 		return (ret);
892 	}
893 
894 	/**
895 	 * Return true if the solver is capable of working with
896 	 * statistically valid data.
897 	 */
isValid()898 	public boolean isValid()
899 	{
900 		return (monitor.isValid());
901 	}
902 
903 	/**
904 	 * Return the monitor used by this solver.
905 	 */
getMonitor()906 	public Monitor getMonitor()
907 	{
908 		return (monitor);
909 	}
910 
911 	/**
912 	 * Return the set of objectives associated with the supplied
913 	 * element.
914 	 *
915 	 * @param elem Retrieve objectives for this element.
916 	 */
getObjectives(Element elem)917 	public Set getObjectives(Element elem)
918 	{
919 		return ((Set)objMap.get(elem));
920 	}
921 
922 	/**
923 	 * Holds details about the score of a proposed configuration
924 	 * move. Each move must be scored so that they can ranked in
925 	 * terms of increasing desirability
926 	 */
927 	static class ScoreMove implements Comparable {
928 		/**
929 		 * The move which is being scored.
930 		 */
931 		private final Move m;
932 
933 		/**
934 		 * The score of the move.
935 		 */
936 		private final double score;
937 
938 		/**
939 		 * Score formatter.
940 		 */
941 		private static final DecimalFormat scoreFormat =
942 		    new DecimalFormat("0.00");
943 
944 		/**
945 		 * Constructor.
946 		 *
947 		 * @param m The move under consideration.
948 		 * @param score The score of the move.
949 		 */
ScoreMove(Move m, double score)950 		public ScoreMove(Move m, double score)
951 		{
952 			this.m = m;
953 			this.score = score;
954 		}
955 
compareTo(Object o)956 		public int compareTo(Object o)
957 		{
958 			ScoreMove other = (ScoreMove) o;
959 
960 			return ((score < other.getScore()) ? -1 :
961 			    (score > other.getScore()) ? 1 : 0);
962 		}
963 
toString()964 		public String toString()
965 		{
966 			return ("move (" + m + ") score "
967 			    + scoreFormat.format(score));
968 		}
969 
970 		/**
971 		 * Return the score.
972 		 */
getScore()973 		double getScore()
974 		{
975 			return (score);
976 		}
977 
978 		/**
979 		 * Return the move.
980 		 */
getMove()981 		Move getMove()
982 		{
983 			return (m);
984 		}
985 	}
986 }
987