http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/location/dynamic/LocationOwner.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/dynamic/LocationOwner.java
 
b/core/src/main/java/org/apache/brooklyn/core/location/dynamic/LocationOwner.java
new file mode 100644
index 0000000..1d4d4c1
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/core/location/dynamic/LocationOwner.java
@@ -0,0 +1,85 @@
+/*
+ * 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.core.location.dynamic;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationDefinition;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.sensor.core.BasicAttributeSensorAndConfigKey;
+import org.apache.brooklyn.sensor.core.Sensors;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+
+/**
+ * An entity that owns a particular location.
+ * <p>
+ * The entity should be able to dynamically create an instance of the required 
type of location, and will manage
+ * the lifecycle of the location in parallel with its own.
+ *
+ * @param L the location type
+ * @param E the entity type
+ */
+@Beta
+public interface LocationOwner<L extends Location & DynamicLocation<E, L>, E 
extends Entity & LocationOwner<L, E>> {
+
+    @SetFromFlag("locationPrefix")
+    ConfigKey<String> LOCATION_NAME_PREFIX = ConfigKeys.newStringConfigKey(
+            "entity.dynamicLocation.prefix", "The name prefix for the location 
owned by this entity", "dynamic");
+
+    @SetFromFlag("locationSuffix")
+    ConfigKey<String> LOCATION_NAME_SUFFIX = ConfigKeys.newStringConfigKey(
+            "entity.dynamicLocation.suffix", "The name suffix for the location 
owned by this entity");
+
+    @SetFromFlag("locationName")
+    BasicAttributeSensorAndConfigKey<String> LOCATION_NAME = new 
BasicAttributeSensorAndConfigKey<String>(String.class,
+            "entity.dynamicLocation.name", "The name of the location owned by 
this entity (default is auto-generated using prefix and suffix keys)");
+
+    ConfigKey<Map<String, Object>> LOCATION_FLAGS = 
ConfigKeys.newConfigKey(new TypeToken<Map<String, Object>>() { },
+            "entity.dynamicLocation.flags", "Extra creation flags for the 
Location owned by this entity",
+            ImmutableMap.<String, Object>of());
+
+    AttributeSensor<Location> DYNAMIC_LOCATION = 
Sensors.newSensor(Location.class,
+            "entity.dynamicLocation", "The location owned by this entity");
+
+    AttributeSensor<String> LOCATION_SPEC = Sensors.newStringSensor(
+            "entity.dynamicLocation.spec", "The specification string for the 
location owned by this entity");
+
+    AttributeSensor<Boolean> DYNAMIC_LOCATION_STATUS = 
Sensors.newBooleanSensor(
+            "entity.dynamicLocation.status", "The status of the location owned 
by this entity");
+
+    AttributeSensor<LocationDefinition> LOCATION_DEFINITION = 
Sensors.newSensor(
+        LocationDefinition.class, "entity.dynamicLocation.definition", "The 
location definition for the location owned by this entity");
+
+    L getDynamicLocation();
+
+    L createLocation(Map<String, ?> flags);
+
+    boolean isLocationAvailable();
+
+    void deleteLocation();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/location/geo/GeoBytesHostGeoLookup.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/geo/GeoBytesHostGeoLookup.java
 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/GeoBytesHostGeoLookup.java
new file mode 100644
index 0000000..3e46720
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/GeoBytesHostGeoLookup.java
@@ -0,0 +1,104 @@
+/*
+ * 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.core.location.geo;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Properties;
+
+import org.apache.brooklyn.util.net.Networking;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** @deprecated Mar 2015 - the API has changed; GetLocation now discouraged 
for free access, and valuepairs.txt not supported */
+@Deprecated
+public class GeoBytesHostGeoLookup implements HostGeoLookup {
+
+    public static final Logger log = 
LoggerFactory.getLogger(GeoBytesHostGeoLookup.class);
+    
+    /*
+    curl 
"http://www.geobytes.com/IpLocator.htm?GetLocation&template=valuepairs.txt&IpAddress=geobytes.com";
+    known=1
+    countryid=254
+    country=United States
+    fips104=US
+    iso2=US
+    iso3=USA
+    ison=840
+    internet=US
+    comment=
+    regionid=142
+    region=Maryland
+    code=MD
+    adm1code=    
+    cityid=8909
+    city=Baltimore
+    latitude=39.2894
+    longitude=-76.6384
+    timezone=-05:00
+    dmaid=512
+    dma=512
+    market=Baltimore
+    certainty=78
+    isproxy=false
+    mapbytesremaining=Free
+    */
+    
+    public String getPropertiesLookupUrlForPublicIp(String ip) {
+        return 
"http://www.geobytes.com/IpLocator.htm?GetLocation&template=valuepairs.txt&IpAddress="+ip.trim();
+    }
+
+    public String getPropertiesLookupUrlForLocalhost() {
+        return 
"http://www.geobytes.com/IpLocator.htm?GetLocation&template=valuepairs.txt";;
+    }
+
+    /** returns URL to get properties for the given address (assuming 
localhost if address is on a subnet) */
+    public String getPropertiesLookupUrlFor(InetAddress address) {
+        if (Networking.isPrivateSubnet(address)) return 
getPropertiesLookupUrlForLocalhost();
+        return getPropertiesLookupUrlForPublicIp(address.getHostAddress());
+    }
+    
+    private static boolean LOGGED_GEO_LOOKUP_UNAVAILABLE = false;
+    
+    public HostGeoInfo getHostGeoInfo(InetAddress address) throws 
MalformedURLException, IOException {
+        String url = getPropertiesLookupUrlFor(address);
+        if (log.isDebugEnabled())
+            log.debug("Geo info lookup for "+address+" at "+url);
+        Properties props = new Properties();
+        try {
+            props.load( new URL(url).openStream() );
+            HostGeoInfo geo = new HostGeoInfo(address.getHostName(), 
props.getProperty("city")+" ("+props.getProperty("iso2")+")", 
+                Double.parseDouble(props.getProperty("latitude")), 
Double.parseDouble(props.getProperty("longitude")));
+            log.info("Geo info lookup for "+address+" returned: "+geo);
+            return geo;
+        } catch (Exception e) {
+            // may be web not available, or gateway giving us funny crap
+            if (log.isDebugEnabled())
+                log.debug("Geo info lookup for "+address+" failed: "+e);
+            if (!LOGGED_GEO_LOOKUP_UNAVAILABLE) {
+                LOGGED_GEO_LOOKUP_UNAVAILABLE = true;
+                log.info("Geo info lookup unavailable (for "+address+"; cause 
"+e+")");
+            }
+            return null;
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/location/geo/HasHostGeoInfo.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/geo/HasHostGeoInfo.java 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/HasHostGeoInfo.java
new file mode 100644
index 0000000..97e64d5
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/HasHostGeoInfo.java
@@ -0,0 +1,25 @@
+/*
+ * 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.core.location.geo;
+
+public interface HasHostGeoInfo {
+
+    HostGeoInfo getHostGeoInfo();
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/location/geo/HostGeoInfo.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/geo/HostGeoInfo.java 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/HostGeoInfo.java
new file mode 100644
index 0000000..7089de9
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/location/geo/HostGeoInfo.java
@@ -0,0 +1,205 @@
+/*
+ * 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.core.location.geo;
+
+import java.io.Serializable;
+import java.net.InetAddress;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.AddressableLocation;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.location.LocationConfigKeys;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.internal.BrooklynSystemProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+
+/**
+ * Encapsulates geo-IP information for a given host.
+ */
+public class HostGeoInfo implements Serializable {
+    
+    private static final long serialVersionUID = -5866759901535266181L;
+
+    public static final Logger log = 
LoggerFactory.getLogger(HostGeoInfo.class);
+
+    /** the IP address */
+    public final String address;
+    
+    public final String displayName;
+    
+    public final double latitude;
+    public final double longitude;
+    
+    private static Maybe<HostGeoLookup> cachedLookup = null;
+
+    public static HostGeoInfo create(String address, String displayName, 
double latitude, double longitude) {
+        return new HostGeoInfo(address, displayName, latitude, longitude);
+    }
+    
+    public static HostGeoInfo fromIpAddress(InetAddress address) {
+        try {
+            HostGeoLookup lookup = getDefaultLookup();
+            if (lookup!=null)
+                return lookup.getHostGeoInfo(address);
+        } catch (Exception e) {
+            if (log.isDebugEnabled())
+                log.debug("unable to look up geo DNS info for "+address, e);
+        }
+        return null;
+    }
+
+    @Nullable
+    public static HostGeoLookup getDefaultLookup() throws 
InstantiationException, IllegalAccessException, ClassNotFoundException {
+        if (cachedLookup==null) {
+            cachedLookup = Maybe.of(findHostGeoLookupImpl());
+        }                
+        return cachedLookup.get();
+    }
+    
+    public static void clearCachedLookup() {
+        cachedLookup = null;
+    }
+    
+    /** returns null if cannot be set */
+    public static HostGeoInfo fromLocation(Location l) {
+        if (l==null) return null;
+        
+        Location la = l;
+        HostGeoInfo resultFromLocation = null;
+        while (la!=null) {
+            if (la instanceof HasHostGeoInfo) {
+                resultFromLocation = ((HasHostGeoInfo)l).getHostGeoInfo();
+                if (resultFromLocation!=null) break;
+            }
+            la = la.getParent();
+        }
+        if (resultFromLocation!=null && l==la) {
+            // from the location
+            return resultFromLocation;
+        }
+        // resultFromLocation may be inherited, in which case we will copy it 
later
+        
+        InetAddress address = findIpAddress(l);
+        Object latitude = l.getConfig(LocationConfigKeys.LATITUDE);
+        Object longitude = l.getConfig(LocationConfigKeys.LONGITUDE);
+
+        if (resultFromLocation!=null && (latitude == null || longitude == 
null)) {
+            latitude = resultFromLocation.latitude;
+            longitude = resultFromLocation.longitude;            
+        }
+        if (address!=null && (latitude == null || longitude == null)) {
+            HostGeoInfo geo = fromIpAddress(address);
+            if (geo==null) return null;
+            latitude = geo.latitude;
+            longitude = geo.longitude;
+        }
+        
+        if (latitude==null || longitude==null)
+            return null;
+        
+        Exception error=null;
+        try {
+            latitude = TypeCoercions.castPrimitive(latitude, Double.class);
+            longitude = TypeCoercions.castPrimitive(longitude, Double.class);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            error = e;
+        }
+        if (error!=null || !(latitude instanceof Double) || !(longitude 
instanceof Double))
+            throw new IllegalArgumentException("Location "+l+" specifies 
invalid type of lat/long: " +
+                    "lat="+latitude+" (type "+(latitude==null ? null : 
latitude.getClass())+"); " +
+                    "lon="+longitude+" (type "+(longitude==null ? null : 
longitude.getClass())+")", error);
+        
+        HostGeoInfo result = new HostGeoInfo(address!=null ? 
address.getHostAddress() : null, l.getDisplayName(), (Double) latitude, 
(Double) longitude);
+        if (l instanceof AbstractLocation) {
+            ((AbstractLocation)l).setHostGeoInfo(result);
+        }
+        return result;
+    }
+    
+    private static HostGeoLookup findHostGeoLookupImpl() throws 
InstantiationException, IllegalAccessException, ClassNotFoundException {
+        String type = BrooklynSystemProperties.HOST_GEO_LOOKUP_IMPL.getValue();
+        /* utrace seems more accurate than geobytes, and it gives a report of 
how many tokens are left;
+         * but maxmind if it's installed locally is even better (does not 
require remote lookup),
+         * so use it if available */
+        if (type==null) {
+            if (MaxMind2HostGeoLookup.getDatabaseReader()!=null)
+                return new MaxMind2HostGeoLookup();
+            log.debug("Using Utrace remote for geo lookup because MaxMind2 is 
not available");
+            return new UtraceHostGeoLookup();
+        }
+        if (type.isEmpty()) return null;
+        return (HostGeoLookup) Class.forName(type).newInstance();
+    }
+
+    public static HostGeoInfo fromEntity(Entity e) {
+        for (Location l : e.getLocations()) {
+            HostGeoInfo hgi = fromLocation(l);
+            if (hgi != null)
+                return hgi;
+        }
+        return null;
+    }
+    
+    public static InetAddress findIpAddress(Location l) {
+        if (l == null)
+            return null;
+        if (l instanceof AddressableLocation)
+            return ((AddressableLocation) l).getAddress();
+        return findIpAddress(l.getParent());
+    }
+    
+    public HostGeoInfo(String address, String displayName, double latitude, 
double longitude) {
+        this.address = address;
+        this.displayName = displayName==null ? "" : displayName;
+        this.latitude = latitude;
+        this.longitude = longitude;
+    }
+
+    public String getAddress() {
+        return address;
+    }
+    
+    @Override
+    public String toString() {
+        return "HostGeoInfo["+displayName+": "+(address!=null ? address : 
"(no-address)")+" at ("+latitude+","+longitude+")]";
+    }
+    
+    @Override
+    public boolean equals(Object o) {
+        // Slight cheat: only includes the address + displayName field 
(displayName to allow overloading localhost etc)
+        return (o instanceof HostGeoInfo) && Objects.equal(address, 
((HostGeoInfo) o).address)
+                && Objects.equal(displayName, ((HostGeoInfo) o).displayName);
+    }
+    
+    @Override
+    public int hashCode() {
+        // Slight cheat: only includes the address + displayName field 
(displayName to allow overloading localhost etc)
+        return Objects.hashCode(address, displayName);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/location/geo/HostGeoLookup.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/geo/HostGeoLookup.java 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/HostGeoLookup.java
new file mode 100644
index 0000000..ec25e07
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/HostGeoLookup.java
@@ -0,0 +1,27 @@
+/*
+ * 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.core.location.geo;
+
+import java.net.InetAddress;
+
+public interface HostGeoLookup {
+
+    public HostGeoInfo getHostGeoInfo(InetAddress address) throws Exception;
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/location/geo/LocalhostExternalIpLoader.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/geo/LocalhostExternalIpLoader.java
 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/LocalhostExternalIpLoader.java
new file mode 100644
index 0000000..fd95585
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/LocalhostExternalIpLoader.java
@@ -0,0 +1,177 @@
+/*
+ * 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.core.location.geo;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.brooklyn.util.core.ResourceUtils;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.text.StringPredicates;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Durations;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Predicates;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+public class LocalhostExternalIpLoader {
+
+    public static final Logger LOG = 
LoggerFactory.getLogger(LocalhostExternalIpLoader.class);
+
+    private static final AtomicBoolean retrievingLocalExternalIp = new 
AtomicBoolean(false);
+    private static final CountDownLatch triedLocalExternalIp = new 
CountDownLatch(1);
+    private static volatile String localExternalIp;
+
+    private static class IpLoader implements Callable<String> {
+        private static final Pattern ipPattern = Pattern.compile(
+                
"\\b((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\b");
+        final String url;
+
+        protected IpLoader(String url) {
+            this.url = url;
+        }
+
+        @Override
+        public String call() {
+            String response = 
ResourceUtils.create(LocalhostExternalIpLoader.class)
+                    .getResourceAsString(url).trim();
+            return postProcessResponse(response);
+        }
+
+        String postProcessResponse(String response) {
+            Matcher matcher = ipPattern.matcher(response);
+            boolean matched = matcher.find();
+            if (!matched) {
+                LOG.error("No IP address matched in output from {}: {}", url, 
response);
+                return null;
+            } else {
+                return matcher.group();
+            }
+        }
+    }
+
+    @VisibleForTesting
+    static List<String> getIpAddressWebsites() {
+        String file = new ResourceUtils(LocalhostExternalIpLoader.class)
+                
.getResourceAsString("classpath://org/apache/brooklyn/location/geo/external-ip-address-resolvers.txt");
+        Iterable<String> lines = Splitter.on('\n')
+                .omitEmptyStrings()
+                .trimResults()
+                .split(file);
+        List<String> urls = Lists.newArrayList(Iterables.filter(lines, 
Predicates.not(StringPredicates.startsWith("#"))));
+        Collections.shuffle(urls);
+        return urls;
+    }
+
+    @VisibleForTesting
+    static String getIpAddressFrom(String url) {
+        return new IpLoader(url).call();
+    }
+    
+    /** As {@link #getLocalhostIpWithin(Duration)} but returning 127.0.0.1 if 
not accessible */
+    public static String getLocalhostIpQuicklyOrDefault() {
+        String result = doLoad(Duration.seconds(2));
+        if (result==null) return "127.0.0.1";
+        return result;
+    }
+
+    /** As {@link #getLocalhostIpWithin(Duration)} but without the time limit 
cut-off, failing if the load gives an error. */
+    public static String getLocalhostIpWaiting() {
+        return getLocalhostIpWithin(null);
+    }
+
+    /**
+     * Attempts to load the public IP address of localhost, failing if the load
+     * does not complete within the given duration.
+     * @return The public IP address of localhost
+     */
+    public static String getLocalhostIpWithin(Duration timeout) {
+        String result = doLoad(timeout);
+        if (result == null) {
+            throw new IllegalStateException("Unable to retrieve external IP 
for localhost; network may be down or slow or remote service otherwise not 
responding");
+        }
+        return result;
+    }
+
+    /**
+     * Requests URLs returned by {@link #getIpAddressWebsites()} until one 
returns an IP address.
+     * The address is assumed to be the external IP address of localhost.
+     * @param blockFor The maximum duration to wait for the IP address to be 
resolved.
+     *                 An indefinite way if null.
+     * @return A string in IPv4 format, or null if no such address could be 
ascertained.
+     */
+    private static String doLoad(Duration blockFor) {
+        if (localExternalIp != null) {
+            return localExternalIp;
+        }
+
+        final List<String> candidateUrls = getIpAddressWebsites();
+        if (candidateUrls.isEmpty()) {
+            LOG.debug("No candidate URLs to use to determine external IP of 
localhost");
+            return null;
+        }
+
+        // do in private thread, otherwise blocks for 30s+ on dodgy network!
+        // (we can skip it if someone else is doing it, we have synch lock so 
we'll get notified)
+        if (retrievingLocalExternalIp.compareAndSet(false, true)) {
+            new Thread() {
+                public void run() {
+                    for (String url : candidateUrls) {
+                        try {
+                            LOG.debug("Looking up external IP of this host 
from {} in private thread {}", url, Thread.currentThread());
+                            localExternalIp = new IpLoader(url).call();
+                            LOG.debug("Finished looking up external IP of this 
host from {} in private thread, result {}", url, localExternalIp);
+                            break;
+                        } catch (Throwable t) {
+                            LOG.debug("Unable to look up external IP of this 
host from {}, probably offline {})", url, t);
+                        } finally {
+                            retrievingLocalExternalIp.set(false);
+                            triedLocalExternalIp.countDown();
+                        }
+                    }
+                }
+            }.start();
+        }
+
+        try {
+            if (blockFor!=null) {
+                Durations.await(triedLocalExternalIp, blockFor);
+            } else {
+                triedLocalExternalIp.await();
+            }
+        } catch (InterruptedException e) {
+            throw Exceptions.propagate(e);
+        }
+        if (localExternalIp == null) {
+            return null;
+        }
+        return localExternalIp;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/location/geo/MaxMind2HostGeoLookup.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/geo/MaxMind2HostGeoLookup.java
 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/MaxMind2HostGeoLookup.java
new file mode 100644
index 0000000..1880441
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/MaxMind2HostGeoLookup.java
@@ -0,0 +1,114 @@
+/*
+ * 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.core.location.geo;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+
+import org.apache.brooklyn.util.internal.BrooklynSystemProperties;
+import org.apache.brooklyn.util.net.Networking;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+import com.maxmind.geoip2.DatabaseReader;
+import com.maxmind.geoip2.model.CityResponse;
+import com.maxmind.geoip2.record.Subdivision;
+
+public class MaxMind2HostGeoLookup implements HostGeoLookup {
+
+    public static final Logger log = 
LoggerFactory.getLogger(MaxMind2HostGeoLookup.class);
+    
+    static final String MAXMIND_DB_URL = 
"http://dev.maxmind.com/geoip/geoip2/geolite2/#Downloads";;
+    // TODO this should be configurable from system property or 
brooklyn.properties
+    // TODO and should use properties BrooklynServerConfig.MGMT_BASE_DIR (but 
hard to get mgmt properties here!)
+    static final String MAXMIND_DB_PATH = 
System.getProperty("user.home")+"/"+".brooklyn/"+"GeoLite2-City.mmdb";
+    
+    static boolean lookupFailed = false;
+    static DatabaseReader databaseReader = null;
+    
+    public static synchronized DatabaseReader getDatabaseReader() {
+        if (databaseReader!=null) return databaseReader;
+        try {
+            File f = new File(MAXMIND_DB_PATH);
+            databaseReader = new DatabaseReader.Builder(f).build();
+        } catch (IOException e) {
+            lookupFailed = true;
+            log.debug("MaxMind geo lookup unavailable; either download and 
unpack the latest "+
+                    "binary from "+MAXMIND_DB_URL+" into "+MAXMIND_DB_PATH+", 
"+
+                    "or specify a different HostGeoLookup implementation with 
the key "+
+                    
BrooklynSystemProperties.HOST_GEO_LOOKUP_IMPL.getPropertyName()+" (error trying 
to read: "+e+")");
+        }
+        return databaseReader;
+    }
+    
+    public HostGeoInfo getHostGeoInfo(InetAddress address) throws 
MalformedURLException, IOException {
+        if (lookupFailed) return null;
+        
+        DatabaseReader ll = getDatabaseReader();
+        if (ll==null) return null;
+        
+        InetAddress extAddress = address;
+        if (Networking.isPrivateSubnet(extAddress)) extAddress = 
InetAddress.getByName(LocalhostExternalIpLoader.getLocalhostIpQuicklyOrDefault());
+        
+        try {
+            CityResponse l = ll.city(extAddress);
+            if (l==null) {
+                if (log.isDebugEnabled()) log.debug("Geo info failed to find 
location for address {}, using {}", extAddress, ll);
+                return null;
+            }
+            
+            StringBuilder name = new StringBuilder();
+            
+            if (l.getCity()!=null && l.getCity().getName()!=null) 
name.append(l.getCity().getName());
+            
+            if (l.getSubdivisions()!=null) {
+                for (Subdivision subd: Lists.reverse(l.getSubdivisions())) {
+                    if (name.length()>0) name.append(", ");
+                    // prefer e.g. USA state codes over state names
+                    if (!Strings.isBlank(subd.getIsoCode())) 
+                        name.append(subd.getIsoCode());
+                    else
+                        name.append(subd.getName());
+                }
+            }
+            
+            if (l.getCountry()!=null) {
+                if (name.length()==0) {
+                    name.append(l.getCountry().getName());
+                } else {
+                    name.append(" ("); 
name.append(l.getCountry().getIsoCode()); name.append(")");
+                }
+            }
+
+            
+            HostGeoInfo geo = new HostGeoInfo(address.getHostName(), 
name.toString(), l.getLocation().getLatitude(), l.getLocation().getLongitude());
+            log.debug("Geo info lookup (MaxMind DB) for "+address+" returned: 
"+geo);
+            return geo;
+        } catch (Exception e) {
+            if (log.isDebugEnabled())
+                log.debug("Geo info lookup failed: "+e);
+            throw Throwables.propagate(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/location/geo/UtraceHostGeoLookup.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/geo/UtraceHostGeoLookup.java
 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/UtraceHostGeoLookup.java
new file mode 100644
index 0000000..5026ac4
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/core/location/geo/UtraceHostGeoLookup.java
@@ -0,0 +1,209 @@
+/*
+ * 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.core.location.geo;
+
+import groovy.util.Node;
+import groovy.util.NodeList;
+import groovy.util.XmlParser;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
+import org.apache.brooklyn.util.net.Networking;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Durations;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Throwables;
+
+public class UtraceHostGeoLookup implements HostGeoLookup {
+
+
+    /*
+     * 
+http://xml.utrace.de/?query=88.198.156.18 
+(IP address or hostname)
+
+The XML result is as follows:
+
+<?xml version="1.0" encoding="iso-8869-1"?>
+<results>
+<result>
+<ip>88.198.156.18</ip>
+<host>utrace.de</host>
+<isp>Hetzner Online AG</isp>
+<org>Pagedesign GmbH</org>
+<region>Hamburg</region>
+<countrycode>DE</countrycode>
+<latitude>53.5499992371</latitude>
+<longitude>10</longitude>
+<queries>10</queries>
+</result>
+</results>
+
+Note the queries count field -- you are permitted 100 per day.
+Beyond this you get blacklisted and requests may time out, or return none.
+(This may last for several days once blacklisting, not sure how long.)
+     */
+    
+    /** after failures, subsequent retries within this time interval are 
blocked */
+    private static final Duration RETRY_INTERVAL = Duration.FIVE_MINUTES;
+    /** requests taking longer than this period are deemed to have timed out 
and failed;
+     * set reasonably low so that if we are blacklisted for making too many 
requests,
+     * the call to get geo info does not take very long */
+    private static final Duration REQUEST_TIMEOUT = Duration.seconds(3);
+    
+    public static final Logger log = 
LoggerFactory.getLogger(UtraceHostGeoLookup.class);
+    
+    public String getLookupUrlForPublicIp(String ip) {
+        return "http://xml.utrace.de/?query="+ip.trim();
+    }
+
+    /**
+     * @deprecated since 0.7.0. Use {@link LocalhostExternalIpLoader} instead.
+     */
+    @Deprecated
+    public static String getLocalhostExternalIp() {
+        return 
LocalhostExternalIpLoader.getLocalhostIpWithin(Duration.seconds(2));
+    }
+    
+    /**
+     * @deprecated since 0.7.0. Use {@link LocalhostExternalIpLoader} instead.
+     */
+    @Deprecated
+    public static String getLocalhostExternalIpImpl() {
+        return 
LocalhostExternalIpLoader.getLocalhostIpWithin(Duration.seconds(2));
+    }
+    
+    public String getLookupUrlForLocalhost() {
+        return 
getLookupUrlForPublicIp(LocalhostExternalIpLoader.getLocalhostIpQuicklyOrDefault());
+    }
+
+    /** returns URL to get properties for the given address (assuming 
localhost if address is on a subnet) */
+    public String getLookupUrlFor(InetAddress address) {
+        if (Networking.isPrivateSubnet(address)) return 
getLookupUrlForLocalhost();
+        return getLookupUrlForPublicIp(address.getHostAddress());
+    }
+    
+    private static boolean LOGGED_GEO_LOOKUP_UNAVAILABLE = false;
+    private static long LAST_FAILURE_UTC = -1;
+    
+    /** does the {@link #retrieveHostGeoInfo(InetAddress)}, but in the 
background with a default timeout */
+    public HostGeoInfo getHostGeoInfo(InetAddress address) throws 
MalformedURLException, IOException {
+        if (Duration.sinceUtc(LAST_FAILURE_UTC).compareTo(RETRY_INTERVAL) < 0) 
{
+            // wait at least 60s since a failure
+            return null;
+        }
+        return getHostGeoInfo(address, REQUEST_TIMEOUT);
+    }
+    
+    /** does a {@link #retrieveHostGeoInfo(InetAddress)} with a timeout 
(returning null, interrupting, and setting failure time) */
+    public HostGeoInfo getHostGeoInfo(final InetAddress address, Duration 
timeout) throws MalformedURLException, IOException {
+        final AtomicReference<HostGeoInfo> result = new 
AtomicReference<HostGeoInfo>();
+        Thread lt = new Thread() {
+            public void run() {
+                try {
+                    result.set(retrieveHostGeoInfo(address));
+                } catch (Exception e) {
+                    log.warn("Error computing geo info for "+address+"; 
internet issues or too many requests to (free) servers for 
"+JavaClassNames.simpleClassName(UtraceHostGeoLookup.this)+": "+e);
+                    log.debug("Detail of host geo error: "+e, e);
+                }
+            }
+        };
+        lt.start();
+
+        try {
+            Durations.join(lt, timeout);
+        } catch (InterruptedException e) {
+            throw Exceptions.propagate(e);
+        }
+        
+        if (lt.isAlive()) {
+            // interrupt and set the failure time so that subsequent attempts 
do not face this timeout
+            lt.interrupt();
+            LAST_FAILURE_UTC = System.currentTimeMillis();
+            log.debug("Geo info lookup for "+address+" timed out after 
"+timeout);
+        }
+        
+        return result.get();
+    }
+    
+    public HostGeoInfo retrieveHostGeoInfo(InetAddress address) throws 
MalformedURLException, IOException {
+        String url = getLookupUrlFor(address);
+        if (log.isDebugEnabled())
+            log.debug("Geo info lookup for "+address+" at "+url);
+        Node xml;
+        try {
+            xml = new XmlParser().parse(getLookupUrlFor(address));
+        } catch (Exception e) {
+            LAST_FAILURE_UTC = System.currentTimeMillis();
+            if (log.isDebugEnabled())
+                log.debug("Geo info lookup for "+address+" failed: "+e);
+            if (!LOGGED_GEO_LOOKUP_UNAVAILABLE) {
+                LOGGED_GEO_LOOKUP_UNAVAILABLE = true;
+                log.info("Geo info lookup unavailable (for "+address+"; cause 
"+e+")");
+            }
+            return null;
+        }
+        try {
+            String org = getXmlResultsField(xml, "org").trim();
+            if (org.isEmpty()) org = getXmlResultsField(xml, "isp").trim();
+            String region = getXmlResultsField(xml, "region").trim();
+            if (!org.isEmpty()) {
+                if (!region.isEmpty()) region = org+", "+region;
+                else region = org;
+            }
+            if (region.isEmpty()) region = getXmlResultsField(xml, 
"isp").trim();
+            if (region.isEmpty()) region = address.toString();
+            HostGeoInfo geo = new HostGeoInfo(address.getHostName(), 
+                    region+
+                    " ("+getXmlResultsField(xml, "countrycode")+")", 
+                    Double.parseDouble(""+getXmlResultsField(xml, 
"latitude")), 
+                    Double.parseDouble(""+getXmlResultsField(xml, 
"longitude")));
+            log.info("Geo info lookup for "+address+" returned: "+geo);
+            return geo;
+        } catch (Exception e) {
+            if (log.isDebugEnabled())
+                log.debug("Geo info lookup failed, for "+address+" at "+url+", 
due to "+e+"; response is "+xml);
+            throw Throwables.propagate(e);
+        }
+    }
+    
+    @Nullable
+    private static Node getFirstChild(Node xml, String field) {
+        if (xml==null) return null;
+        NodeList nl = (NodeList)xml.get(field);
+        if (nl==null || nl.isEmpty()) return null;
+        return (Node)nl.get(0);
+    }
+    @Nonnull
+    private static String getXmlResultsField(Node xml, String field) {
+        Node f1 = getFirstChild(getFirstChild(xml, "result"), field);
+        if (f1==null) return "";
+        return f1.text();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/location/internal/LocationDynamicType.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/internal/LocationDynamicType.java
 
b/core/src/main/java/org/apache/brooklyn/core/location/internal/LocationDynamicType.java
new file mode 100644
index 0000000..59f0d34
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/core/location/internal/LocationDynamicType.java
@@ -0,0 +1,40 @@
+/*
+ * 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.core.location.internal;
+
+import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.objs.BrooklynDynamicType;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.LocationType;
+
+public class LocationDynamicType extends BrooklynDynamicType<Location, 
AbstractLocation> {
+
+    public LocationDynamicType(AbstractLocation location) {
+        super(location);
+    }
+    
+    public LocationType getSnapshot() {
+        return (LocationType) super.getSnapshot();
+    }
+
+    @Override
+    protected LocationTypeSnapshot newSnapshot() {
+        return new LocationTypeSnapshot(name, value(configKeys));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/location/internal/LocationInternal.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/internal/LocationInternal.java
 
b/core/src/main/java/org/apache/brooklyn/core/location/internal/LocationInternal.java
new file mode 100644
index 0000000..18bceef
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/core/location/internal/LocationInternal.java
@@ -0,0 +1,93 @@
+/*
+ * 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.core.location.internal;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.mgmt.rebind.RebindSupport;
+import org.apache.brooklyn.api.mgmt.rebind.mementos.LocationMemento;
+import org.apache.brooklyn.config.ConfigInheritance;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Information about locations private to Brooklyn.
+ */
+public interface LocationInternal extends BrooklynObjectInternal, Location {
+
+    @Beta
+    public static final ConfigKey<String> ORIGINAL_SPEC = 
ConfigKeys.newStringConfigKey("spec.original", "The original spec used to 
instantiate a location");
+    @Beta
+    public static final ConfigKey<String> FINAL_SPEC = 
ConfigKeys.newStringConfigKey("spec.final", "The actual spec (in a chain) which 
instantiates a location");
+    @Beta
+    public static final ConfigKey<String> NAMED_SPEC_NAME = 
ConfigKeys.newStringConfigKey("spec.named.name", "The name on the (first) named 
spec in a chain");
+    
+    /**
+     * Registers the given extension for the given type. If an extension 
already existed for
+     * this type, then this will override it.
+     * 
+     * @throws NullPointerException if extensionType or extension are null
+     * @throws IllegalArgumentException if extension does not implement 
extensionType
+     */
+    <T> void addExtension(Class<T> extensionType, T extension);
+
+    /**
+     * Get a record of the metadata of this location.
+     * <p/>
+     * <p>Metadata records are used to record an audit trail of events 
relating to location usage
+     * (for billing purposes, for example). Implementations (and subclasses) 
should override this
+     * method to return information useful for this purpose.</p>
+     *
+     * @return
+     */
+    public Map<String, String> toMetadataRecord();
+
+    /**
+     * @deprecated since 0.7.0; use {@link #config()}, such as {@code 
((LocationInternal)location).config().getLocalBag()}
+     */
+    @Deprecated
+    ConfigBag getLocalConfigBag();
+
+    /**
+     * Returns all config, including that inherited from parents.
+     * 
+     * This method does not respect {@link ConfigInheritance} and so usage is 
discouraged.
+     * 
+     * @deprecated since 0.7.0; use {@link #config()}, such as {@code 
((LocationInternal)location).config().getBag()}
+     */
+    @Deprecated
+    ConfigBag getAllConfigBag();
+
+    /**
+     * Users are strongly discouraged from calling or overriding this method.
+     * It is for internal calls only, relating to persisting/rebinding 
entities.
+     * This method may change (or be removed) in a future release without 
notice.
+     */
+    @Override
+    @Beta
+    RebindSupport<LocationMemento> getRebindSupport();
+    
+    ManagementContext getManagementContext();
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/location/internal/LocationTypeSnapshot.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/internal/LocationTypeSnapshot.java
 
b/core/src/main/java/org/apache/brooklyn/core/location/internal/LocationTypeSnapshot.java
new file mode 100644
index 0000000..1c45da2
--- /dev/null
+++ 
b/core/src/main/java/org/apache/brooklyn/core/location/internal/LocationTypeSnapshot.java
@@ -0,0 +1,40 @@
+/*
+ * 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.core.location.internal;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.sensor.EnricherType;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.objs.BrooklynTypeSnapshot;
+
+public class LocationTypeSnapshot extends BrooklynTypeSnapshot implements 
EnricherType {
+    
+    private static final long serialVersionUID = 9150132836104748237L;
+
+    LocationTypeSnapshot(String name, Map<String, ConfigKey<?>> configKeys) {
+        super(name, configKeys);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        return (obj instanceof LocationTypeSnapshot) && super.equals(obj);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
index d49397b..163ccc6 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
@@ -63,13 +63,13 @@ import org.apache.brooklyn.core.internal.storage.DataGrid;
 import org.apache.brooklyn.core.internal.storage.DataGridFactory;
 import org.apache.brooklyn.core.internal.storage.impl.BrooklynStorageImpl;
 import 
org.apache.brooklyn.core.internal.storage.impl.inmemory.InMemoryDataGridFactory;
+import org.apache.brooklyn.core.location.BasicLocationRegistry;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.mgmt.classloading.BrooklynClassLoadingContext;
 import 
org.apache.brooklyn.core.mgmt.classloading.JavaBrooklynClassLoadingContext;
 import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
 import org.apache.brooklyn.core.mgmt.ha.HighAvailabilityManagerImpl;
 import org.apache.brooklyn.core.mgmt.rebind.RebindManagerImpl;
-import org.apache.brooklyn.location.core.BasicLocationRegistry;
 import org.apache.brooklyn.util.GroovyJavaMethods;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalLocationManager.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalLocationManager.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalLocationManager.java
index d07b6ea..dcd1b43 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalLocationManager.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalLocationManager.java
@@ -35,12 +35,12 @@ import 
org.apache.brooklyn.core.BrooklynLogging.LoggingLevel;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
 import org.apache.brooklyn.core.internal.storage.BrooklynStorage;
+import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.location.internal.LocationInternal;
 import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
 import org.apache.brooklyn.core.objs.proxy.InternalLocationFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.location.core.AbstractLocation;
-import org.apache.brooklyn.location.core.internal.LocationInternal;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalUsageManager.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalUsageManager.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalUsageManager.java
index b25803d..ba47c02 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalUsageManager.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalUsageManager.java
@@ -39,6 +39,9 @@ import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
 import org.apache.brooklyn.core.internal.storage.BrooklynStorage;
+import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.location.LocationConfigKeys;
+import org.apache.brooklyn.core.location.internal.LocationInternal;
 import org.apache.brooklyn.core.mgmt.ManagementContextInjectable;
 import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
 import org.apache.brooklyn.core.mgmt.usage.ApplicationUsage;
@@ -46,9 +49,6 @@ import org.apache.brooklyn.core.mgmt.usage.LocationUsage;
 import org.apache.brooklyn.core.mgmt.usage.UsageManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.location.core.AbstractLocation;
-import org.apache.brooklyn.location.core.LocationConfigKeys;
-import org.apache.brooklyn.location.core.internal.LocationInternal;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.javalang.Reflections;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicLocationRebindSupport.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicLocationRebindSupport.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicLocationRebindSupport.java
index c3b657d..b789501 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicLocationRebindSupport.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/BasicLocationRebindSupport.java
@@ -27,10 +27,10 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.mgmt.rebind.RebindContext;
 import org.apache.brooklyn.api.mgmt.rebind.mementos.LocationMemento;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.location.AbstractLocation;
 import org.apache.brooklyn.core.mgmt.rebind.dto.MementosGenerators;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.location.core.AbstractLocation;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/ImmediateDeltaChangeListener.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/ImmediateDeltaChangeListener.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/ImmediateDeltaChangeListener.java
index 81785bc..6252d28 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/ImmediateDeltaChangeListener.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/ImmediateDeltaChangeListener.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.core.mgmt.rebind;
 import java.util.Collection;
 import java.util.Map;
 
+import org.apache.brooklyn.core.location.internal.LocationInternal;
 import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
 import org.apache.brooklyn.api.catalog.CatalogItem;
 import org.apache.brooklyn.api.entity.Entity;
@@ -37,7 +38,6 @@ import 
org.apache.brooklyn.api.mgmt.rebind.mementos.PolicyMemento;
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.policy.Policy;
 import org.apache.brooklyn.api.sensor.Enricher;
-import org.apache.brooklyn.location.core.internal.LocationInternal;
 
 import com.google.common.collect.Maps;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
index c481ade..e6d030d 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
@@ -67,6 +67,8 @@ import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.entity.AbstractApplication;
 import org.apache.brooklyn.core.entity.AbstractEntity;
 import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.location.internal.LocationInternal;
 import org.apache.brooklyn.core.mgmt.classloading.BrooklynClassLoadingContext;
 import org.apache.brooklyn.core.mgmt.internal.BrooklynObjectManagementMode;
 import org.apache.brooklyn.core.mgmt.internal.BrooklynObjectManagerInternal;
@@ -82,8 +84,6 @@ import 
org.apache.brooklyn.core.objs.proxy.InternalEntityFactory;
 import org.apache.brooklyn.core.objs.proxy.InternalFactory;
 import org.apache.brooklyn.core.objs.proxy.InternalLocationFactory;
 import org.apache.brooklyn.core.objs.proxy.InternalPolicyFactory;
-import org.apache.brooklyn.location.core.AbstractLocation;
-import org.apache.brooklyn.location.core.internal.LocationInternal;
 import org.apache.brooklyn.policy.core.AbstractPolicy;
 import org.apache.brooklyn.sensor.enricher.AbstractEnricher;
 import org.apache.brooklyn.sensor.feed.AbstractFeed;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
index 53892c4..3710cfb 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/dto/MementosGenerators.java
@@ -51,11 +51,11 @@ import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.catalog.internal.CatalogItemDo;
 import org.apache.brooklyn.core.entity.EntityDynamicType;
 import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.location.internal.LocationInternal;
 import org.apache.brooklyn.core.mgmt.persist.BrooklynPersistenceUtils;
 import 
org.apache.brooklyn.core.mgmt.rebind.AbstractBrooklynObjectRebindSupport;
 import org.apache.brooklyn.core.mgmt.rebind.TreeUtils;
 import org.apache.brooklyn.core.objs.BrooklynTypes;
-import org.apache.brooklyn.location.core.internal.LocationInternal;
 import org.apache.brooklyn.policy.core.AbstractPolicy;
 import org.apache.brooklyn.sensor.enricher.AbstractEnricher;
 import org.apache.brooklyn.sensor.feed.AbstractFeed;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalLocationFactory.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalLocationFactory.java
 
b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalLocationFactory.java
index e66c8b3..9391a57 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalLocationFactory.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalLocationFactory.java
@@ -26,10 +26,10 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.location.LocationSpec;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.core.location.internal.LocationInternal;
 import org.apache.brooklyn.core.mgmt.internal.LocalLocationManager;
 import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
-import org.apache.brooklyn.location.core.AbstractLocation;
-import org.apache.brooklyn.location.core.internal.LocationInternal;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
 import org.apache.brooklyn.util.exceptions.Exceptions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/effector/core/EffectorTasks.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/effector/core/EffectorTasks.java 
b/core/src/main/java/org/apache/brooklyn/effector/core/EffectorTasks.java
index 4f1b8d5..249da53 100644
--- a/core/src/main/java/org/apache/brooklyn/effector/core/EffectorTasks.java
+++ b/core/src/main/java/org/apache/brooklyn/effector/core/EffectorTasks.java
@@ -29,11 +29,11 @@ import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.mgmt.TaskAdaptable;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.location.Machines;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.mgmt.internal.EffectorUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.location.core.Machines;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
 import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
 import org.apache.brooklyn.util.core.config.ConfigBag;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java 
b/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
index 67b2b91..7a0c31b 100644
--- 
a/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
+++ 
b/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
@@ -48,12 +48,12 @@ import 
org.apache.brooklyn.core.entity.lifecycle.QuorumCheck.QuorumChecks;
 import 
org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ServiceProblemsLogic;
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.entity.trait.StartableMethods;
+import org.apache.brooklyn.core.location.Locations;
+import org.apache.brooklyn.core.location.cloud.AvailabilityZoneExtension;
 import org.apache.brooklyn.effector.core.Effectors;
 import org.apache.brooklyn.entity.stock.DelegateEntity;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.location.cloud.AvailabilityZoneExtension;
-import org.apache.brooklyn.location.core.Locations;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartable.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartable.java 
b/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartable.java
index 2607d53..2e56fab 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartable.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartable.java
@@ -26,7 +26,7 @@ import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.entity.trait.Startable;
-import org.apache.brooklyn.location.core.Locations;
+import org.apache.brooklyn.core.location.Locations;
 
 import com.google.common.collect.ImmutableList;
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java 
b/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
index 70f071a..4906839 100644
--- 
a/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
+++ 
b/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
@@ -33,7 +33,7 @@ import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
 import org.apache.brooklyn.core.entity.trait.Startable;
 import org.apache.brooklyn.core.entity.trait.StartableMethods;
-import org.apache.brooklyn.location.core.Locations;
+import org.apache.brooklyn.core.location.Locations;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/538324e1/core/src/main/java/org/apache/brooklyn/location/access/BrooklynAccessUtils.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/location/access/BrooklynAccessUtils.java
 
b/core/src/main/java/org/apache/brooklyn/location/access/BrooklynAccessUtils.java
deleted file mode 100644
index c2eb7b7..0000000
--- 
a/core/src/main/java/org/apache/brooklyn/location/access/BrooklynAccessUtils.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.access;
-
-import java.util.Collection;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.location.MachineLocation;
-import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.core.config.BasicConfigKey;
-import org.apache.brooklyn.core.entity.Attributes;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.location.core.Machines;
-import org.apache.brooklyn.location.core.SupportsPortForwarding;
-import org.apache.brooklyn.location.ssh.SshMachineLocation;
-import org.apache.brooklyn.util.core.task.DynamicTasks;
-import org.apache.brooklyn.util.core.task.Tasks;
-import org.apache.brooklyn.util.core.task.ssh.SshTasks;
-import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
-import org.apache.brooklyn.util.exceptions.Exceptions;
-import org.apache.brooklyn.util.guava.Maybe;
-import org.apache.brooklyn.util.net.Cidr;
-import org.apache.brooklyn.util.text.Strings;
-import org.python.google.common.base.Predicates;
-import org.python.google.common.collect.Iterables;
-
-import com.google.common.base.Supplier;
-import com.google.common.net.HostAndPort;
-
-public class BrooklynAccessUtils {
-
-    private static final Logger log = 
LoggerFactory.getLogger(BrooklynAccessUtils.class);
-    
-    public static final ConfigKey<PortForwardManager> PORT_FORWARDING_MANAGER 
= new BasicConfigKey<PortForwardManager>(
-            PortForwardManager.class, "brooklyn.portforwarding.manager", "A 
port-forwarding manager to use at an entity "
-                + "or a location, where supported; note this should normally 
be a serializable client instance to prevent "
-                + "the creation of multiple disconnected instances via config 
duplication");
-    
-    public static final ConfigKey<Cidr> MANAGEMENT_ACCESS_CIDR = new 
BasicConfigKey<Cidr>(
-            Cidr.class, "brooklyn.portforwarding.management.cidr", "CIDR to 
enable by default for port-forwarding for management",
-            null);  // TODO should be a list
-
-    public static HostAndPort getBrooklynAccessibleAddress(Entity entity, int 
port) {
-        String host;
-        
-        // look up port forwarding
-        PortForwardManager pfw = entity.getConfig(PORT_FORWARDING_MANAGER);
-        if (pfw!=null) {
-            Collection<Location> ll = entity.getLocations();
-            
-            synchronized (BrooklynAccessUtils.class) {
-                // TODO finer-grained synchronization
-                
-                for (MachineLocation machine : Iterables.filter(ll, 
MachineLocation.class)) {
-                    HostAndPort hp = pfw.lookup(machine, port);
-                    if (hp!=null) {
-                        log.debug("BrooklynAccessUtils found port-forwarded 
address {} for entity {}, port {}, using machine {}",
-                                new Object[] {hp, entity, port, machine});
-                        return hp;
-                    }
-                }
-                
-                Maybe<SupportsPortForwarding> supportPortForwardingLoc = 
Machines.findUniqueElement(ll, SupportsPortForwarding.class);
-                if (supportPortForwardingLoc.isPresent()) {
-                    Cidr source = entity.getConfig(MANAGEMENT_ACCESS_CIDR);
-                    SupportsPortForwarding loc = 
supportPortForwardingLoc.get();
-                    if (source!=null) {
-                        log.debug("BrooklynAccessUtils requesting new 
port-forwarding rule to access "+port+" on "+entity+" (at "+loc+", enabled for 
"+source+")");
-                        // TODO discuss, is this the best way to do it
-                        // (will probably _create_ the port forwarding rule!)
-                        HostAndPort hp = loc.getSocketEndpointFor(source, 
port);
-                        if (hp!=null) {
-                            log.debug("BrooklynAccessUtils created 
port-forwarded address {} for entity {}, port {}, using {}",
-                                    new Object[] {hp, entity, port, loc});
-                            return hp;
-                        }
-                    } else {
-                        log.warn("No "+MANAGEMENT_ACCESS_CIDR.getName()+" 
configured for "+entity+", so cannot forward "
-                                +"port "+port+" "+"even though 
"+PORT_FORWARDING_MANAGER.getName()+" was supplied, and "
-                                +"have location supporting port forwarding 
"+loc);
-                    }
-                }
-            }
-        }
-        
-        host = entity.getAttribute(Attributes.HOSTNAME);
-        if (host!=null) return HostAndPort.fromParts(host, port);
-        
-        throw new IllegalStateException("Cannot find way to access port 
"+port+" on "+entity+" from Brooklyn (no host.name)");
-    }
-
-    /** attempts to resolve hostnameTarget from origin
-     * @return null if it definitively can't be resolved,  
-     * best-effort IP address if possible, or blank if we could not run ssh or 
make sense of the output */
-    public static String getResolvedAddress(Entity entity, SshMachineLocation 
origin, String hostnameTarget) {
-        ProcessTaskWrapper<Integer> task = 
SshTasks.newSshExecTaskFactory(origin, "ping -c 1 -t 1 "+hostnameTarget)
-            .summary("checking resolution of 
"+hostnameTarget).allowingNonZeroExitCode().newTask();
-        
DynamicTasks.queueIfPossible(task).orSubmitAndBlock(entity).asTask().blockUntilEnded();
-        if (task.asTask().isError()) {
-            log.warn("ping could not be run, at "+entity+" / "+origin+": 
"+Tasks.getError(task.asTask()));
-            return "";
-        }
-        if (task.getExitCode()==null || task.getExitCode()!=0) {
-            if (task.getExitCode()!=null && task.getExitCode()<10) {
-                // small number means ping failed to resolve or ping the 
hostname
-                log.debug("not able to resolve "+hostnameTarget+" from 
"+origin+" for "+entity+" because exit code was "+task.getExitCode());
-                return null;
-            }
-            // large number means ping probably did not run
-            log.warn("ping not run as expected, at "+entity+" / "+origin+" 
(code "+task.getExitCode()+"):\n"+task.getStdout().trim()+" --- 
"+task.getStderr().trim());
-            return "";
-        }
-        String out = task.getStdout();
-        try {
-            String line1 = Strings.getFirstLine(out);
-            String ip = Strings.getFragmentBetween(line1, "(", ")");
-            if (Strings.isNonBlank(ip)) 
-                return ip;
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            /* ignore non-parseable output */ 
-        }
-        if (out.contains("127.0.0.1")) return "127.0.0.1";
-        return "";
-    }
-
-    public static Supplier<String> resolvedAddressSupplier(final Entity 
entity, final SshMachineLocation origin, final String hostnameTarget) {
-        return new Supplier<String>() {
-            @Override
-            public String get() {
-                return getResolvedAddress(entity, origin, hostnameTarget);
-            }
-        };
-    }
-
-}

Reply via email to