You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2014/07/25 19:57:00 UTC

[04/11] git commit: ServerPools refuse to remove manually-added machines until they are stopped

ServerPools refuse to remove manually-added machines until they are stopped


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

Branch: refs/heads/master
Commit: f1a465c01e75ba143dd4a1137ef13501ce058be6
Parents: e7f1291
Author: Sam Corbett <sa...@cloudsoftcorp.com>
Authored: Tue Jul 22 19:47:14 2014 +0100
Committer: Sam Corbett <sa...@cloudsoftcorp.com>
Committed: Tue Jul 22 19:47:14 2014 +0100

----------------------------------------------------------------------
 .../brooklyn/entity/pool/ServerPoolImpl.java    | 95 +++++++++++++-------
 .../brooklyn/entity/pool/ServerPoolTest.java    | 40 +++++++--
 2 files changed, 99 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f1a465c0/software/base/src/main/java/brooklyn/entity/pool/ServerPoolImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/pool/ServerPoolImpl.java b/software/base/src/main/java/brooklyn/entity/pool/ServerPoolImpl.java
index 1ef26e3..c0b88cd 100644
--- a/software/base/src/main/java/brooklyn/entity/pool/ServerPoolImpl.java
+++ b/software/base/src/main/java/brooklyn/entity/pool/ServerPoolImpl.java
@@ -30,7 +30,6 @@ import brooklyn.entity.Entity;
 import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.EntityInternal;
-import brooklyn.entity.basic.EntityPredicates;
 import brooklyn.entity.basic.Lifecycle;
 import brooklyn.entity.effector.Effectors;
 import brooklyn.entity.group.AbstractMembershipTrackingPolicy;
@@ -55,6 +54,7 @@ import brooklyn.util.task.DynamicTasks;
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
 import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -68,8 +68,14 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
     private static final Logger LOG = LoggerFactory.getLogger(ServerPoolImpl.class);
 
     private static enum MachinePoolMemberStatus {
+        /** The server is available for use */
         AVAILABLE,
+        /** The server has been leased to another application */
         CLAIMED,
+        /**
+         * The server will not be leased to other applications. It will be the first
+         * candidate to release when the pool is shrunk.
+         */
         UNUSABLE
     }
 
@@ -129,6 +135,10 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
     public void stop() {
         super.stop();
         deleteLocation();
+        setAttribute(AVAILABLE_COUNT, 0);
+        setAttribute(CLAIMED_COUNT, 0);
+        getAttribute(ENTITY_MACHINE).clear();
+        getAttribute(MACHINE_ENTITY).clear();
     }
 
     private void addMembershipTrackerPolicy() {
@@ -180,10 +190,12 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
             LOG.debug("{} deleting and unmanaging location {}", this, location);
             mgr.unmanage(location);
         }
+        // definition will only be null if deleteLocation has already been called, e.g. by two calls to stop().
         LocationDefinition definition = getAttribute(DYNAMIC_LOCATION_DEFINITION);
-        LOG.debug("{} unregistering dynamic location {}", this, definition);
-        getManagementContext().getLocationRegistry().removeDefinedLocation(definition.getId());
-
+        if (definition != null) {
+            LOG.debug("{} unregistering dynamic location {}", this, definition);
+            getManagementContext().getLocationRegistry().removeDefinedLocation(definition.getId());
+        }
         setAttribute(LOCATION_SPEC, null);
         setAttribute(DYNAMIC_LOCATION, null);
         setAttribute(LOCATION_NAME, null);
@@ -247,10 +259,10 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
             Iterable<MachineLocation> machines = FluentIterable.from(location.getChildren())
                     .filter(MachineLocation.class);
             LOG.info("{} adding additional machines: {}", this, machines);
-            synchronized (mutex) {
-                for (MachineLocation machine : machines) {
-                    additions.add(addExistingMachine(machine));
-                }
+            // Doesn't need to be synchronised on mutex: it will be claimed per-machine
+            // as the new members are handled by the membership tracking policy.
+            for (MachineLocation machine : machines) {
+                additions.add(addExistingMachine(machine));
             }
             LOG.debug("{} added additional machines", this);
         }
@@ -268,14 +280,26 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
      */
     @Override
     protected Collection<Entity> shrink(int delta) {
+        if (Lifecycle.STOPPING.equals(getAttribute(Attributes.SERVICE_STATE))) {
+            return super.shrink(delta);
+        }
+
         synchronized (mutex) {
-            int unusable = Iterables.size(getMembersWithStatus(MachinePoolMemberStatus.UNUSABLE));
-            int removeable = getAttribute(AVAILABLE_COUNT) + unusable;
-            if (-delta > removeable && !Lifecycle.STOPPING.equals(getAttribute(Attributes.SERVICE_STATE))) {
-                LOG.info("Too few unclaimed machines in {} to shrink by delta {}. Altered delta to {}",
-                        new Object[]{this, delta, -removeable});
-                delta = -removeable;
+            int removable = 0;
+            for (Entity entity : getMembers()) {
+                // Skip machine marked not for removal and machines that are claimed
+                if (!Boolean.FALSE.equals(entity.getConfig(REMOVABLE)) &&
+                        !MachinePoolMemberStatus.CLAIMED.equals(entity.getAttribute(SERVER_STATUS))) {
+                    removable -= 1;
+                }
+            }
+
+            if (delta < removable) {
+                LOG.info("Too few removable machines in {} to shrink by delta {}. Altered delta to {}",
+                        new Object[]{this, delta, removable});
+                delta = removable;
             }
+
             Collection<Entity> removed = super.shrink(delta);
             updateCountSensors();
             return removed;
@@ -291,11 +315,6 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
     }
 
     @Override
-    public Integer getCurrentSize() {
-        return super.getCurrentSize();
-    }
-
-    @Override
     public Function<Collection<Entity>, Entity> getRemovalStrategy() {
         return UNCLAIMED_REMOVAL_STRATEGY;
     }
@@ -303,12 +322,15 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
     private final Function<Collection<Entity>, Entity> UNCLAIMED_REMOVAL_STRATEGY = new Function<Collection<Entity>, Entity>() {
         // Semantics of superclass mean that mutex should already be held when apply is called
         @Override
-        public Entity apply(Collection<Entity> input) {
+        public Entity apply(Collection<Entity> members) {
             synchronized (mutex) {
-                Optional<Entity> choice = getMemberWithStatus(MachinePoolMemberStatus.UNUSABLE)
-                        .or(getMemberWithStatus(MachinePoolMemberStatus.AVAILABLE));
-                if (!choice.isPresent() && Lifecycle.STOPPING.equals(getAttribute(Attributes.SERVICE_STATE))) {
-                    choice = getMemberWithStatus(MachinePoolMemberStatus.CLAIMED);
+                Optional<Entity> choice;
+                if (Lifecycle.STOPPING.equals(getAttribute(Attributes.SERVICE_STATE))) {
+                    choice = Optional.of(members.iterator().next());
+                } else {
+                    // Otherwise should only choose between removable + unusable or available
+                    choice = getMemberWithStatusExcludingUnremovable(members, MachinePoolMemberStatus.UNUSABLE)
+                            .or(getMemberWithStatusExcludingUnremovable(members, MachinePoolMemberStatus.AVAILABLE));
                 }
                 if (!choice.isPresent()) {
                     LOG.warn("{} has no machines available to remove!", this);
@@ -352,13 +374,27 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
     }
 
     private Optional<Entity> getMemberWithStatus(MachinePoolMemberStatus status) {
-        return Iterables.tryFind(getMembers(),
-                EntityPredicates.attributeEqualTo(SERVER_STATUS, status));
+        return getMemberWithStatus0(getMembers(), status, true);
     }
 
-    private Iterable<Entity> getMembersWithStatus(MachinePoolMemberStatus status) {
-        return Iterables.filter(getMembers(),
-                EntityPredicates.attributeEqualTo(SERVER_STATUS, status));
+    private Optional<Entity> getMemberWithStatusExcludingUnremovable(Collection<Entity> entities, MachinePoolMemberStatus status) {
+        return getMemberWithStatus0(entities, status, false);
+    }
+
+    private Optional<Entity> getMemberWithStatus0(Collection<Entity> entities, final MachinePoolMemberStatus status, final boolean includeUnremovableMachines) {
+        return Iterables.tryFind(entities,
+                new Predicate<Entity>() {
+                    @Override
+                    public boolean apply(Entity input) {
+                        return (includeUnremovableMachines || isRemovable(input)) &&
+                                status.equals(input.getAttribute(SERVER_STATUS));
+                    }
+                });
+    }
+
+    /** @return true if the entity has {@link #REMOVABLE} set to null or true. */
+    private boolean isRemovable(Entity entity) {
+        return !Boolean.FALSE.equals(entity.getConfig(REMOVABLE));
     }
 
     private void updateCountSensors() {
@@ -377,7 +413,6 @@ public class ServerPoolImpl extends DynamicClusterImpl implements ServerPool {
         }
     }
 
-
     public static class MemberTrackingPolicy extends AbstractMembershipTrackingPolicy {
         @Override
         protected void onEntityEvent(EventType type, Entity member) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f1a465c0/software/base/src/test/java/brooklyn/entity/pool/ServerPoolTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/pool/ServerPoolTest.java b/software/base/src/test/java/brooklyn/entity/pool/ServerPoolTest.java
index 88317ea..f565c54 100644
--- a/software/base/src/test/java/brooklyn/entity/pool/ServerPoolTest.java
+++ b/software/base/src/test/java/brooklyn/entity/pool/ServerPoolTest.java
@@ -22,10 +22,14 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
+import java.util.Collection;
+import java.util.Iterator;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
@@ -34,6 +38,7 @@ import brooklyn.entity.basic.Attributes;
 import brooklyn.location.LocationSpec;
 import brooklyn.location.basic.LocalhostMachineProvisioningLocation.LocalhostMachine;
 import brooklyn.test.Asserts;
+import brooklyn.test.EntityTestUtils;
 import brooklyn.test.entity.TestApplication;
 
 public class ServerPoolTest extends AbstractServerPoolTest {
@@ -134,16 +139,39 @@ public class ServerPoolTest extends AbstractServerPoolTest {
         LocalhostMachine loc = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachine.class));
         Entity added = pool.addExistingMachine(loc);
         assertFalse(added.getConfig(ServerPoolImpl.REMOVABLE));
-        Asserts.succeedsEventually(new Runnable() {
-            @Override
-            public void run() {
-                assertAvailableCountEquals(1);
-            }
-        });
+        assertAvailableCountEventuallyEquals(1);
 
         TestApplication app2 = createAppWithChildren(1);
         app2.start(ImmutableList.of(pool.getDynamicLocation()));
         assertAvailableCountEquals(0);
     }
 
+    @Test
+    public void testExistingMachinesAreNotRemovedFromThePoolOnShrinkButAreOnStop() {
+        LocalhostMachine loc = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachine.class));
+        pool.addExistingMachine(loc);
+        assertAvailableCountEventuallyEquals(getInitialPoolSize() + 1);
+        pool.resize(0);
+        assertAvailableCountEventuallyEquals(1);
+        pool.stop();
+        assertAvailableCountEventuallyEquals(0);
+    }
+
+    @Test
+    public void testAddExistingMachineFromSpec() {
+        TestApplication app = createAppWithChildren(getInitialPoolSize());
+        app.start(ImmutableList.of(pool.getDynamicLocation()));
+        assertAvailableCountEquals(0);
+
+        Collection<Entity> added = pool.addExistingMachinesFromSpec("byon:(hosts=\"localhost,localhost\")");
+        assertEquals(added.size(), 2, "Added: " + Joiner.on(", ").join(added));
+        Iterator<Entity> it = added.iterator();
+        assertFalse(it.next().getConfig(ServerPoolImpl.REMOVABLE));
+        assertFalse(it.next().getConfig(ServerPoolImpl.REMOVABLE));
+        assertAvailableCountEventuallyEquals(2);
+
+        TestApplication app2 = createAppWithChildren(2);
+        app2.start(ImmutableList.of(pool.getDynamicLocation()));
+        assertAvailableCountEquals(0);
+    }
 }