brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hadr...@apache.org
Subject [34/54] incubator-brooklyn git commit: [BROOKLYN-162] Renaming package brooklyn.location
Date Fri, 14 Aug 2015 03:43:03 GMT
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/main/java/org/apache/brooklyn/location/basic/ByonLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/ByonLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/basic/ByonLocationResolver.java
new file mode 100644
index 0000000..a93d3dd
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/ByonLocationResolver.java
@@ -0,0 +1,251 @@
+/*
+ * 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.basic;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.location.Location;
+import org.apache.brooklyn.location.LocationRegistry;
+import org.apache.brooklyn.location.LocationSpec;
+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;
+import org.apache.brooklyn.location.MachineLocation;
+import brooklyn.management.internal.LocalLocationManager;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.flags.TypeCoercions;
+import brooklyn.util.net.UserAndHostAndPort;
+import brooklyn.util.text.WildcardGlobs;
+import brooklyn.util.text.WildcardGlobs.PhraseTreatment;
+
+/**
+ * Examples of valid specs:
+ *   <ul>
+ *     <li>byon:(hosts=myhost)
+ *     <li>byon:(hosts=myhost,myhost2)
+ *     <li>byon:(hosts="myhost, myhost2")
+ *     <li>byon:(hosts=myhost,myhost2, name=abc)
+ *     <li>byon:(hosts="myhost, myhost2", name="my location name")
+ *   </ul>
+ * 
+ * @author aled
+ */
+@SuppressWarnings({"rawtypes"})
+public class ByonLocationResolver extends AbstractLocationResolver {
+
+    public static final Logger log = LoggerFactory.getLogger(ByonLocationResolver.class);
+    
+    public static final String BYON = "byon";
+
+    public static final ConfigKey<String> OS_FAMILY = ConfigKeys.newStringConfigKey("osfamily", "OS Family of the machine, either windows or linux", "linux");
+
+    public static final Map<String, Class<? extends MachineLocation>> OS_TO_MACHINE_LOCATION_TYPE = ImmutableMap.<String, Class<? extends MachineLocation>>of(
+            "windows", WinRmMachineLocation.class,
+            "linux", SshMachineLocation.class);
+
+    @Override
+    public String getPrefix() {
+        return BYON;
+    }
+
+    @Override
+    protected Class<? extends Location> getLocationType() {
+        return FixedListMachineProvisioningLocation.class;
+    }
+
+    @Override
+    protected SpecParser getSpecParser() {
+        return new AbstractLocationResolver.SpecParser(getPrefix()).setExampleUsage("\"byon(hosts='addr1,addr2')\"");
+    }
+
+    @Override
+    protected ConfigBag extractConfig(Map<?,?> locationFlags, String spec, LocationRegistry registry) {
+        ConfigBag config = super.extractConfig(locationFlags, spec, registry);
+
+        Object hosts = config.getStringKey("hosts");
+        config.remove("hosts");
+        String user = (String) config.getStringKey("user");
+        Integer port = (Integer) TypeCoercions.coerce(config.getStringKey("port"), Integer.class);
+        Class<? extends MachineLocation> locationClass = OS_TO_MACHINE_LOCATION_TYPE.get(config.get(OS_FAMILY));
+
+        MutableMap<String, Object> defaultProps = MutableMap.of();
+        defaultProps.addIfNotNull("user", user);
+        defaultProps.addIfNotNull("port", port);
+
+        List<String> hostAddresses;
+        
+        if (hosts instanceof String) {
+            if (((String) hosts).isEmpty()) {
+                hostAddresses = ImmutableList.of();
+            } else {
+                hostAddresses = WildcardGlobs.getGlobsAfterBraceExpansion("{"+hosts+"}",
+                        true /* numeric */, /* no quote support though */ PhraseTreatment.NOT_A_SPECIAL_CHAR, PhraseTreatment.NOT_A_SPECIAL_CHAR);
+            }
+        } else if (hosts instanceof Iterable) {
+            hostAddresses = ImmutableList.copyOf((Iterable<String>)hosts);
+        } else {
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; at least one host must be defined");
+        }
+        if (hostAddresses.isEmpty()) {
+            throw new IllegalArgumentException("Invalid location '"+spec+"'; at least one host must be defined");
+        }
+        
+        List<MachineLocation> machines = Lists.newArrayList();
+        for (Object host : hostAddresses) {
+            LocationSpec<? extends MachineLocation> machineSpec;
+            if (host instanceof String) {
+                machineSpec = parseMachine((String)host, locationClass, defaultProps, spec);
+            } else if (host instanceof Map) {
+                machineSpec = parseMachine((Map<String, ?>)host, locationClass, defaultProps, spec);
+            } else {
+                throw new IllegalArgumentException("Expected machine to be String or Map, but was "+host.getClass().getName()+" ("+host+")");
+            }
+            machineSpec.configureIfNotNull(LocalLocationManager.CREATE_UNMANAGED, config.get(LocalLocationManager.CREATE_UNMANAGED));
+            MachineLocation machine = managementContext.getLocationManager().createLocation(machineSpec);
+            machines.add(machine);
+        }
+        
+        config.putStringKey("machines", machines);
+
+        return config;
+    }
+    
+    protected LocationSpec<? extends MachineLocation> parseMachine(Map<String, ?> vals, Class<? extends MachineLocation> locationClass, Map<String, ?> defaults, String specForErrMsg) {
+        Map<String, Object> valSanitized = Sanitizer.sanitize(vals);
+        Map<String, Object> machineConfig = MutableMap.copyOf(vals);
+        
+        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, 22);
+        } else {
+            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();
+        }
+        
+        machineConfig.put("address", host);
+        try {
+            InetAddress.getByName(host);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Invalid host '"+host+"' specified in '"+specForErrMsg+"': "+e);
+        }
+
+        if (userAndHostAndPort.getUser() != null) {
+            checkArgument(!vals.containsKey("user"), "Must not specify user twice for machine: %s", valSanitized);
+            machineConfig.put("user", userAndHostAndPort.getUser());
+        }
+        if (userAndHostAndPort.getHostAndPort().hasPort()) {
+            checkArgument(!vals.containsKey("port"), "Must not specify port twice for machine: %s", valSanitized);
+            machineConfig.put("port", port);
+        }
+        for (Map.Entry<String, ?> entry : defaults.entrySet()) {
+            if (!machineConfig.containsKey(entry.getKey())) {
+                machineConfig.put(entry.getKey(), entry.getValue());
+            }
+        }
+        
+        Class<? extends MachineLocation> locationClassHere = locationClass;
+        if (osfamily != null) {
+            locationClassHere = OS_TO_MACHINE_LOCATION_TYPE.get(osfamily);
+        }
+
+        return LocationSpec.create(locationClassHere).configure(machineConfig);
+    }
+
+    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();
+        machineConfig.put("address", host);
+        try {
+            InetAddress.getByName(host.trim());
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Invalid host '"+host+"' specified in '"+specForErrMsg+"': "+e);
+        }
+        
+        if (userAndHostAndPort.getUser() != null) {
+            machineConfig.put("user", userAndHostAndPort.getUser());
+        }
+        if (userAndHostAndPort.getHostAndPort().hasPort()) {
+            machineConfig.put("port", userAndHostAndPort.getHostAndPort().getPort());
+        }
+        for (Map.Entry<String, ?> entry : defaults.entrySet()) {
+            if (!machineConfig.containsKey(entry.getKey())) {
+                machineConfig.put(entry.getKey(), entry.getValue());
+            }
+        }
+
+        return LocationSpec.create(locationClass).configure(machineConfig);
+    }
+    
+    private UserAndHostAndPort parseUserAndHostAndPort(String val) {
+        String userPart = null;
+        String hostPart = val;
+        if (val.contains("@")) {
+            userPart = val.substring(0, val.indexOf("@"));
+            hostPart = val.substring(val.indexOf("@")+1);
+        }
+        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/e2c57058/core/src/main/java/org/apache/brooklyn/location/basic/CatalogLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/CatalogLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/basic/CatalogLocationResolver.java
new file mode 100644
index 0000000..7d55d57
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/CatalogLocationResolver.java
@@ -0,0 +1,80 @@
+/*
+ * 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.basic;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+import org.apache.brooklyn.location.Location;
+import org.apache.brooklyn.location.LocationRegistry;
+import org.apache.brooklyn.location.LocationResolver;
+import org.apache.brooklyn.location.LocationSpec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.management.ManagementContext;
+
+import brooklyn.catalog.internal.CatalogUtils;
+
+/**
+ * Given a location spec in the form {@code brooklyn.catalog:<symbolicName>:<version>}, 
+ * looks up the catalog to get its definition and creates such a location.
+ */
+public class CatalogLocationResolver implements LocationResolver {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(CatalogLocationResolver.class);
+
+    public static final String NAME = "brooklyn.catalog";
+
+    private ManagementContext managementContext;
+
+    @Override
+    public void init(ManagementContext managementContext) {
+        this.managementContext = checkNotNull(managementContext, "managementContext");
+    }
+    
+    @Override
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public Location newLocationFromString(Map locationFlags, String spec, LocationRegistry registry) {
+        String id = spec.substring(NAME.length()+1);
+        CatalogItem<?, ?> item = CatalogUtils.getCatalogItemOptionalVersion(managementContext, id);
+        LocationSpec<?> origLocSpec = managementContext.getCatalog().createSpec((CatalogItem<Location, LocationSpec<?>>)item);
+        LocationSpec<?> locSpec = LocationSpec.create(origLocSpec)
+                .configure(locationFlags);
+        return managementContext.getLocationManager().createLocation(locSpec);
+    }
+
+    @Override
+    public String getPrefix() {
+        return NAME;
+    }
+    
+    /**
+     * accepts anything that looks like it will be a YAML catalog item (e.g. starting "brooklyn.locations")
+     */
+    @Override
+    public boolean accepts(String spec, LocationRegistry registry) {
+        if (BasicLocationRegistry.isResolverPrefixForSpec(this, spec, false)) return true;
+        if (registry.getDefinedLocationByName(spec)!=null) return true;
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/main/java/org/apache/brooklyn/location/basic/DefinedLocationByIdResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/DefinedLocationByIdResolver.java b/core/src/main/java/org/apache/brooklyn/location/basic/DefinedLocationByIdResolver.java
new file mode 100644
index 0000000..7277fc9
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/DefinedLocationByIdResolver.java
@@ -0,0 +1,74 @@
+/*
+ * 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.basic;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.management.ManagementContext;
+import org.apache.brooklyn.location.Location;
+import org.apache.brooklyn.location.LocationDefinition;
+import org.apache.brooklyn.location.LocationRegistry;
+import org.apache.brooklyn.location.LocationResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * looks up based on ID in DefinedLocations map
+ */
+public class DefinedLocationByIdResolver implements LocationResolver {
+
+    public static final Logger log = LoggerFactory.getLogger(DefinedLocationByIdResolver.class);
+
+    public static final String ID = "id";
+    
+    private volatile ManagementContext managementContext;
+
+    @Override
+    public void init(ManagementContext managementContext) {
+        this.managementContext = checkNotNull(managementContext, "managementContext");
+    }
+    
+    @SuppressWarnings({ "rawtypes" })
+    @Override
+    public Location newLocationFromString(Map locationFlags, String spec, LocationRegistry registry) {
+        String id = spec;
+        if (spec.toLowerCase().startsWith(ID+":")) {
+            id = spec.substring( (ID+":").length() );
+        }
+        LocationDefinition ld = registry.getDefinedLocationById(id);
+        ld.getSpec();
+        return ((BasicLocationRegistry)registry).resolveLocationDefinition(ld, locationFlags, null);
+    }
+
+    @Override
+    public String getPrefix() {
+        return ID;
+    }
+    
+    /** accepts anything starting  id:xxx  or just   xxx where xxx is a defined location ID */
+    @Override
+    public boolean accepts(String spec, LocationRegistry registry) {
+        if (BasicLocationRegistry.isResolverPrefixForSpec(this, spec, false)) return true;
+        if (registry.getDefinedLocationById(spec)!=null) return true;
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/main/java/org/apache/brooklyn/location/basic/DeprecatedKeysMappingBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/DeprecatedKeysMappingBuilder.java b/core/src/main/java/org/apache/brooklyn/location/basic/DeprecatedKeysMappingBuilder.java
new file mode 100644
index 0000000..88593d4
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/DeprecatedKeysMappingBuilder.java
@@ -0,0 +1,67 @@
+/*
+ * 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.basic;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+
+import brooklyn.config.ConfigKey;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.collect.ImmutableMap;
+
+/**
+* @deprecated since 0.6; for use only in converting deprecated flags; will be deleted in future version.
+*/
+public class DeprecatedKeysMappingBuilder {
+    private final ImmutableMap.Builder<String,String> builder = new ImmutableMap.Builder<String,String>();
+    private final Logger logger;
+    
+    public DeprecatedKeysMappingBuilder(Logger logger) {
+        this.logger = logger;
+    }
+
+    public DeprecatedKeysMappingBuilder camelToHyphen(ConfigKey<?> key) {
+        return camelToHyphen(key.getName());
+    }
+    
+    public DeprecatedKeysMappingBuilder camelToHyphen(String key) {
+        String hyphen = toHyphen(key);
+        if (key.equals(hyphen)) {
+            logger.warn("Invalid attempt to convert camel-case key {} to deprecated hyphen-case: both the same", hyphen);
+        } else {
+            builder.put(hyphen, key);
+        }
+        return this;
+    }
+    
+    public DeprecatedKeysMappingBuilder putAll(Map<String,String> vals) {
+        builder.putAll(vals);
+        return this;
+    }
+
+    public Map<String,String> build() {
+        return builder.build();
+    }
+    
+    private String toHyphen(String word) {
+        return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, word);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/main/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocation.java b/core/src/main/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocation.java
new file mode 100644
index 0000000..45bc585
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/FixedListMachineProvisioningLocation.java
@@ -0,0 +1,474 @@
+/*
+ * 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.basic;
+
+import static brooklyn.util.GroovyJavaMethods.truth;
+
+import java.io.Closeable;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.management.LocationManager;
+import org.apache.brooklyn.location.Location;
+import org.apache.brooklyn.location.LocationSpec;
+import org.apache.brooklyn.location.MachineLocationCustomizer;
+import org.apache.brooklyn.location.NoMachinesAvailableException;
+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 org.apache.brooklyn.location.MachineLocation;
+import org.apache.brooklyn.location.MachineProvisioningLocation;
+import org.apache.brooklyn.location.cloud.CloudLocationConfig;
+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;
+
+/**
+ * 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,
+ * for example a list of machines which can be SSH'd to. 
+ * 
+ * This can be extended to have a mechanism to make more machines to be available
+ * (override provisionMore and canProvisionMore).
+ */
+public class FixedListMachineProvisioningLocation<T extends MachineLocation> extends AbstractLocation 
+implements MachineProvisioningLocation<T>, Closeable {
+
+    // TODO Synchronization looks very wrong for accessing machines/inUse 
+    // e.g. removeChild doesn't synchronize when doing machines.remove(...),
+    // and getMachines() returns the real sets risking 
+    // ConcurrentModificationException in the caller if it iterates over them etc.
+    
+    private static final Logger log = LoggerFactory.getLogger(FixedListMachineProvisioningLocation.class);
+    
+    public static final ConfigKey<Function<Iterable<? extends MachineLocation>, MachineLocation>> MACHINE_CHOOSER =
+            ConfigKeys.newConfigKey(
+                    new TypeToken<Function<Iterable<? extends MachineLocation>, MachineLocation>>() {}, 
+                    "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();
+    
+    @SetFromFlag
+    protected Set<T> machines;
+    
+    @SetFromFlag
+    protected Set<T> inUse;
+
+    @SetFromFlag
+    protected Set<T> pendingRemoval;
+    
+    @SetFromFlag
+    protected Map<T, Map<String, Object>> origConfigs;
+
+    public FixedListMachineProvisioningLocation() {
+        this(Maps.newLinkedHashMap());
+    }
+    public FixedListMachineProvisioningLocation(Map properties) {
+        super(properties);
+
+        if (isLegacyConstruction()) {
+            init();
+        }
+    }
+
+    @Override
+    public void init() {
+        super.init();
+        
+        Set<T> machinesCopy = MutableSet.of();
+        for (T location: machines) {
+            if (location==null) {
+                log.warn(""+this+" initialized with null location, removing (may be due to rebind with reference to an unmanaged location)");
+            } else {
+                Location parent = location.getParent();
+                if (parent == null) {
+                    addChild(location);
+                }
+                machinesCopy.add(location);
+            }
+        }
+        if (!machinesCopy.equals(machines)) {
+            machines = machinesCopy;
+        }
+    }
+    
+    @Override
+    public String toVerboseString() {
+        return Objects.toStringHelper(this).omitNullValues()
+                .add("id", getId()).add("name", getDisplayName())
+                .add("machinesAvailable", getAvailable()).add("machinesInUse", getInUse())
+                .toString();
+    }
+
+    @Override
+    public AbstractLocation configure(Map<?,?> properties) {
+        if (machines == null) machines = Sets.newLinkedHashSet();
+        if (inUse == null) inUse = Sets.newLinkedHashSet();
+        if (pendingRemoval == null) pendingRemoval = Sets.newLinkedHashSet();
+        if (origConfigs == null) origConfigs = Maps.newLinkedHashMap();
+        return super.configure(properties);
+    }
+    
+    @SuppressWarnings("unchecked")
+    public FixedListMachineProvisioningLocation<T> newSubLocation(Map<?,?> newFlags) {
+        // TODO shouldn't have to copy config bag as it should be inherited (but currently it is not used inherited everywhere; just most places)
+        return getManagementContext().getLocationManager().createLocation(LocationSpec.create(getClass())
+                .parent(this)
+                .configure(config().getLocalBag().getAllConfig())  // FIXME Should this just be inherited?
+                .configure(newFlags));
+    }
+
+    @Override
+    public void close() {
+        for (T machine : machines) {
+            if (machine instanceof Closeable) Streams.closeQuietly((Closeable)machine);
+        }
+    }
+    
+    public void addMachine(T machine) {
+        synchronized (lock) {
+            if (machines.contains(machine)) {
+                throw new IllegalArgumentException("Cannot add "+machine+" to "+toString()+", because already contained");
+            }
+            
+            Location existingParent = ((Location)machine).getParent();
+            if (existingParent == null) {
+                addChild(machine);
+            }
+            
+            machines.add(machine);
+        }
+    }
+    
+    public void removeMachine(T machine) {
+        synchronized (lock) {
+            if (inUse.contains(machine)) {
+                pendingRemoval.add(machine);
+            } else {
+                machines.remove(machine);
+                pendingRemoval.remove(machine);
+                if (this.equals(machine.getParent())) {
+                    removeChild((Location)machine);
+                }
+            }
+        }
+    }
+    
+    protected Set<T> getMachines() {
+        return machines;
+    }
+    
+    public Set<T> getAvailable() {
+        Set<T> a = Sets.newLinkedHashSet(machines);
+        a.removeAll(inUse);
+        return a;
+    }   
+     
+    public Set<T> getInUse() {
+        return Sets.newLinkedHashSet(inUse);
+    }   
+     
+    public Set<T> getAllMachines() {
+        return ImmutableSet.copyOf(machines);
+    }   
+     
+    @Override
+    public void addChild(Location child) {
+        super.addChild(child);
+        machines.add((T)child);
+    }
+
+    @Override
+    public boolean removeChild(Location child) {
+        if (inUse.contains(child)) {
+            throw new IllegalStateException("Child location "+child+" is in use; cannot remove from "+this);
+        }
+        machines.remove(child);
+        return super.removeChild(child);
+    }
+
+    protected boolean canProvisionMore() {
+        return false;
+    }
+    
+    protected void provisionMore(int size) {
+        provisionMore(size, ImmutableMap.of());
+    }
+
+    protected void provisionMore(int size, Map<?,?> flags) {
+        throw new IllegalStateException("more not permitted");
+    }
+
+    public T obtain() throws NoMachinesAvailableException {
+        return obtain(Maps.<String,Object>newLinkedHashMap());
+    }
+    
+    @Override
+    public T obtain(Map<?,?> flags) throws NoMachinesAvailableException {
+        T machine;
+        T desiredMachine = (T) flags.get("desiredMachine");
+        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, allflags.getAllConfig());
+                    a = getAvailable();
+                }
+                if (a.isEmpty())
+                    throw new NoMachinesAvailableException("No machines available in "+toString());
+            }
+            if (desiredMachine != null) {
+                if (a.contains(desiredMachine)) {
+                    machine = desiredMachine;
+                } else {
+                    throw new IllegalStateException("Desired machine "+desiredMachine+" not available in "+toString()+"; "+
+                            (inUse.contains(desiredMachine) ? "machine in use" : "machine unknown"));
+                }
+            } else {
+                machine = (T) chooser.apply(a);
+                if (!a.contains(machine)) {
+                    throw new IllegalStateException("Machine chooser attempted to choose '"+machine+"' from outside the available set, in "+this);
+                }
+            }
+            inUse.add(machine);
+            updateMachineConfig(machine, flags);
+        }
+        
+        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");
+            restoreMachineConfig(machine);
+            inUse.remove(machine);
+            
+            if (pendingRemoval.contains(machine)) {
+                removeMachine(machine);
+            }
+        }
+    }
+
+    @Override
+    public Map<String,Object> getProvisioningFlags(Collection<String> tags) {
+        return Maps.<String,Object>newLinkedHashMap();
+    }
+    
+    protected void updateMachineConfig(T machine, Map<?, ?> flags) {
+        if (origConfigs == null) {
+            // For backwards compatibility, where peristed state did not have this.
+            origConfigs = Maps.newLinkedHashMap();
+        }
+        Map<String, Object> strFlags = ConfigBag.newInstance(flags).getAllConfig();
+        Map<String, Object> origConfig = ((ConfigurationSupportInternal)machine.config()).getLocalBag().getAllConfig();
+        origConfigs.put(machine, origConfig);
+        requestPersist();
+        
+        ((ConfigurationSupportInternal)machine.config()).addToLocalBag(strFlags);
+    }
+    
+    protected void restoreMachineConfig(MachineLocation machine) {
+        if (origConfigs == null) {
+            // For backwards compatibility, where peristed state did not have this.
+            origConfigs = Maps.newLinkedHashMap();
+        }
+        Map<String, Object> origConfig = origConfigs.remove(machine);
+        if (origConfig == null) return;
+        requestPersist();
+        
+        Set<String> currentKeys = ((ConfigurationSupportInternal)machine.config()).getLocalBag().getAllConfig().keySet();
+        Set<String> newKeys = Sets.difference(currentKeys, origConfig.entrySet());
+        for (String key : newKeys) {
+            ((ConfigurationSupportInternal)machine.config()).removeFromLocalBag(key);
+        }
+        ((ConfigurationSupportInternal)machine.config()).addToLocalBag(origConfig);
+    }
+    
+    @SuppressWarnings("unchecked")
+    private <K> K getConfigPreferringOverridden(ConfigKey<K> key, Map<?,?> overrides) {
+        K result = (K) overrides.get(key);
+        if (result == null) result = (K) overrides.get(key.getName());
+        if (result == null) result = getConfig(key);
+        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>
+     * {@code
+     *   new FixedListMachineProvisioningLocation.Builder()
+     *           .user("alex")
+     *           .keyFile("/Users/alex/.ssh/id_rsa")
+     *           .addAddress("10.0.0.1")
+     *           .addAddress("10.0.0.2")
+     *           .addAddress("10.0.0.3")
+     *           .addAddressMultipleTimes("me@127.0.0.1", 5)
+     *           .build();
+     * }
+     * </pre>
+     */
+    public static class Builder {
+        LocationManager lm;
+        String user;
+        String privateKeyPassphrase;
+        String privateKeyFile;
+        String privateKeyData;
+        File localTempDir;
+        List machines = Lists.newArrayList();
+
+        public Builder(LocationManager lm) {
+            this.lm = lm;
+        }
+        public Builder user(String user) {
+            this.user = user;
+            return this;
+        }
+        public Builder keyPassphrase(String keyPassphrase) {
+            this.privateKeyPassphrase = keyPassphrase;
+            return this; 
+        }
+        public Builder keyFile(String keyFile) {
+            this.privateKeyFile = keyFile;
+            return this; 
+        }
+        public Builder keyData(String keyData) {
+            this.privateKeyData = keyData;
+            return this;
+        }
+        public Builder localTempDir(File val) {
+            this.localTempDir = val;
+            return this;
+        }
+        /** adds the locations; user and keyfile set in the builder are _not_ applied to the machine
+         * (use add(String address) for that)
+         */
+        public Builder add(SshMachineLocation location) {
+            machines.add(location);
+            return this;
+        }
+        public Builder addAddress(String address) {
+            return addAddresses(address);
+        }
+        public Builder addAddressMultipleTimes(String address, int n) {
+            for (int i=0; i<n; i++)
+                addAddresses(address);
+            return this;
+        }
+        public Builder addAddresses(String address1, String ...others) {
+            List<String> addrs = new ArrayList<String>();
+            addrs.addAll(WildcardGlobs.getGlobsAfterBraceExpansion("{"+address1+"}",
+                    true /* numeric */, /* no quote support though */ PhraseTreatment.NOT_A_SPECIAL_CHAR, PhraseTreatment.NOT_A_SPECIAL_CHAR));
+            for (String address: others) 
+                addrs.addAll(WildcardGlobs.getGlobsAfterBraceExpansion("{"+address+"}",
+                        true /* numeric */, /* no quote support though */ PhraseTreatment.NOT_A_SPECIAL_CHAR, PhraseTreatment.NOT_A_SPECIAL_CHAR));
+            for (String addr: addrs)
+                add(createMachine(addr)); 
+            return this;
+        }
+        protected SshMachineLocation createMachine(String addr) {
+            if (lm==null)
+                return new SshMachineLocation(makeConfig(addr));
+            else
+                return lm.createLocation(makeConfig(addr), SshMachineLocation.class);
+        }
+        private Map makeConfig(String address) {
+            String user = this.user;
+            if (address.contains("@")) {
+                user = address.substring(0, address.indexOf("@"));
+                address = address.substring(address.indexOf("@")+1);
+            }
+            Map config = MutableMap.of("address", address);
+            if (truth(user)) {
+                config.put("user", user);
+                config.put("sshconfig.user", user);
+            }
+            if (truth(privateKeyPassphrase)) config.put("sshconfig.privateKeyPassphrase", privateKeyPassphrase);
+            if (truth(privateKeyFile)) config.put("sshconfig.privateKeyFile", privateKeyFile);
+            if (truth(privateKeyData)) config.put("sshconfig.privateKey", privateKeyData);
+            if (truth(localTempDir)) config.put("localTempDir", localTempDir);
+            return config;
+        }
+        @SuppressWarnings("unchecked")
+        public FixedListMachineProvisioningLocation<SshMachineLocation> build() {
+            if (lm==null)
+                return new FixedListMachineProvisioningLocation<SshMachineLocation>(MutableMap.builder()
+                    .putIfNotNull("machines", machines)
+                    .putIfNotNull("user", user)
+                    .putIfNotNull("privateKeyPassphrase", privateKeyPassphrase)
+                    .putIfNotNull("privateKeyFile", privateKeyFile)
+                    .putIfNotNull("privateKeyData", privateKeyData)
+                    .putIfNotNull("localTempDir", localTempDir)
+                    .build());
+            else
+                return lm.createLocation(MutableMap.builder()
+                    .putIfNotNull("machines", machines)
+                    .putIfNotNull("user", user)
+                    .putIfNotNull("privateKeyPassphrase", privateKeyPassphrase)
+                    .putIfNotNull("privateKeyFile", privateKeyFile)
+                    .putIfNotNull("privateKeyData", privateKeyData)
+                    .putIfNotNull("localTempDir", localTempDir)
+                    .build(), 
+                FixedListMachineProvisioningLocation.class);
+        }        
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/main/java/org/apache/brooklyn/location/basic/HasSubnetHostname.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/HasSubnetHostname.java b/core/src/main/java/org/apache/brooklyn/location/basic/HasSubnetHostname.java
new file mode 100644
index 0000000..e9fbece
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/HasSubnetHostname.java
@@ -0,0 +1,32 @@
+/*
+ * 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.basic;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public interface HasSubnetHostname {
+
+    /** returns a hostname for use internally within a subnet / VPC */
+    @Beta
+    String getSubnetHostname();
+
+    /** returns an IP for use internally within a subnet / VPC */
+    String getSubnetIp();
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/main/java/org/apache/brooklyn/location/basic/HostLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/HostLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/basic/HostLocationResolver.java
new file mode 100644
index 0000000..0e5225b
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/HostLocationResolver.java
@@ -0,0 +1,90 @@
+/*
+ * 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.basic;
+
+import java.util.Map;
+
+import org.apache.brooklyn.location.Location;
+import org.apache.brooklyn.location.LocationRegistry;
+import org.apache.brooklyn.location.LocationSpec;
+import org.apache.brooklyn.location.basic.AbstractLocationResolver.SpecParser.ParsedSpec;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.guava.Maybe.Absent;
+import brooklyn.util.text.KeyValueParser;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+public class HostLocationResolver extends AbstractLocationResolver {
+    
+    private static final String HOST = "host";
+    
+    @SuppressWarnings("rawtypes")
+    @Override
+    public Location newLocationFromString(Map locationFlags, String spec, LocationRegistry registry) {
+        // Extract args from spec
+        ParsedSpec parsedSpec = specParser.parse(spec);
+        Map<String, String> argsMap = parsedSpec.argsMap;
+        if (argsMap.isEmpty()) {
+            throw new IllegalArgumentException("Invalid host spec (no host supplied): "+spec);
+        } else if (argsMap.size() == 1 && Iterables.get(argsMap.values(), 0) == null) {
+            // only given ip or hostname
+            argsMap = ImmutableMap.of("hosts", Iterables.get(argsMap.keySet(), 0));
+        } else if (!(argsMap.containsKey("host") || argsMap.containsKey("hosts"))) {
+            throw new IllegalArgumentException("Invalid host spec (no host supplied): "+spec);
+        }
+
+        // Find generic applicable properties
+        Map globalProperties = registry.getProperties();
+        String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName());
+        Map<String, Object> filteredProperties = new LocationPropertiesFromBrooklynProperties().getLocationProperties(null, namedLocation, globalProperties);
+        ConfigBag flags = ConfigBag.newInstance(locationFlags).putIfAbsent(filteredProperties);
+        flags.remove(LocationInternal.NAMED_SPEC_NAME);
+
+        // Generate target spec
+        String target = "byon("+KeyValueParser.toLine(argsMap)+")";
+        Maybe<Location> testResolve = managementContext.getLocationRegistry().resolve(target, false, null);
+        if (!testResolve.isPresent()) {
+            throw new IllegalArgumentException("Invalid target location '" + target + "' for location '"+HOST+"': "+
+                Exceptions.collapseText( ((Absent<?>)testResolve).getException() ), ((Absent<?>)testResolve).getException());
+        }
+        
+        return managementContext.getLocationManager().createLocation(LocationSpec.create(SingleMachineProvisioningLocation.class)
+                .configure("location", target)
+                .configure("locationFlags", flags.getAllConfig())
+                .configure(LocationConfigUtils.finalAndOriginalSpecs(spec, locationFlags, globalProperties, namedLocation)));
+    }
+    
+    @Override
+    public String getPrefix() {
+        return HOST;
+    }
+    
+    @Override
+    protected Class<? extends Location> getLocationType() {
+        return SingleMachineProvisioningLocation.class;
+    }
+
+    @Override
+    protected SpecParser getSpecParser() {
+        return new SpecParser(getPrefix()).setExampleUsage("\"host(1.1.1.1)\" or \"host(host=1.1.1.1,name=myname)\"");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostLocationResolver.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostLocationResolver.java b/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostLocationResolver.java
new file mode 100644
index 0000000..47c8089
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostLocationResolver.java
@@ -0,0 +1,74 @@
+/*
+ * 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.basic;
+
+import java.util.Map;
+
+import org.apache.brooklyn.location.Location;
+import brooklyn.util.config.ConfigBag;
+import org.apache.brooklyn.location.LocationRegistry;
+import org.apache.brooklyn.location.LocationResolver;
+
+/**
+ * Examples of valid specs:
+ *   <ul>
+ *     <li>localhost
+ *     <li>localhost()
+ *     <li>localhost(name=abc)
+ *     <li>localhost(name="abc")
+ *   </ul>
+ * 
+ * @author alex, aled
+ */
+public class LocalhostLocationResolver extends AbstractLocationResolver implements LocationResolver.EnableableLocationResolver {
+
+    public static final String LOCALHOST = "localhost";
+
+    @Override
+    public String getPrefix() {
+        return LOCALHOST;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return LocationConfigUtils.isEnabled(managementContext, "brooklyn.location.localhost");
+    }
+
+    @Override
+    protected Class<? extends Location> getLocationType() {
+        return LocalhostMachineProvisioningLocation.class;
+    }
+
+    @Override
+    protected SpecParser getSpecParser() {
+        return new AbstractLocationResolver.SpecParser(getPrefix()).setExampleUsage("\"localhost\" or \"localhost(displayName=abc)\"");
+    }
+
+    @Override
+    protected Map<String, Object> getFilteredLocationProperties(String provider, String namedLocation, Map<String, ?> globalProperties) {
+        return new LocalhostPropertiesFromBrooklynProperties().getLocationProperties("localhost", namedLocation, globalProperties);
+    }
+
+    @Override
+    protected ConfigBag extractConfig(Map<?,?> locationFlags, String spec, LocationRegistry registry) {
+        ConfigBag config = super.extractConfig(locationFlags, spec, registry);
+        config.putAsStringKeyIfAbsent("name", "localhost");
+        return config;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocation.java b/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocation.java
new file mode 100644
index 0000000..44b95b6
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostMachineProvisioningLocation.java
@@ -0,0 +1,347 @@
+/*
+ * 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.basic;
+
+import static brooklyn.util.GroovyJavaMethods.elvis;
+import static brooklyn.util.GroovyJavaMethods.truth;
+
+import java.io.File;
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.location.AddressableLocation;
+import org.apache.brooklyn.location.LocationSpec;
+import org.apache.brooklyn.location.MachineProvisioningLocation;
+import org.apache.brooklyn.location.OsDetails;
+import org.apache.brooklyn.location.PortRange;
+import org.apache.brooklyn.location.geo.HostGeoInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.config.ConfigKey.HasConfigKey;
+import brooklyn.entity.basic.BrooklynConfigKeys;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.rebind.persister.FileBasedObjectStore;
+import brooklyn.entity.rebind.persister.LocationWithObjectStore;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore;
+import brooklyn.util.BrooklynNetworkUtils;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.flags.SetFromFlag;
+import brooklyn.util.internal.ssh.process.ProcessTool;
+import brooklyn.util.mutex.MutexSupport;
+import brooklyn.util.mutex.WithMutexes;
+import brooklyn.util.net.Networking;
+import brooklyn.util.os.Os;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * An implementation of {@link MachineProvisioningLocation} that can provision a {@link SshMachineLocation} for the
+ * local host.
+ *
+ * By default you can only obtain a single SshMachineLocation for the localhost. Optionally, you can "overload"
+ * and choose to allow localhost to be provisioned multiple times, which may be useful in some testing scenarios.
+ */
+public class LocalhostMachineProvisioningLocation extends FixedListMachineProvisioningLocation<SshMachineLocation> implements AddressableLocation, LocationWithObjectStore {
+
+    public static final Logger LOG = LoggerFactory.getLogger(LocalhostMachineProvisioningLocation.class);
+    
+    public static final ConfigKey<Boolean> SKIP_ON_BOX_BASE_DIR_RESOLUTION = ConfigKeys.newConfigKeyWithDefault(
+            BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, 
+            true);
+    
+    @SetFromFlag("count")
+    int initialCount;
+
+    @SetFromFlag
+    Boolean canProvisionMore;
+    
+    @SetFromFlag
+    InetAddress address;
+
+    private static Set<Integer> portsInUse = Sets.newLinkedHashSet();
+
+    private static HostGeoInfo cachedHostGeoInfo;
+    
+    @VisibleForTesting
+    public static void clearStaticData() {
+        portsInUse.clear();
+        cachedHostGeoInfo = null;
+    }
+        
+    /**
+     * Construct a new instance.
+     *
+     * The constructor recognises the following properties:
+     * <ul>
+     * <li>count - number of localhost machines to make available
+     * </ul>
+     */
+    public LocalhostMachineProvisioningLocation() {
+        this(Maps.newLinkedHashMap());
+    }
+    /**
+     * @param properties the properties of the new instance.
+     * @deprecated since 0.6
+     * @see #LocalhostMachineProvisioningLocation()
+     */
+    public LocalhostMachineProvisioningLocation(Map properties) {
+        super(properties);
+    }
+    public LocalhostMachineProvisioningLocation(String name) {
+        this(name, 0);
+    }
+    public LocalhostMachineProvisioningLocation(String name, int count) {
+        this(MutableMap.of("name", name, "count", count));
+    }
+    
+    public static LocationSpec<LocalhostMachineProvisioningLocation> spec() {
+        return LocationSpec.create(LocalhostMachineProvisioningLocation.class);
+    }
+    
+    public LocalhostMachineProvisioningLocation configure(Map<?,?> flags) {
+        super.configure(flags);
+        
+        if (!truth(getDisplayName())) { setDisplayName("localhost"); }
+        if (!truth(address)) address = getLocalhostInetAddress();
+        // TODO should try to confirm this machine is accessible on the given address ... but there's no 
+        // immediate convenience in java so early-trapping of that particular error is deferred
+        
+        if (canProvisionMore==null) {
+            if (initialCount>0) canProvisionMore = false;
+            else canProvisionMore = true;
+        }
+        if (getHostGeoInfo()==null) {
+            if (cachedHostGeoInfo==null)
+                cachedHostGeoInfo = HostGeoInfo.fromLocation(this);
+            setHostGeoInfo(cachedHostGeoInfo);
+        }
+        if (initialCount > getMachines().size()) {
+            provisionMore(initialCount - getMachines().size());
+        }
+        
+        if (getConfig(BrooklynConfigKeys.ONBOX_BASE_DIR)==null && (getManagementContext()==null || getManagementContext().getConfig().getConfig(BrooklynConfigKeys.ONBOX_BASE_DIR)==null)) {
+            setConfig(BrooklynConfigKeys.ONBOX_BASE_DIR, "/tmp/brooklyn-"+Os.user());
+        }
+        
+        return this;
+    }
+    
+    public static InetAddress getLocalhostInetAddress() {
+        return BrooklynNetworkUtils.getLocalhostInetAddress();
+    }
+
+    @Override
+    public InetAddress getAddress() {
+        return address;
+    }
+    
+    @Override
+    public boolean canProvisionMore() {
+        return canProvisionMore;
+    }
+    
+    @Override
+    protected void provisionMore(int size, Map<?,?> flags) {
+        for (int i=0; i<size; i++) {
+            Map<Object,Object> flags2 = MutableMap.<Object,Object>builder()
+                    .putAll(flags)
+                    .put("address", elvis(address, Networking.getLocalHost()))
+                    .put("mutexSupport", LocalhostMachine.mutexSupport)
+                    .build();
+            
+            // copy inherited keys for ssh; 
+            // shouldn't be necessary but not sure that all contexts traverse the hierarchy
+            // NOTE: changed Nov 2013 to copy only those ssh config keys actually set, rather than all of them
+            // TODO should take the plunge and try removing this altogether!
+            // (or alternatively switch to copying all ancestor keys)
+            for (HasConfigKey<?> k: SshMachineLocation.ALL_SSH_CONFIG_KEYS) {
+                if (config().getRaw(k).isPresent())
+                    flags2.put(k, getConfig(k));
+            }
+            
+            if (isManaged()) {
+                addChild(LocationSpec.create(LocalhostMachine.class).configure(flags2));
+            } else {
+                addChild(new LocalhostMachine(flags2)); // TODO legacy way
+            }
+       }
+    }
+
+    public static synchronized boolean obtainSpecificPort(InetAddress localAddress, int portNumber) {
+        if (portsInUse.contains(portNumber)) {
+            return false;
+        } else {
+            //see if it is available?
+            if (!checkPortAvailable(localAddress, portNumber)) {
+                return false;
+            }
+            portsInUse.add(portNumber);
+            return true;
+        }
+    }
+    /** checks the actual availability of the port on localhost, ie by binding to it; cf {@link Networking#isPortAvailable(int)} */
+    public static boolean checkPortAvailable(InetAddress localAddress, int portNumber) {
+        if (portNumber<1024) {
+            if (LOG.isDebugEnabled()) LOG.debug("Skipping system availability check for privileged localhost port "+portNumber);
+            return true;
+        }
+        return Networking.isPortAvailable(localAddress, portNumber);
+    }
+    public static int obtainPort(PortRange range) {
+        return obtainPort(getLocalhostInetAddress(), range);
+    }
+    public static int obtainPort(InetAddress localAddress, PortRange range) {
+        for (int p: range)
+            if (obtainSpecificPort(localAddress, p)) return p;
+        if (LOG.isDebugEnabled()) LOG.debug("unable to find port in {} on {}; returning -1", range, localAddress);
+        return -1;
+    }
+
+    public static synchronized void releasePort(InetAddress localAddress, int portNumber) {
+        portsInUse.remove((Object) portNumber);
+    }
+
+    public void release(SshMachineLocation machine) {
+        LocalhostMachine localMachine = (LocalhostMachine) machine;
+        Set<Integer> portsObtained = Sets.newLinkedHashSet();
+        synchronized (localMachine.portsObtained) {
+            portsObtained.addAll(localMachine.portsObtained);
+        }
+        
+        super.release(machine);
+        
+        for (int p: portsObtained)
+            releasePort(null, p);
+    }
+    
+    public static class LocalhostMachine extends SshMachineLocation implements HasSubnetHostname {
+        // declaring this here (as well as on LocalhostMachineProvisioningLocation) because:
+        //  1. machine.getConfig(key) will not inherit default value of machine.getParent()'s key
+        //  2. things might instantiate a `LocalhostMachine` without going through LocalhostMachineProvisioningLocation
+        //     so not sufficient for LocalhostMachineProvisioningLocation to just push its config value into
+        //     the LocalhostMachine instance.
+        public static final ConfigKey<Boolean> SKIP_ON_BOX_BASE_DIR_RESOLUTION = ConfigKeys.newConfigKeyWithDefault(
+                BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, 
+                true);
+
+        private static final WithMutexes mutexSupport = new MutexSupport();
+        
+        private final Set<Integer> portsObtained = Sets.newLinkedHashSet();
+
+        public LocalhostMachine() {
+            super();
+        }
+        /** @deprecated since 0.6.0 use no-arg constructor (and spec) then configure */
+        public LocalhostMachine(Map properties) {
+            super(MutableMap.builder().putAll(properties).put("mutexSupport", mutexSupport).build());
+        }
+        
+        @Override
+        protected WithMutexes getMutexSupport() {
+            return mutexSupport;
+        }
+        
+        public boolean obtainSpecificPort(int portNumber) {
+            if (!isSudoAllowed() && portNumber <= 1024)
+                return false;
+            return LocalhostMachineProvisioningLocation.obtainSpecificPort(getAddress(), portNumber);
+        }
+        
+        public int obtainPort(PortRange range) {
+            int r = LocalhostMachineProvisioningLocation.obtainPort(getAddress(), range);
+            synchronized (portsObtained) {
+                if (r>0) portsObtained.add(r);
+            }
+            LOG.debug("localhost.obtainPort("+range+"), returning "+r);
+            return r;
+        }
+        
+        @Override
+        public void releasePort(int portNumber) {
+            synchronized (portsObtained) {
+                portsObtained.remove((Object)portNumber);
+            }
+            LocalhostMachineProvisioningLocation.releasePort(getAddress(), portNumber);
+        }
+        
+        @Override
+        public OsDetails getOsDetails() {
+            return BasicOsDetails.Factory.newLocalhostInstance();
+        }
+        
+        @Override
+        public LocalhostMachine configure(Map<?,?> properties) {
+            if (address==null || !properties.containsKey("address"))
+                address = Networking.getLocalHost();
+            super.configure(properties);
+            return this;
+        }
+        @Override
+        public String getSubnetHostname() {
+           return Networking.getLocalHost().getHostName();
+        }
+        @Override
+        public String getSubnetIp() {
+            return Networking.getLocalHost().getHostAddress();
+        }
+    }
+
+    private static class SudoChecker {
+        static volatile long lastSudoCheckTime = -1;
+        static boolean lastSudoResult = false;
+        public static boolean isSudoAllowed() {
+            if (Time.hasElapsedSince(lastSudoCheckTime, Duration.FIVE_MINUTES))
+                checkIfNeeded();                    
+            return lastSudoResult;
+        }
+        private static synchronized void checkIfNeeded() {
+            if (Time.hasElapsedSince(lastSudoCheckTime, Duration.FIVE_MINUTES)) {
+                try {
+                    lastSudoResult = new ProcessTool().execCommands(MutableMap.<String,Object>of(), Arrays.asList(
+                            BashCommands.sudo("date"))) == 0;
+                } catch (Exception e) {
+                    lastSudoResult = false;
+                    LOG.debug("Error checking sudo at localhost: "+e, e);
+                }
+                lastSudoCheckTime = System.currentTimeMillis();
+            }
+        }
+    }
+
+    public static boolean isSudoAllowed() {
+        return SudoChecker.isSudoAllowed();
+    }
+
+    @Override
+    public PersistenceObjectStore newPersistenceObjectStore(String container) {
+        File basedir = new File(container);
+        if (basedir.isFile()) throw new IllegalArgumentException("Destination directory must not be a file");
+        return new FileBasedObjectStore(basedir);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostPropertiesFromBrooklynProperties.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostPropertiesFromBrooklynProperties.java b/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostPropertiesFromBrooklynProperties.java
new file mode 100644
index 0000000..1830ddd
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/LocalhostPropertiesFromBrooklynProperties.java
@@ -0,0 +1,57 @@
+/*
+ * 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.basic;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.util.config.ConfigBag;
+
+import com.google.common.base.Strings;
+
+/**
+ * @author aledsage
+ **/
+public class LocalhostPropertiesFromBrooklynProperties extends LocationPropertiesFromBrooklynProperties {
+
+    // TODO Once delete support for deprecated "location.localhost.*" then can get rid of this class, and use
+    // LocationPropertiesFromBrooklynProperties directly
+    
+    @SuppressWarnings("unused")
+    private static final Logger LOG = LoggerFactory.getLogger(LocalhostPropertiesFromBrooklynProperties.class);
+
+    @Override
+    public Map<String, Object> getLocationProperties(String provider, String namedLocation, Map<String, ?> properties) {
+        if (Strings.isNullOrEmpty(namedLocation) && Strings.isNullOrEmpty(provider)) {
+            throw new IllegalArgumentException("Neither cloud provider/API nor location name have been specified correctly");
+        }
+
+        ConfigBag result = ConfigBag.newInstance();
+        
+        result.putAll(transformDeprecated(getGenericLocationSingleWordProperties(properties)));
+        result.putAll(transformDeprecated(getMatchingSingleWordProperties("brooklyn.location.", properties)));
+        result.putAll(transformDeprecated(getMatchingProperties("brooklyn.location.localhost.", "brooklyn.localhost.", properties)));
+        if (!Strings.isNullOrEmpty(namedLocation)) result.putAll(transformDeprecated(getNamedLocationProperties(namedLocation, properties)));
+        setLocalTempDir(properties, result);
+        
+        return result.getAllConfigRaw();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/core/src/main/java/org/apache/brooklyn/location/basic/LocationConfigKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/location/basic/LocationConfigKeys.java b/core/src/main/java/org/apache/brooklyn/location/basic/LocationConfigKeys.java
new file mode 100644
index 0000000..05e15f9
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/location/basic/LocationConfigKeys.java
@@ -0,0 +1,79 @@
+/*
+ * 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.basic;
+
+import java.io.File;
+import java.util.Set;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.event.basic.BasicConfigKey;
+import brooklyn.util.os.Os;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.reflect.TypeToken;
+
+public class LocationConfigKeys {
+
+    public static final ConfigKey<String> LOCATION_ID = ConfigKeys.newStringConfigKey("id");
+    public static final ConfigKey<String> DISPLAY_NAME = ConfigKeys.newStringConfigKey("displayName");
+    public static final ConfigKey<Boolean> ENABLED = ConfigKeys.newBooleanConfigKey("enabled", "Whether the location is enabled for listing and use "
+        + "(only supported for selected locations)", true);
+    
+    public static final ConfigKey<String> ACCESS_IDENTITY = ConfigKeys.newStringConfigKey("identity"); 
+    public static final ConfigKey<String> ACCESS_CREDENTIAL = ConfigKeys.newStringConfigKey("credential"); 
+
+    public static final ConfigKey<Double> LATITUDE = new BasicConfigKey<Double>(Double.class, "latitude"); 
+    public static final ConfigKey<Double> LONGITUDE = new BasicConfigKey<Double>(Double.class, "longitude"); 
+
+    public static final ConfigKey<String> CLOUD_PROVIDER = ConfigKeys.newStringConfigKey("provider");
+    public static final ConfigKey<String> CLOUD_ENDPOINT = ConfigKeys.newStringConfigKey("endpoint");
+    public static final ConfigKey<String> CLOUD_REGION_ID = ConfigKeys.newStringConfigKey("region");
+    public static final ConfigKey<String> CLOUD_AVAILABILITY_ZONE_ID = ConfigKeys.newStringConfigKey("availabilityZone");
+
+    @SuppressWarnings("serial")
+    public static final ConfigKey<Set<String>> ISO_3166 = ConfigKeys.newConfigKey(new TypeToken<Set<String>>() {}, "iso3166", "ISO-3166 or ISO-3166-2 location codes"); 
+
+    public static final ConfigKey<String> USER = ConfigKeys.newStringConfigKey("user", 
+            "user account for normal access to the remote machine, defaulting to local user", System.getProperty("user.name"));
+    
+    public static final ConfigKey<String> PASSWORD = ConfigKeys.newStringConfigKey("password", "password to use for ssh; note some images do not allow password-based ssh access");
+    public static final ConfigKey<String> PUBLIC_KEY_FILE = ConfigKeys.newStringConfigKey("publicKeyFile", "ssh public key file to use; if blank will infer from privateKeyFile by appending \".pub\"");
+    public static final ConfigKey<String> PUBLIC_KEY_DATA = ConfigKeys.newStringConfigKey("publicKeyData", "ssh public key string to use (takes precedence over publicKeyFile)");
+    public static final ConfigKey<String> PRIVATE_KEY_FILE = ConfigKeys.newStringConfigKey("privateKeyFile", "a '" + File.pathSeparator + "' separated list of ssh private key files; uses first in list that can be read",
+                                                                                           Os.fromHome(".ssh/id_rsa") + File.pathSeparator + Os.fromHome(".ssh/id_dsa"));
+    public static final ConfigKey<String> PRIVATE_KEY_DATA = ConfigKeys.newStringConfigKey("privateKeyData", "ssh private key string to use (takes precedence over privateKeyFile)");
+    public static final ConfigKey<String> PRIVATE_KEY_PASSPHRASE = ConfigKeys.newStringConfigKey("privateKeyPassphrase");
+
+    /** @deprecated since 0.6.0; included here so it gets picked up in auto-detect routines */ @Deprecated
+    public static final ConfigKey<String> LEGACY_PUBLIC_KEY_FILE = ConfigKeys.convert(PUBLIC_KEY_FILE, CaseFormat.LOWER_CAMEL, CaseFormat.LOWER_HYPHEN);
+    /** @deprecated since 0.6.0; included here so it gets picked up in auto-detect routines */ @Deprecated
+    public static final ConfigKey<String> LEGACY_PUBLIC_KEY_DATA = ConfigKeys.convert(PUBLIC_KEY_DATA, CaseFormat.LOWER_CAMEL, CaseFormat.LOWER_HYPHEN);
+    /** @deprecated since 0.6.0; included here so it gets picked up in auto-detect routines */ @Deprecated
+    public static final ConfigKey<String> LEGACY_PRIVATE_KEY_FILE = ConfigKeys.convert(PRIVATE_KEY_FILE, CaseFormat.LOWER_CAMEL, CaseFormat.LOWER_HYPHEN);
+    /** @deprecated since 0.6.0; included here so it gets picked up in auto-detect routines */ @Deprecated
+    public static final ConfigKey<String> LEGACY_PRIVATE_KEY_DATA = ConfigKeys.convert(PRIVATE_KEY_DATA, CaseFormat.LOWER_CAMEL, CaseFormat.LOWER_HYPHEN);
+    /** @deprecated since 0.6.0; included here so it gets picked up in auto-detect routines */ @Deprecated
+    public static final ConfigKey<String> LEGACY_PRIVATE_KEY_PASSPHRASE = ConfigKeys.convert(PRIVATE_KEY_PASSPHRASE, CaseFormat.LOWER_CAMEL, CaseFormat.LOWER_HYPHEN);
+
+    public static final ConfigKey<Object> CALLER_CONTEXT = new BasicConfigKey<Object>(Object.class, "callerContext",
+            "An object whose toString is used for logging, to indicate wherefore a VM is being created");
+    public static final ConfigKey<String> CLOUD_MACHINE_NAMER_CLASS = ConfigKeys.newStringConfigKey("cloudMachineNamer", "fully qualified class name of a class that extends CloudMachineNamer and has a single-parameter constructor that takes a ConfigBag");
+    
+}


Mime
View raw message