You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ha...@apache.org on 2015/08/14 05:42:44 UTC

[15/54] incubator-brooklyn git commit: [BROOKLYN-162] Renaming package brooklyn.location

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java
new file mode 100644
index 0000000..9fce742
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationConfig.java
@@ -0,0 +1,280 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Semaphore;
+
+import org.apache.brooklyn.location.jclouds.networking.JcloudsPortForwarderExtension;
+import org.jclouds.Constants;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.domain.LoginCredentials;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.event.basic.BasicConfigKey;
+import org.apache.brooklyn.location.access.BrooklynAccessUtils;
+import org.apache.brooklyn.location.access.PortForwardManager;
+import org.apache.brooklyn.location.basic.LocationConfigKeys;
+import org.apache.brooklyn.location.cloud.CloudLocationConfig;
+import brooklyn.util.internal.ssh.SshTool;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.reflect.TypeToken;
+
+public interface JcloudsLocationConfig extends CloudLocationConfig {
+
+    public static final ConfigKey<String> CLOUD_PROVIDER = LocationConfigKeys.CLOUD_PROVIDER;
+
+    public static final ConfigKey<Boolean> RUN_AS_ROOT = ConfigKeys.newBooleanConfigKey("runAsRoot", 
+            "Whether to run initial setup as root (default true)", null);
+    public static final ConfigKey<String> LOGIN_USER = ConfigKeys.newStringConfigKey("loginUser",
+            "Override the user who logs in initially to perform setup " +
+            "(otherwise it is detected from the cloud or known defaults in cloud or VM OS)", null);
+    public static final ConfigKey<String> LOGIN_USER_PASSWORD = ConfigKeys.newStringConfigKey("loginUser.password",
+            "Custom password for the user who logs in initially", null);
+    public static final ConfigKey<String> LOGIN_USER_PRIVATE_KEY_DATA = ConfigKeys.newStringConfigKey("loginUser.privateKeyData",
+            "Custom private key for the user who logs in initially", null);   
+    public static final ConfigKey<String> KEY_PAIR = ConfigKeys.newStringConfigKey("keyPair",
+            "Custom keypair name to be re-used", null);
+    public static final ConfigKey<Boolean> AUTO_GENERATE_KEYPAIRS = ConfigKeys.newBooleanConfigKey("jclouds.openstack-nova.auto-generate-keypairs",
+            "Whether to generate keypairs for Nova");
+    /**
+     * @deprecated since 0.7.0 Use {@link #AUTO_ASSIGN_FLOATING_IP} instead
+     */
+    public static final ConfigKey<Boolean> AUTO_CREATE_FLOATING_IPS = ConfigKeys.newBooleanConfigKey("jclouds.openstack-nova.auto-create-floating-ips",
+            "Whether to generate floating ips for Nova");
+    public static final ConfigKey<Boolean> AUTO_ASSIGN_FLOATING_IP = ConfigKeys.newBooleanConfigKey("autoAssignFloatingIp",
+            "Whether to generate floating ips (in Nova paralance), or elastic IPs (in CloudStack parlance)");
+    // not supported in jclouds
+//    public static final ConfigKey<String> LOGIN_USER_PRIVATE_KEY_PASSPHRASE = ConfigKeys.newStringKey("loginUser.privateKeyPassphrase", 
+//            "Passphrase for the custom private key for the user who logs in initially", null);
+    public static final ConfigKey<String> LOGIN_USER_PRIVATE_KEY_FILE = ConfigKeys.newStringConfigKey("loginUser.privateKeyFile",
+            "Custom private key for the user who logs in initially", null); 
+    public static final ConfigKey<String> EXTRA_PUBLIC_KEY_DATA_TO_AUTH = ConfigKeys.newStringConfigKey("extraSshPublicKeyData",
+        "Additional public key data to add to authorized_keys", null);
+    @SuppressWarnings("serial")
+    public static final ConfigKey<List<String>> EXTRA_PUBLIC_KEY_URLS_TO_AUTH = ConfigKeys.newConfigKey(new TypeToken<List<String>>() {}, 
+        "extraSshPublicKeyUrls", "Additional public keys (files or URLs, in SSH2/RFC4716/id_rsa.pub format) to add to authorized_keys", null);
+
+    public static final ConfigKey<Boolean> DONT_CREATE_USER = ConfigKeys.newBooleanConfigKey("dontCreateUser", 
+            "Whether to skip creation of 'user' when provisioning machines (default false)", false);
+    public static final ConfigKey<Boolean> GRANT_USER_SUDO = ConfigKeys.newBooleanConfigKey("grantUserSudo",
+            "Whether to grant the created user sudo privileges. Irrelevant if dontCreateUser is true. Default: true.", true);
+    public static final ConfigKey<Boolean> DISABLE_ROOT_AND_PASSWORD_SSH = ConfigKeys.newBooleanConfigKey("disableRootAndPasswordSsh",
+        "Whether to disable direct SSH access for root and disable password-based SSH, "
+        + "if creating a user with a key-based login; "
+        + "defaults to true (set false to leave root users alone)", true);
+    public static final ConfigKey<String> CUSTOM_TEMPLATE_OPTIONS_SCRIPT_CONTENTS = ConfigKeys.newStringConfigKey("customTemplateOptionsScriptContents",
+        "A custom script to pass to jclouds as part of template options, run after AdminAccess, "
+        + "for use primarily where a command which must run as root on first login before switching to the admin user, "
+        + "e.g. to customize sudoers; may start in an odd location (e.g. /tmp/bootstrap); "
+        + "NB: most commands should be run by entities, or if VM-specific but sudo is okay, then via setup.script, not via this");
+    
+    public static final ConfigKey<LoginCredentials> CUSTOM_CREDENTIALS = new BasicConfigKey<LoginCredentials>(LoginCredentials.class,
+            "customCredentials", "Custom jclouds LoginCredentials object to be used to connect to the VM", null);
+    
+    public static final ConfigKey<String> GROUP_ID = ConfigKeys.newStringConfigKey("groupId",
+            "The Jclouds group provisioned machines should be members of. " +
+            "Users of this config key are also responsible for configuring security groups.");
+    
+    // jclouds compatibility
+    public static final ConfigKey<String> JCLOUDS_KEY_USERNAME = ConfigKeys.newStringConfigKey(
+            "userName", "Equivalent to 'user'; provided for jclouds compatibility", null);
+    public static final ConfigKey<String> JCLOUDS_KEY_ENDPOINT = ConfigKeys.newStringConfigKey(
+            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", 
+            "[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", 
+            "[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",
+            "A system-specific identifier for the hardware profile or machine type to be used when creating a VM", null);
+    
+    public static final ConfigKey<String> IMAGE_ID = ConfigKeys.newStringConfigKey("imageId", 
+            "A system-specific identifier for the VM image to be used when creating a VM", null);
+    public static final ConfigKey<String> IMAGE_NAME_REGEX = ConfigKeys.newStringConfigKey("imageNameRegex", 
+            "A regular expression to be compared against the 'name' when selecting the VM image to be used when creating a VM", null);
+    public static final ConfigKey<String> IMAGE_DESCRIPTION_REGEX = ConfigKeys.newStringConfigKey("imageDescriptionRegex", 
+            "A regular expression to be compared against the 'description' when selecting the VM image to be used when creating a VM", null);
+
+    public static final ConfigKey<String> TEMPLATE_SPEC = ConfigKeys.newStringConfigKey("templateSpec", 
+            "A jclouds 'spec' string consisting of properties and values to be used when creating a VM " +
+            "(in most cases the properties can, and should, be specified individually using other Brooklyn location config keys)", null);
+
+    public static final ConfigKey<String> DEFAULT_IMAGE_ID = ConfigKeys.newStringConfigKey("defaultImageId", 
+            "A system-specific identifier for the VM image to be used by default when creating a VM " +
+            "(if no other VM image selection criteria are supplied)", null);
+
+    public static final ConfigKey<TemplateBuilder> TEMPLATE_BUILDER = ConfigKeys.newConfigKey(TemplateBuilder.class,
+            "templateBuilder", "A TemplateBuilder instance provided programmatically, to be used when creating a VM");
+
+    public static final ConfigKey<Object> SECURITY_GROUPS = new BasicConfigKey<Object>(Object.class, "securityGroups",
+            "Security groups to be applied when creating a VM, on supported clouds " +
+            "(either a single group identifier as a String, or an Iterable<String> or String[])", null);
+
+    public static final ConfigKey<String> USER_METADATA_STRING = ConfigKeys.newStringConfigKey("userMetadataString", 
+        "Arbitrary user data, as a single string, on supported clouds (AWS)", null);
+
+    @Deprecated /** @deprecated since 0.7.0 even AWS (the only one where this was supported) does not seem to want this uuencoded;
+      use #USER_METADATA_STRING */
+    public static final ConfigKey<String> USER_DATA_UUENCODED = ConfigKeys.newStringConfigKey("userData", 
+        "Arbitrary user data, as a single string in uuencoded format, on supported clouds (AWS)", null);
+
+    public static final ConfigKey<Object> STRING_TAGS = new BasicConfigKey<Object>(Object.class, "tags", 
+            "Tags to be applied when creating a VM, on supported clouds " +
+            "(either a single tag as a String, or an Iterable<String> or String[];" +
+            "note this is not key-value pairs (e.g. what AWS calls 'tags'), for that see userMetadata)", null);
+
+    @Deprecated /** @deprecated since 0.7.0 use #STRING_TAGS */
+    public static final ConfigKey<Object> TAGS = STRING_TAGS;
+
+    public static final ConfigKey<Object> USER_METADATA_MAP = new BasicConfigKey<Object>(Object.class, "userMetadata", 
+            "Arbitrary user metadata, as a map (or String of comma-separated key=value pairs), on supported clouds; " +
+            "note often values cannot be null", null);
+    @Deprecated /** @deprecated since 0.7.0 use #USER_METADATA_MAP */
+    public static final ConfigKey<Object> USER_METADATA = USER_METADATA_MAP;
+
+    public static final ConfigKey<Boolean> INCLUDE_BROOKLYN_USER_METADATA = ConfigKeys.newBooleanConfigKey("includeBrooklynUserMetadata", 
+        "Whether to set metadata about the context of a machine, e.g. brooklyn-entity-id, brooklyn-app-name (default true)", true);
+
+    public static final ConfigKey<Boolean> MAP_DEV_RANDOM_TO_DEV_URANDOM = ConfigKeys.newBooleanConfigKey(
+            "installDevUrandom", "Map /dev/random to /dev/urandom to prevent halting on insufficient entropy", true);
+
+    /** @deprecated since 0.7.0; use {@link #JCLOUDS_LOCATION_CUSTOMIZERS} instead */
+    @Deprecated
+    public static final ConfigKey<JcloudsLocationCustomizer> JCLOUDS_LOCATION_CUSTOMIZER = ConfigKeys.newConfigKey(JcloudsLocationCustomizer.class,
+            "customizer", "Optional location customizer");
+
+    @SuppressWarnings("serial")
+    public static final ConfigKey<Collection<JcloudsLocationCustomizer>> JCLOUDS_LOCATION_CUSTOMIZERS = ConfigKeys.newConfigKey(
+            new TypeToken<Collection<JcloudsLocationCustomizer>>() {},
+            "customizers", "Optional location customizers");
+
+    /** @deprecated since 0.7.0; use {@link #JCLOUDS_LOCATION_CUSTOMIZERS} instead */
+    @Deprecated
+    public static final ConfigKey<String> JCLOUDS_LOCATION_CUSTOMIZER_TYPE = ConfigKeys.newStringConfigKey(
+            "customizerType", "Optional location customizer type (to be class-loaded and constructed with no-arg constructor)");
+
+    /** @deprecated since 0.7.0; use {@link #JCLOUDS_LOCATION_CUSTOMIZERS} instead */
+    @Deprecated
+    public static final ConfigKey<String> JCLOUDS_LOCATION_CUSTOMIZERS_SUPPLIER_TYPE = ConfigKeys.newStringConfigKey(
+            "customizersSupplierType", "Optional type of a Supplier<Collection<JcloudsLocationCustomizer>> " +
+            "(to be class-loaded and constructed with ConfigBag or no-arg constructor)");
+
+    public static final ConfigKey<String> LOCAL_TEMP_DIR = SshTool.PROP_LOCAL_TEMP_DIR;
+    
+    public static final ConfigKey<Integer> OVERRIDE_RAM = ConfigKeys.newIntegerConfigKey("overrideRam", "Custom ram value");    
+    
+    public static final ConfigKey<String> NETWORK_NAME = ConfigKeys.newStringConfigKey(
+        "networkName", "Network name or ID where the instance should be created (e.g. the subnet ID in AWS");
+
+    /**
+     * CUSTOM_MACHINE_SETUP_SCRIPT_URL accepts a URL location that points to a shell script. 
+     * Please have a look at locations/jclouds/src/main/resources/sample/script/setup-server.sh as an example
+     */
+    public static final ConfigKey<String> CUSTOM_MACHINE_SETUP_SCRIPT_URL = ConfigKeys.newStringConfigKey(
+            "setup.script", "Custom script to customize a node");
+    
+    @SuppressWarnings("serial")
+    public static final ConfigKey<List<String>> CUSTOM_MACHINE_SETUP_SCRIPT_URL_LIST = ConfigKeys.newConfigKey(new TypeToken<List<String>>() {},
+            "setup.scripts", "A list of scripts to customize a node");
+    
+    public static final ConfigKey<String> CUSTOM_MACHINE_SETUP_SCRIPT_VARS = ConfigKeys.newStringConfigKey(
+            "setup.script.vars", "vars to customize a setup.script i.e.: key1:value1,key2:value2");
+    
+    public static final ConfigKey<Boolean> GENERATE_HOSTNAME = ConfigKeys.newBooleanConfigKey(
+            "generate.hostname", "Use the nodename generated by jclouds", false);
+
+    public static final ConfigKey<Boolean> USE_PORT_FORWARDING = ConfigKeys.newBooleanConfigKey(
+            "portforwarding.enabled", 
+            "Whether to setup port-forwarding to subsequently access the VM (over the ssh port)",
+            false);
+    
+    @Beta
+    public static final ConfigKey<Boolean> USE_JCLOUDS_SSH_INIT = ConfigKeys.newBooleanConfigKey(
+            "useJcloudsSshInit", 
+            "Whether to use jclouds for initial ssh-based setup (i.e. as part of the 'TemplateOptions'); "
+                    + "if false will use core brooklyn ssh utilities. "
+                    + "This config is beta; its default could be changed and/or the option removed in an upcoming release.", 
+            true);
+    
+    public static final ConfigKey<JcloudsPortForwarderExtension> PORT_FORWARDER = ConfigKeys.newConfigKey(
+            JcloudsPortForwarderExtension.class, "portforwarding.forwarder", "The port-forwarder to use");
+    
+    public static final ConfigKey<PortForwardManager> PORT_FORWARDING_MANAGER = BrooklynAccessUtils
+            .PORT_FORWARDING_MANAGER;
+
+    public static final ConfigKey<Integer> MACHINE_CREATE_ATTEMPTS = ConfigKeys.newIntegerConfigKey(
+            "machineCreateAttempts", "Number of times to retry if jclouds fails to create a VM", 1);
+
+    public static final ConfigKey<Integer> MAX_CONCURRENT_MACHINE_CREATIONS = ConfigKeys.newIntegerConfigKey(
+            "maxConcurrentMachineCreations", "Maximum number of concurrent machine-creations", Integer.MAX_VALUE);
+
+    public static final ConfigKey<Semaphore> MACHINE_CREATION_SEMAPHORE = ConfigKeys.newConfigKey(
+            Semaphore.class, "machineCreationSemaphore", "Semaphore for controlling concurrent machine creation", null);
+
+    @SuppressWarnings("serial")
+    public static final ConfigKey<Function<Iterable<? extends Image>,Image>> IMAGE_CHOOSER = ConfigKeys.newConfigKey(
+        new TypeToken<Function<Iterable<? extends Image>,Image>>() {},
+        "imageChooser", "An image chooser function to control which images are preferred", 
+        new BrooklynImageChooser().chooser());
+
+    public static final ConfigKey<OsFamily> OS_FAMILY = ConfigKeys.newConfigKey(OsFamily.class, "osFamily", 
+        "OS family, e.g. CentOS, Debian, RHEL, Ubuntu");
+    public static final ConfigKey<String> OS_VERSION_REGEX = ConfigKeys.newStringConfigKey("osVersionRegex", 
+        "Regular expression for the OS version to load");
+
+    public static final ConfigKey<OsFamily> OS_FAMILY_OVERRIDE = ConfigKeys.newConfigKey(OsFamily.class, "osFamilyOverride", 
+            "OS family of VMs (ignores VM metadata from jclouds, and assumes this value)");
+
+    public static final ConfigKey<ComputeServiceRegistry> COMPUTE_SERVICE_REGISTRY = ConfigKeys.newConfigKey(
+            ComputeServiceRegistry.class,
+            "jclouds.computeServiceRegistry",
+            "Registry/Factory for creating jclouds ComputeService; default is almost always fine, except where tests want to customize behaviour",
+            ComputeServiceRegistryImpl.INSTANCE);
+    
+    @SuppressWarnings("serial")
+    public static final ConfigKey<Map<String,Object>> TEMPLATE_OPTIONS = ConfigKeys.newConfigKey(
+            new TypeToken<Map<String, Object>>() {}, "templateOptions", "Additional jclouds template options");
+
+    // TODO
+    
+//  "noDefaultSshKeys" - hints that local ssh keys should not be read as defaults
+    // this would be useful when we need to indicate a password
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java
new file mode 100644
index 0000000..b6cb363
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java
@@ -0,0 +1,105 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.options.TemplateOptions;
+
+import com.google.common.annotations.Beta;
+
+import brooklyn.util.config.ConfigBag;
+
+/**
+ * Customization hooks to allow apps to perform specific customisation at each stage of jclouds machine provisioning.
+ * For example, an app could attach an EBS volume to an EC2 node, or configure a desired availability zone.
+ * <p/>
+ * Instances will be invoked with the {@link ConfigBag} being used to obtain a machine by the
+ * {@link JcloudsLocation }if such a constructor exists. If not, the default no argument constructor
+ * will be invoked.
+ */
+@Beta
+public interface JcloudsLocationCustomizer {
+
+    /**
+     * Override to configure {@link org.jclouds.compute.domain.TemplateBuilder templateBuilder}
+     * before it is built and immutable.
+     */
+    void customize(JcloudsLocation location, ComputeService computeService, TemplateBuilder templateBuilder);
+
+    /**
+     * Override to configure a subclass of this with the built template, or to configure the built
+     * template's {@link org.jclouds.compute.options.TemplateOptions}.
+     * <p/>
+     * This method will be called before {@link #customize(JcloudsLocation, ComputeService, TemplateOptions)}.
+     */
+    void customize(JcloudsLocation location, ComputeService computeService, Template template);
+
+    /**
+     * Override to configure the {@link org.jclouds.compute.options.TemplateOptions} that will
+     * be used by {@link JcloudsLocation} to obtain machines.
+     */
+    void customize(JcloudsLocation location, ComputeService computeService, TemplateOptions templateOptions);
+
+    /**
+     * Override to configure the given machine once it has been created and started by Jclouds.
+     * <p/>
+     * If {@link JcloudsLocationConfig#WAIT_FOR_SSHABLE} is true the
+     * machine is guaranteed to be SSHable when this method is called.
+     * 
+     * @since 0.7.0; use {@link #customize(JcloudsLocation, ComputeService, JcloudsMachineLocation)}
+     */
+    @Deprecated
+    void customize(JcloudsLocation location, ComputeService computeService, JcloudsSshMachineLocation machine);
+    
+    /**
+     * Override to handle machine-related cleanup before Jclouds is called to release (destroy) the machine.
+     * 
+     * @since 0.7.0; use {@link #preRelease(JcloudsMachineLocation)}
+     */
+    @Deprecated
+    void preRelease(JcloudsSshMachineLocation machine);
+
+    /**
+     * Override to handle machine-related cleanup after Jclouds is called to release (destroy) the machine.
+     * 
+     * @since 0.7.0; use {@link #postRelesae(JcloudsMachineLocation)}
+     */
+    @Deprecated
+    void postRelease(JcloudsSshMachineLocation machine);
+
+    /**
+     * Override to configure the given machine once it has been created and started by Jclouds.
+     * <p/>
+     * If {@link JcloudsLocationConfig#WAIT_FOR_SSHABLE} is true the
+     * machine is guaranteed to be SSHable when this method is called.
+     */
+    void customize(JcloudsLocation location, ComputeService computeService, JcloudsMachineLocation machine);
+    
+    /**
+     * Override to handle machine-related cleanup before Jclouds is called to release (destroy) the machine.
+     */
+    void preRelease(JcloudsMachineLocation machine);
+
+    /**
+     * Override to handle machine-related cleanup after Jclouds is called to release (destroy) the machine.
+     */
+    void postRelease(JcloudsMachineLocation machine);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java
new file mode 100644
index 0000000..1ca33c2
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationResolver.java
@@ -0,0 +1,227 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.location.LocationRegistry;
+import org.apache.brooklyn.location.LocationResolver;
+import org.apache.brooklyn.location.LocationSpec;
+import org.apache.brooklyn.location.basic.LocationConfigKeys;
+import org.apache.brooklyn.location.basic.LocationConfigUtils;
+import org.apache.brooklyn.location.basic.LocationInternal;
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.apis.Apis;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.Providers;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.brooklyn.location.basic.BasicLocationRegistry;
+import brooklyn.util.text.Strings;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+@SuppressWarnings("rawtypes")
+public class JcloudsLocationResolver implements LocationResolver {
+
+    public static final Logger log = LoggerFactory.getLogger(JcloudsLocationResolver.class);
+    
+    private static final String JCLOUDS = "jclouds";
+    
+    public static final Map<String,ProviderMetadata> PROVIDERS = getProvidersMap();
+    public static final Map<String,ApiMetadata> APIS = getApisMap();
+    
+    private static Map<String,ProviderMetadata> getProvidersMap() {
+        Map<String,ProviderMetadata> result = Maps.newLinkedHashMap();
+        for (ProviderMetadata p: Providers.all()) {
+            result.put(p.getId(), p);
+        }
+        return ImmutableMap.copyOf(result);
+    }
+
+    private static Map<String,ApiMetadata> getApisMap() {
+        Map<String,ApiMetadata> result = Maps.newLinkedHashMap();
+        for (ApiMetadata api: Apis.all()) {
+            result.put(api.getId(), api);
+        }
+        return ImmutableMap.copyOf(result);
+    }
+
+    public static final Collection<String> AWS_REGIONS = Arrays.asList(
+            // from http://docs.amazonwebservices.com/general/latest/gr/rande.html as of Apr 2012.
+            // it is suggested not to maintain this list here, instead to require aws-ec2 explicitly named.
+            "eu-west-1","us-east-1","us-west-1","us-west-2","ap-southeast-1","ap-northeast-1","sa-east-1");
+         
+    private ManagementContext managementContext;
+
+    @Override
+    public void init(ManagementContext managementContext) {
+        this.managementContext = checkNotNull(managementContext, "managementContext");
+    }
+    
+    protected class JcloudsSpecParser {
+        String providerOrApi;
+        String parameter;
+        
+        public JcloudsSpecParser parse(String spec, boolean dryrun) {
+            JcloudsSpecParser result = new JcloudsSpecParser();
+            int split = spec.indexOf(':');
+            if (split<0) {
+                if (spec.equalsIgnoreCase(getPrefix())) {
+                    if (dryrun) return null;
+                    throw new IllegalArgumentException("Cannot use '"+spec+"' as a location ID; it is insufficient. "+
+                           "Try jclouds:aws-ec2 (for example).");
+                }
+                result.providerOrApi = spec;
+                result.parameter = null;
+            } else {
+                result.providerOrApi = spec.substring(0, split);
+                result.parameter = spec.substring(split+1);
+                int numJcloudsPrefixes = 0;
+                while (result.providerOrApi.equalsIgnoreCase(getPrefix())) {
+                    //strip any number of jclouds: prefixes, for use by static "resolve" method
+                    numJcloudsPrefixes++;
+                    result.providerOrApi = result.parameter;
+                    result.parameter = null;
+                    split = result.providerOrApi.indexOf(':');
+                    if (split>=0) {
+                        result.parameter = result.providerOrApi.substring(split+1);
+                        result.providerOrApi = result.providerOrApi.substring(0, split);
+                    }
+                }
+                if (!dryrun && numJcloudsPrefixes > 1) {
+                    log.warn("Use of deprecated location spec '"+spec+"'; in future use a single \"jclouds\" prefix");
+                }
+            }
+            
+            if (result.parameter==null && AWS_REGIONS.contains(result.providerOrApi)) {
+                // treat amazon as a default
+                result.parameter = result.providerOrApi;
+                result.providerOrApi = "aws-ec2";
+                if (!dryrun)
+                    log.warn("Use of deprecated location '"+result.parameter+"'; in future refer to with explicit " +
+                            "provider '"+result.providerOrApi+":"+result.parameter+"'");
+            }
+            
+            return result;
+        }
+
+        public boolean isProvider() {
+            return PROVIDERS.containsKey(providerOrApi);
+        }
+
+        public boolean isApi() {
+            return APIS.containsKey(providerOrApi);
+        }
+        
+        public String getProviderOrApi() {
+            return providerOrApi;
+        }
+        
+        public String getParameter() {
+            return parameter;
+        }
+    }
+    
+    @Override
+    @SuppressWarnings("unchecked")
+    public JcloudsLocation newLocationFromString(Map locationFlags, String spec, LocationRegistry registry) {
+        Map globalProperties = registry.getProperties();
+
+        JcloudsSpecParser details = new JcloudsSpecParser().parse(spec, false);
+        String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName());
+
+        boolean isProvider = details.isProvider();
+        String providerOrApi = details.providerOrApi;
+        // gce claims to be an api ... perhaps just a bug? email sent to jclouds dev list, 28 mar 2014
+        isProvider = isProvider || "google-compute-engine".equals(providerOrApi);
+        
+        if (Strings.isEmpty(providerOrApi)) {
+            throw new IllegalArgumentException("Cloud provider/API type not specified in spec \""+spec+"\"");
+        }
+        if (!isProvider && !details.isApi()) {
+            throw new NoSuchElementException("Cloud provider/API type "+providerOrApi+" is not supported by jclouds");
+        }
+        
+        // For everything in brooklyn.properties, only use things with correct prefix (and remove that prefix).
+        // But for everything passed in via locationFlags, pass those as-is.
+        // TODO Should revisit the locationFlags: where are these actually used? Reason accepting properties without
+        //      full prefix is that the map's context is explicitly this location, rather than being generic properties.
+        Map allProperties = getAllProperties(registry, globalProperties);
+        String regionOrEndpoint = details.parameter;
+        if (regionOrEndpoint==null && isProvider) regionOrEndpoint = (String)locationFlags.get(LocationConfigKeys.CLOUD_REGION_ID.getName());
+        Map jcloudsProperties = new JcloudsPropertiesFromBrooklynProperties().getJcloudsProperties(providerOrApi, regionOrEndpoint, namedLocation, allProperties);
+        jcloudsProperties.putAll(locationFlags);
+        
+        if (regionOrEndpoint!=null) {
+            // apply the regionOrEndpoint (e.g. from the parameter) as appropriate -- but only if it has not been overridden
+            if (isProvider) {
+                // providers from ServiceLoader take a location (endpoint already configured), and optionally a region name
+                // NB blank might be supplied if spec string is "mycloud:" -- that should be respected, 
+                // whereas no parameter/regionName ie null value -- "mycloud" -- means don't set
+                if (Strings.isBlank(Strings.toString(jcloudsProperties.get(JcloudsLocationConfig.CLOUD_REGION_ID.getName()))))
+                    jcloudsProperties.put(JcloudsLocationConfig.CLOUD_REGION_ID.getName(), regionOrEndpoint);
+            } else {
+                // other "providers" are APIs so take an _endpoint_ (but not a location);
+                // see note above re null here
+                if (Strings.isBlank(Strings.toString(jcloudsProperties.get(JcloudsLocationConfig.CLOUD_ENDPOINT.getName()))))
+                    jcloudsProperties.put(JcloudsLocationConfig.CLOUD_ENDPOINT.getName(), regionOrEndpoint);
+            }
+        }
+        
+        return managementContext.getLocationManager().createLocation(LocationSpec.create(getLocationClass())
+                .configure(LocationConfigUtils.finalAndOriginalSpecs(spec, jcloudsProperties, globalProperties, namedLocation))
+                .configure(jcloudsProperties) );
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map getAllProperties(LocationRegistry registry, Map<?,?> properties) {
+        Map<Object,Object> allProperties = Maps.newHashMap();
+        if (registry!=null) allProperties.putAll(registry.getProperties());
+        allProperties.putAll(properties);
+        return allProperties;
+    }
+    
+    @Override
+    public String getPrefix() {
+        return JCLOUDS;
+    }
+
+    protected Class<? extends JcloudsLocation> getLocationClass() {
+        return JcloudsLocation.class;
+    }
+    
+    @Override
+    public boolean accepts(String spec, LocationRegistry registry) {
+        if (BasicLocationRegistry.isResolverPrefixForSpec(this, spec, true)) return true;
+        JcloudsSpecParser details = new JcloudsSpecParser().parse(spec, true);
+        if (details==null) return false;
+        if (details.isProvider() || details.isApi()) return true;
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineLocation.java
new file mode 100644
index 0000000..2619206
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineLocation.java
@@ -0,0 +1,45 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import org.apache.brooklyn.location.basic.HasSubnetHostname;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+
+import org.apache.brooklyn.location.MachineLocation;
+
+public interface JcloudsMachineLocation extends MachineLocation, HasSubnetHostname {
+    
+    @Override
+    public JcloudsLocation getParent();
+    
+    public NodeMetadata getNode();
+    
+    public Template getTemplate();
+
+    public String getJcloudsId();
+
+    /** In most clouds, the public hostname is the only way to ensure VMs in different zones can access each other. */
+    @Override
+    public String getSubnetHostname();
+
+    String getUser();
+
+    int getPort();
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamer.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamer.java
new file mode 100644
index 0000000..4f4587e
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsMachineNamer.java
@@ -0,0 +1,44 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import org.apache.brooklyn.location.cloud.names.BasicCloudMachineNamer;
+import brooklyn.util.config.ConfigBag;
+
+public class JcloudsMachineNamer extends BasicCloudMachineNamer {
+
+    @Override
+    /** returns the max length of a VM name for the cloud specified in setup;
+     * this value is typically decremented by 9 to make room for jclouds labels */
+    public Integer getCustomMaxNameLength(ConfigBag setup) {
+        // otherwise, for some known clouds which only allow a short name, use that length
+        if ("vcloud".equals( setup.peek(JcloudsLocationConfig.CLOUD_PROVIDER) ))
+            return 24;
+        if ("abiquo".equals( setup.peek(JcloudsLocationConfig.CLOUD_PROVIDER) ))
+            return 39;
+        if ("google-compute-engine".equals( setup.peek(JcloudsLocationConfig.CLOUD_PROVIDER) ))
+            return 39;
+        if ("softlayer".equals( setup.peek(JcloudsLocationConfig.CLOUD_PROVIDER) ))
+            return 55;
+        // TODO other cloud max length rules
+
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPredicates.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPredicates.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPredicates.java
new file mode 100644
index 0000000..565425d
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPredicates.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import org.jclouds.compute.domain.ComputeMetadata;
+import org.jclouds.domain.Location;
+
+import com.google.common.base.Predicate;
+
+public class JcloudsPredicates {
+
+    public static class NodeInLocation implements Predicate<ComputeMetadata> {
+        private String regionId;
+        private boolean matchNullLocations;
+        public NodeInLocation(String regionId, boolean matchNullLocations) {
+            this.regionId = regionId;
+            this.matchNullLocations = matchNullLocations;
+        }
+        @Override
+        public boolean apply(ComputeMetadata input) {
+            boolean exclude;
+            Location nodeLocation = input.getLocation();
+            if (nodeLocation==null) return matchNullLocations;
+            
+            exclude = true;
+            while (nodeLocation!=null && exclude) {
+                if (nodeLocation.getId().equals(regionId)) {
+                    // matching location info found
+                    exclude = false;
+                }
+                nodeLocation = nodeLocation.getParent();
+            }
+            return !exclude;
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynProperties.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynProperties.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynProperties.java
new file mode 100644
index 0000000..f307041
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsPropertiesFromBrooklynProperties.java
@@ -0,0 +1,159 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import java.util.Map;
+
+import org.apache.brooklyn.location.basic.DeprecatedKeysMappingBuilder;
+import org.apache.brooklyn.location.basic.LocationPropertiesFromBrooklynProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigUtils;
+import org.apache.brooklyn.location.basic.LocationConfigKeys;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.javalang.JavaClassNames;
+
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+
+/**
+ * <p>
+ * The properties to use for a jclouds location, loaded from brooklyn.properties file
+ * </p>
+ * 
+ * Preferred format is:
+ * 
+ * <ul>
+ * <li>
+ * brooklyn.location.named.NAME.key 
+ * </li>
+ * <li>
+ * brooklyn.location.jclouds.PROVIDER.key
+ * </li>
+ * </ul>
+ * 
+ * <p>
+ * A number of properties are also supported, listed in the {@code JcloudsLocationConfig}
+ * </p>
+ * 
+ * @author andrea
+ **/
+public class JcloudsPropertiesFromBrooklynProperties extends LocationPropertiesFromBrooklynProperties {
+
+    private static final Logger LOG = LoggerFactory.getLogger(JcloudsPropertiesFromBrooklynProperties.class);
+
+    @SuppressWarnings("deprecation")
+    private static final Map<String, String> DEPRECATED_JCLOUDS_KEYS_MAPPING = new DeprecatedKeysMappingBuilder(LOG)
+            .putAll(LocationPropertiesFromBrooklynProperties.DEPRECATED_KEYS_MAPPING)
+            .camelToHyphen(JcloudsLocation.IMAGE_ID)
+            .camelToHyphen(JcloudsLocation.IMAGE_NAME_REGEX)
+            .camelToHyphen(JcloudsLocation.IMAGE_DESCRIPTION_REGEX)
+            .camelToHyphen(JcloudsLocation.HARDWARE_ID)
+            .build();
+
+    @Override
+    public Map<String, Object> getLocationProperties(String provider, String namedLocation, Map<String, ?> properties) {
+        throw new UnsupportedOperationException("Instead use getJcloudsProperties(String,String,String,Map)");
+    }
+    
+    /**
+     * @see LocationPropertiesFromBrooklynProperties#getLocationProperties(String, String, Map)
+     */
+    public Map<String, Object> getJcloudsProperties(String providerOrApi, String regionOrEndpoint, String namedLocation, Map<String, ?> properties) {
+        if(Strings.isNullOrEmpty(namedLocation) && Strings.isNullOrEmpty(providerOrApi)) {
+            throw new IllegalArgumentException("Neither cloud provider/API nor location name have been specified correctly");
+        }
+
+        ConfigBag jcloudsProperties = ConfigBag.newInstance();
+        String provider = getProviderName(providerOrApi, namedLocation, properties);
+        
+        // named properties are preferred over providerOrApi properties
+        jcloudsProperties.put(LocationConfigKeys.CLOUD_PROVIDER, provider);
+        jcloudsProperties.putAll(transformDeprecated(getGenericLocationSingleWordProperties(properties)));
+        jcloudsProperties.putAll(transformDeprecated(getGenericJcloudsSingleWordProperties(providerOrApi, properties)));
+        jcloudsProperties.putAll(transformDeprecated(getProviderOrApiJcloudsProperties(providerOrApi, properties)));
+        jcloudsProperties.putAll(transformDeprecated(getRegionJcloudsProperties(providerOrApi, regionOrEndpoint, properties)));
+        if (!Strings.isNullOrEmpty(namedLocation)) jcloudsProperties.putAll(transformDeprecated(getNamedJcloudsProperties(namedLocation, properties)));
+        setLocalTempDir(properties, jcloudsProperties);
+
+        return jcloudsProperties.getAllConfigRaw();
+    }
+
+    protected String getProviderName(String providerOrApi, String namedLocationName, Map<String, ?> properties) {
+        String provider = providerOrApi;
+        if (!Strings.isNullOrEmpty(namedLocationName)) {
+            String providerDefinition = (String) properties.get(String.format("brooklyn.location.named.%s", namedLocationName));
+            if (providerDefinition!=null) {
+                String provider2 = getProviderFromDefinition(providerDefinition);
+                if (provider==null) {
+                    // 0.7.0 25 Feb -- is this even needed?
+                    LOG.warn(JavaClassNames.niceClassAndMethod()+" NOT set with provider, inferring from locationName "+namedLocationName+" as "+provider2);
+                    provider = provider2;
+                } else if (!provider.equals(provider2)) {
+                    // 0.7.0 25 Feb -- previously we switched to provider2 in this case, but that was wrong when
+                    // working with chains of names; not sure why this case would ever occur (apart from tests which have been changed)
+                    // 28 Mar seen this warning many times but only in cases when NOT changing is the right behaviour 
+                    LOG.debug(JavaClassNames.niceClassAndMethod()+" NOT changing provider from "+provider+" to candidate "+provider2);
+                }
+            }
+        }
+        return provider;
+    }
+    
+    protected String getProviderFromDefinition(String definition) {
+        return Iterables.get(Splitter.on(":").split(definition), 1);
+    }
+
+    protected Map<String, Object> getGenericJcloudsSingleWordProperties(String providerOrApi, Map<String, ?> properties) {
+        if (Strings.isNullOrEmpty(providerOrApi)) return Maps.newHashMap();
+        String deprecatedPrefix = "brooklyn.jclouds.";
+        String preferredPrefix = "brooklyn.location.jclouds.";
+        return getMatchingSingleWordProperties(preferredPrefix, deprecatedPrefix, properties);
+    }
+
+    protected Map<String, Object> getProviderOrApiJcloudsProperties(String providerOrApi, Map<String, ?> properties) {
+        if (Strings.isNullOrEmpty(providerOrApi)) return Maps.newHashMap();
+        String preferredPrefix = String.format("brooklyn.location.jclouds.%s.", providerOrApi);
+        String deprecatedPrefix = String.format("brooklyn.jclouds.%s.", providerOrApi);
+        
+        return getMatchingProperties(preferredPrefix, deprecatedPrefix, properties);
+    }
+
+    protected Map<String, Object> getRegionJcloudsProperties(String providerOrApi, String regionName, Map<String, ?> properties) {
+        if (Strings.isNullOrEmpty(providerOrApi) || Strings.isNullOrEmpty(regionName)) return Maps.newHashMap();
+        String preferredPrefix = String.format("brooklyn.location.jclouds.%s@%s.", providerOrApi, regionName);
+        String deprecatedPrefix = String.format("brooklyn.jclouds.%s@%s.", providerOrApi, regionName);
+        
+        return getMatchingProperties(preferredPrefix, deprecatedPrefix, properties);
+    }
+
+    protected Map<String, Object> getNamedJcloudsProperties(String locationName, Map<String, ?> properties) {
+        if(locationName == null) return Maps.newHashMap();
+        String prefix = String.format("brooklyn.location.named.%s.", locationName);
+        return ConfigUtils.filterForPrefixAndStrip(properties, prefix).asMapWithStringKeys();
+    }
+
+    @Override
+    protected Map<String, String> getDeprecatedKeysMapping() {
+        return DEPRECATED_JCLOUDS_KEYS_MAPPING;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsSshMachineLocation.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsSshMachineLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsSshMachineLocation.java
new file mode 100644
index 0000000..08fd3b0
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsSshMachineLocation.java
@@ -0,0 +1,338 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import static brooklyn.util.JavaGroovyEquivalents.groovyTruth;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.location.HardwareDetails;
+import org.apache.brooklyn.location.MachineDetails;
+import org.apache.brooklyn.location.OsDetails;
+import org.apache.brooklyn.location.basic.BasicHardwareDetails;
+import org.apache.brooklyn.location.basic.BasicMachineDetails;
+import org.apache.brooklyn.location.basic.BasicOsDetails;
+import org.apache.brooklyn.location.basic.SshMachineLocation;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.callables.RunScriptOnNode;
+import org.jclouds.compute.domain.ExecResponse;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.compute.domain.Processor;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.options.RunScriptOptions;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.scriptbuilder.domain.InterpretableStatement;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.flags.SetFromFlag;
+import brooklyn.util.net.Networking;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.net.HostAndPort;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class JcloudsSshMachineLocation extends SshMachineLocation implements JcloudsMachineLocation {
+    
+    private static final Logger LOG = LoggerFactory.getLogger(JcloudsSshMachineLocation.class);
+    private static final long serialVersionUID = -443866395634771659L;
+
+    @SetFromFlag
+    JcloudsLocation jcloudsParent;
+    
+    @SetFromFlag
+    NodeMetadata node;
+    
+    @SetFromFlag
+    Template template;
+    
+    private RunScriptOnNode.Factory runScriptFactory;
+    
+    public JcloudsSshMachineLocation() {
+    }
+    
+    /**
+     * @deprecated since 0.6; use LocationSpec (which calls no-arg constructor)
+     */
+    @Deprecated
+    public JcloudsSshMachineLocation(Map<?,?> flags, JcloudsLocation jcloudsParent, NodeMetadata node) {
+        super(flags);
+        this.jcloudsParent = jcloudsParent;
+        this.node = node;
+        
+        init();
+    }
+
+    @Override
+    public void init() {
+        if (jcloudsParent != null) {
+            super.init();
+            ComputeServiceContext context = jcloudsParent.getComputeService().getContext();
+            runScriptFactory = context.utils().injector().getInstance(RunScriptOnNode.Factory.class);
+        } else {
+            // TODO Need to fix the rebind-detection, and not call init() on rebind.
+            // This will all change when locations become entities.
+            if (LOG.isDebugEnabled()) LOG.debug("Not doing init() of {} because parent not set; presuming rebinding", this);
+        }
+    }
+    
+    @Override
+    public void rebind() {
+        super.rebind();
+        ComputeServiceContext context = jcloudsParent.getComputeService().getContext();
+        runScriptFactory = context.utils().injector().getInstance(RunScriptOnNode.Factory.class);
+    }
+    
+    @Override
+    public String toVerboseString() {
+        return Objects.toStringHelper(this).omitNullValues()
+                .add("id", getId()).add("name", getDisplayName())
+                .add("user", getUser()).add("address", getAddress()).add("port", getConfig(SSH_PORT))
+                .add("node", getNode())
+                .add("jcloudsId", getJcloudsId())
+                .add("privateAddresses", node.getPrivateAddresses())
+                .add("publicAddresses", node.getPublicAddresses())
+                .add("parentLocation", getParent())
+                .add("osDetails", getOsDetails())
+                .toString();
+    }
+
+    @Override
+    public NodeMetadata getNode() {
+        return node;
+    }
+    
+    @Override
+    public Template getTemplate() {
+        return template;
+    }
+    
+    @Override
+    public JcloudsLocation getParent() {
+        return jcloudsParent;
+    }
+    
+    @Override
+    public String getHostname() {
+        return node.getHostname();
+    }
+    
+    /** In most clouds, the public hostname is the only way to ensure VMs in different zones can access each other. */
+    @Override
+    public Set<String> getPublicAddresses() {
+        return node.getPublicAddresses();
+    }
+    
+    @Override
+    public Set<String> getPrivateAddresses() {
+        return node.getPrivateAddresses();
+    }
+
+    @Override
+    public String getSubnetHostname() {
+        String privateHostname = jcloudsParent.getPrivateHostname(node, Optional.<HostAndPort>absent(), config().getBag());
+        return privateHostname;
+    }
+
+    @Override
+    public String getSubnetIp() {
+        Optional<String> privateAddress = getPrivateAddress();
+        if (privateAddress.isPresent()) {
+            return privateAddress.get();
+        }
+
+        String hostname = jcloudsParent.getPublicHostname(node, Optional.<HostAndPort>absent(), config().getBag());
+        if (hostname != null && !Networking.isValidIp4(hostname)) {
+            try {
+                return InetAddress.getByName(hostname).getHostAddress();
+            } catch (UnknownHostException e) {
+                LOG.debug("Cannot resolve IP for hostname {} of machine {} (so returning hostname): {}", new Object[] {hostname, this, e});
+            }
+        }
+        return hostname;
+    }
+
+    protected Optional<String> getPrivateAddress() {
+        if (groovyTruth(node.getPrivateAddresses())) {
+            for (String p : node.getPrivateAddresses()) {
+                // disallow local only addresses
+                if (Networking.isLocalOnly(p)) continue;
+                // other things may be public or private, but either way, return it
+                return Optional.of(p);
+            }
+        }
+        return Optional.absent();
+    }
+    
+    @Override
+    public String getJcloudsId() {
+        return node.getId();
+    }
+    
+    /** executes the given statements on the server using jclouds ScriptBuilder,
+     * wrapping in a script which is polled periodically.
+     * the output is returned once the script completes (disadvantage compared to other methods)
+     * but the process is nohupped and the SSH session is not kept, 
+     * so very useful for long-running processes
+     */
+    public ListenableFuture<ExecResponse> submitRunScript(String ...statements) {
+        return submitRunScript(new InterpretableStatement(statements));
+    }
+    public ListenableFuture<ExecResponse> submitRunScript(Statement script) {
+        return submitRunScript(script, new RunScriptOptions());            
+    }
+    public ListenableFuture<ExecResponse> submitRunScript(Statement script, RunScriptOptions options) {
+        return runScriptFactory.submit(node, script, options);
+    }
+    /** uses submitRunScript to execute the commands, and throws error if it fails or returns non-zero */
+    public void execRemoteScript(String ...commands) {
+        try {
+            ExecResponse result = submitRunScript(commands).get();
+            if (result.getExitStatus()!=0)
+                throw new IllegalStateException("Error running remote commands (code "+result.getExitStatus()+"): "+commands);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw Throwables.propagate(e);
+        } catch (ExecutionException e) {
+            throw Throwables.propagate(e);
+        }
+    }
+
+    /**
+     * Retrieves the password for this VM, if one exists. The behaviour/implementation is different for different clouds.
+     * e.g. on Rackspace, the password for a windows VM is available immediately; on AWS-EC2, for a Windows VM you need 
+     * to poll repeatedly until the password is available which can take up to 15 minutes.
+     */
+    public String waitForPassword() {
+        // TODO Hacky; don't want aws specific stuff here but what to do?!
+        if (jcloudsParent.getProvider().equals("aws-ec2")) {
+            try {
+                return JcloudsUtil.waitForPasswordOnAws(jcloudsParent.getComputeService(), node, 15, TimeUnit.MINUTES);
+            } catch (TimeoutException e) {
+                throw Throwables.propagate(e);
+            }
+        } else {
+            LoginCredentials credentials = node.getCredentials();
+            return (credentials != null) ? credentials.getPassword() : null;
+        }
+    }
+
+    @Override
+    protected MachineDetails inferMachineDetails() {
+        Optional<String> name = Optional.absent();
+        Optional<String> version = Optional.absent();
+        Optional<String> architecture = Optional.absent();
+
+        OperatingSystem os = node.getOperatingSystem();
+        if (os == null && getTemplate() != null && getTemplate().getImage() != null)
+            // some nodes (eg cloudstack, gce) might not get OS available on the node,
+            // so also try taking it from the template if available
+            os = getTemplate().getImage().getOperatingSystem();
+
+        if (os != null) {
+            // Note using family rather than name. Name is often unset.
+            name = Optional.fromNullable(os.getFamily() != null && !OsFamily.UNRECOGNIZED.equals(os.getFamily()) ? os.getFamily().toString() : null);
+            version = Optional.fromNullable(!Strings.isBlank(os.getVersion()) ? os.getVersion() : null);
+            // Using is64Bit rather then getArch because getArch often returns "paravirtual"
+            architecture = Optional.fromNullable(os.is64Bit() ? BasicOsDetails.OsArchs.X_86_64 : BasicOsDetails.OsArchs.I386);
+        }
+
+        Hardware hardware = node.getHardware();
+        Optional<Integer> ram = hardware==null ? Optional.<Integer>absent() : Optional.fromNullable(hardware.getRam());
+        Optional<Integer> cpus = hardware==null ? Optional.<Integer>absent() : Optional.fromNullable(hardware.getProcessors() != null ? hardware.getProcessors().size() : null);
+
+        // Skip superclass' SSH to machine if all data is present, otherwise defer to super
+        if (name.isPresent() && version.isPresent() && architecture.isPresent() && ram.isPresent() && cpus.isPresent()) {
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("Gathered machine details from Jclouds, skipping SSH test on {}", this);
+            }
+            OsDetails osD = new BasicOsDetails(name.get(), architecture.get(), version.get());
+            HardwareDetails hwD = new BasicHardwareDetails(cpus.get(), ram.get());
+            return new BasicMachineDetails(hwD, osD);
+        } else if ("false".equalsIgnoreCase(getConfig(JcloudsLocation.WAIT_FOR_SSHABLE))) {
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("Machine details for {} missing from Jclouds, but skipping SSH test because waitForSshable=false. name={}, version={}, " +
+                        "arch={}, ram={}, #cpus={}",
+                        new Object[]{this, name, version, architecture, ram, cpus});
+            }
+            OsDetails osD = new BasicOsDetails(name.orNull(), architecture.orNull(), version.orNull());
+            HardwareDetails hwD = new BasicHardwareDetails(cpus.orNull(), ram.orNull());
+            return new BasicMachineDetails(hwD, osD);
+        } else {
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("Machine details for {} missing from Jclouds, using SSH test instead. name={}, version={}, " +
+                                "arch={}, ram={}, #cpus={}",
+                        new Object[]{this, name, version, architecture, ram, cpus});
+            }
+            return super.inferMachineDetails();
+        }
+    }
+
+    @Override
+    public Map<String, String> toMetadataRecord() {
+        Hardware hardware = node.getHardware();
+        List<? extends Processor> processors = (hardware != null) ? hardware.getProcessors() : null;
+        
+        ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
+        builder.putAll(super.toMetadataRecord());
+        putIfNotNull(builder, "provider", getParent().getProvider());
+        putIfNotNull(builder, "account", getParent().getIdentity());
+        putIfNotNull(builder, "serverId", node.getProviderId());
+        putIfNotNull(builder, "imageId", node.getImageId());
+        putIfNotNull(builder, "instanceTypeName", (hardware != null ? hardware.getName() : null));
+        putIfNotNull(builder, "instanceTypeId", (hardware != null ? hardware.getProviderId() : null));
+        putIfNotNull(builder, "ram", "" + (hardware != null ? hardware.getRam() : null));
+        putIfNotNull(builder, "cpus", "" + (processors != null ? processors.size() : null));
+        
+        try {
+            OsDetails osDetails = getOsDetails();
+            putIfNotNull(builder, "osName", osDetails.getName());
+            putIfNotNull(builder, "osArch", osDetails.getArch());
+            putIfNotNull(builder, "is64bit", osDetails.is64bit() ? "true" : "false");
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            LOG.warn("Unable to get OS Details for "+node+"; continuing", e);
+        }
+        
+        return builder.build();
+    }
+    
+    private void putIfNotNull(ImmutableMap.Builder<String, String> builder, String key, @Nullable String value) {
+        if (value != null) builder.put(key, value);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsUtil.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsUtil.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsUtil.java
new file mode 100644
index 0000000..e3026df
--- /dev/null
+++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsUtil.java
@@ -0,0 +1,448 @@
+/*
+ * 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 org.apache.brooklyn.location.jclouds;
+
+import static org.jclouds.compute.options.RunScriptOptions.Builder.overrideLoginCredentials;
+import static org.jclouds.compute.util.ComputeServiceUtils.execHttpResponse;
+import static org.jclouds.scriptbuilder.domain.Statements.appendFile;
+import static org.jclouds.scriptbuilder.domain.Statements.exec;
+import static org.jclouds.scriptbuilder.domain.Statements.interpret;
+import static org.jclouds.scriptbuilder.domain.Statements.newStatementList;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.annotation.Nullable;
+
+import org.jclouds.Constants;
+import org.jclouds.ContextBuilder;
+import org.jclouds.aws.ec2.AWSEC2Api;
+import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.RunScriptOnNodesException;
+import org.jclouds.compute.domain.ExecResponse;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.OperatingSystem;
+import org.jclouds.compute.options.RunScriptOptions;
+import org.jclouds.compute.predicates.OperatingSystemPredicates;
+import org.jclouds.docker.DockerApi;
+import org.jclouds.docker.domain.Container;
+import org.jclouds.domain.LoginCredentials;
+import org.jclouds.ec2.compute.domain.PasswordDataAndPrivateKey;
+import org.jclouds.ec2.compute.functions.WindowsLoginCredentialsFromEncryptedData;
+import org.jclouds.ec2.domain.PasswordData;
+import org.jclouds.ec2.features.WindowsApi;
+import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule;
+import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.jclouds.scriptbuilder.domain.Statements;
+import org.jclouds.ssh.SshClient;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.jclouds.util.Predicates2;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+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.io.Files;
+import com.google.inject.Module;
+
+import brooklyn.entity.basic.Sanitizer;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.exceptions.Exceptions;
+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;
+
+public class JcloudsUtil implements JcloudsLocationConfig {
+
+    // TODO Review what utility methods are needed, and what is now supported in jclouds 1.1
+
+    private static final Logger LOG = LoggerFactory.getLogger(JcloudsUtil.class);
+
+    /**
+     * @deprecated since 0.7; see {@link BashCommands}
+     */
+    @Deprecated
+    public static String APT_INSTALL = "apt-get install -f -y -qq --force-yes";
+
+    /**
+     * @deprecated since 0.7; see {@link BashCommands}
+     */
+    @Deprecated
+    public static String installAfterUpdatingIfNotPresent(String cmd) {
+       String aptInstallCmd = APT_INSTALL + " " + cmd;
+       return String.format("which %s || (%s || (apt-get update && %s))", cmd, aptInstallCmd, aptInstallCmd);
+    }
+
+    /**
+     * @deprecated since 0.7
+     */
+    @Deprecated
+    public static Predicate<NodeMetadata> predicateMatchingById(final NodeMetadata node) {
+        return predicateMatchingById(node.getId());
+    }
+
+    /**
+     * @deprecated since 0.7
+     */
+    @Deprecated
+    public static Predicate<NodeMetadata> predicateMatchingById(final String id) {
+        Predicate<NodeMetadata> nodePredicate = new Predicate<NodeMetadata>() {
+            @Override public boolean apply(NodeMetadata arg0) {
+                return id.equals(arg0.getId());
+            }
+            @Override public String toString() {
+                return "node.id=="+id;
+            }
+        };
+        return nodePredicate;
+    }
+
+    /**
+     * @deprecated since 0.7; see {@link IptablesCommands}
+     */
+    @Deprecated
+    public static Statement authorizePortInIpTables(int port) {
+        // TODO gogrid rules only allow ports 22, 3389, 80 and 443.
+        // the first rule will be ignored, so we have to apply this
+        // directly
+        return Statements.newStatementList(// just in case iptables are being used, try to open 8080
+                exec("iptables -I INPUT 1 -p tcp --dport " + port + " -j ACCEPT"),//
+                exec("iptables -I RH-Firewall-1-INPUT 1 -p tcp --dport " + port + " -j ACCEPT"),//
+                exec("iptables-save"));
+    }
+
+    /**
+     * @throws RunScriptOnNodesException
+     * @throws IllegalStateException     If do not find exactly one matching node
+     *
+     * @deprecated since 0.7
+     */
+    @Deprecated
+    public static ExecResponse runScriptOnNode(ComputeService computeService, NodeMetadata node, Statement statement, String scriptName) throws RunScriptOnNodesException {
+        // TODO Includes workaround for NodeMetadata's equals/hashcode method being wrong.
+
+        Map<? extends NodeMetadata, ExecResponse> scriptResults = computeService.runScriptOnNodesMatching(
+                JcloudsUtil.predicateMatchingById(node),
+                statement,
+                new RunScriptOptions().nameTask(scriptName));
+        if (scriptResults.isEmpty()) {
+            throw new IllegalStateException("No matching node found when executing script "+scriptName+": expected="+node);
+        } else if (scriptResults.size() > 1) {
+            throw new IllegalStateException("Multiple nodes matched predicate: id="+node.getId()+"; expected="+node+"; actual="+scriptResults.keySet());
+        } else {
+            return Iterables.getOnlyElement(scriptResults.values());
+        }
+    }
+
+    /**
+     * @deprecated since 0.7; {@see #installJavaAndCurl(OperatingSystem)}
+     */
+    @Deprecated
+    public static final Statement APT_RUN_SCRIPT = newStatementList(//
+          exec(installAfterUpdatingIfNotPresent("curl")),//
+          exec("(which java && java -fullversion 2>&1|egrep -q 1.6 ) ||"),//
+          execHttpResponse(URI.create("http://whirr.s3.amazonaws.com/0.2.0-incubating-SNAPSHOT/sun/java/install")),//
+          exec(new StringBuilder()//
+                .append("echo nameserver 208.67.222.222 >> /etc/resolv.conf\n")//
+                // jeos hasn't enough room!
+                .append("rm -rf /var/cache/apt /usr/lib/vmware-tools\n")//
+                .append("echo \"export PATH=\\\"$JAVA_HOME/bin/:$PATH\\\"\" >> /root/.bashrc")//
+                .toString()));
+
+    /**
+     * @deprecated since 0.7; {@see #installJavaAndCurl(OperatingSystem)}
+     */
+    @Deprecated
+    public static final Statement YUM_RUN_SCRIPT = newStatementList(
+          exec("which curl ||yum --nogpgcheck -y install curl"),//
+          exec("(which java && java -fullversion 2>&1|egrep -q 1.6 ) ||"),//
+          execHttpResponse(URI.create("http://whirr.s3.amazonaws.com/0.2.0-incubating-SNAPSHOT/sun/java/install")),//
+          exec(new StringBuilder()//
+                .append("echo nameserver 208.67.222.222 >> /etc/resolv.conf\n") //
+                .append("echo \"export PATH=\\\"$JAVA_HOME/bin/:$PATH\\\"\" >> /root/.bashrc")//
+                .toString()));
+
+    /**
+     * @deprecated since 0.7; {@see #installJavaAndCurl(OperatingSystem)}
+     */
+    @Deprecated
+    public static final Statement ZYPPER_RUN_SCRIPT = exec(new StringBuilder()//
+          .append("echo nameserver 208.67.222.222 >> /etc/resolv.conf\n")//
+          .append("which curl || zypper install curl\n")//
+          .append("(which java && java -fullversion 2>&1|egrep -q 1.6 ) || zypper install java-1.6.0-openjdk\n")//
+          .toString());
+
+    // Code taken from RunScriptData
+    /**
+     * @deprecated since 0.7; see {@link BashCommands#installJava7()} and {@link BashCommands#INSTALL_CURL}
+     */
+    @Deprecated
+    public static Statement installJavaAndCurl(OperatingSystem os) {
+       if (os == null || OperatingSystemPredicates.supportsApt().apply(os))
+          return APT_RUN_SCRIPT;
+       else if (OperatingSystemPredicates.supportsYum().apply(os))
+          return YUM_RUN_SCRIPT;
+       else if (OperatingSystemPredicates.supportsZypper().apply(os))
+          return ZYPPER_RUN_SCRIPT;
+       else
+          throw new IllegalArgumentException("don't know how to handle" + os.toString());
+    }
+
+    /**
+     * @deprecated since 0.7; see {@link ComputeServiceRegistry#findComputeService(ConfigBag, boolean)}
+     */
+    @Deprecated
+    public static ComputeService findComputeService(ConfigBag conf) {
+        return ComputeServiceRegistryImpl.INSTANCE.findComputeService(conf, true);
+    }
+
+    /**
+     * @deprecated since 0.7; see {@link ComputeServiceRegistry#findComputeService(ConfigBag, boolean)}
+     */
+    @Deprecated
+    public static ComputeService findComputeService(ConfigBag conf, boolean allowReuse) {
+        return ComputeServiceRegistryImpl.INSTANCE.findComputeService(conf, allowReuse);
+    }
+
+    /**
+     * Returns the jclouds modules we typically install
+     *
+     * @deprecated since 0.7; see {@link ComputeServiceRegistry}
+     */
+    @Deprecated
+    public static ImmutableSet<Module> getCommonModules() {
+        return ImmutableSet.<Module> of(
+                new SshjSshClientModule(),
+                new SLF4JLoggingModule(),
+                new BouncyCastleCryptoModule());
+    }
+
+    /**
+     *  Temporary constructor to address https://issues.apache.org/jira/browse/JCLOUDS-615.
+     *  <p>
+     *  See https://issues.apache.org/jira/browse/BROOKLYN-6 .
+     *  When https://issues.apache.org/jira/browse/JCLOUDS-615 is fixed in the jclouds we use,
+     *  we can remove the useSoftlayerFix argument.
+     *  <p>
+     *  (Marked Beta as that argument will likely be removed.)
+     *
+     *  @since 0.7.0 */
+    @Beta
+    public static BlobStoreContext newBlobstoreContext(String provider, @Nullable String endpoint, String identity, String credential) {
+        Properties overrides = new Properties();
+        // * Java 7,8 bug workaround - sockets closed by GC break the internal bookkeeping
+        //   of HttpUrlConnection, leading to invalid handling of the "HTTP/1.1 100 Continue"
+        //   response. Coupled with a bug when using SSL sockets reads will block
+        //   indefinitely even though a read timeout is explicitly set.
+        // * Java 6 ignores the header anyways as it is included in its restricted headers black list.
+        // * Also there's a bug in SL object store which still expects Content-Length bytes
+        //   even when it responds with a 408 timeout response, leading to incorrectly
+        //   interpreting the next request (triggered by above problem).
+        overrides.setProperty(Constants.PROPERTY_STRIP_EXPECT_HEADER, "true");
+
+        ContextBuilder contextBuilder = ContextBuilder.newBuilder(provider).credentials(identity, credential);
+        contextBuilder.modules(MutableList.copyOf(JcloudsUtil.getCommonModules()));
+        if (!brooklyn.util.text.Strings.isBlank(endpoint)) {
+            contextBuilder.endpoint(endpoint);
+        }
+        contextBuilder.overrides(overrides);
+        BlobStoreContext context = contextBuilder.buildView(BlobStoreContext.class);
+        return context;
+    }
+
+    /**
+     * @deprecated since 0.7
+     */
+    @Deprecated
+    protected static String getDeprecatedProperty(ConfigBag conf, String key) {
+        if (conf.containsKey(key)) {
+            LOG.warn("Jclouds using deprecated brooklyn-jclouds property "+key+": "+Sanitizer.sanitize(conf.getAllConfig()));
+            return (String) conf.getStringKey(key);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @deprecated since 0.7
+     */
+    @Deprecated
+    // Do this so that if there's a problem with our USERNAME's ssh key, we can still get in to check
+    // TODO Once we're really confident there are not going to be regular problems, then delete this
+    public static Statement addAuthorizedKeysToRoot(File publicKeyFile) throws IOException {
+        String publicKey = Files.toString(publicKeyFile, Charsets.UTF_8);
+        return addAuthorizedKeysToRoot(publicKey);
+    }
+
+    /**
+     * @deprecated since 0.7
+     */
+    @Deprecated
+    public static Statement addAuthorizedKeysToRoot(String publicKey) {
+        return newStatementList(
+                appendFile("/root/.ssh/authorized_keys", Splitter.on('\n').split(publicKey)),
+                interpret("chmod 600 /root/.ssh/authorized_keys"));
+    }
+
+    public static String getFirstReachableAddress(ComputeServiceContext context, NodeMetadata node) {
+        // To pick the address, it relies on jclouds `sshForNode().apply(Node)` to check all IPs of node (private+public),
+        // to find one that is reachable. It does `openSocketFinder.findOpenSocketOnNode(node, node.getLoginPort(), ...)`.
+        // This keeps trying for time org.jclouds.compute.reference.ComputeServiceConstants.Timeouts.portOpen.
+        // TODO Want to configure this timeout here.
+        //
+        // TODO We could perhaps instead just set `templateOptions.blockOnPort(loginPort, 120)`, but need
+        // to be careful to only set that if config WAIT_FOR_SSHABLE is true. For some advanced networking examples
+        // (e.g. using DNAT on CloudStack), the brooklyn machine won't be able to reach the VM until some additional
+        // setup steps have been done. See links from Andrea:
+        //     https://github.com/jclouds/jclouds/pull/895
+        //     https://issues.apache.org/jira/browse/WHIRR-420
+        //     jclouds.ssh.max-retries
+        //     jclouds.ssh.retry-auth
+
+        SshClient client;
+        try {
+            client = context.utils().sshForNode().apply(node);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            /* i've seen: java.lang.IllegalStateException: Optional.get() cannot be called on an absent value
+             * from org.jclouds.crypto.ASN1Codec.createASN1Sequence(ASN1Codec.java:86), if the ssh key has a passphrase, against AWS.
+             *
+             * others have reported: java.lang.IllegalArgumentException: DER length more than 4 bytes
+             * when using a key with a passphrase (perhaps from other clouds?); not sure if that's this callpath or a different one.
+             */
+            throw new IllegalStateException("Unable to connect SshClient to "+node+"; check that the node is accessible and that the SSH key exists and is correctly configured, including any passphrase defined", e);
+        }
+        return client.getHostAddress();
+    }
+
+    // Suggest at least 15 minutes for timeout
+    public static String waitForPasswordOnAws(ComputeService computeService, final NodeMetadata node, long timeout, TimeUnit timeUnit) throws TimeoutException {
+        ComputeServiceContext computeServiceContext = computeService.getContext();
+        AWSEC2Api ec2Client = computeServiceContext.unwrapApi(AWSEC2Api.class);
+        final WindowsApi client = ec2Client.getWindowsApi().get();
+        final String region = node.getLocation().getParent().getId();
+
+        // The Administrator password will take some time before it is ready - Amazon says sometimes 15 minutes.
+        // So we create a predicate that tests if the password is ready, and wrap it in a retryable predicate.
+        Predicate<String> passwordReady = new Predicate<String>() {
+            @Override public boolean apply(String s) {
+                if (Strings.isNullOrEmpty(s)) return false;
+                PasswordData data = client.getPasswordDataInRegion(region, s);
+                if (data == null) return false;
+                return !Strings.isNullOrEmpty(data.getPasswordData());
+            }
+        };
+
+        LOG.info("Waiting for password, for "+node.getProviderId()+":"+node.getId());
+        Predicate<String> passwordReadyRetryable = Predicates2.retry(passwordReady, timeUnit.toMillis(timeout), 10*1000, TimeUnit.MILLISECONDS);
+        boolean ready = passwordReadyRetryable.apply(node.getProviderId());
+        if (!ready) throw new TimeoutException("Password not available for "+node+" in region "+region+" after "+timeout+" "+timeUnit.name());
+
+        // Now pull together Amazon's encrypted password blob, and the private key that jclouds generated
+        PasswordDataAndPrivateKey dataAndKey = new PasswordDataAndPrivateKey(
+                client.getPasswordDataInRegion(region, node.getProviderId()),
+                node.getCredentials().getPrivateKey());
+
+        // And apply it to the decryption function
+        WindowsLoginCredentialsFromEncryptedData f = computeServiceContext.utils().injector().getInstance(WindowsLoginCredentialsFromEncryptedData.class);
+        LoginCredentials credentials = f.apply(dataAndKey);
+
+        return credentials.getPassword();
+    }
+
+    public static Map<Integer, Integer> dockerPortMappingsFor(JcloudsLocation docker, String containerId) {
+        ComputeServiceContext context = null;
+        try {
+            Properties properties = new Properties();
+            properties.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, Boolean.toString(true));
+            properties.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, Boolean.toString(true));
+            context = ContextBuilder.newBuilder("docker")
+                    .endpoint(docker.getEndpoint())
+                    .credentials(docker.getIdentity(), docker.getCredential())
+                    .overrides(properties)
+                    .modules(ImmutableSet.<Module>of(new SLF4JLoggingModule(), new SshjSshClientModule()))
+                    .build(ComputeServiceContext.class);
+            DockerApi api = context.unwrapApi(DockerApi.class);
+            Container container = api.getContainerApi().inspectContainer(containerId);
+            Map<Integer, Integer> portMappings = Maps.newLinkedHashMap();
+            Map<String, List<Map<String, String>>> ports = container.networkSettings().ports();
+            if (ports == null) ports = ImmutableMap.<String, List<Map<String,String>>>of();
+
+            LOG.debug("Docker will forward these ports {}", ports);
+            for (Map.Entry<String, List<Map<String, String>>> entrySet : ports.entrySet()) {
+                String containerPort = Iterables.get(Splitter.on("/").split(entrySet.getKey()), 0);
+                String hostPort = Iterables.getOnlyElement(Iterables.transform(entrySet.getValue(),
+                        new Function<Map<String, String>, String>() {
+                            @Override
+                            public String apply(Map<String, String> hostIpAndPort) {
+                                return hostIpAndPort.get("HostPort");
+                            }
+                        }));
+                portMappings.put(Integer.parseInt(containerPort), Integer.parseInt(hostPort));
+            }
+            return portMappings;
+        } finally {
+            if (context != null) {
+                context.close();
+            }
+        }
+    }
+
+    /**
+     * @deprecated since 0.7
+     */
+    @Deprecated
+    public static void mapSecurityGroupRuleToIpTables(ComputeService computeService, NodeMetadata node,
+            LoginCredentials credentials, String networkInterface, Iterable<Integer> ports) {
+        for (Integer port : ports) {
+            String insertIptableRule = IptablesCommands.insertIptablesRule(Chain.INPUT, networkInterface,
+                    Protocol.TCP, port, Policy.ACCEPT);
+            Statement statement = Statements.newStatementList(exec(insertIptableRule));
+            ExecResponse response = computeService.runScriptOnNode(node.getId(), statement,
+                    overrideLoginCredentials(credentials).runAsRoot(false));
+            if (response.getExitStatus() != 0) {
+                String msg = String.format("Cannot insert the iptables rule for port %d. Error: %s", port,
+                        response.getError());
+                LOG.error(msg);
+                throw new RuntimeException(msg);
+            }
+        }
+    }
+
+}