brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [3/7] brooklyn-server git commit: Set highlights on many more things
Date Sat, 16 Sep 2017 01:52:02 GMT
Set highlights on many more things

* selected policies (autoscaler, restarter, replacer, membership trackers)  - triggers, actions, and confirmations/violations
* triggers for most enrichers (with new conveniences added for sensor triggers)
* period trigger for all feeds (from `Poller` using new `highlightTriggerPeriod` in `AbstractFeed`)
* publish sensor actions for all enrichers (new `highlightActionPublishSensor` convenience for `AbstractEntityAdjuct`, called in `AbstractEnricher`, and in feed handlers via new `AbstractFeed.onSensor..` methods)


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/71708469
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/71708469
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/71708469

Branch: refs/heads/master
Commit: 717084697c206e84a19922ec1dc30945b82523db
Parents: 5ae9c41
Author: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Authored: Wed Sep 13 11:39:30 2017 +0100
Committer: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Committed: Wed Sep 13 12:48:56 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/api/objs/HighlightTuple.java       |  5 ++
 .../core/enricher/AbstractEnricher.java         | 19 ++++--
 .../entity/lifecycle/ServiceStateLogic.java     |  3 +
 .../apache/brooklyn/core/feed/AbstractFeed.java | 16 ++++-
 .../core/feed/AttributePollHandler.java         |  2 +
 .../org/apache/brooklyn/core/feed/Poller.java   | 20 +++++--
 .../core/objs/AbstractEntityAdjunct.java        | 53 +++++++++++++++++
 .../enricher/stock/AbstractAggregator.java      | 12 ++++
 .../stock/AbstractMultipleSensorAggregator.java |  2 -
 .../enricher/stock/AbstractTransformer.java     |  3 +
 .../stock/AbstractTypeTransformingEnricher.java |  3 +
 .../brooklyn/enricher/stock/Aggregator.java     |  5 ++
 .../brooklyn/enricher/stock/Combiner.java       |  2 +
 .../apache/brooklyn/enricher/stock/Joiner.java  |  1 +
 .../brooklyn/enricher/stock/Propagator.java     |  6 ++
 .../brooklyn/enricher/stock/UpdatingMap.java    |  2 +
 .../enricher/stock/reducer/Reducer.java         |  1 +
 .../group/AbstractMembershipTrackingPolicy.java | 23 +++++++-
 .../SshCommandMembershipTrackingPolicy.java     | 15 ++++-
 .../policy/autoscaling/AutoScalerPolicy.java    | 62 ++++++++++++++------
 .../brooklyn/policy/ha/ServiceReplacer.java     | 46 ++++++++-------
 .../brooklyn/policy/ha/ServiceRestarter.java    |  2 +-
 .../autoscaling/AutoScalerPolicyRebindTest.java |  5 +-
 .../entity/machine/pool/ServerPoolImpl.java     |  1 +
 .../software/base/SoftwareProcessImpl.java      |  3 +
 .../brooklynnode/SelectMasterEffectorTest.java  |  2 +-
 26 files changed, 257 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/api/src/main/java/org/apache/brooklyn/api/objs/HighlightTuple.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/brooklyn/api/objs/HighlightTuple.java b/api/src/main/java/org/apache/brooklyn/api/objs/HighlightTuple.java
index 7e99c1c..97e6633 100644
--- a/api/src/main/java/org/apache/brooklyn/api/objs/HighlightTuple.java
+++ b/api/src/main/java/org/apache/brooklyn/api/objs/HighlightTuple.java
@@ -84,4 +84,9 @@ public class HighlightTuple {
         result = 31 * result + (taskId != null ? taskId.hashCode() : 0);
         return result;
     }
+    
+    @Override
+    public String toString() {
+        return "highlight["+description+"; time="+System.currentTimeMillis()+(taskId==null ? "" : "; task="+taskId)+"]";
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/core/enricher/AbstractEnricher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/enricher/AbstractEnricher.java b/core/src/main/java/org/apache/brooklyn/core/enricher/AbstractEnricher.java
index 39b23ba..08a39be 100644
--- a/core/src/main/java/org/apache/brooklyn/core/enricher/AbstractEnricher.java
+++ b/core/src/main/java/org/apache/brooklyn/core/enricher/AbstractEnricher.java
@@ -51,7 +51,7 @@ public abstract class AbstractEnricher extends AbstractEntityAdjunct implements
             "Whether duplicate values published by this enricher should be suppressed");
 
     private static class DeduplicatingAttributeModifier<T> implements Function<T, Maybe<T>> {
-        public static <T> Function<T, Maybe<T>> create(T newVal) {
+        public static <T> DeduplicatingAttributeModifier<T> create(T newVal) {
             return new DeduplicatingAttributeModifier<T>(newVal);
         }
 
@@ -60,15 +60,20 @@ public abstract class AbstractEnricher extends AbstractEntityAdjunct implements
         }
 
         private T newValue;
+        private Maybe<T> lastValue;
 
         @Override
         public Maybe<T> apply(T oldValue) {
             if (Objects.equal(oldValue, newValue)) {
-                return Maybe.absent("Skipping update, values equal");
+                return lastValue = Maybe.absent("Skipping update, values equal");
             } else {
-                return Maybe.of(newValue);
+                return lastValue = Maybe.of(newValue);
             }
         }
+        
+        public Maybe<T> getLastValue() {
+            return lastValue;
+        }
     }
 
     private final EnricherDynamicType enricherType;
@@ -129,16 +134,22 @@ public abstract class AbstractEnricher extends AbstractEntityAdjunct implements
         }
         
         T newVal = TypeCoercions.coerce(val, sensor.getTypeToken());
+        Maybe<T> published = Maybe.of(newVal);
         if (sensor instanceof AttributeSensor) {
             AttributeSensor<T> attribute = (AttributeSensor<T>)sensor;
             if (Boolean.TRUE.equals(suppressDuplicates)) {
-                entity.sensors().modify(attribute, DeduplicatingAttributeModifier.create(newVal));
+                DeduplicatingAttributeModifier<T> modifier = DeduplicatingAttributeModifier.create(newVal);
+                entity.sensors().modify(attribute, modifier);
+                published = modifier.getLastValue();
             } else {
                 entity.sensors().set(attribute, newVal);
             }
         } else { 
             entity.sensors().emit(sensor, newVal);
         }
+        if (published!=null && published.isPresent()) {
+            highlightActionPublishSensor(sensor, published.get());
+        }
     }
     
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java b/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
index 0ce9169..2b7113e 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
@@ -285,6 +285,7 @@ public class ServiceStateLogic {
             if (uniqueTag==null) uniqueTag = DEFAULT_ENRICHER_UNIQUE_TAG;
         }
 
+        @SuppressWarnings("unchecked")
         @Override
         public void setEntity(EntityLocal entity) {
             super.setEntity(entity);
@@ -297,6 +298,8 @@ public class ServiceStateLogic {
             subscriptions().subscribe(notifyOfInitialValue, entity, SERVICE_PROBLEMS, this);
             subscriptions().subscribe(notifyOfInitialValue, entity, SERVICE_UP, this);
             subscriptions().subscribe(notifyOfInitialValue, entity, SERVICE_STATE_EXPECTED, this);
+            
+            highlightTriggers(MutableList.of(SERVICE_PROBLEMS, SERVICE_UP, SERVICE_STATE_EXPECTED), null);
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java b/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java
index fba15ca..84165ea 100644
--- a/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java
+++ b/core/src/main/java/org/apache/brooklyn/core/feed/AbstractFeed.java
@@ -24,6 +24,7 @@ import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.mgmt.rebind.RebindSupport;
 import org.apache.brooklyn.api.mgmt.rebind.mementos.FeedMemento;
 import org.apache.brooklyn.api.sensor.Feed;
+import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.BrooklynFeatureEnablement;
 import org.apache.brooklyn.core.config.ConfigKeys;
@@ -32,6 +33,7 @@ import org.apache.brooklyn.core.mgmt.rebind.BasicFeedRebindSupport;
 import org.apache.brooklyn.core.objs.AbstractEntityAdjunct;
 import org.apache.brooklyn.util.javalang.JavaClassNames;
 import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -101,7 +103,7 @@ public abstract class AbstractFeed extends AbstractEntityAdjunct implements Feed
             throw new IllegalStateException(String.format("Attempt to re-start feed %s of entity %s", this, entity));
         }
         
-        poller = new Poller<Object>(entity, getConfig(ONLY_IF_SERVICE_UP));
+        poller = new Poller<Object>(entity, this, getConfig(ONLY_IF_SERVICE_UP));
         activated = true;
         preStart();
         synchronized (pollerStateMutex) {
@@ -225,4 +227,16 @@ public abstract class AbstractFeed extends AbstractEntityAdjunct implements Feed
     protected Poller<?> getPoller() {
         return poller;
     }
+
+    void highlightTriggerPeriod(Duration minPeriod) {
+        highlightTriggers("Running every "+minPeriod);
+    }
+
+    void onRemoveSensor(Sensor<?> sensor) {
+        highlightActionPublishSensor("Clear sensor "+sensor.getName());
+    }
+
+    void onPublishSensor(Sensor<?> sensor, Object v) {
+        highlightActionPublishSensor(sensor, v);
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/core/feed/AttributePollHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/feed/AttributePollHandler.java b/core/src/main/java/org/apache/brooklyn/core/feed/AttributePollHandler.java
index ec69e51..fca844b 100644
--- a/core/src/main/java/org/apache/brooklyn/core/feed/AttributePollHandler.java
+++ b/core/src/main/java/org/apache/brooklyn/core/feed/AttributePollHandler.java
@@ -219,6 +219,7 @@ public class AttributePollHandler<V> implements PollHandler<V> {
             // nothing
         } else if (v == FeedConfig.REMOVE) {
             ((EntityInternal)entity).removeAttribute(sensor);
+            feed.onRemoveSensor(sensor);
         } else if (sensor == FeedConfig.NO_SENSOR) {
             // nothing
         } else {
@@ -227,6 +228,7 @@ public class AttributePollHandler<V> implements PollHandler<V> {
                 // no change; nothing
             } else {
                 entity.sensors().set(sensor, coercedV);
+                feed.onPublishSensor(sensor, coercedV);
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/core/feed/Poller.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/feed/Poller.java b/core/src/main/java/org/apache/brooklyn/core/feed/Poller.java
index 1a29c48..5a78f37 100644
--- a/core/src/main/java/org/apache/brooklyn/core/feed/Poller.java
+++ b/core/src/main/java/org/apache/brooklyn/core/feed/Poller.java
@@ -51,6 +51,7 @@ public class Poller<V> {
     public static final Logger log = LoggerFactory.getLogger(Poller.class);
 
     private final Entity entity;
+    private final AbstractFeed feed;
     private final boolean onlyIfServiceUp;
     private final Set<Callable<?>> oneOffJobs = new LinkedHashSet<Callable<?>>();
     private final Set<PollJob<V>> pollJobs = new LinkedHashSet<PollJob<V>>();
@@ -92,14 +93,15 @@ public class Poller<V> {
             };
         }
     }
-    
-    /** @deprecated since 0.7.0, pass in whether should run onlyIfServiceUp */
+
+    /** @deprecated since 0.12.0 pass in feed */
     @Deprecated
-    public Poller(Entity entity) {
-        this(entity, false);
-    }
     public Poller(Entity entity, boolean onlyIfServiceUp) {
+        this(entity, null, onlyIfServiceUp);
+    }
+    public Poller(Entity entity, AbstractFeed feed, boolean onlyIfServiceUp) {
         this.entity = entity;
+        this.feed = feed;
         this.onlyIfServiceUp = onlyIfServiceUp;
     }
     
@@ -140,6 +142,7 @@ public class Poller<V> {
             oneOffTasks.add(((EntityInternal)entity).getExecutionContext().submit(task));
         }
         
+        Duration minPeriod = null;
         for (final PollJob<V> pollJob : pollJobs) {
             final String scheduleName = pollJob.handler.getDescription();
             if (pollJob.pollPeriod.compareTo(Duration.ZERO) > 0) {
@@ -166,10 +169,17 @@ public class Poller<V> {
                         .period(pollJob.pollPeriod)
                         .cancelOnException(false);
                 tasks.add(Entities.submit(entity, task));
+                if (minPeriod==null || (pollJob.pollPeriod.isShorterThan(minPeriod))) {
+                    minPeriod = pollJob.pollPeriod;
+                }
             } else {
                 if (log.isDebugEnabled()) log.debug("Activating poll (but leaving off, as period {}) for {} (using {})", new Object[] {pollJob.pollPeriod, entity, this});
             }
         }
+        
+        if (minPeriod!=null && feed!=null) {
+            feed.highlightTriggerPeriod(minPeriod);
+        }
     }
     
     public void stop() {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
index 7ced5f9..c157988 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
@@ -592,10 +592,55 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple
     protected void highlight(String name, String description, @Nullable Task<?> t) {
         highlights.put(name, new HighlightTuple(description, System.currentTimeMillis(), t!=null ? t.getId() : null));
     }
+    
     /** As {@link #setHighlight(String, HighlightTuple)} for {@link #HIGHLIGHT_NAME_TRIGGERS} (as ongoing). */
     protected void highlightTriggers(String description) {
         highlightOngoing(HIGHLIGHT_NAME_TRIGGERS, description);
     }
+    protected <T> void highlightTriggers(Sensor<?> s, Object source) {
+        highlightTriggers(Collections.singleton(s), Collections.singleton(source));
+    }
+    protected <T> void highlightTriggers(Iterable<? extends Sensor<? extends T>> s, Object source) {
+        highlightTriggers(s, (Iterable<?>) (source instanceof Iterable ? (Iterable<?>)source : Collections.singleton(source)));
+    }
+    protected <U> void highlightTriggers(Sensor<?> s, Iterable<U> sources) {
+        highlightTriggers(Collections.singleton(s), sources);
+    }
+    protected <T,U> void highlightTriggers(Iterable<? extends Sensor<? extends T>> sensors, Iterable<U> sources) {
+        StringBuilder msg = new StringBuilder("Listening for ");
+        boolean firstWord = true;
+        if (sensors!=null) for (Object s: sensors) {
+            if (s==null) continue;
+            if (!firstWord) { msg.append(", "); } else { firstWord = false; }
+            // s is normally a sensor but forgive other things if caller cheated generics
+            msg.append(s instanceof Sensor ? ((Sensor<?>)s).getName() : s.toString());
+        }
+        if (firstWord) msg.append("<nothing>");
+        
+        firstWord = true;
+        boolean selfWasFirst = false;
+        if (sources!=null) for (Object s: sources) {
+            if (s==null) continue;
+            if (!firstWord) { 
+                msg.append(", "); 
+            } else {
+                if (s.equals(getEntity())) {
+                    // don't log self unless there is another
+                    selfWasFirst = true;
+                    continue;
+                }
+                msg.append(" on ");
+                if (selfWasFirst) {
+                    msg.append("self, ");
+                }
+                firstWord = false; 
+            }
+            if (s.equals(getEntity())) msg.append("self");
+            else msg.append(""+s);
+        }
+        highlightTriggers(msg.toString());
+    }
+
     /** As {@link #setHighlight(String, HighlightTuple)} for {@link #HIGHLIGHT_NAME_LAST_CONFIRMATION}. */
     protected void highlightConfirmation(String description) {
         highlightNow(HIGHLIGHT_NAME_LAST_CONFIRMATION, description);
@@ -604,6 +649,14 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple
     protected void highlightAction(String description, Task<?> t) {
         highlight(HIGHLIGHT_NAME_LAST_ACTION, description, t);
     }
+    /** As {@link #setHighlight(String, HighlightTuple)} for {@link #HIGHLIGHT_NAME_LAST_ACTION} when publishing a sensor. */
+    protected void highlightActionPublishSensor(Sensor<?> s, Object v) {
+        highlightActionPublishSensor("Publish "+s.getName()+" "+v);
+    }
+    /** As {@link #setHighlight(String, HighlightTuple)} for {@link #HIGHLIGHT_NAME_LAST_ACTION} when publishing a sensor (freeform text). */
+    protected void highlightActionPublishSensor(String description) {
+        highlight(HIGHLIGHT_NAME_LAST_ACTION, description, null);
+    }
     /** As {@link #setHighlight(String, HighlightTuple)} for {@link #HIGHLIGHT_NAME_LAST_VIOLATION}. */
     protected void highlightViolation(String description) {
         highlightNow(HIGHLIGHT_NAME_LAST_VIOLATION, description);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractAggregator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractAggregator.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractAggregator.java
index 92363e6..9cb7b99 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractAggregator.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractAggregator.java
@@ -20,6 +20,8 @@ package org.apache.brooklyn.enricher.stock;
 
 import static com.google.common.base.Preconditions.checkState;
 
+import java.util.Collection;
+import java.util.List;
 import java.util.Set;
 
 import org.apache.brooklyn.api.entity.Entity;
@@ -33,6 +35,7 @@ import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
 import org.apache.brooklyn.core.entity.AbstractEntity;
 import org.apache.brooklyn.core.entity.trait.Changeable;
+import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.StringPredicates;
@@ -103,24 +106,31 @@ public abstract class AbstractAggregator<T,U> extends AbstractEnricher implement
         checkState(fromHardcodedProducers != null ^ producer != null, "must specify one of %s (%s) or %s (%s)", 
                 PRODUCER.getName(), producer, FROM_HARDCODED_PRODUCERS.getName(), fromHardcodedProducers);
 
+        List<Object> producers = MutableList.of();
+
         if (fromHardcodedProducers != null) {
             for (Entity producer : Iterables.filter(fromHardcodedProducers, entityFilter)) {
+                producers.add(producer);
                 addProducerHardcoded(producer);
             }
         }
         
         if (isAggregatingMembers()) {
+            producers.add(0, "members");
             setEntityBeforeSubscribingProducerMemberEvents(entity);
             setEntitySubscribeProducerMemberEvents();
             setEntityAfterSubscribingProducerMemberEvents();
         }
         
         if (isAggregatingChildren()) {
+            producers.add(0, "children");
             setEntityBeforeSubscribingProducerChildrenEvents();
             setEntitySubscribingProducerChildrenEvents();
             setEntityAfterSubscribingProducerChildrenEvents();
         }
         
+        highlightTriggers(getSourceSensors(), producers);
+
         onUpdated();
     }
 
@@ -137,6 +147,8 @@ public abstract class AbstractAggregator<T,U> extends AbstractEnricher implement
         setEntityLoadingTargetConfig();
     }
     
+    protected abstract Collection<Sensor<?>> getSourceSensors();
+    
     protected Predicate<?> getDefaultValueFilter() {
         if (getConfig(EXCLUDE_BLANK))
             return StringPredicates.isNonBlank();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractMultipleSensorAggregator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractMultipleSensorAggregator.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractMultipleSensorAggregator.java
index c0cd36d..7de27ca 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractMultipleSensorAggregator.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractMultipleSensorAggregator.java
@@ -47,8 +47,6 @@ public abstract class AbstractMultipleSensorAggregator<U> extends AbstractAggreg
 
     public AbstractMultipleSensorAggregator() { }
 
-    protected abstract Collection<Sensor<?>> getSourceSensors();
-
     @Override
     protected void setEntityLoadingConfig() {
         super.setEntityLoadingConfig();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractTransformer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractTransformer.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractTransformer.java
index 35138af..e2e325c 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractTransformer.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractTransformer.java
@@ -31,6 +31,7 @@ import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
 import org.apache.brooklyn.core.sensor.BasicSensorEvent;
 import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.javalang.JavaClassNames;
 import org.slf4j.Logger;
@@ -138,6 +139,8 @@ public abstract class AbstractTransformer<T,U> extends AbstractEnricher implemen
                 subscriptions().subscribe(MutableMap.of("notifyOfInitialValue", true), producer, (Sensor<?>)sensor, triggerListener);
             }
         }
+        
+        highlightTriggers(MutableList.<Sensor<?>>of(sourceSensor).appendAll(triggerSensors), producer);
     }
 
     /** returns a function for transformation, for immediate use only (not for caching, as it may change) */

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractTypeTransformingEnricher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractTypeTransformingEnricher.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractTypeTransformingEnricher.java
index 4c5294d..b59d998 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractTypeTransformingEnricher.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/AbstractTypeTransformingEnricher.java
@@ -33,6 +33,7 @@ import org.apache.brooklyn.util.core.flags.SetFromFlag;
  * @deprecated since 0.7.0; use {@link Enrichers.builder()}
  */
 @Deprecated
+// TODO this has active subclasses so i don't think it should be deprecated; possibly protected was the intention?
 public abstract class AbstractTypeTransformingEnricher<T,U> extends AbstractEnricher implements SensorEventListener<T> {
     
     @SetFromFlag
@@ -66,5 +67,7 @@ public abstract class AbstractTypeTransformingEnricher<T,U> extends AbstractEnri
             if (value!=null)
                 onEvent(new BasicSensorEvent(source, producer, value, -1));
         }
+        
+        highlightTriggers(source, producer);
     }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/enricher/stock/Aggregator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/Aggregator.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/Aggregator.java
index f1a85fa..1797d0d 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/Aggregator.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/Aggregator.java
@@ -91,6 +91,11 @@ public class Aggregator<T,U> extends AbstractAggregator<T,U> implements SensorEv
     public Aggregator() {}
 
     @Override
+    protected Collection<Sensor<?>> getSourceSensors() {
+        return Collections.singleton(sourceSensor);
+    }
+    
+    @Override
     @SuppressWarnings("unchecked")
     protected void setEntityLoadingConfig() {
         super.setEntityLoadingConfig();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/enricher/stock/Combiner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/Combiner.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/Combiner.java
index 961e16a..e2ab06c 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/Combiner.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/Combiner.java
@@ -107,6 +107,8 @@ public class Combiner<T,U> extends AbstractEnricher implements SensorEventListen
                 }
             }
         }
+        
+        highlightTriggers(sourceSensors, producer);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/enricher/stock/Joiner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/Joiner.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/Joiner.java
index 51f599e..0b562f4 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/Joiner.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/Joiner.java
@@ -93,6 +93,7 @@ public class Joiner<T> extends AbstractEnricher implements SensorEventListener<T
         this.sourceSensor = (AttributeSensor<T>) getRequiredConfig(SOURCE_SENSOR);
         this.targetSensor = (Sensor<String>) getRequiredConfig(TARGET_SENSOR);
 
+        highlightTriggers(sourceSensor, producer);
         subscriptions().subscribe(producer, sourceSensor, this);
 
         Object value = producer.getAttribute((AttributeSensor<?>) sourceSensor);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/enricher/stock/Propagator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/Propagator.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/Propagator.java
index 21b7ffe..1446d8d 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/Propagator.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/Propagator.java
@@ -162,10 +162,16 @@ public class Propagator extends AbstractEnricher implements SensorEventListener<
 
         if (propagatingAll) {
             subscriptions().subscribe(producer, null, this);
+            highlightTriggers("Listening for all sensors on "+producer);
         } else {
             for (Sensor<?> sensor : sensorMapping.keySet()) {
                 subscriptions().subscribe(producer, sensor, this);
             }
+            if (sensorMapping.keySet().size() > 3) {
+                highlightTriggers("Listening for "+sensorMapping.keySet()+" sensors on "+producer);
+            } else {
+                highlightTriggers(sensorMapping.keySet(), producer);
+            }
         }
         
         emitAllAttributes();

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/enricher/stock/UpdatingMap.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/UpdatingMap.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/UpdatingMap.java
index acd4dbd..f304bdc 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/UpdatingMap.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/UpdatingMap.java
@@ -133,6 +133,8 @@ public class UpdatingMap<S,TKey,TVal> extends AbstractEnricher implements Sensor
         this.removingIfResultIsNull = getConfig(REMOVING_IF_RESULT_IS_NULL);
 
         subscriptions().subscribe(ImmutableMap.of("notifyOfInitialValue", true), producer, sourceSensor, this);
+        
+        highlightTriggers(sourceSensor, producer);
     }
     
     @Override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/enricher/stock/reducer/Reducer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/reducer/Reducer.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/reducer/Reducer.java
index c24869d..f9b9950 100644
--- a/core/src/main/java/org/apache/brooklyn/enricher/stock/reducer/Reducer.java
+++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/reducer/Reducer.java
@@ -113,6 +113,7 @@ public class Reducer extends AbstractEnricher implements SensorEventListener<Obj
         }
 
         subscribedSensors = ImmutableList.copyOf(sensorListTemp);
+        highlightTriggers(subscribedSensors, producer);
     }
     
     // Default implementation, subclasses should override

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/entity/group/AbstractMembershipTrackingPolicy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/entity/group/AbstractMembershipTrackingPolicy.java b/core/src/main/java/org/apache/brooklyn/entity/group/AbstractMembershipTrackingPolicy.java
index d60ec84..d61944c 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/group/AbstractMembershipTrackingPolicy.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/group/AbstractMembershipTrackingPolicy.java
@@ -33,9 +33,11 @@ import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.BrooklynLogging;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.objs.AbstractEntityAdjunct;
 import org.apache.brooklyn.core.policy.AbstractPolicy;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.javalang.JavaClassNames;
+import org.apache.brooklyn.util.text.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -202,6 +204,7 @@ public abstract class AbstractMembershipTrackingPolicy extends AbstractPolicy {
                 }
             });
         }
+        highlightTriggers(getSensorsToTrack(), "group members");
 
         // The policy will have already fired events for its members.
         if (!isRebinding()) {
@@ -216,10 +219,28 @@ public abstract class AbstractMembershipTrackingPolicy extends AbstractPolicy {
         if (getSubscriptionTracker() != null && group != null) subscriptions().unsubscribe(group);
     }
 
+    /** Invoked by framework prior to all entity events, to provide default highlight info;
+     * if subclasses provide their own they can overwrite this method to be no-op,
+     * or they can change the message passed to {@link #defaultHighlightAction(EventType, Entity, String)}
+     * which defaults to "Update on %s %s"  */
+    // bit of a cheat but more informative than doing nothing; callers can override of course
+    protected void defaultHighlightAction(EventType type, Entity entity) {
+        defaultHighlightAction(type, entity, "Update on %s %s");
+    }
+    /** Records an {@link AbstractEntityAdjunct#HIGHLIGHT_NAME_LAST_ACTION} with the given message,
+     * formatted with arguments entity and either 'added', 'changed', or 'removed'.
+     * Can be overridden to be no-op if caller wants to manage their own such highlights,
+     * or to provide further information. See also {@link #defaultHighlightAction(EventType, Entity)}. */
+    protected void defaultHighlightAction(EventType type, Entity entity, String formattedMessage) {
+        highlightAction(String.format(formattedMessage, entity, Strings.removeFromStart(type.toString().toLowerCase(), "entity_")), null);
+    }
+    
     /** All entity events pass through this method. Default impl delegates to onEntityXxxx methods, whose default behaviours are no-op.
      * Callers may override this to intercept all entity events in a single place, and to suppress subsequent processing if desired. 
      */
-    protected void onEntityEvent(EventType type, Entity entity) {
+    protected void onEntityEvent(EventType type, Entity entity) {  
+        defaultHighlightAction(type, entity);
+        
         switch (type) {
         case ENTITY_CHANGE: onEntityChange(entity); break;
         case ENTITY_ADDED: onEntityAdded(entity); break;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/core/src/main/java/org/apache/brooklyn/entity/group/SshCommandMembershipTrackingPolicy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/entity/group/SshCommandMembershipTrackingPolicy.java b/core/src/main/java/org/apache/brooklyn/entity/group/SshCommandMembershipTrackingPolicy.java
index 32ea94c..ea2ec55 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/group/SshCommandMembershipTrackingPolicy.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/group/SshCommandMembershipTrackingPolicy.java
@@ -24,6 +24,7 @@ import java.util.concurrent.ExecutionException;
 
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.config.MapConfigKey;
@@ -143,8 +144,9 @@ public class SshCommandMembershipTrackingPolicy extends AbstractMembershipTracki
                     execute(member, command, type.name(), member.getId());
                     break;
                 case ALL_MEMBERS:
+                    highlightAction("Run at all members ("+getGroup().getMembers().size()+")", null);
                     for (Entity each : getGroup().getMembers()) {
-                        execute(each, command, type.name(), member.getId());
+                        execute(each, command, type.name(), member.getId(), false);
                     }
                     break;
                 default:
@@ -153,8 +155,11 @@ public class SshCommandMembershipTrackingPolicy extends AbstractMembershipTracki
         }
     }
 
-    @SuppressWarnings("unchecked")
     public void execute(Entity target, String command, String type, String memberId) {
+        execute(target, command, type, memberId, true);
+    }
+    @SuppressWarnings("unchecked")
+    private void execute(Entity target, String command, String type, String memberId, boolean highlight) {
         if (Entities.isNoLongerManaged(target)) return;
         Lifecycle state = target.getAttribute(Attributes.SERVICE_STATE_ACTUAL);
         if (state==Lifecycle.STOPPING || state==Lifecycle.STOPPED) return;
@@ -194,7 +199,11 @@ public class SshCommandMembershipTrackingPolicy extends AbstractMembershipTracki
                 .summary("group-" + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, type))
                 .environmentVariables(serializer.serialize(env));
 
-        String output = DynamicTasks.submit(task.newTask(), target).getUnchecked();
+        Task<String> taskI = DynamicTasks.submit(task.newTask(), target);
+        if (highlight) {
+            highlightAction("Run at "+machine.get().getAddress().getHostAddress(), taskI);
+        }
+        String output = taskI.getUnchecked();
         LOG.trace("Command returned: {}", output);
     }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java b/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
index a09c39d..0ce3f9c 100644
--- a/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
+++ b/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
@@ -32,6 +32,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.api.objs.HighlightTuple;
 import org.apache.brooklyn.api.policy.PolicySpec;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.api.sensor.Sensor;
@@ -52,6 +54,7 @@ import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.flags.SetFromFlag;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -470,13 +473,24 @@ public class AutoScalerPolicy extends AbstractPolicy {
             Sensor<Integer> sensor = event.getSensor();
             Preconditions.checkArgument(sensor.equals(DynamicCluster.GROUP_SIZE), "Unexpected sensor " + sensor);
             Integer size = event.getValue();
+            String targetRange = getMinPoolSize()+" - "+getMaxPoolSize();
             if (size > getMaxPoolSize()) {
-                scheduleResize(getMaxPoolSize());
+                highlightViolation("Size "+size+" too large (target "+targetRange+")");
+                scheduleResize(getMaxPoolSize(), decap(getHighlights().get(HIGHLIGHT_NAME_LAST_VIOLATION)));
             } else if (size < getMinPoolSize()) {
-                scheduleResize(getMinPoolSize());
+                highlightViolation("Size "+size+" too small (target "+targetRange+")");
+                scheduleResize(getMinPoolSize(), decap(getHighlights().get(HIGHLIGHT_NAME_LAST_VIOLATION)));
+            } else {
+                highlightConfirmation("Size "+size+" in target range "+targetRange);
             }
         }
     };
+    
+    private String decap(HighlightTuple t) {
+        String msg = t==null ? null : t.getDescription();
+        if (Strings.isBlank(msg)) return null;
+        return Character.toLowerCase(msg.charAt(0))+msg.substring(1);
+    }
 
     public AutoScalerPolicy() {
     }
@@ -703,6 +717,9 @@ public class AutoScalerPolicy extends AbstractPolicy {
         if (getMetric() != null) {
             Entity entityToSubscribeTo = (getEntityWithMetric() != null) ? getEntityWithMetric() : entity;
             subscriptions().subscribe(entityToSubscribeTo, getMetric(), metricEventHandler);
+            highlightTriggers(getMetric(), entityToSubscribeTo);
+        } else {
+            highlightTriggers("Listening for standard size and pool hot/cold sensors (no specific metric)");
         }
         subscriptions().subscribe(poolEntity, getPoolColdSensor(), utilizationEventHandler);
         subscriptions().subscribe(poolEntity, getPoolHotSensor(), utilizationEventHandler);
@@ -831,15 +848,21 @@ public class AutoScalerPolicy extends AbstractPolicy {
          */
         if (data.isHot()) {
             // scale out
+            highlightViolation("Metric "+String.format("%.02f", data.currentMetricValue)+" too hot "
+                + "(target range "+String.format("%.02f", data.metricLowerBound)+"-"+String.format("%.02f", data.metricUpperBound)+")");
             desiredSizeUnconstrained = (int)Math.ceil(data.getCurrentTotalActivity() / data.metricUpperBound);
             data.scalingMode = ScalingType.HOT;
             
         } else if (data.isCold()) {
             // scale back
+            highlightViolation("Metric "+String.format("%.02f", data.currentMetricValue)+" too cold "
+                + "(target range "+String.format("%.02f", data.metricLowerBound)+"-"+String.format("%.02f", data.metricUpperBound)+")");
             desiredSizeUnconstrained = (int)Math.floor(data.getCurrentTotalActivity() / data.metricLowerBound);
             data.scalingMode = ScalingType.COLD;
             
         } else {
+            highlightConfirmation("Metric "+String.format("%.02f", data.currentMetricValue)+" in "
+                + "target range "+String.format("%.02f", data.metricLowerBound)+"-"+String.format("%.02f", data.metricUpperBound));
             if (LOG.isTraceEnabled()) LOG.trace("{} not resizing pool {} from {} ({} within range {}..{})", new Object[] {this, poolEntity, data.currentSize, data.currentMetricValue, data.metricLowerBound, data.metricUpperBound});
             abortResize(data.currentSize);
             return; // within the healthy range; no-op
@@ -893,14 +916,14 @@ public class AutoScalerPolicy extends AbstractPolicy {
     
         if (data.scalingMode!=null) {
             if (LOG.isDebugEnabled()) LOG.debug("{} provisionally resizing {} {} from {} to {} ({} < {}; ideal size {})", new Object[] {this, description, poolEntity, data.currentSize, desiredSize, data.currentMetricValue, data.metricLowerBound, desiredSizeUnconstrained});
-            scheduleResize(desiredSize);
+            scheduleResize(desiredSize, "metric "+data.currentMetricValue+" out of range");
         } else {
             if (LOG.isTraceEnabled()) LOG.trace("{} not resizing {} {} from {} to {}, {} out of healthy range {}..{} but unconstrained size {} blocked by bounds/check", new Object[] {this, description, poolEntity, data.currentSize, desiredSize, data.currentMetricValue, data.metricLowerBound, data.metricUpperBound, desiredSizeUnconstrained});
             abortResize(data.currentSize);
             // but add to the unbounded record for future consideration
         }
         
-        onNewUnboundedPoolSize(desiredSizeUnconstrained);
+        onNewUnboundedPoolSize(desiredSizeUnconstrained, "ideal unconstrained size is "+desiredSizeUnconstrained);
     }
 
     private int applyMinMaxConstraints(long desiredSize) {
@@ -931,10 +954,10 @@ public class AutoScalerPolicy extends AbstractPolicy {
      * executes, it will resize to whatever the latest value is to be (rather than what it was told
      * to do at the point the job was queued).
      */
-    private void scheduleResize(final int newSize) {
+    private void scheduleResize(final int newSize, String reason) {
         recentDesiredResizes.add(newSize);
         
-        scheduleResize();
+        scheduleResize(reason);
     }
 
     /**
@@ -944,10 +967,10 @@ public class AutoScalerPolicy extends AbstractPolicy {
      * Piggy-backs off the existing scheduleResize execution, which now also checks if the listener
      * needs to be called.
      */
-    private void onNewUnboundedPoolSize(final int val) {
+    private void onNewUnboundedPoolSize(final int val, String reason) {
         if (getMaxSizeReachedSensor() != null) {
             recentUnboundedResizes.add(val);
-            scheduleResize();
+            scheduleResize(reason);
         }
     }
     
@@ -966,7 +989,7 @@ public class AutoScalerPolicy extends AbstractPolicy {
         }
     }
 
-    private void scheduleResize() {
+    private void scheduleResize(String reason) {
         // TODO Make scale-out calls concurrent, rather than waiting for first resize to entirely 
         // finish. On ec2 for example, this can cause us to grow very slowly if first request is for
         // just one new VM to be provisioned.
@@ -982,8 +1005,8 @@ public class AutoScalerPolicy extends AbstractPolicy {
                         executorTime = System.currentTimeMillis();
                         executorQueued.set(false);
 
-                        resizeNow();
-                        notifyMaxReachedIfRequiredNow();
+                        resizeNow(reason);
+                        notifyMaxReachedIfRequiredNow(reason);
                         
                     } catch (Exception e) {
                         if (isRunning()) {
@@ -1006,8 +1029,9 @@ public class AutoScalerPolicy extends AbstractPolicy {
      * those values have been within a time window. The time window used is the "maxReachedNotificationDelay",
      * which determines how many milliseconds after being consistently above the max-size will it take before
      * we emit the sensor event (if any).
+     * @param reason 
      */
-    private void notifyMaxReachedIfRequiredNow() {
+    private void notifyMaxReachedIfRequiredNow(String reason) {
         BasicNotificationSensor<? super MaxPoolSizeReachedEvent> maxSizeReachedSensor = getMaxSizeReachedSensor();
         if (maxSizeReachedSensor == null) {
             return;
@@ -1044,14 +1068,14 @@ public class AutoScalerPolicy extends AbstractPolicy {
             // TODO Could check if there has been anything bigger than "min" since min happened (would be more efficient)
             if (LOG.isTraceEnabled()) LOG.trace("{} re-scheduling max-reached check for {}, as unbounded size not stable (min {}, max {}, latest {})", 
                     new Object[] {this, poolEntity, valsSummary.min, valsSummary.max, valsSummary.latest});
-            scheduleResize();
+            scheduleResize(reason);
             
         } else {
             // nothing to write home about; continually below maxAllowed
         }
     }
 
-    private void resizeNow() {
+    private void resizeNow(String reason) {
         final int currentPoolSize = getCurrentSizeOperator().apply(poolEntity);
         CalculatedDesiredPoolSize calculatedDesiredPoolSize = calculateDesiredPoolSize(currentPoolSize);
         long desiredPoolSize = calculatedDesiredPoolSize.size;
@@ -1065,7 +1089,7 @@ public class AutoScalerPolicy extends AbstractPolicy {
             // (note we continue now with as "good" a resize as we can given the instability)
             if (LOG.isTraceEnabled()) LOG.trace("{} re-scheduling resize check for {}, as desired size not stable (current {}, desired {}); continuing with resize...", 
                     new Object[] {this, poolEntity, currentPoolSize, targetPoolSize});
-            scheduleResize();
+            scheduleResize(reason);
         }
         if (currentPoolSize == targetPoolSize) {
             if (LOG.isTraceEnabled()) LOG.trace("{} not resizing pool {} from {} to {}", 
@@ -1076,7 +1100,7 @@ public class AutoScalerPolicy extends AbstractPolicy {
         if (LOG.isDebugEnabled()) LOG.debug("{} requesting resize to {}; current {}, min {}, max {}", 
                 new Object[] {this, targetPoolSize, currentPoolSize, getMinPoolSize(), getMaxPoolSize()});
         
-        Entities.submit(entity, Tasks.<Void>builder().displayName("Auto-scaler")
+        Task<Void> t = Entities.submit(entity, Tasks.<Void>builder().displayName("Auto-scaler")
             .description("Auto-scaler recommending resize from "+currentPoolSize+" to "+targetPoolSize)
             .tag(BrooklynTaskTags.NON_TRANSIENT_TASK_TAG)
             .body(new Callable<Void>() {
@@ -1095,8 +1119,10 @@ public class AutoScalerPolicy extends AbstractPolicy {
                     }
                     return null;
                 }
-            }).build())
-            .blockUntilEnded();
+            }).build());
+        highlightAction("Resize from "+currentPoolSize+" to "+targetPoolSize+
+            (reason!=null ? " because "+reason : ""), t);
+        t.blockUntilEnded();
     }
     
     /**

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/policy/src/main/java/org/apache/brooklyn/policy/ha/ServiceReplacer.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/org/apache/brooklyn/policy/ha/ServiceReplacer.java b/policy/src/main/java/org/apache/brooklyn/policy/ha/ServiceReplacer.java
index 9173895..d15b122 100644
--- a/policy/src/main/java/org/apache/brooklyn/policy/ha/ServiceReplacer.java
+++ b/policy/src/main/java/org/apache/brooklyn/policy/ha/ServiceReplacer.java
@@ -29,6 +29,7 @@ import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.entity.Group;
+import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.api.sensor.SensorEvent;
 import org.apache.brooklyn.api.sensor.SensorEventListener;
@@ -116,6 +117,7 @@ public class ServiceReplacer extends AbstractPolicy {
                     // for them; or could write events to a blocking queue and have onDetectedFailure read from that.
                     
                     if (isRunning()) {
+                        highlightViolation("Failure detected");
                         LOG.warn("ServiceReplacer notified; dispatching job for "+entity+" ("+event.getValue()+")");
                         ((EntityInternal)entity).getExecutionContext().submit(MutableMap.of(), new Runnable() {
                             @Override public void run() {
@@ -126,25 +128,45 @@ public class ServiceReplacer extends AbstractPolicy {
                     }
                 }
             });
+        highlightTriggers(failureSensorToMonitor, "members");
     }
     
     // TODO semaphores would be better to allow at-most-one-blocking behaviour
     protected synchronized void onDetectedFailure(SensorEvent<Object> event) {
         final Entity failedEntity = event.getSource();
         final Object reason = event.getValue();
+        String violationText = "Failure detected at "+failedEntity+(reason!=null ? " ("+reason+")" : "");
         
         if (isSuspended()) {
+            highlightViolation(violationText+" but policy is suspended");
             LOG.warn("ServiceReplacer suspended, so not acting on failure detected at "+failedEntity+" ("+reason+", child of "+entity+")");
             return;
         }
 
-        if (isRepeatedlyFailingTooMuch()) {
+
+        Integer failOnNumRecurringFailures = getConfig(FAIL_ON_NUM_RECURRING_FAILURES);
+        long failOnRecurringFailuresInThisDuration = getConfig(FAIL_ON_RECURRING_FAILURES_IN_THIS_DURATION);
+        long oldestPermitted = currentTimeMillis() - failOnRecurringFailuresInThisDuration;
+        // trim old ones
+        for (Iterator<Long> iter = consecutiveReplacementFailureTimes.iterator(); iter.hasNext();) {
+            Long timestamp = iter.next();
+            if (timestamp < oldestPermitted) {
+                iter.remove();
+            } else {
+                break;
+            }
+        }
+        
+        if (consecutiveReplacementFailureTimes.size() >= failOnNumRecurringFailures) {
+            highlightViolation(violationText+" but too many recent failures detected: "
+                + consecutiveReplacementFailureTimes.size()+" in "+failOnRecurringFailuresInThisDuration+" exceeds limit of "+failOnNumRecurringFailures);
             LOG.error("ServiceReplacer not acting on failure detected at "+failedEntity+" ("+reason+", child of "+entity+"), because too many recent replacement failures");
             return;
         }
         
+        highlightViolation(violationText+", triggering restart");
         LOG.warn("ServiceReplacer acting on failure detected at "+failedEntity+" ("+reason+", child of "+entity+")");
-        ((EntityInternal)entity).getExecutionContext().submit(MutableMap.of(), new Runnable() {
+        Task<?> t = ((EntityInternal)entity).getExecutionContext().submit(MutableMap.of(), new Runnable() {
 
             @Override
             public void run() {
@@ -156,28 +178,12 @@ public class ServiceReplacer extends AbstractPolicy {
                         LOG.info("ServiceReplacer: ignoring error reported from stopping failed node "+failedEntity);
                         return;
                     }
+                    highlightViolation(violationText+" and replace attempt failed: "+Exceptions.collapseText(e));
                     onReplacementFailed("Replace failure ("+Exceptions.collapseText(e)+") at "+entity+": "+reason);
                 }
             }
         });
-    }
-
-    private boolean isRepeatedlyFailingTooMuch() {
-        Integer failOnNumRecurringFailures = getConfig(FAIL_ON_NUM_RECURRING_FAILURES);
-        long failOnRecurringFailuresInThisDuration = getConfig(FAIL_ON_RECURRING_FAILURES_IN_THIS_DURATION);
-        long oldestPermitted = currentTimeMillis() - failOnRecurringFailuresInThisDuration;
-        
-        // trim old ones
-        for (Iterator<Long> iter = consecutiveReplacementFailureTimes.iterator(); iter.hasNext();) {
-            Long timestamp = iter.next();
-            if (timestamp < oldestPermitted) {
-                iter.remove();
-            } else {
-                break;
-            }
-        }
-        
-        return (consecutiveReplacementFailureTimes.size() >= failOnNumRecurringFailures);
+        highlightAction("Replacing "+failedEntity, t);
     }
 
     protected long currentTimeMillis() {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/policy/src/main/java/org/apache/brooklyn/policy/ha/ServiceRestarter.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/org/apache/brooklyn/policy/ha/ServiceRestarter.java b/policy/src/main/java/org/apache/brooklyn/policy/ha/ServiceRestarter.java
index 7998abe..09837d8 100644
--- a/policy/src/main/java/org/apache/brooklyn/policy/ha/ServiceRestarter.java
+++ b/policy/src/main/java/org/apache/brooklyn/policy/ha/ServiceRestarter.java
@@ -111,7 +111,7 @@ public class ServiceRestarter extends AbstractPolicy {
                     }
                 }
             });
-        highlightTriggers("Listening for "+getConfig(FAILURE_SENSOR_TO_MONITOR).getName());
+        highlightTriggers(getConfig(FAILURE_SENSOR_TO_MONITOR), entity);
     }
     
     // TODO semaphores would be better to allow at-most-one-blocking behaviour

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyRebindTest.java
----------------------------------------------------------------------
diff --git a/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyRebindTest.java b/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyRebindTest.java
index 10e67e9..bf7d150 100644
--- a/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyRebindTest.java
+++ b/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyRebindTest.java
@@ -40,6 +40,7 @@ import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.core.test.entity.TestApplication;
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.time.Duration;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -110,7 +111,9 @@ public class AutoScalerPolicyRebindTest extends RebindTestFixtureWithApp {
         assertEquals(newPolicy.getConfig(AutoScalerPolicy.POOL_OK_SENSOR), POOL_OK_SENSOR);
         assertEquals(newPolicy.getConfig(AutoScalerPolicy.MAX_SIZE_REACHED_SENSOR), MAX_SIZE_REACHED_SENSOR);
         assertEquals(newPolicy.getConfig(AutoScalerPolicy.MAX_REACHED_NOTIFICATION_DELAY), Duration.of(7, TimeUnit.MILLISECONDS));
-        assertTrue(newPolicy.getHighlights().isEmpty());
+        Asserts.assertSize(newPolicy.getHighlights().keySet(), 1);
+        assertEquals(newPolicy.getHighlights().get(AbstractEntityAdjunct.HIGHLIGHT_NAME_TRIGGERS).getDescription(), 
+            "Listening for "+METRIC_SENSOR.getName());
     }
     
     @Test

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolImpl.java b/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolImpl.java
index e4f82ce..3bb8a50 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolImpl.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/machine/pool/ServerPoolImpl.java
@@ -432,6 +432,7 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
         protected void onEntityEvent(EventType type, Entity member) {
             Boolean isUp = member.getAttribute(Attributes.SERVICE_UP);
             LOG.info("{} in {}: {} service up is {}", new Object[]{type.name(), entity, member, isUp});
+            defaultHighlightAction(type, entity, "Update on %s %s (service "+(isUp==Boolean.TRUE ? "up" : isUp==Boolean.FALSE ? "not up" : "up value not known")+")");
             if (type.equals(EventType.ENTITY_ADDED) || type.equals(EventType.ENTITY_CHANGE)) {
                 if (Boolean.TRUE.equals(isUp)) {
                     ((ServerPoolImpl) entity).serverAdded(member);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
index ea31359..2399287 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
@@ -49,6 +49,7 @@ import org.apache.brooklyn.feed.function.FunctionFeed;
 import org.apache.brooklyn.feed.function.FunctionPollConfig;
 import org.apache.brooklyn.location.jclouds.networking.NetworkingEffectors;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.core.config.ConfigBag;
@@ -221,11 +222,13 @@ public abstract class SoftwareProcessImpl extends AbstractEntity implements Soft
     public static class UpdatingNotUpFromServiceProcessIsRunning extends AbstractEnricher implements SensorEventListener<Object> {
         public UpdatingNotUpFromServiceProcessIsRunning() {}
         
+        @SuppressWarnings("unchecked")
         @Override
         public void setEntity(EntityLocal entity) {
             super.setEntity(entity);
             subscriptions().subscribe(entity, SERVICE_PROCESS_IS_RUNNING, this);
             subscriptions().subscribe(entity, Attributes.SERVICE_UP, this);
+            highlightTriggers(MutableList.of(SERVICE_PROCESS_IS_RUNNING, Attributes.SERVICE_UP), entity);
             onUpdated();
         }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/71708469/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SelectMasterEffectorTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SelectMasterEffectorTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SelectMasterEffectorTest.java
index 20a0ac2..2846625 100644
--- a/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SelectMasterEffectorTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/brooklynnode/SelectMasterEffectorTest.java
@@ -69,7 +69,7 @@ public class SelectMasterEffectorTest extends BrooklynAppUnitTestSupport {
         super.setUp();
 
         // because the effector calls wait for a state change, use a separate thread to drive that 
-        poller = new Poller<Void>(app, false);
+        poller = new Poller<Void>(app, null, false);
         poller.scheduleAtFixedRate(
             new Callable<Void>() {
                 @Override


Mime
View raw message