You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/07/29 01:13:05 UTC

[1/6] incubator-brooklyn git commit: Support softwareProcess config open/stop iptables

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master 946b5dcf7 -> 05b95f377


Support softwareProcess config open/stop iptables

And also dontRequireTtyForSudo


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

Branch: refs/heads/master
Commit: 90f18811328752fa99dad4af9f71a53135942f32
Parents: 42d6091
Author: Aled Sage <al...@gmail.com>
Authored: Sat Jul 25 15:00:57 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Mon Jul 27 13:50:00 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/entity/basic/SoftwareProcess.java  |  16 ++
 .../entity/software/MachineInitTasks.java       | 172 +++++++++++++++++++
 .../software/MachineLifecycleEffectorTasks.java |  41 ++++-
 .../webapp/tomcat/TomcatServerEc2LiveTest.java  |   1 +
 4 files changed, 222 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/90f18811/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java
index 1de84de..1570355 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcess.java
@@ -122,6 +122,22 @@ public interface SoftwareProcess extends Entity, Startable {
     @Deprecated
     ConfigKey<String> SUGGESTED_RUN_DIR = BrooklynConfigKeys.SUGGESTED_RUN_DIR;
 
+    public static final ConfigKey<Boolean> OPEN_IPTABLES = ConfigKeys.newBooleanConfigKey("openIptables", 
+            "Whether to open the INBOUND_PORTS via iptables rules; " +
+            "if true then ssh in to run iptables commands, as part of machine provisioning", false);
+
+    public static final ConfigKey<Boolean> STOP_IPTABLES = ConfigKeys.newBooleanConfigKey("stopIptables", 
+            "Whether to stop iptables entirely; " +
+            "if true then ssh in to stop the iptables service, as part of machine provisioning", false);
+
+    public static final ConfigKey<Boolean> DONT_REQUIRE_TTY_FOR_SUDO = ConfigKeys.newBooleanConfigKey("dontRequireTtyForSudo", 
+            "Whether to explicitly set /etc/sudoers, so don't need tty (will leave unchanged if 'false'); " +
+            "some machines require a tty for sudo; brooklyn by default does not use a tty " +
+            "(so that it can get separate error+stdout streams); you can enable a tty as an " +
+            "option to every ssh command, or you can do it once and " +
+            "modify the machine so that a tty is not subsequently required.",
+            false);
+    
     /**
      * Files to be copied to the server before pre-install.
      * <p>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/90f18811/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java b/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java
new file mode 100644
index 0000000..dfcb61d
--- /dev/null
+++ b/software/base/src/main/java/brooklyn/entity/software/MachineInitTasks.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.entity.software;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import brooklyn.entity.basic.BrooklynTaskTags;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.management.Task;
+import brooklyn.util.net.Protocol;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.ssh.IptablesCommands;
+import brooklyn.util.ssh.IptablesCommands.Chain;
+import brooklyn.util.ssh.IptablesCommands.Policy;
+import brooklyn.util.task.DynamicTasks;
+import brooklyn.util.task.Tasks;
+import brooklyn.util.task.ssh.SshTasks;
+import brooklyn.util.text.Strings;
+
+/**
+ * 
+ */
+@Beta
+public class MachineInitTasks {
+
+    // TODO Move somewhere so code can also be called by JcloudsLocation!
+    
+    private static final Logger log = LoggerFactory.getLogger(MachineInitTasks.class);
+
+    protected EntityInternal entity() {
+        return (EntityInternal) BrooklynTaskTags.getTargetOrContextEntity(Tasks.current());
+    }
+
+    /**
+     * Returns a queued {@link Task} which opens the given ports in iptables on the given machine.
+     */
+    public Task<Void> openIptablesAsync(final Iterable<Integer> inboundPorts, final SshMachineLocation machine) {
+        return DynamicTasks.queue("open iptables "+toTruncatedString(inboundPorts, 6), new Callable<Void>() {
+            public Void call() {
+                openIptablesImpl(inboundPorts, machine);
+                return null;
+            }
+        });
+    }
+
+    /**
+     * Returns a queued {@link Task} which stops iptables on the given machine.
+     */
+    public Task<Void> stopIptablesAsync(final SshMachineLocation machine) {
+        return DynamicTasks.queue("stop iptables", new Callable<Void>() {
+            public Void call() {
+                stopIptablesImpl(machine);
+                return null;
+            }
+        });
+    }
+
+    /**
+     * See docs in {@link BashCommands#dontRequireTtyForSudo()}
+     */
+    public Task<Boolean> dontRequireTtyForSudoAsync(final SshMachineLocation machine) {
+        return SshTasks.dontRequireTtyForSudo(machine, true).newTask().asTask();
+    }
+
+    protected void openIptablesImpl(Iterable<Integer> inboundPorts, SshMachineLocation machine) {
+        if (inboundPorts == null || Iterables.isEmpty(inboundPorts)) {
+            log.info("No ports to open in iptables (no inbound ports) for {} at {}", machine, this);
+        } else {
+            log.info("Opening ports in iptables for {} at {}", entity(), machine);
+
+            List<String> iptablesRules = Lists.newArrayList();
+
+            if (isLocationFirewalldEnabled(machine)) {
+                for (Integer port : inboundPorts) {
+                    iptablesRules.add(IptablesCommands.addFirewalldRule(Chain.INPUT, Protocol.TCP, port, Policy.ACCEPT));
+                 }
+            } else {
+                iptablesRules = createIptablesRulesForNetworkInterface(inboundPorts);
+                iptablesRules.add(IptablesCommands.saveIptablesRules());
+            }
+            List<String> batch = Lists.newArrayList();
+            // Some entities, such as Riak (erlang based) have a huge range of ports, which leads to a script that
+            // is too large to run (fails with a broken pipe). Batch the rules into batches of 50
+            for (String rule : iptablesRules) {
+                batch.add(rule);
+                if (batch.size() == 50) {
+                    machine.execCommands("Inserting iptables rules, 50 command batch", batch);
+                    batch.clear();
+                }
+            }
+            if (batch.size() > 0) {
+                machine.execCommands("Inserting iptables rules", batch);
+            }
+            machine.execCommands("List iptables rules", ImmutableList.of(IptablesCommands.listIptablesRule()));
+        }
+    }
+
+    protected void stopIptablesImpl(SshMachineLocation machine) {
+        log.info("Stopping iptables for {} at {}", entity(), machine);
+
+        List<String> cmds = ImmutableList.<String>of();
+        if (isLocationFirewalldEnabled(machine)) {
+            cmds = ImmutableList.of(IptablesCommands.firewalldServiceStop(), IptablesCommands.firewalldServiceStatus());
+        } else {
+            cmds = ImmutableList.of(IptablesCommands.iptablesServiceStop(), IptablesCommands.iptablesServiceStatus());
+        }
+        machine.execCommands("Stopping iptables", cmds);
+    }
+    
+    private List<String> createIptablesRulesForNetworkInterface(Iterable<Integer> ports) {
+        List<String> iptablesRules = Lists.newArrayList();
+        for (Integer port : ports) {
+           iptablesRules.add(IptablesCommands.insertIptablesRule(Chain.INPUT, Protocol.TCP, port, Policy.ACCEPT));
+        }
+        return iptablesRules;
+     }
+
+    public boolean isLocationFirewalldEnabled(SshMachineLocation location) {
+        int result = location.execCommands("checking if firewalld is active", 
+                ImmutableList.of(IptablesCommands.firewalldServiceIsActive()));
+        if (result == 0) {
+            return true;
+        }
+        
+        return false;
+    }
+    
+    protected String toTruncatedString(Iterable<?> vals, int maxShown) {
+        StringBuilder result = new StringBuilder("[");
+        int shown = 0;
+        for (Object val : (vals == null ? ImmutableList.of() : vals)) {
+            if (shown != 0) {
+                result.append(", ");
+            }
+            if (shown < maxShown) {
+                result.append(Strings.toString(val));
+                shown++;
+            } else {
+                result.append("...");
+                break;
+            }
+        }
+        result.append("]");
+        return result.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/90f18811/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java
index 2ba42f7..ec3ae1c 100644
--- a/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java
+++ b/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java
@@ -25,11 +25,19 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Callable;
+
 import javax.annotation.Nullable;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.Effector;
 import brooklyn.entity.Entity;
@@ -62,6 +70,7 @@ import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import brooklyn.location.basic.Locations;
 import brooklyn.location.basic.Machines;
 import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.location.cloud.CloudLocationConfig;
 import brooklyn.management.Task;
 import brooklyn.management.TaskFactory;
 import brooklyn.util.collections.MutableMap;
@@ -77,13 +86,6 @@ import brooklyn.util.task.system.ProcessTaskWrapper;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 
-import com.google.common.annotations.Beta;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-
 /**
  * Default skeleton for start/stop/restart tasks on machines.
  * <p>
@@ -116,6 +118,8 @@ public abstract class MachineLifecycleEffectorTasks {
     public static final ConfigKey<Duration> STOP_PROCESS_TIMEOUT = ConfigKeys.newConfigKey(Duration.class,
             "process.stop.timeout", "How long to wait for the processes to be stopped; use null to mean forever", Duration.TWO_MINUTES);
 
+    protected final MachineInitTasks machineInitTasks = new MachineInitTasks();
+    
     /** Attaches lifecycle effectors (start, restart, stop) to the given entity post-creation. */
     public void attachLifecycleEffectors(Entity entity) {
         ((EntityInternal) entity).getMutableEntityType().addEffector(newStartEffector());
@@ -344,6 +348,28 @@ public abstract class MachineLifecycleEffectorTasks {
                 entity().setAttribute(Attributes.SSH_ADDRESS, sshAddress);
             }
 
+            if (Boolean.TRUE.equals(entity().getConfig(SoftwareProcess.OPEN_IPTABLES))) {
+                if (machine instanceof SshMachineLocation) {
+                    Iterable<Integer> inboundPorts = (Iterable<Integer>) machine.config().get(CloudLocationConfig.INBOUND_PORTS);
+                    machineInitTasks.openIptablesAsync(inboundPorts, (SshMachineLocation)machine);
+                } else {
+                    log.warn("Ignoring flag OPEN_IPTABLES on non-ssh location {}", machine);
+                }
+            }
+            if (Boolean.TRUE.equals(entity().getConfig(SoftwareProcess.STOP_IPTABLES))) {
+                if (machine instanceof SshMachineLocation) {
+                    machineInitTasks.stopIptablesAsync((SshMachineLocation)machine);
+                } else {
+                    log.warn("Ignoring flag STOP_IPTABLES on non-ssh location {}", machine);
+                }
+            }
+            if (Boolean.TRUE.equals(entity().getConfig(SoftwareProcess.DONT_REQUIRE_TTY_FOR_SUDO))) {
+                if (machine instanceof SshMachineLocation) {
+                    machineInitTasks.dontRequireTtyForSudoAsync((SshMachineLocation)machine);
+                } else {
+                    log.warn("Ignoring flag DONT_REQUIRE_TTY_FOR_SUDO on non-ssh location {}", machine);
+                }
+            }
             resolveOnBoxDir(entity(), machine);
             preStartCustom(machine);
         }});
@@ -761,5 +787,4 @@ public abstract class MachineLifecycleEffectorTasks {
         }
         return new StopMachineDetails<Integer>("Decommissioned "+machine, 1);
     }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/90f18811/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java
----------------------------------------------------------------------
diff --git a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java
index ec7f0a1..e0775b7 100644
--- a/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java
+++ b/software/webapp/src/test/java/brooklyn/entity/webapp/tomcat/TomcatServerEc2LiveTest.java
@@ -47,6 +47,7 @@ public class TomcatServerEc2LiveTest extends AbstractEc2LiveTest {
     @Override
     protected void doTest(Location loc) throws Exception {
         final TomcatServer server = app.createAndManageChild(EntitySpec.create(TomcatServer.class)
+                .configure(TomcatServer.OPEN_IPTABLES, true)
                 .configure("war", getTestWar()));
         
         app.start(ImmutableList.of(loc));


[2/6] incubator-brooklyn git commit: Adds BYON tcpPortMappings

Posted by al...@apache.org.
Adds BYON tcpPortMappings


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

Branch: refs/heads/master
Commit: 9633236d9ee2adf6e63fab2682465944df78f667
Parents: c2e55c0
Author: Aled Sage <al...@gmail.com>
Authored: Mon Jul 20 23:31:53 2015 -0700
Committer: Aled Sage <al...@gmail.com>
Committed: Mon Jul 27 13:50:00 2015 +0100

----------------------------------------------------------------------
 .../location/basic/ByonLocationResolver.java    | 47 ++++++++++---
 .../location/basic/SshMachineLocation.java      | 72 +++++++++++++-------
 .../location/basic/WinRmMachineLocation.java    | 42 +++++++++---
 .../camp/brooklyn/ByonLocationsYamlTest.java    | 66 ++++++++++++++++--
 4 files changed, 175 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9633236d/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java b/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java
index 3695e3e..7838e24 100644
--- a/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java
+++ b/core/src/main/java/brooklyn/location/basic/ByonLocationResolver.java
@@ -27,6 +27,12 @@ import java.util.Map;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.net.HostAndPort;
+
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Sanitizer;
@@ -41,12 +47,6 @@ import brooklyn.util.net.UserAndHostAndPort;
 import brooklyn.util.text.WildcardGlobs;
 import brooklyn.util.text.WildcardGlobs.PhraseTreatment;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.net.HostAndPort;
-
 /**
  * Examples of valid specs:
  *   <ul>
@@ -146,16 +146,33 @@ public class ByonLocationResolver extends AbstractLocationResolver {
         String osfamily = (String) machineConfig.remove(OS_FAMILY.getName());
         String ssh = (String) machineConfig.remove("ssh");
         String winrm = (String) machineConfig.remove("winrm");
+        Map<Integer, String> tcpPortMappings = (Map<Integer, String>) machineConfig.get("tcpPortMappings");
+        
         checkArgument(ssh != null ^ winrm != null, "Must specify exactly one of 'ssh' or 'winrm' for machine: %s", valSanitized);
         
         UserAndHostAndPort userAndHostAndPort;
+        String host;
+        int port;
         if (ssh != null) {
-            userAndHostAndPort = parseUserAndHostAndPort((String)ssh);
+            userAndHostAndPort = parseUserAndHostAndPort((String)ssh, 22);
         } else {
-            userAndHostAndPort = parseUserAndHostAndPort((String)winrm);
+            userAndHostAndPort = parseUserAndHostAndPort((String)winrm, 5985);
+        }
+        
+        // If there is a tcpPortMapping defined for the connection-port, then use that for ssh/winrm machine
+        port = userAndHostAndPort.getHostAndPort().getPort();
+        if (tcpPortMappings != null && tcpPortMappings.containsKey(port)) {
+            String override = tcpPortMappings.get(port);
+            HostAndPort hostAndPortOverride = HostAndPort.fromString(override);
+            if (!hostAndPortOverride.hasPort()) {
+                throw new IllegalArgumentException("Invalid portMapping ('"+override+"') for port "+port+" in "+specForErrMsg);
+            }
+            port = hostAndPortOverride.getPort();
+            host = hostAndPortOverride.getHostText().trim();
+        } else {
+            host = userAndHostAndPort.getHostAndPort().getHostText().trim();
         }
         
-        String host = userAndHostAndPort.getHostAndPort().getHostText().trim();
         machineConfig.put("address", host);
         try {
             InetAddress.getByName(host);
@@ -169,7 +186,7 @@ public class ByonLocationResolver extends AbstractLocationResolver {
         }
         if (userAndHostAndPort.getHostAndPort().hasPort()) {
             checkArgument(!vals.containsKey("port"), "Must not specify port twice for machine: %s", valSanitized);
-            machineConfig.put("port", userAndHostAndPort.getHostAndPort().getPort());
+            machineConfig.put("port", port);
         }
         for (Map.Entry<String, ?> entry : defaults.entrySet()) {
             if (!machineConfig.containsKey(entry.getKey())) {
@@ -187,7 +204,7 @@ public class ByonLocationResolver extends AbstractLocationResolver {
 
     protected LocationSpec<? extends MachineLocation> parseMachine(String val, Class<? extends MachineLocation> locationClass, Map<String, ?> defaults, String specForErrMsg) {
         Map<String, Object> machineConfig = Maps.newLinkedHashMap();
-
+        
         UserAndHostAndPort userAndHostAndPort = parseUserAndHostAndPort(val);
         
         String host = userAndHostAndPort.getHostAndPort().getHostText().trim();
@@ -222,4 +239,12 @@ public class ByonLocationResolver extends AbstractLocationResolver {
         }
         return UserAndHostAndPort.fromParts(userPart, HostAndPort.fromString(hostPart));
     }
+    
+    private UserAndHostAndPort parseUserAndHostAndPort(String val, int defaultPort) {
+        UserAndHostAndPort result = parseUserAndHostAndPort(val);
+        if (!result.getHostAndPort().hasPort()) {
+            result = UserAndHostAndPort.fromParts(result.getUser(), result.getHostAndPort().getHostText(), defaultPort);
+        }
+        return result;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9633236d/core/src/main/java/brooklyn/location/basic/SshMachineLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/SshMachineLocation.java b/core/src/main/java/brooklyn/location/basic/SshMachineLocation.java
index 68f224d..8c06274 100644
--- a/core/src/main/java/brooklyn/location/basic/SshMachineLocation.java
+++ b/core/src/main/java/brooklyn/location/basic/SshMachineLocation.java
@@ -20,10 +20,6 @@ package brooklyn.location.basic;
 
 import static brooklyn.util.GroovyJavaMethods.truth;
 
-import com.google.common.annotations.Beta;
-
-import groovy.lang.Closure;
-
 import java.io.Closeable;
 import java.io.File;
 import java.io.FileInputStream;
@@ -52,6 +48,27 @@ import javax.annotation.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.base.Throwables;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.cache.RemovalListener;
+import com.google.common.cache.RemovalNotification;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.net.HostAndPort;
+import com.google.common.reflect.TypeToken;
+
 import brooklyn.config.BrooklynLogging;
 import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
@@ -66,6 +83,7 @@ import brooklyn.location.MachineLocation;
 import brooklyn.location.OsDetails;
 import brooklyn.location.PortRange;
 import brooklyn.location.PortSupplier;
+import brooklyn.location.access.PortForwardManager;
 import brooklyn.management.Task;
 import brooklyn.util.ResourceUtils;
 import brooklyn.util.collections.MutableMap;
@@ -96,26 +114,7 @@ import brooklyn.util.task.system.internal.ExecWithLoggingHelpers;
 import brooklyn.util.task.system.internal.ExecWithLoggingHelpers.ExecRunner;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
-
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.base.Supplier;
-import com.google.common.base.Throwables;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.cache.RemovalListener;
-import com.google.common.cache.RemovalNotification;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.net.HostAndPort;
-import com.google.common.reflect.TypeToken;
+import groovy.lang.Closure;
 
 /**
  * Operations on a machine that is accessible via ssh.
@@ -153,6 +152,12 @@ public class SshMachineLocation extends AbstractLocation implements MachineLocat
             "Private addresses of this machine, e.g. those within the private network", 
             null);
 
+    public static final ConfigKey<Map<Integer, String>> TCP_PORT_MAPPINGS = ConfigKeys.newConfigKey(
+            new TypeToken<Map<Integer, String>>() {},
+            "tcpPortMappings",
+            "NAT'ed ports, giving the mapping from private TCP port to a public host:port", 
+            null);
+
     @SetFromFlag
     protected String user;
 
@@ -251,6 +256,25 @@ public class SshMachineLocation extends AbstractLocation implements MachineLocat
         usedPorts = (usedPorts != null) ? Sets.newLinkedHashSet(usedPorts) : Sets.<Integer>newLinkedHashSet();
     }
 
+    @Override
+    public void init() {
+        super.init();
+
+        // Register any pre-existing port-mappings with the PortForwardManager
+        Map<Integer, String> tcpPortMappings = getConfig(TCP_PORT_MAPPINGS);
+        if (tcpPortMappings != null) {
+            PortForwardManager pfm = (PortForwardManager) getManagementContext().getLocationRegistry().resolve("portForwardManager(scope=global)");
+            for (Map.Entry<Integer, String> entry : tcpPortMappings.entrySet()) {
+                int targetPort = entry.getKey();
+                HostAndPort publicEndpoint = HostAndPort.fromString(entry.getValue());
+                if (!publicEndpoint.hasPort()) {
+                    throw new IllegalArgumentException("Invalid portMapping ('"+entry.getValue()+"') for port "+targetPort+" in machine "+this);
+                }
+                pfm.associate(publicEndpoint.getHostText(), publicEndpoint, this, targetPort);
+            }
+        }
+    }
+    
     private final transient Object poolCacheMutex = new Object();
     @Nonnull
     private LoadingCache<Map<String, ?>, Pool<SshTool>> getSshPoolCache() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9633236d/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java b/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java
index 54ee63a..3504ec0 100644
--- a/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java
+++ b/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java
@@ -19,8 +19,6 @@
 package brooklyn.location.basic;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import io.cloudsoft.winrm4j.winrm.WinRmTool;
-import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -30,6 +28,7 @@ import java.net.InetAddress;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import javax.annotation.Nullable;
@@ -38,22 +37,26 @@ import org.apache.commons.codec.binary.Base64;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Charsets;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.net.HostAndPort;
+import com.google.common.reflect.TypeToken;
+
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.location.MachineDetails;
 import brooklyn.location.MachineLocation;
 import brooklyn.location.OsDetails;
+import brooklyn.location.access.PortForwardManager;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.stream.Streams;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
-
-import com.google.common.base.Charsets;
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.reflect.TypeToken;
+import io.cloudsoft.winrm4j.winrm.WinRmTool;
+import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
 
 public class WinRmMachineLocation extends AbstractLocation implements MachineLocation {
 
@@ -98,6 +101,12 @@ public class WinRmMachineLocation extends AbstractLocation implements MachineLoc
             "Private addresses of this machine, e.g. those within the private network", 
             null);
 
+    public static final ConfigKey<Map<Integer, String>> TCP_PORT_MAPPINGS = ConfigKeys.newConfigKey(
+            new TypeToken<Map<Integer, String>>() {},
+            "tcpPortMappings",
+            "NAT'ed ports, giving the mapping from private TCP port to a public host:port", 
+            null);
+
     @Override
     public InetAddress getAddress() {
         return getConfig(ADDRESS);
@@ -245,8 +254,21 @@ public class WinRmMachineLocation extends AbstractLocation implements MachineLoc
     @Override
     public void init() {
         super.init();
-    }
 
+        // Register any pre-existing port-mappings with the PortForwardManager
+        Map<Integer, String> tcpPortMappings = getConfig(TCP_PORT_MAPPINGS);
+        if (tcpPortMappings != null) {
+            PortForwardManager pfm = (PortForwardManager) getManagementContext().getLocationRegistry().resolve("portForwardManager(scope=global)");
+            for (Map.Entry<Integer, String> entry : tcpPortMappings.entrySet()) {
+                int targetPort = entry.getKey();
+                HostAndPort publicEndpoint = HostAndPort.fromString(entry.getValue());
+                if (!publicEndpoint.hasPort()) {
+                    throw new IllegalArgumentException("Invalid portMapping ('"+entry.getValue()+"') for port "+targetPort+" in machine "+this);
+                }
+                pfm.associate(publicEndpoint.getHostText(), publicEndpoint, this, targetPort);
+            }
+        }
+    }
     public String getUser() {
         return config().get(USER);
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9633236d/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java
index 5ce0dac..3ddc5ad 100644
--- a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/ByonLocationsYamlTest.java
@@ -19,6 +19,7 @@
 package io.brooklyn.camp.brooklyn;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
 
 import java.io.StringReader;
 import java.util.Map;
@@ -28,21 +29,23 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.Test;
 
+import com.google.api.client.repackaged.com.google.common.base.Joiner;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.net.HostAndPort;
+
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.location.MachineLocation;
+import brooklyn.location.access.PortForwardManager;
 import brooklyn.location.basic.FixedListMachineProvisioningLocation;
 import brooklyn.location.basic.LocationPredicates;
 import brooklyn.location.basic.SshMachineLocation;
 import brooklyn.location.basic.WinRmMachineLocation;
 import brooklyn.util.net.UserAndHostAndPort;
 
-import com.google.api.client.repackaged.com.google.common.base.Joiner;
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-
 public class ByonLocationsYamlTest extends AbstractYamlTest {
     private static final Logger log = LoggerFactory.getLogger(ByonLocationsYamlTest.class);
 
@@ -165,7 +168,56 @@ public class ByonLocationsYamlTest extends AbstractYamlTest {
                 "mykey", "myval3"));
         assertEquals(machine3.getPrivateAddresses(), ImmutableSet.of("10.0.0.3"));
     }
-    
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testByonPortMapping() throws Exception {
+        String yaml = Joiner.on("\n").join(
+                "location:",
+                "  byon:",
+                "    hosts:",
+                "    - ssh: 1.1.1.1:22",
+                "      privateAddresses: [10.0.0.1]",
+                "      tcpPortMappings: {22: \"83.222.229.1:12001\", 8080: \"83.222.229.1:12002\"}",
+                "      password: mypassword",
+                "      user: myuser",
+                "      mykey: myval1",
+                "    - winrm: 1.1.1.2:8985",
+                "      privateAddresses: [10.0.0.2]",
+                "      tcpPortMappings: {8985: \"83.222.229.2:12003\", 8080: \"83.222.229.2:12004\"}",
+                "      password: mypassword",
+                "      user: myuser",
+                "      mykey: myval2",
+                "      osfamily: windows",
+                "services:",
+                "- serviceType: brooklyn.entity.basic.BasicApplication");
+
+        Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
+        FixedListMachineProvisioningLocation<MachineLocation> loc = (FixedListMachineProvisioningLocation<MachineLocation>) Iterables.get(app.getLocations(), 0);
+        PortForwardManager pfm = (PortForwardManager) mgmt().getLocationRegistry().resolve("portForwardManager(scope=global)");
+        
+        Set<MachineLocation> machines = loc.getAvailable();
+        assertEquals(machines.size(), 2, "machines="+machines);
+        SshMachineLocation machine1 = (SshMachineLocation) Iterables.find(machines, LocationPredicates.configEqualTo(ConfigKeys.newStringConfigKey("mykey"), "myval1"));
+        WinRmMachineLocation machine2 = (WinRmMachineLocation) Iterables.find(machines, Predicates.instanceOf(WinRmMachineLocation.class));
+
+        assertMachine(machine1, UserAndHostAndPort.fromParts("myuser", "83.222.229.1", 12001), ImmutableMap.of(
+                SshMachineLocation.PASSWORD.getName(), "mypassword",
+                "mykey", "myval1"));
+        assertEquals(machine1.getPrivateAddresses(), ImmutableSet.of("10.0.0.1"));
+        assertEquals(pfm.lookup(machine1, 22), HostAndPort.fromParts("83.222.229.1", 12001));
+        assertEquals(pfm.lookup(machine1, 8080), HostAndPort.fromParts("83.222.229.1", 12002));
+        assertNull(pfm.lookup(machine1, 12345));
+        
+        assertMachine(machine2, UserAndHostAndPort.fromParts("myuser", "83.222.229.2",  12003), ImmutableMap.of(
+                SshMachineLocation.PASSWORD.getName(), "mypassword",
+                "mykey", "myval2"));
+        assertEquals(machine2.getPrivateAddresses(), ImmutableSet.of("10.0.0.2"));
+        assertEquals(pfm.lookup(machine2, 8985), HostAndPort.fromParts("83.222.229.2", 12003));
+        assertEquals(pfm.lookup(machine2, 8080), HostAndPort.fromParts("83.222.229.2", 12004));
+        assertNull(pfm.lookup(machine2, 12345));
+    }
+
     private void assertMachine(SshMachineLocation machine, UserAndHostAndPort conn, Map<String, ?> config) {
         assertEquals(machine.getAddress().getHostAddress(), conn.getHostAndPort().getHostText());
         assertEquals(machine.getPort(), conn.getHostAndPort().getPort());


[6/6] incubator-brooklyn git commit: This closes #776

Posted by al...@apache.org.
This closes #776


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

Branch: refs/heads/master
Commit: 05b95f377197cc4033131de26c846db1c91575b5
Parents: 946b5dc bac43b0
Author: Aled Sage <al...@gmail.com>
Authored: Wed Jul 29 00:13:16 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Jul 29 00:13:16 2015 +0100

----------------------------------------------------------------------
 .../BasicMachineLocationCustomizer.java         |  41 +++++
 .../location/MachineLocationCustomizer.java     |  42 +++++
 .../location/basic/ByonLocationResolver.java    |  47 +++--
 .../FixedListMachineProvisioningLocation.java   |  44 +++--
 .../java/brooklyn/location/basic/Machines.java  |  11 ++
 .../location/basic/SshMachineLocation.java      |  72 +++++---
 .../location/basic/WinRmMachineLocation.java    |  42 +++--
 .../location/cloud/CloudLocationConfig.java     |   8 +
 ...ixedListMachineProvisioningLocationTest.java |  44 ++++-
 .../RecordingMachineLocationCustomizer.java     |  71 ++++++++
 .../location/jclouds/JcloudsLocation.java       |  79 +++++----
 .../location/jclouds/JcloudsLocationConfig.java |  12 +-
 .../location/jclouds/JcloudsLocationTest.java   |  10 ++
 .../brooklyn/entity/basic/SoftwareProcess.java  |  16 ++
 .../entity/software/MachineInitTasks.java       | 172 +++++++++++++++++++
 .../software/MachineLifecycleEffectorTasks.java |  41 ++++-
 .../webapp/tomcat/TomcatServerEc2LiveTest.java  |   1 +
 .../camp/brooklyn/ByonLocationsYamlTest.java    |  66 ++++++-
 18 files changed, 707 insertions(+), 112 deletions(-)
----------------------------------------------------------------------



[4/6] incubator-brooklyn git commit: Set subnet_address if has HasNetworkAddresses.privateAddresses

Posted by al...@apache.org.
Set subnet_address if has HasNetworkAddresses.privateAddresses


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

Branch: refs/heads/master
Commit: 4980904fe1a93f5b5580484ccc5124d2a68152c4
Parents: 9633236
Author: Aled Sage <al...@gmail.com>
Authored: Fri Jul 24 19:15:43 2015 -0700
Committer: Aled Sage <al...@gmail.com>
Committed: Mon Jul 27 13:50:00 2015 +0100

----------------------------------------------------------------------
 core/src/main/java/brooklyn/location/basic/Machines.java | 11 +++++++++++
 1 file changed, 11 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4980904f/core/src/main/java/brooklyn/location/basic/Machines.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/Machines.java b/core/src/main/java/brooklyn/location/basic/Machines.java
index 103870f..7c143a8 100644
--- a/core/src/main/java/brooklyn/location/basic/Machines.java
+++ b/core/src/main/java/brooklyn/location/basic/Machines.java
@@ -21,16 +21,20 @@ package brooklyn.location.basic;
 import java.net.InetAddress;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.Set;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.Iterables;
+
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.Attributes;
 import brooklyn.location.Location;
 import brooklyn.location.MachineLocation;
 import brooklyn.location.basic.LocalhostMachineProvisioningLocation.LocalhostMachine;
 import brooklyn.util.guava.Maybe;
+import brooklyn.util.net.HasNetworkAddresses;
 
 /** utilities for working with MachineLocations */
 public class Machines {
@@ -38,6 +42,7 @@ public class Machines {
     private static final Logger log = LoggerFactory.getLogger(Machines.class);
     
     public static Maybe<String> getSubnetHostname(Location where) {
+        // TODO Should we look at HasNetworkAddresses? But that's not a hostname.
         String hostname = null;
         if (where instanceof HasSubnetHostname) {
             hostname = ((HasSubnetHostname) where).getSubnetHostname();
@@ -58,6 +63,12 @@ public class Machines {
         if (where instanceof HasSubnetHostname) {
             result = ((HasSubnetHostname) where).getSubnetIp();
         }
+        if (where instanceof HasNetworkAddresses) {
+            Set<String> privateAddrs = ((HasNetworkAddresses) where).getPrivateAddresses();
+            if (privateAddrs.size() > 0) {
+                result = Iterables.get(privateAddrs, 0);
+            }
+        }
         if (result == null && where instanceof MachineLocation) {
             InetAddress addr = ((MachineLocation) where).getAddress();
             if (addr != null) result = addr.getHostAddress();


[3/6] incubator-brooklyn git commit: Deprecates JcloudsLocation.open/stopIptables config

Posted by al...@apache.org.
Deprecates JcloudsLocation.open/stopIptables config


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

Branch: refs/heads/master
Commit: 42d6091663495665dc68c0d45d2e640984b3ad89
Parents: 4980904
Author: Aled Sage <al...@gmail.com>
Authored: Sat Jul 25 14:58:03 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Mon Jul 27 13:50:00 2015 +0100

----------------------------------------------------------------------
 .../java/brooklyn/location/jclouds/JcloudsLocation.java |  8 ++++++--
 .../location/jclouds/JcloudsLocationConfig.java         | 12 ++++++++++--
 2 files changed, 16 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/42d60916/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
index c4996df..60119ea 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
@@ -877,8 +877,10 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
 
                 if (setup.get(OPEN_IPTABLES)) {
                     if (windows) {
-                        LOG.warn("Ignoring flag OPEN_IPTABLES on Windows location {}", machineLocation);
+                        LOG.warn("Ignoring DEPRECATED flag OPEN_IPTABLES on Windows location {}", machineLocation);
                     } else {
+                        LOG.warn("Using DEPRECATED flag OPEN_IPTABLES (will not be supported in future versions) for {} at {}", machineLocation, this);
+                        
                         @SuppressWarnings("unchecked")
                         Iterable<Integer> inboundPorts = (Iterable<Integer>) setup.get(INBOUND_PORTS);
 
@@ -917,8 +919,10 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
 
                 if (setup.get(STOP_IPTABLES)) {
                     if (windows) {
-                        LOG.warn("Ignoring flag OPEN_IPTABLES on Windows location {}", machineLocation);
+                        LOG.warn("Ignoring DEPRECATED flag OPEN_IPTABLES on Windows location {}", machineLocation);
                     } else {
+                        LOG.warn("Using DEPRECATED flag STOP_IPTABLES (will not be supported in future versions) for {} at {}", machineLocation, this);
+                        
                         customisationForLogging.add("stop iptables");
 
                         List<String> cmds = ImmutableList.<String>of();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/42d60916/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
index c37d915..2d570ae 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java
@@ -106,12 +106,20 @@ public interface JcloudsLocationConfig extends CloudLocationConfig {
             Constants.PROPERTY_ENDPOINT, "Equivalent to 'endpoint'; provided for jclouds compatibility", null);
     
     // note causing problems on centos due to use of `sudo -n`; but required for default RHEL VM
+    /**
+     * @deprecated since 0.8.0; instead configure this on the entity. See SoftwareProcess.OPEN_IPTABLES.
+     */
+    @Deprecated
     public static final ConfigKey<Boolean> OPEN_IPTABLES = ConfigKeys.newBooleanConfigKey("openIptables", 
-            "Whether to open the INBOUND_PORTS via iptables rules; " +
+            "[DEPRECATED - use openIptables on SoftwareProcess entity] Whether to open the INBOUND_PORTS via iptables rules; " +
             "if true then ssh in to run iptables commands, as part of machine provisioning", false);
 
+    /**
+     * @deprecated since 0.8.0; instead configure this on the entity. See SoftwareProcess.STOP_IPTABLES.
+     */
+    @Deprecated
     public static final ConfigKey<Boolean> STOP_IPTABLES = ConfigKeys.newBooleanConfigKey("stopIptables", 
-            "Whether to stop iptables entirely; " +
+            "[DEPRECATED - use stopIptables on SoftwareProcess entity] Whether to stop iptables entirely; " +
             "if true then ssh in to stop the iptables service, as part of machine provisioning", false);
 
     public static final ConfigKey<String> HARDWARE_ID = ConfigKeys.newStringConfigKey("hardwareId",


[5/6] incubator-brooklyn git commit: Adds MACHINE_LOCATION_CUSTOMIZERS

Posted by al...@apache.org.
Adds MACHINE_LOCATION_CUSTOMIZERS

A similar idea to the JcloudsMachineLocationCustomizer, but more 
generic - it deals just with the MachineLocation. It can be wired
in to JcloudsLocation and to BYON (i.e. FixedListMachineProvisioningLocation).

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

Branch: refs/heads/master
Commit: bac43b0705f6d2fd51e89402b6631f8f07619dbb
Parents: 90f1881
Author: Aled Sage <al...@gmail.com>
Authored: Mon Jul 27 17:48:02 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Mon Jul 27 17:48:02 2015 +0100

----------------------------------------------------------------------
 .../BasicMachineLocationCustomizer.java         | 41 +++++++++++
 .../location/MachineLocationCustomizer.java     | 42 ++++++++++++
 .../FixedListMachineProvisioningLocation.java   | 44 +++++++++---
 .../location/cloud/CloudLocationConfig.java     |  8 +++
 ...ixedListMachineProvisioningLocationTest.java | 44 ++++++++++--
 .../RecordingMachineLocationCustomizer.java     | 71 ++++++++++++++++++++
 .../location/jclouds/JcloudsLocation.java       | 71 +++++++++++---------
 .../location/jclouds/JcloudsLocationTest.java   | 10 +++
 8 files changed, 283 insertions(+), 48 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bac43b07/api/src/main/java/brooklyn/location/BasicMachineLocationCustomizer.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/location/BasicMachineLocationCustomizer.java b/api/src/main/java/brooklyn/location/BasicMachineLocationCustomizer.java
new file mode 100644
index 0000000..a05dd44
--- /dev/null
+++ b/api/src/main/java/brooklyn/location/BasicMachineLocationCustomizer.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.location;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * A default no-op implementation, which can be extended to override the appropriate methods.
+ * 
+ * Sub-classing will give the user some protection against future API changes - note that 
+ * {@link MachineLocationCustomizer} is marked {@link Beta}.
+ */
+@Beta
+public class BasicMachineLocationCustomizer implements MachineLocationCustomizer {
+
+    @Override
+    public void customize(MachineLocation machine) {
+        // no-op
+    }
+    
+    @Override
+    public void preRelease(MachineLocation machine) {
+        // no-op
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bac43b07/api/src/main/java/brooklyn/location/MachineLocationCustomizer.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/location/MachineLocationCustomizer.java b/api/src/main/java/brooklyn/location/MachineLocationCustomizer.java
new file mode 100644
index 0000000..83e1009
--- /dev/null
+++ b/api/src/main/java/brooklyn/location/MachineLocationCustomizer.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.location;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Customization hooks to allow apps to perform specific customisation of obtained machines.
+ * <p>
+ * Users are strongly encouraged to sub-class {@link BasicMachineLocationCustomizer}, to give
+ * some protection against this {@link Beta} API changing in future releases.
+ */
+@Beta
+public interface MachineLocationCustomizer {
+
+    /**
+     * Override to configure the given machine once it has been created (prior to any use).
+     */
+    void customize(MachineLocation machine);
+    
+    /**
+     * Override to handle machine-related cleanup prior to {@link MachineProvisioningLocation} 
+     * releasing the machine.
+     */
+    void preRelease(MachineLocation machine);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bac43b07/core/src/main/java/brooklyn/location/basic/FixedListMachineProvisioningLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/FixedListMachineProvisioningLocation.java b/core/src/main/java/brooklyn/location/basic/FixedListMachineProvisioningLocation.java
index d8aad46..50a2a17 100644
--- a/core/src/main/java/brooklyn/location/basic/FixedListMachineProvisioningLocation.java
+++ b/core/src/main/java/brooklyn/location/basic/FixedListMachineProvisioningLocation.java
@@ -31,31 +31,35 @@ import java.util.Set;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.reflect.TypeToken;
+
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.location.Location;
 import brooklyn.location.LocationSpec;
 import brooklyn.location.MachineLocation;
+import brooklyn.location.MachineLocationCustomizer;
 import brooklyn.location.MachineProvisioningLocation;
 import brooklyn.location.NoMachinesAvailableException;
+import brooklyn.location.cloud.CloudLocationConfig;
 import brooklyn.management.LocationManager;
 import brooklyn.util.collections.CollectionFunctionals;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.collections.MutableSet;
+import brooklyn.util.config.ConfigBag;
 import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.stream.Streams;
 import brooklyn.util.text.WildcardGlobs;
 import brooklyn.util.text.WildcardGlobs.PhraseTreatment;
 
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.reflect.TypeToken;
-
 /**
  * A provisioner of {@link MachineLocation}s which takes a list of machines it can connect to.
  * The collection of initial machines should be supplied in the 'machines' flag in the constructor,
@@ -80,6 +84,8 @@ implements MachineProvisioningLocation<T>, Closeable {
                     "byon.machineChooser",
                     "For choosing which of the possible machines is chosen and returned by obtain()",
                     CollectionFunctionals.<MachineLocation>firstElement());
+
+    public static final ConfigKey<Collection<MachineLocationCustomizer>> MACHINE_LOCATION_CUSTOMIZERS = CloudLocationConfig.MACHINE_LOCATION_CUSTOMIZERS;
     
     private final Object lock = new Object();
     
@@ -238,13 +244,14 @@ implements MachineProvisioningLocation<T>, Closeable {
     public T obtain(Map<?,?> flags) throws NoMachinesAvailableException {
         T machine;
         T desiredMachine = (T) flags.get("desiredMachine");
-        Function<Iterable<? extends MachineLocation>, MachineLocation> chooser = getConfigPreferringOverridden(MACHINE_CHOOSER, flags);
+        ConfigBag allflags = ConfigBag.newInstanceExtending(config().getBag()).putAll(flags);
+        Function<Iterable<? extends MachineLocation>, MachineLocation> chooser = allflags.get(MACHINE_CHOOSER);
         
         synchronized (lock) {
             Set<T> a = getAvailable();
             if (a.isEmpty()) {
                 if (canProvisionMore()) {
-                    provisionMore(1, flags);
+                    provisionMore(1, allflags.getAllConfig());
                     a = getAvailable();
                 }
                 if (a.isEmpty())
@@ -265,11 +272,21 @@ implements MachineProvisioningLocation<T>, Closeable {
             }
             inUse.add(machine);
         }
+        
+        for (MachineLocationCustomizer customizer : getMachineCustomizers(allflags)) {
+            customizer.customize(machine);
+        }
+        
         return machine;
     }
 
     @Override
     public void release(T machine) {
+        ConfigBag machineConfig = ((ConfigurationSupportInternal)machine.config()).getBag();
+        for (MachineLocationCustomizer customizer : getMachineCustomizers(machineConfig)) {
+            customizer.preRelease(machine);
+        }
+
         synchronized (lock) {
             if (inUse.contains(machine) == false)
                 throw new IllegalStateException("Request to release machine "+machine+", but this machine is not currently allocated");
@@ -294,6 +311,11 @@ implements MachineProvisioningLocation<T>, Closeable {
         return result;
     }
 
+    protected Collection<MachineLocationCustomizer> getMachineCustomizers(ConfigBag setup) {
+        Collection<MachineLocationCustomizer> customizers = setup.get(MACHINE_LOCATION_CUSTOMIZERS);
+        return (customizers == null ? ImmutableList.<MachineLocationCustomizer>of() : customizers);
+    }
+
     /**
      * Facilitates fluent/programmatic style for constructing a fixed pool of machines.
      * <pre>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bac43b07/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java b/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
index 9134af7..66c6065 100644
--- a/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
+++ b/core/src/main/java/brooklyn/location/cloud/CloudLocationConfig.java
@@ -18,11 +18,15 @@
  */
 package brooklyn.location.cloud;
 
+import java.util.Collection;
+
 import com.google.common.annotations.Beta;
+import com.google.common.reflect.TypeToken;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.event.basic.BasicConfigKey;
+import brooklyn.location.MachineLocationCustomizer;
 import brooklyn.location.basic.LocationConfigKeys;
 import brooklyn.util.flags.SetFromFlag;
 
@@ -105,4 +109,8 @@ public interface CloudLocationConfig {
     public static final ConfigKey<String> DOMAIN_NAME = new BasicConfigKey<String>(String.class, "domainName",
         "DNS domain where the host should be created, e.g. yourdomain.com (selected clouds only)", null);
 
+    @SuppressWarnings("serial")
+    public static final ConfigKey<Collection<MachineLocationCustomizer>> MACHINE_LOCATION_CUSTOMIZERS = ConfigKeys.newConfigKey(
+            new TypeToken<Collection<MachineLocationCustomizer>>() {},
+            "machineCustomizers", "Optional machine customizers");
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bac43b07/core/src/test/java/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java b/core/src/test/java/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java
index b0edf8c..78a61a9 100644
--- a/core/src/test/java/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java
+++ b/core/src/test/java/brooklyn/location/basic/FixedListMachineProvisioningLocationTest.java
@@ -34,10 +34,18 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
 import brooklyn.entity.basic.Entities;
 import brooklyn.location.LocationSpec;
 import brooklyn.location.MachineLocation;
 import brooklyn.location.NoMachinesAvailableException;
+import brooklyn.location.basic.RecordingMachineLocationCustomizer.Call;
 import brooklyn.management.internal.LocalManagementContext;
 import brooklyn.test.entity.LocalManagementContextForTests;
 import brooklyn.util.collections.MutableList;
@@ -45,13 +53,6 @@ import brooklyn.util.collections.MutableMap;
 import brooklyn.util.net.Networking;
 import brooklyn.util.stream.Streams;
 
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
 /**
  * Provisions {@link SshMachineLocation}s in a specific location from a list of known machines
  */
@@ -472,6 +473,35 @@ public class FixedListMachineProvisioningLocationTest {
         }
     }
 
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testMachineCustomizerSetOnByon() throws Exception {
+        machine = mgmt.getLocationManager().createLocation(MutableMap.of("address", Inet4Address.getByName("192.168.144.200")), SshMachineLocation.class);
+        RecordingMachineLocationCustomizer customizer = new RecordingMachineLocationCustomizer();
+        
+        provisioner2 = mgmt.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+                .configure("machines", ImmutableList.of(machine))
+                .configure(FixedListMachineProvisioningLocation.MACHINE_LOCATION_CUSTOMIZERS.getName(), ImmutableList.of(customizer)));
+                
+        SshMachineLocation obtained = provisioner2.obtain();
+        assertEquals(Iterables.getOnlyElement(customizer.calls), new Call("customize", ImmutableList.of(obtained)));
+        
+        provisioner2.release(obtained);
+        assertEquals(customizer.calls.size(), 2);
+        assertEquals(Iterables.get(customizer.calls, 1), new Call("preRelease", ImmutableList.of(obtained)));
+    }
+
+    @Test
+    public void testMachineCustomizerSetOnObtainCall() throws Exception {
+        RecordingMachineLocationCustomizer customizer = new RecordingMachineLocationCustomizer();
+        
+        SshMachineLocation obtained = provisioner.obtain(ImmutableMap.of(FixedListMachineProvisioningLocation.MACHINE_LOCATION_CUSTOMIZERS, ImmutableList.of(customizer)));
+        assertEquals(Iterables.getOnlyElement(customizer.calls), new Call("customize", ImmutableList.of(obtained)));
+        
+        // TODO Does not call preRelease, because customizer is not config on provisioner, and is not config on machine
+        provisioner.release(obtained);
+    }
+
     private static <T> List<T> randomized(Iterable<T> list) {
         // TODO inefficient implementation, but don't care for small tests
         Random random = new Random();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bac43b07/core/src/test/java/brooklyn/location/basic/RecordingMachineLocationCustomizer.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/location/basic/RecordingMachineLocationCustomizer.java b/core/src/test/java/brooklyn/location/basic/RecordingMachineLocationCustomizer.java
new file mode 100644
index 0000000..0422b36
--- /dev/null
+++ b/core/src/test/java/brooklyn/location/basic/RecordingMachineLocationCustomizer.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.location.basic;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import brooklyn.location.MachineLocation;
+import brooklyn.location.MachineLocationCustomizer;
+
+public class RecordingMachineLocationCustomizer implements MachineLocationCustomizer {
+    public static class Call {
+        public final String methodName;
+        public final List<?> args;
+        
+        Call(String methodName, List<?> args) {
+            this.methodName = checkNotNull(methodName);
+            this.args = checkNotNull(args);
+        }
+        
+        @Override
+        public String toString() {
+            return methodName+args;
+        }
+        
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(methodName, args);
+        }
+        
+        @Override
+        public boolean equals(Object other) {
+            return (other instanceof RecordingMachineLocationCustomizer.Call) && 
+                    methodName.equals(((RecordingMachineLocationCustomizer.Call)other).methodName) && 
+                    args.equals(((RecordingMachineLocationCustomizer.Call)other).args);
+        }
+    }
+    
+    public final List<RecordingMachineLocationCustomizer.Call> calls = Lists.newCopyOnWriteArrayList();
+    
+    @Override
+    public void customize(MachineLocation machine) {
+        calls.add(new Call("customize", ImmutableList.of(machine)));
+    }
+
+    @Override
+    public void preRelease(MachineLocation machine) {
+        calls.add(new Call("preRelease", ImmutableList.of(machine)));
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bac43b07/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
index 60119ea..c7d9f95 100644
--- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java
@@ -26,11 +26,6 @@ import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.jclouds.compute.options.RunScriptOptions.Builder.overrideLoginCredentials;
 import static org.jclouds.scriptbuilder.domain.Statements.exec;
 
-import brooklyn.util.flags.MethodCoercions;
-import brooklyn.location.basic.AbstractLocation;
-import io.cloudsoft.winrm4j.pywinrm.Session;
-import io.cloudsoft.winrm4j.pywinrm.WinRMFactory;
-
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
@@ -93,6 +88,31 @@ import org.jclouds.softlayer.compute.options.SoftLayerTemplateOptions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.base.Splitter;
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Supplier;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+import com.google.common.io.Files;
+import com.google.common.net.HostAndPort;
+import com.google.common.primitives.Ints;
+
 import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigKey.HasConfigKey;
 import brooklyn.config.ConfigUtils;
@@ -103,11 +123,13 @@ import brooklyn.entity.rebind.persister.PersistenceObjectStore;
 import brooklyn.entity.rebind.persister.jclouds.JcloudsBlobStoreBasedObjectStore;
 import brooklyn.location.LocationSpec;
 import brooklyn.location.MachineLocation;
+import brooklyn.location.MachineLocationCustomizer;
 import brooklyn.location.MachineManagementMixins.MachineMetadata;
 import brooklyn.location.MachineManagementMixins.RichMachineProvisioningLocation;
 import brooklyn.location.NoMachinesAvailableException;
 import brooklyn.location.access.PortForwardManager;
 import brooklyn.location.access.PortMapping;
+import brooklyn.location.basic.AbstractLocation;
 import brooklyn.location.basic.BasicMachineMetadata;
 import brooklyn.location.basic.LocationConfigKeys;
 import brooklyn.location.basic.LocationConfigUtils;
@@ -132,6 +154,7 @@ import brooklyn.util.crypto.SecureKeys;
 import brooklyn.util.exceptions.CompoundRuntimeException;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.ReferenceWithError;
+import brooklyn.util.flags.MethodCoercions;
 import brooklyn.util.flags.SetFromFlag;
 import brooklyn.util.flags.TypeCoercions;
 import brooklyn.util.guava.Maybe;
@@ -156,31 +179,8 @@ import brooklyn.util.text.Strings;
 import brooklyn.util.text.TemplateProcessor;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Charsets;
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.base.Objects;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.base.Splitter;
-import com.google.common.base.Stopwatch;
-import com.google.common.base.Supplier;
-import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Sets.SetView;
-import com.google.common.io.Files;
-import com.google.common.net.HostAndPort;
-import com.google.common.primitives.Ints;
+import io.cloudsoft.winrm4j.pywinrm.Session;
+import io.cloudsoft.winrm4j.pywinrm.WinRMFactory;
 
 /**
  * For provisioning and managing VMs in a particular provider/region, using jclouds.
@@ -440,6 +440,11 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
         return result;
     }
 
+    protected Collection<MachineLocationCustomizer> getMachineCustomizers(ConfigBag setup) {
+        Collection<MachineLocationCustomizer> customizers = setup.get(MACHINE_LOCATION_CUSTOMIZERS);
+        return (customizers == null ? ImmutableList.<MachineLocationCustomizer>of() : customizers);
+    }
+
     public void setDefaultImageId(String val) {
         config().set(DEFAULT_IMAGE_ID, val);
     }
@@ -958,6 +963,9 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
             for (JcloudsLocationCustomizer customizer : getCustomizers(setup)) {
                 customizer.customize(this, computeService, machineLocation);
             }
+            for (MachineLocationCustomizer customizer : getMachineCustomizers(setup)) {
+                customizer.customize(machineLocation);
+            }
 
             customizedTimestamp = Duration.of(provisioningStopwatch);
 
@@ -2167,6 +2175,9 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im
                 if (tothrow==null) tothrow = e;
             }
         }
+        for (MachineLocationCustomizer customizer : getMachineCustomizers(setup)) {
+            customizer.preRelease(machine);
+        }
 
         try {
             // FIXME: Needs to release port forwarding for WinRmMachineLocations

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bac43b07/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java b/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java
index c175e29..5676787 100644
--- a/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java
@@ -24,6 +24,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
+
 import javax.annotation.Nullable;
 
 import org.jclouds.scriptbuilder.domain.OsFamily;
@@ -48,6 +49,7 @@ import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
 import brooklyn.location.LocationSpec;
 import brooklyn.location.MachineLocation;
+import brooklyn.location.MachineLocationCustomizer;
 import brooklyn.location.NoMachinesAvailableException;
 import brooklyn.location.basic.LocationConfigKeys;
 import brooklyn.location.cloud.names.CustomMachineNamer;
@@ -383,6 +385,9 @@ public class JcloudsLocationTest implements JcloudsLocationConfig {
             for (JcloudsLocationCustomizer customizer : getCustomizers(config().getBag())) {
                 customizer.customize(this, null, (JcloudsMachineLocation)result);
             }
+            for (MachineLocationCustomizer customizer : getMachineCustomizers(config().getBag())) {
+                customizer.customize((JcloudsMachineLocation)result);
+            }
 
             return result;
         }
@@ -440,22 +445,27 @@ public class JcloudsLocationTest implements JcloudsLocationConfig {
     @Test
     public void testInvokesCustomizerCallbacks() throws Exception {
         JcloudsLocationCustomizer customizer = Mockito.mock(JcloudsLocationCustomizer.class);
+        MachineLocationCustomizer machineCustomizer = Mockito.mock(MachineLocationCustomizer.class);
 //        Mockito.when(customizer.customize(Mockito.any(JcloudsLocation.class), Mockito.any(ComputeService.class), Mockito.any(JcloudsSshMachineLocation.class)));
         ConfigBag allConfig = ConfigBag.newInstance()
             .configure(CLOUD_PROVIDER, "aws-ec2")
             .configure(ACCESS_IDENTITY, "bogus")
             .configure(ACCESS_CREDENTIAL, "bogus")
             .configure(JcloudsLocationConfig.JCLOUDS_LOCATION_CUSTOMIZERS, ImmutableList.of(customizer))
+            .configure(JcloudsLocation.MACHINE_LOCATION_CUSTOMIZERS, ImmutableList.of(machineCustomizer))
             .configure(JcloudsLocation.MACHINE_CREATE_ATTEMPTS, 1);
         FakeLocalhostWithParentJcloudsLocation ll = managementContext.getLocationManager().createLocation(LocationSpec.create(FakeLocalhostWithParentJcloudsLocation.class).configure(allConfig.getAllConfig()));
         JcloudsMachineLocation l = (JcloudsMachineLocation)ll.obtain();
         Mockito.verify(customizer, Mockito.times(1)).customize(ll, null, l);
         Mockito.verify(customizer, Mockito.never()).preRelease(l);
         Mockito.verify(customizer, Mockito.never()).postRelease(l);
+        Mockito.verify(machineCustomizer, Mockito.times(1)).customize(l);
+        Mockito.verify(machineCustomizer, Mockito.never()).preRelease(l);
         
         ll.release(l);
         Mockito.verify(customizer, Mockito.times(1)).preRelease(l);
         Mockito.verify(customizer, Mockito.times(1)).postRelease(l);
+        Mockito.verify(machineCustomizer, Mockito.times(1)).preRelease(l);
     }
 
     // now test creating users