You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2015/12/23 12:06:29 UTC

[06/71] [abbrv] incubator-brooklyn git commit: Merge commit 'e430723' into reorg2

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsRebindStubTest.java
----------------------------------------------------------------------
diff --cc brooklyn-server/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsRebindStubTest.java
index 0000000,ac7fd56..ea9e1c6
mode 000000,100644..100644
--- a/brooklyn-server/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsRebindStubTest.java
+++ b/brooklyn-server/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsRebindStubTest.java
@@@ -1,0 -1,250 +1,256 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one
+  * or more contributor license agreements.  See the NOTICE file
+  * distributed with this work for additional information
+  * regarding copyright ownership.  The ASF licenses this file
+  * to you under the Apache License, Version 2.0 (the
+  * "License"); you may not use this file except in compliance
+  * with the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing,
+  * software distributed under the License is distributed on an
+  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  * KIND, either express or implied.  See the License for the
+  * specific language governing permissions and limitations
+  * under the License.
+  */
+ package org.apache.brooklyn.location.jclouds;
+ 
+ import static com.google.common.base.Preconditions.checkNotNull;
+ import static org.testng.Assert.assertEquals;
++import static org.testng.Assert.assertFalse;
+ 
+ import java.net.URI;
+ import java.util.List;
+ import java.util.Set;
+ 
+ import org.apache.brooklyn.api.mgmt.ManagementContext;
+ import org.apache.brooklyn.core.entity.Entities;
+ import org.apache.brooklyn.core.internal.BrooklynProperties;
+ import org.apache.brooklyn.core.mgmt.rebind.RebindTestFixtureWithApp;
+ import org.apache.brooklyn.core.test.entity.TestApplication;
+ import org.apache.brooklyn.util.core.config.ConfigBag;
+ import org.apache.brooklyn.util.exceptions.CompoundRuntimeException;
+ import org.jclouds.compute.ComputeService;
+ import org.jclouds.compute.RunNodesException;
+ import org.jclouds.compute.domain.Image;
+ import org.jclouds.compute.domain.NodeMetadata;
+ import org.jclouds.compute.domain.NodeMetadata.Status;
+ import org.jclouds.compute.domain.OperatingSystem;
+ import org.jclouds.compute.domain.OsFamily;
+ import org.jclouds.compute.domain.Processor;
+ import org.jclouds.compute.domain.Template;
+ import org.jclouds.compute.domain.Volume;
+ import org.jclouds.compute.domain.internal.HardwareImpl;
+ import org.jclouds.compute.domain.internal.NodeMetadataImpl;
+ import org.jclouds.compute.options.TemplateOptions;
+ import org.jclouds.domain.LocationScope;
+ import org.jclouds.domain.LoginCredentials;
+ import org.jclouds.domain.internal.LocationImpl;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+ import org.testng.annotations.AfterMethod;
+ import org.testng.annotations.BeforeMethod;
+ import org.testng.annotations.Test;
+ 
++import com.google.common.base.Optional;
+ import com.google.common.base.Predicates;
+ import com.google.common.collect.ArrayListMultimap;
+ 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.Multimap;
+ import com.google.common.collect.Multimaps;
+ 
+ /**
+  * Tests rebind (i.e. restarting Brooklyn server) when there are live JcloudsSshMachineLocation object(s).
+  * 
+  * It is still a live test because it connects to the Softlayer API for finding images, etc.
+  * But it does not provision any VMs, so is much faster/cheaper.
+  */
+ public class JcloudsRebindStubTest extends RebindTestFixtureWithApp {
+ 
+     // TODO Duplication of AbstractJcloudsLiveTest, because we're subclassing RebindTestFixture instead.
+ 
+     private static final Logger LOG = LoggerFactory.getLogger(JcloudsRebindStubTest.class);
+ 
+     public static final String SOFTLAYER_LOCATION_SPEC = "jclouds:" + AbstractJcloudsLiveTest.SOFTLAYER_PROVIDER;
+     public static final String SOFTLAYER_IMAGE_ID = "UBUNTU_14_64";
+     
+     protected List<ManagementContext> mgmts;
+     protected Multimap<ManagementContext, JcloudsSshMachineLocation> machines;
+     protected BrooklynProperties brooklynProperties;
+     
+     @BeforeMethod(alwaysRun=true)
+     public void setUp() throws Exception {
+         super.setUp();
+         mgmts = Lists.newCopyOnWriteArrayList(ImmutableList.<ManagementContext>of(origManagementContext));
+         machines = Multimaps.synchronizedMultimap(ArrayListMultimap.<ManagementContext, JcloudsSshMachineLocation>create());
+         
+         // Don't let any defaults from brooklyn.properties (except credentials) interfere with test
+         brooklynProperties = origManagementContext.getBrooklynProperties();
+         AbstractJcloudsLiveTest.stripBrooklynProperties(brooklynProperties);
+     }
+ 
+     @AfterMethod(alwaysRun=true)
+     public void tearDown() throws Exception {
+         List<Exception> exceptions = Lists.newArrayList();
+         for (ManagementContext mgmt : mgmts) {
+             try {
+                 if (mgmt.isRunning()) Entities.destroyAll(mgmt);
+             } catch (Exception e) {
+                 LOG.warn("Error destroying management context", e);
+                 exceptions.add(e);
+             }
+         }
+         mgmts.clear();
+         origManagementContext = null;
+         newManagementContext = null;
+         origApp = null;
+         newApp = null;
+         
+         super.tearDown();
+         
+         if (exceptions.size() > 0) {
+             throw new CompoundRuntimeException("Error in tearDown of "+getClass(), exceptions);
+         }
+     }
+ 
+     @Override
+     protected boolean useLiveManagementContext() {
+         return true;
+     }
+ 
+     @Override
+     protected TestApplication rebind() throws Exception {
+         TestApplication result = super.rebind();
+         mgmts.add(newManagementContext);
+         return result;
+     }
+     
+     @Test(groups={"Live", "Live-sanity"})
+     public void testRebind() throws Exception {
+         LocationImpl locImpl = new LocationImpl(
+                 LocationScope.REGION, 
+                 "myLocId", 
+                 "myLocDescription", 
+                 null, 
+                 ImmutableList.<String>of(), // iso3166Codes 
+                 ImmutableMap.<String,Object>of()); // metadata
+         
+         NodeMetadata node = new NodeMetadataImpl(
+                 "softlayer", 
+                 "myname", 
 -                "myid", 
++                "123", // ids in SoftLayer are numeric
+                 locImpl,
+                 URI.create("http://myuri.com"), 
+                 ImmutableMap.<String, String>of(), // userMetadata 
+                 ImmutableSet.<String>of(), // tags
+                 "mygroup",
+                 new HardwareImpl(
+                         "myHardwareProviderId", 
+                         "myHardwareName", 
+                         "myHardwareId", 
+                         locImpl, 
+                         URI.create("http://myuri.com"), 
+                         ImmutableMap.<String, String>of(), // userMetadata 
+                         ImmutableSet.<String>of(), // tags
+                         ImmutableList.<Processor>of(), 
+                         1024, 
+                         ImmutableList.<Volume>of(), 
+                         Predicates.<Image>alwaysTrue(), // supportsImage, 
+                         (String)null, // hypervisor
+                         false),
+                 SOFTLAYER_IMAGE_ID,
+                 new OperatingSystem(
+                         OsFamily.CENTOS, 
+                         "myOsName", 
+                         "myOsVersion", 
+                         "myOsArch", 
+                         "myDescription", 
+                         true), // is64Bit
+                 Status.RUNNING,
+                 "myBackendStatus",
+                 22, // login-port
+                 ImmutableList.of("1.2.3.4"), // publicAddresses, 
+                 ImmutableList.of("10.2.3.4"), // privateAddresses, 
+                 LoginCredentials.builder().identity("myidentity").password("mypassword").build(), 
+                 "myHostname");
+         
+         ByonComputeServiceRegistry computeServiceRegistry = new ByonComputeServiceRegistry(node);
+         JcloudsLocation origJcloudsLoc = (JcloudsLocation) mgmt().getLocationRegistry().resolve("jclouds:softlayer", ImmutableMap.of(
+                 JcloudsLocation.COMPUTE_SERVICE_REGISTRY, computeServiceRegistry,
+                 JcloudsLocation.WAIT_FOR_SSHABLE, false,
+                 JcloudsLocation.USE_JCLOUDS_SSH_INIT, false));
+     
+         JcloudsSshMachineLocation origMachine = (JcloudsSshMachineLocation) origJcloudsLoc.obtain(ImmutableMap.of("imageId", SOFTLAYER_IMAGE_ID));
+         
+         String origHostname = origMachine.getHostname();
+         NodeMetadata origNode = origMachine.getNode();
+         Template origTemplate = origMachine.getTemplate();
+ 
+         rebind();
+         
 -        // Check the machine is as before
++        // Check the machine is as before.
++        // Call to getOptionalNode() will cause it to try to resolve this node in Softlayer; but it won't find it.
+         JcloudsSshMachineLocation newMachine = (JcloudsSshMachineLocation) newManagementContext.getLocationManager().getLocation(origMachine.getId());
+         JcloudsLocation newJcloudsLoc = newMachine.getParent();
+         String newHostname = newMachine.getHostname();
 -        NodeMetadata newNode = newMachine.getNode();
 -        Template newTemplate = newMachine.getTemplate();
++        String newNodeId = newMachine.getJcloudsId();
++        Optional<NodeMetadata> newNode = newMachine.getOptionalNode();
++        Optional<Template> newTemplate = newMachine.getOptionalTemplate();
+         
+         assertEquals(newHostname, origHostname);
 -        assertEquals(origNode.getId(), newNode.getId());
++        assertEquals(origNode.getId(), newNodeId);
++        assertFalse(newNode.isPresent(), "newNode="+newNode);
++        assertFalse(newTemplate.isPresent(), "newTemplate="+newTemplate);
+         
+         assertEquals(newJcloudsLoc.getProvider(), origJcloudsLoc.getProvider());
+     }
+     
+     protected static class ByonComputeServiceRegistry extends ComputeServiceRegistryImpl implements ComputeServiceRegistry {
+         private final NodeMetadata node;
+ 
+         ByonComputeServiceRegistry(NodeMetadata node) throws Exception {
+             this.node = node;
+         }
+ 
+         @Override
+         public ComputeService findComputeService(ConfigBag conf, boolean allowReuse) {
+             ComputeService delegate = super.findComputeService(conf, allowReuse);
+             return new StubComputeService(delegate, node);
+         }
+     }
+ 
+     static class StubComputeService extends DelegatingComputeService {
+         private final NodeMetadata node;
+         
+         public StubComputeService(ComputeService delegate, NodeMetadata node) {
+             super(delegate);
+             this.node = checkNotNull(node, "node");
+         }
+         
+         @Override
+         public void destroyNode(String id) {
+             // no-op
+         }
+         
+         @Override
+         public Set<? extends NodeMetadata> createNodesInGroup(String group, int count) throws RunNodesException {
+             return ImmutableSet.of(node);
+         }
+         
+         @Override
+         public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, Template template) throws RunNodesException {
+             return ImmutableSet.of(node);
+         }
+         
+         @Override
+         public Set<? extends NodeMetadata> createNodesInGroup(String group, int count, TemplateOptions templateOptions) throws RunNodesException {
+             return ImmutableSet.of(node);
+         }
+     }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java
----------------------------------------------------------------------
diff --cc brooklyn-server/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java
index 0000000,c9de22b..57c803c
mode 000000,100644..100644
--- a/brooklyn-server/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java
+++ b/brooklyn-server/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/RebindJcloudsLocationLiveTest.java
@@@ -1,0 -1,149 +1,326 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one
+  * or more contributor license agreements.  See the NOTICE file
+  * distributed with this work for additional information
+  * regarding copyright ownership.  The ASF licenses this file
+  * to you under the Apache License, Version 2.0 (the
+  * "License"); you may not use this file except in compliance
+  * with the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing,
+  * software distributed under the License is distributed on an
+  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  * KIND, either express or implied.  See the License for the
+  * specific language governing permissions and limitations
+  * under the License.
+  */
+ package org.apache.brooklyn.location.jclouds;
+ 
+ import static org.testng.Assert.assertEquals;
++import static org.testng.Assert.assertFalse;
+ import static org.testng.Assert.assertNull;
+ import static org.testng.Assert.assertTrue;
+ 
+ import java.io.File;
+ 
+ import org.apache.brooklyn.api.entity.EntitySpec;
+ import org.apache.brooklyn.api.location.OsDetails;
+ import org.apache.brooklyn.core.entity.Entities;
+ import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
+ import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
++import org.apache.brooklyn.core.mgmt.rebind.RebindOptions;
+ import org.apache.brooklyn.core.mgmt.rebind.RebindTestUtils;
+ import org.apache.brooklyn.core.test.entity.TestApplication;
++import org.apache.brooklyn.util.core.ResourceUtils;
+ import org.apache.brooklyn.util.core.config.ConfigBag;
++import org.apache.commons.io.FileUtils;
+ import org.testng.annotations.AfterMethod;
+ import org.testng.annotations.BeforeMethod;
+ import org.testng.annotations.Test;
+ 
++import com.google.common.base.Optional;
+ import com.google.common.base.Predicates;
+ import com.google.common.collect.ImmutableList;
++import com.google.common.collect.ImmutableSet;
+ import com.google.common.collect.Iterables;
+ import com.google.common.io.Files;
++import com.google.common.net.HostAndPort;
+ 
+ public class RebindJcloudsLocationLiveTest extends AbstractJcloudsLiveTest {
+ 
+     public static final String AWS_EC2_REGION_NAME = AWS_EC2_USEAST_REGION_NAME;
+     public static final String AWS_EC2_LOCATION_SPEC = "jclouds:" + AWS_EC2_PROVIDER + ":" + AWS_EC2_REGION_NAME;
+ 
+     private ClassLoader classLoader = getClass().getClassLoader();
 -    private TestApplication origApp;
 -    private LiveTestEntity origEntity;
+     private File mementoDir;
++    private TestApplication origApp;
+     
+     @BeforeMethod(alwaysRun=true)
+     @Override
+     public void setUp() throws Exception {
+         super.setUp();
 -        origApp = ApplicationBuilder.newManagedApp(EntitySpec.create(TestApplication.class), managementContext);
 -        origEntity = origApp.createAndManageChild(EntitySpec.create(LiveTestEntity.class));
+ 
+         jcloudsLocation = (JcloudsLocation) managementContext.getLocationRegistry().resolve(AWS_EC2_LOCATION_SPEC);
+         jcloudsLocation.config().set(JcloudsLocation.HARDWARE_ID, AWS_EC2_SMALL_HARDWARE_ID);
++        
++        origApp = TestApplication.Factory.newManagedInstanceForTests(managementContext);
+     }
+ 
+     @AfterMethod(alwaysRun = true)
+     @Override
+     public void tearDown() throws Exception {
 -        super.tearDown();
 -        if (origApp != null) Entities.destroyAll(origApp.getManagementContext());
 -        if (mementoDir != null) RebindTestUtils.deleteMementoDir(mementoDir);
++        try {
++            super.tearDown();
++        } finally {
++            if (mementoDir != null) RebindTestUtils.deleteMementoDir(mementoDir);
++        }
+     }
+     
+     @Override
+     protected LocalManagementContext newManagementContext() {
+         mementoDir = Files.createTempDir();
+         return RebindTestUtils.newPersistingManagementContext(mementoDir, classLoader, 1);
+     }
+     
+     @Test(groups="Live")
+     public void testRebindsToJcloudsMachine() throws Exception {
++        LiveTestEntity origEntity = origApp.addChild(EntitySpec.create(LiveTestEntity.class));
++
+         origApp.start(ImmutableList.of(jcloudsLocation));
+         JcloudsLocation origJcloudsLocation = jcloudsLocation;
+         System.out.println("orig locations: " + origEntity.getLocations());
+         JcloudsSshMachineLocation origMachine = (JcloudsSshMachineLocation) Iterables.find(origEntity.getLocations(), Predicates.instanceOf(JcloudsSshMachineLocation.class));
+ 
+         TestApplication newApp = rebind();
+         LiveTestEntity newEntity = (LiveTestEntity) Iterables.find(newApp.getChildren(), Predicates.instanceOf(LiveTestEntity.class));
+         JcloudsSshMachineLocation newMachine = (JcloudsSshMachineLocation) Iterables.find(newEntity.getLocations(), Predicates.instanceOf(JcloudsSshMachineLocation.class));
+ 
 -        assertMachineEquals(newMachine, origMachine);
++        assertMachineEquals(newMachine, origMachine, true); // Don't expect OsDetails, because that is not persisted.
+         assertTrue(newMachine.isSshable());
+ 
+         JcloudsLocation newJcloudsLoction = newMachine.getParent();
+         assertJcloudsLocationEquals(newJcloudsLoction, origJcloudsLocation);
+     }
+ 
 -    private void assertMachineEquals(JcloudsSshMachineLocation actual, JcloudsSshMachineLocation expected) {
++    // TODO In jclouds-azure, the AzureComputeTemplateOptions fields changed, which meant old 
++    // persisted state could not be deserialized. These files are examples of the old format.
++    @Test(groups={"Live", "WIP"}, enabled=false)
++    public void testRebindsToJcloudsMachineWithInvalidTemplate() throws Exception {
++        ResourceUtils resourceUtils = ResourceUtils.create(this);
++        FileUtils.write(
++                new File(mementoDir, "locations/briByOel"),
++                resourceUtils.getResourceAsString("classpath://org/apache/brooklyn/location/jclouds/persisted-azure-parent-briByOel"));
++        FileUtils.write(
++                new File(mementoDir, "locations/VNapYjwp"),
++                resourceUtils.getResourceAsString("classpath://org/apache/brooklyn/location/jclouds/persisted-azure-machine-VNapYjwp"));
++        
++        TestApplication newApp = rebind();
++        
++        JcloudsLocation loc = (JcloudsLocation) newApp.getManagementContext().getLocationManager().getLocation("briByOel");
++        JcloudsSshMachineLocation machine = (JcloudsSshMachineLocation) newApp.getManagementContext().getLocationManager().getLocation("VNapYjwp");
++        assertEquals(ImmutableSet.of(loc.getChildren()), ImmutableSet.of(machine));
++    }
++
++    @Test(groups={"Live", "Live-sanity"})
++    public void testRebindsToJcloudsSshMachineWithTemplateAndNode() throws Exception {
++        // Populate the mementoDir with some old-style peristed state
++        ResourceUtils resourceUtils = ResourceUtils.create(this);
++        String origParentXml = resourceUtils.getResourceAsString("classpath://org/apache/brooklyn/location/jclouds/persisted-aws-parent-lCYB3mTb");
++        String origMachineXml = resourceUtils.getResourceAsString("classpath://org/apache/brooklyn/location/jclouds/persisted-aws-machine-aKEcbxKN");
++        File persistedParentFile = new File(mementoDir, "locations/lCYB3mTb");
++        File persistedMachineFile = new File(mementoDir, "locations/aKEcbxKN");
++        FileUtils.write(
++                persistedParentFile,
++                origParentXml);
++        FileUtils.write(
++                persistedMachineFile,
++                origMachineXml);
++
++        assertTrue(origMachineXml.contains("AWSEC2TemplateOptions"), origMachineXml);
++        assertTrue(origMachineXml.contains("NodeMetadataImpl"), origMachineXml);
++
++        // Rebind to the old-style persisted state, which includes the NodeMetadata and the Template objects.
++        // Expect to parse that ok.
++        TestApplication app2 = rebind();
++        
++        JcloudsLocation loc2 = (JcloudsLocation) app2.getManagementContext().getLocationManager().getLocation("lCYB3mTb");
++        JcloudsSshMachineLocation machine2 = (JcloudsSshMachineLocation) app2.getManagementContext().getLocationManager().getLocation("aKEcbxKN");
++        assertEquals(ImmutableSet.copyOf(loc2.getChildren()), ImmutableSet.of(machine2));
++        
++        String errmsg = "loc="+loc2.toVerboseString()+"; machine="+machine2.toVerboseString();
++        assertEquals(machine2.getId(), "aKEcbxKN", errmsg);
++        assertEquals(machine2.getJcloudsId(), "ap-southeast-1/i-56fd53f2", errmsg);
++        assertEquals(machine2.getSshHostAndPort(), HostAndPort.fromParts("ec2-54-254-23-53.ap-southeast-1.compute.amazonaws.com", 22), errmsg);
++        assertEquals(machine2.getPrivateAddresses(), ImmutableSet.of("10.144.66.5"), errmsg);
++        assertEquals(machine2.getPublicAddresses(), ImmutableSet.of("54.254.23.53"), errmsg);
++        assertEquals(machine2.getPrivateAddress(), Optional.of("10.144.66.5"), errmsg);
++        assertEquals(machine2.getHostname(), "ip-10-144-66-5", errmsg); // TODO would prefer the hostname that works inside and out
++        assertEquals(machine2.getOsDetails().isWindows(), false, errmsg);
++        assertEquals(machine2.getOsDetails().isLinux(), true, errmsg);
++        assertEquals(machine2.getOsDetails().isMac(), false, errmsg);
++        assertEquals(machine2.getOsDetails().getName(), "centos", errmsg);
++        assertEquals(machine2.getOsDetails().getArch(), "x86_64", errmsg);
++        assertEquals(machine2.getOsDetails().getVersion(), "6.5", errmsg);
++        assertEquals(machine2.getOsDetails().is64bit(), true, errmsg);
++
++        // Force it to be persisted again. Expect to pesist without the NodeMetadata and Template.
++        app2.getManagementContext().getRebindManager().getChangeListener().onChanged(loc2);
++        app2.getManagementContext().getRebindManager().getChangeListener().onChanged(machine2);
++        RebindTestUtils.waitForPersisted(app2);
++        
++        String newMachineXml = new String(java.nio.file.Files.readAllBytes(persistedMachineFile.toPath()));
++        assertFalse(newMachineXml.contains("AWSEC2TemplateOptions"), newMachineXml);
++        assertFalse(newMachineXml.contains("NodeMetadataImpl"), newMachineXml);
++        
++        // Rebind again, with the re-written persisted state.
++        TestApplication app3 = rebind();
++        
++        JcloudsLocation loc3 = (JcloudsLocation) app3.getManagementContext().getLocationManager().getLocation("lCYB3mTb");
++        JcloudsSshMachineLocation machine3 = (JcloudsSshMachineLocation) app3.getManagementContext().getLocationManager().getLocation("aKEcbxKN");
++        assertEquals(ImmutableSet.copyOf(loc3.getChildren()), ImmutableSet.of(machine3));
++        
++        errmsg = "loc="+loc3.toVerboseString()+"; machine="+machine3.toVerboseString();
++        assertEquals(machine3.getId(), "aKEcbxKN", errmsg);
++        assertEquals(machine3.getJcloudsId(), "ap-southeast-1/i-56fd53f2", errmsg);
++        assertEquals(machine3.getSshHostAndPort(), HostAndPort.fromParts("ec2-54-254-23-53.ap-southeast-1.compute.amazonaws.com", 22), errmsg);
++        assertEquals(machine3.getPrivateAddresses(), ImmutableSet.of("10.144.66.5"), errmsg);
++        assertEquals(machine3.getPublicAddresses(), ImmutableSet.of("54.254.23.53"), errmsg);
++        assertEquals(machine3.getPrivateAddress(), Optional.of("10.144.66.5"), errmsg);
++        assertEquals(machine3.getHostname(), "ip-10-144-66-5", errmsg); // TODO would prefer the hostname that works inside and out
++        
++        // The VM is no longer running, so won't be able to infer OS Details.
++        assertFalse(machine3.getOptionalOsDetails().isPresent(), errmsg);
++    }
++
++    @Test(groups={"Live", "Live-sanity"})
++    public void testRebindsToJcloudsWinrmMachineWithTemplateAndNode() throws Exception {
++        // Populate the mementoDir with some old-style peristed state
++        ResourceUtils resourceUtils = ResourceUtils.create(this);
++        String origParentXml = resourceUtils.getResourceAsString("classpath://org/apache/brooklyn/location/jclouds/persisted-aws-winrm-parent-fKc0Ofyn");
++        String origMachineXml = resourceUtils.getResourceAsString("classpath://org/apache/brooklyn/location/jclouds/persisted-aws-winrm-machine-KYSryzW8");
++        File persistedParentFile = new File(mementoDir, "locations/fKc0Ofyn");
++        File persistedMachineFile = new File(mementoDir, "locations/KYSryzW8");
++        FileUtils.write(
++                persistedParentFile,
++                origParentXml);
++        FileUtils.write(
++                persistedMachineFile,
++                origMachineXml);
++
++        assertTrue(origMachineXml.contains("NodeMetadataImpl"), origMachineXml);
++
++        // Rebind to the old-style persisted state, which includes the NodeMetadata and the Template objects.
++        // Expect to parse that ok.
++        TestApplication app2 = rebind();
++        
++        JcloudsLocation loc2 = (JcloudsLocation) app2.getManagementContext().getLocationManager().getLocation("fKc0Ofyn");
++        JcloudsWinRmMachineLocation machine2 = (JcloudsWinRmMachineLocation) app2.getManagementContext().getLocationManager().getLocation("KYSryzW8");
++        assertEquals(ImmutableSet.copyOf(loc2.getChildren()), ImmutableSet.of(machine2));
++        
++        String errmsg = "loc="+loc2.toVerboseString()+"; machine="+machine2.toVerboseString();
++        assertEquals(machine2.getId(), "KYSryzW8", errmsg);
++        assertEquals(machine2.getJcloudsId(), "eu-central-1/i-372eda8a", errmsg);
++        assertEquals(machine2.getAddress().getHostAddress(), "52.28.153.46", errmsg);
++        assertEquals(machine2.getPort(), 5985, errmsg);
++        // FIXME assertEquals(machine2.getAddress().getHostAddress(), HostAndPort.fromParts("ec2-52-28-153-46.eu-central-1.compute.amazonaws.com", 22), errmsg);
++        assertEquals(machine2.getPrivateAddresses(), ImmutableSet.of("172.31.18.175"), errmsg);
++        assertEquals(machine2.getPublicAddresses(), ImmutableSet.of("52.28.153.46"), errmsg);
++        assertEquals(machine2.getPrivateAddress(), Optional.of("172.31.18.175"), errmsg);
++        assertEquals(machine2.getHostname(), "ip-172-31-18-175", errmsg); // TODO would prefer the hostname that works inside and out
++        assertNull(machine2.getOsDetails(), errmsg); // JcloudsWinRmMachineLocation never had OsDetails
++
++        // Force it to be persisted again. Expect to pesist without the NodeMetadata and Template.
++        app2.getManagementContext().getRebindManager().getChangeListener().onChanged(loc2);
++        app2.getManagementContext().getRebindManager().getChangeListener().onChanged(machine2);
++        RebindTestUtils.waitForPersisted(app2);
++        
++        String newMachineXml = new String(java.nio.file.Files.readAllBytes(persistedMachineFile.toPath()));
++        assertFalse(newMachineXml.contains("NodeMetadataImpl"), newMachineXml);
++        
++        // Rebind again, with the re-written persisted state.
++        TestApplication app3 = rebind();
++        
++        JcloudsLocation loc3 = (JcloudsLocation) app3.getManagementContext().getLocationManager().getLocation("fKc0Ofyn");
++        JcloudsWinRmMachineLocation machine3 = (JcloudsWinRmMachineLocation) app3.getManagementContext().getLocationManager().getLocation("KYSryzW8");
++        assertEquals(ImmutableSet.copyOf(loc3.getChildren()), ImmutableSet.of(machine3));
++        
++        errmsg = "loc="+loc3.toVerboseString()+"; machine="+machine3.toVerboseString();
++        assertEquals(machine3.getId(), "KYSryzW8", errmsg);
++        assertEquals(machine3.getJcloudsId(), "eu-central-1/i-372eda8a", errmsg);
++        assertEquals(machine3.getAddress().getHostAddress(), "52.28.153.46", errmsg);
++        assertEquals(machine3.getPort(), 5985, errmsg);
++        assertEquals(machine3.getPrivateAddresses(), ImmutableSet.of("172.31.18.175"), errmsg);
++        assertEquals(machine3.getPublicAddresses(), ImmutableSet.of("52.28.153.46"), errmsg);
++        assertEquals(machine3.getPrivateAddress(), Optional.of("172.31.18.175"), errmsg);
++        assertEquals(machine3.getHostname(), "ip-172-31-18-175", errmsg); // TODO would prefer the hostname that works inside and out
++        assertNull(machine2.getOsDetails(), errmsg); // JcloudsWinRmMachineLocation never had OsDetails
++    }
++
++    private void assertMachineEquals(JcloudsSshMachineLocation actual, JcloudsSshMachineLocation expected, boolean expectNoOsDetails) {
+         String errmsg = "actual="+actual.toVerboseString()+"; expected="+expected.toVerboseString();
+         assertEquals(actual.getId(), expected.getId(), errmsg);
+         assertEquals(actual.getJcloudsId(), expected.getJcloudsId(), errmsg);
 -        assertOsDetailEquals(actual.getOsDetails(), expected.getOsDetails());
++        if (expectNoOsDetails) {
++            assertOsDetailEquals(actual.getOptionalOsDetails(), Optional.<OsDetails>absent());
++        } else {
++            assertOsDetailEquals(actual.getOptionalOsDetails(), expected.getOptionalOsDetails());
++        }
+         assertEquals(actual.getSshHostAndPort(), expected.getSshHostAndPort());
+         assertEquals(actual.getPrivateAddress(), expected.getPrivateAddress());
+         assertConfigBagEquals(actual.config().getBag(), expected.config().getBag(), errmsg);
+     }
+ 
 -    private void assertOsDetailEquals(OsDetails actual, OsDetails expected) {
++    private void assertOsDetailEquals(Optional<OsDetails> actual, Optional<OsDetails> expected) {
+         String errmsg = "actual="+actual+"; expected="+expected;
 -        if (actual == null) assertNull(expected, errmsg);
 -        assertEquals(actual.isWindows(), expected.isWindows());
 -        assertEquals(actual.isLinux(), expected.isLinux());
 -        assertEquals(actual.isMac(), expected.isMac());
 -        assertEquals(actual.getName(), expected.getName());
 -        assertEquals(actual.getArch(), expected.getArch());
 -        assertEquals(actual.getVersion(), expected.getVersion());
 -        assertEquals(actual.is64bit(), expected.is64bit());
++        if (actual.isPresent()) {
++            assertEquals(actual.get().isWindows(), expected.get().isWindows());
++            assertEquals(actual.get().isLinux(), expected.get().isLinux());
++            assertEquals(actual.get().isMac(), expected.get().isMac());
++            assertEquals(actual.get().getName(), expected.get().getName());
++            assertEquals(actual.get().getArch(), expected.get().getArch());
++            assertEquals(actual.get().getVersion(), expected.get().getVersion());
++            assertEquals(actual.get().is64bit(), expected.get().is64bit());
++        } else {
++            assertFalse(expected.isPresent(), errmsg);
++        }
+     }
+ 
+     private void assertJcloudsLocationEquals(JcloudsLocation actual, JcloudsLocation expected) {
+         String errmsg = "actual="+actual.toVerboseString()+"; expected="+expected.toVerboseString();
+         assertEquals(actual.getId(), expected.getId(), errmsg);
+         assertEquals(actual.getProvider(), expected.getProvider(), errmsg);
+         assertEquals(actual.getRegion(), expected.getRegion(), errmsg);
+         assertEquals(actual.getIdentity(), expected.getIdentity(), errmsg);
+         assertEquals(actual.getCredential(), expected.getCredential(), errmsg);
+         assertEquals(actual.getHostGeoInfo(), expected.getHostGeoInfo(), errmsg);
+         assertConfigBagEquals(actual.config().getBag(), expected.config().getBag(), errmsg);
+     }
+ 
+     private void assertConfigBagEquals(ConfigBag actual, ConfigBag expected, String errmsg) {
+         // TODO revisit the strong assertion that configBags are equal
+         
+ //        // TODO Can we include all of these things (e.g. when locations are entities, so flagged fields not treated special)?
+ //        List<String> configToIgnore = ImmutableList.of("id", "template", "usedPorts", "machineCreationSemaphore", "config");
+ //        MutableMap<Object, Object> actualMap = MutableMap.builder().putAll(actual.getAllConfig())
+ //                .removeAll(configToIgnore)
+ //                .build();
+ //        MutableMap<Object, Object> expectedMap = MutableMap.builder().putAll(expected.getAllConfig())
+ //                .removeAll(configToIgnore)
+ //                .build();
+ //        
+ //        assertEquals(actualMap, expectedMap, errmsg+"; actualBag="+actualMap+"; expectedBag="+expectedMap);
+     }
+     
+     private TestApplication rebind() throws Exception {
++        return rebind(RebindOptions.create()
++                .mementoDir(mementoDir)
++                .classLoader(classLoader));
++    }
++    
++    private TestApplication rebind(RebindOptions options) throws Exception {
+         RebindTestUtils.waitForPersisted(origApp);
 -        return (TestApplication) RebindTestUtils.rebind(mementoDir, getClass().getClassLoader());
++        return (TestApplication) RebindTestUtils.rebind(options);
+     }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-aws-machine-aKEcbxKN
----------------------------------------------------------------------
diff --cc brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-aws-machine-aKEcbxKN
index 0000000,0000000..eeac2be
new file mode 100644
--- /dev/null
+++ b/brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-aws-machine-aKEcbxKN
@@@ -1,0 -1,0 +1,329 @@@
++<!--
++    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.
++-->
++<location>
++  <brooklynVersion>0.9.0-20151120.1523</brooklynVersion>
++  <type>org.apache.brooklyn.location.jclouds.JcloudsSshMachineLocation</type>
++  <id>aKEcbxKN</id>
++  <displayName>ec2-54-254-23-53.ap-southeast-1.compute.amazonaws.com</displayName>
++  <parent>lCYB3mTb</parent>
++  <locationConfig>
++    <displayName>ec2-54-254-23-53.ap-southeast-1.compute.amazonaws.com</displayName>
++    <address>
++      <java.net.Inet4Address>ec2-54-254-23-53.ap-southeast-1.compute.amazonaws.com/54.254.23.53</java.net.Inet4Address>
++    </address>
++    <user>amp</user>
++    <privateKeyData>-----BEGIN RSA PRIVATE KEY-----
++myPrivateKey
++-----END RSA PRIVATE KEY-----
++</privateKeyData>
++    <config>
++      <java.util.Collections_-UnmodifiableMap>
++        <m class="MutableMap">
++          <privateKeyData>-----BEGIN RSA PRIVATE KEY-----
++myPrivateKey
++-----END RSA PRIVATE KEY-----
++</privateKeyData>
++        </m>
++      </java.util.Collections_-UnmodifiableMap>
++    </config>
++    <jcloudsParent>
++      <locationProxy>lCYB3mTb</locationProxy>
++    </jcloudsParent>
++    <node>
++      <org.jclouds.compute.domain.internal.NodeMetadataImpl>
++        <providerId>i-56fd53f2</providerId>
++        <name>brooklyn-nv6pd3-amp-auto-qa-post-un67-postgresql-ryhi-b87c</name>
++        <location class="org.jclouds.domain.internal.LocationImpl">
++          <scope>ZONE</scope>
++          <id>ap-southeast-1a</id>
++          <description>ap-southeast-1a</description>
++          <parent class="org.jclouds.domain.internal.LocationImpl">
++            <scope>REGION</scope>
++            <id>ap-southeast-1</id>
++            <description>ap-southeast-1</description>
++            <parent class="org.jclouds.domain.internal.LocationImpl">
++              <scope>PROVIDER</scope>
++              <id>aws-ec2</id>
++              <description>https://ec2.us-east-1.amazonaws.com</description>
++              <iso3166Codes class="com.google.common.collect.RegularImmutableSet">
++                <string>US-VA</string>
++                <string>US-CA</string>
++                <string>US-OR</string>
++                <string>BR-SP</string>
++                <string>IE</string>
++                <string>DE-HE</string>
++                <string>SG</string>
++                <string>AU-NSW</string>
++                <string>JP-13</string>
++              </iso3166Codes>
++              <metadata class="com.google.common.collect.EmptyImmutableBiMap"/>
++            </parent>
++            <iso3166Codes class="com.google.common.collect.SingletonImmutableSet">
++              <string>SG</string>
++            </iso3166Codes>
++            <metadata class="com.google.common.collect.EmptyImmutableBiMap" reference="../parent/metadata"/>
++          </parent>
++          <iso3166Codes class="com.google.common.collect.SingletonImmutableSet" reference="../parent/iso3166Codes"/>
++          <metadata class="com.google.common.collect.EmptyImmutableBiMap" reference="../parent/parent/metadata"/>
++        </location>
++        <userMetadata>
++          <Name>brooklyn-nv6pd3-amp-auto-qa-post-un67-postgresql-ryhi-b87c</Name>
++          <entry key="brooklyn-user">amp</entry>
++          <entry key="brooklyn-app-id">Un679FS8</entry>
++          <entry key="brooklyn-app-name">(auto-qa) postgres @ AWS Singapore</entry>
++          <entry key="brooklyn-entity-id">RyHiwukc</entry>
++          <entry key="brooklyn-entity-name">PostgreSQL Server</entry>
++          <entry key="brooklyn-server-creation-date">2015-09-24-1456</entry>
++        </userMetadata>
++        <id>ap-southeast-1/i-56fd53f2</id>
++        <type>NODE</type>
++        <tags class="com.google.common.collect.EmptyImmutableSet"/>
++        <status>RUNNING</status>
++        <backendStatus>running</backendStatus>
++        <loginPort>22</loginPort>
++        <publicAddresses class="com.google.common.collect.SingletonImmutableSet">
++          <string>54.254.23.53</string>
++        </publicAddresses>
++        <privateAddresses class="com.google.common.collect.SingletonImmutableSet">
++          <string>10.144.66.5</string>
++        </privateAddresses>
++        <credentials>
++          <identity>amp</identity>
++          <credential>-----BEGIN RSA PRIVATE KEY-----
++myPrivateKey
++-----END RSA PRIVATE KEY-----
++</credential>
++          <authenticateSudo>false</authenticateSudo>
++          <password class="com.google.common.base.Absent"/>
++          <privateKey class="com.google.common.base.Present">
++            <reference class="string">-----BEGIN RSA PRIVATE KEY-----
++myPrivateKey
++-----END RSA PRIVATE KEY-----
++</reference>
++          </privateKey>
++        </credentials>
++        <group>brooklyn-nv6pd3-amp-auto-qa-post-un67-postgresql-ryhi</group>
++        <imageId>ap-southeast-1/ami-42bfe910</imageId>
++        <hardware class="org.jclouds.compute.domain.internal.HardwareImpl">
++          <providerId>m1.medium</providerId>
++          <userMetadata/>
++          <id>m1.medium</id>
++          <type>HARDWARE</type>
++          <tags class="com.google.common.collect.EmptyImmutableSet" reference="../../tags"/>
++          <processors class="ImmutableList">
++            <org.jclouds.compute.domain.Processor>
++              <cores>1.0</cores>
++              <speed>2.0</speed>
++            </org.jclouds.compute.domain.Processor>
++          </processors>
++          <ram>3750</ram>
++          <volumes class="ImmutableList">
++            <org.jclouds.compute.domain.internal.VolumeImpl>
++              <type>LOCAL</type>
++              <size>420.0</size>
++              <device>/dev/sdb</device>
++              <bootDevice>false</bootDevice>
++              <durable>false</durable>
++            </org.jclouds.compute.domain.internal.VolumeImpl>
++            <org.jclouds.compute.domain.internal.VolumeImpl>
++              <type>LOCAL</type>
++              <size>420.0</size>
++              <device>/dev/sdc</device>
++              <bootDevice>false</bootDevice>
++              <durable>false</durable>
++            </org.jclouds.compute.domain.internal.VolumeImpl>
++            <org.jclouds.compute.domain.internal.VolumeImpl>
++              <id>vol-7e244896</id>
++              <type>SAN</type>
++              <device>/dev/sda1</device>
++              <bootDevice>true</bootDevice>
++              <durable>true</durable>
++            </org.jclouds.compute.domain.internal.VolumeImpl>
++          </volumes>
++          <supportsImage class="com.google.common.base.Predicates$AndPredicate">
++            <components>
++              <com.google.common.base.Predicates_-ObjectPredicate>ALWAYS_TRUE</com.google.common.base.Predicates_-ObjectPredicate>
++              <org.jclouds.ec2.compute.domain.EC2HardwareBuilder_-RequiresVirtualizationType>
++                <type>PARAVIRTUAL</type>
++              </org.jclouds.ec2.compute.domain.EC2HardwareBuilder_-RequiresVirtualizationType>
++              <com.google.common.base.Predicates_-ObjectPredicate>ALWAYS_TRUE</com.google.common.base.Predicates_-ObjectPredicate>
++              <com.google.common.base.Predicates_-ObjectPredicate>ALWAYS_TRUE</com.google.common.base.Predicates_-ObjectPredicate>
++            </components>
++          </supportsImage>
++          <hypervisor>xen</hypervisor>
++          <deprecated>false</deprecated>
++        </hardware>
++        <os>
++          <family>CENTOS</family>
++          <arch>paravirtual</arch>
++          <version>6.5</version>
++          <description>411009282317/RightImage_CentOS_6.5_x64_v13.5.2.2_EBS</description>
++          <is64Bit>true</is64Bit>
++        </os>
++        <hostname>ip-10-144-66-5</hostname>
++      </org.jclouds.compute.domain.internal.NodeMetadataImpl>
++    </node>
++    <brooklyn.ssh.config.port type="int">22</brooklyn.ssh.config.port>
++    <availabilityZone>ap-southeast-1a</availabilityZone>
++    <region>ap-southeast-1</region>
++    <callerContext>
++      <null/>
++    </callerContext>
++    <detectMachineDetails type="boolean">true</detectMachineDetails>
++    <portforwarding.enabled type="boolean">false</portforwarding.enabled>
++    <template>
++      <org.jclouds.compute.domain.internal.TemplateImpl>
++        <image class="org.jclouds.compute.domain.internal.ImageImpl">
++          <providerId>ami-42bfe910</providerId>
++          <name>RightImage_CentOS_6.5_x64_v13.5.2.2_EBS</name>
++          <location class="org.jclouds.domain.internal.LocationImpl" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/location/parent"/>
++          <userMetadata>
++            <owner>411009282317</owner>
++            <rootDeviceType>ebs</rootDeviceType>
++            <virtualizationType>paravirtual</virtualizationType>
++            <hypervisor>xen</hypervisor>
++          </userMetadata>
++          <id>ap-southeast-1/ami-42bfe910</id>
++          <type>IMAGE</type>
++          <tags class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <operatingSystem reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/os"/>
++          <status>AVAILABLE</status>
++          <backendStatus>available</backendStatus>
++          <version>13.5.2.2_EBS</version>
++          <description>RightImage_CentOS_6.5_x64_v13.5.2.2_EBS</description>
++          <defaultCredentials>
++            <identity>root</identity>
++            <authenticateSudo>false</authenticateSudo>
++            <password class="com.google.common.base.Absent" reference="../../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/credentials/password"/>
++            <privateKey class="com.google.common.base.Absent" reference="../../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/credentials/password"/>
++          </defaultCredentials>
++        </image>
++        <hardware class="org.jclouds.compute.domain.internal.HardwareImpl">
++          <providerId>m1.medium</providerId>
++          <userMetadata/>
++          <id>m1.medium</id>
++          <type>HARDWARE</type>
++          <tags class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <processors class="ImmutableList" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/hardware/processors"/>
++          <ram>3750</ram>
++          <volumes class="ImmutableList">
++            <org.jclouds.compute.domain.internal.VolumeImpl>
++              <type>LOCAL</type>
++              <size>10.0</size>
++              <device>/dev/sda1</device>
++              <bootDevice>true</bootDevice>
++              <durable>false</durable>
++            </org.jclouds.compute.domain.internal.VolumeImpl>
++            <org.jclouds.compute.domain.internal.VolumeImpl reference="../../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/hardware/volumes/org.jclouds.compute.domain.internal.VolumeImpl"/>
++            <org.jclouds.compute.domain.internal.VolumeImpl reference="../../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/hardware/volumes/org.jclouds.compute.domain.internal.VolumeImpl[2]"/>
++          </volumes>
++          <supportsImage class="com.google.common.base.Predicates$AndPredicate" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/hardware/supportsImage"/>
++          <deprecated>true</deprecated>
++        </hardware>
++        <location class="org.jclouds.domain.internal.LocationImpl" reference="../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/location/parent"/>
++        <options class="org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions">
++          <port>-1</port>
++          <seconds>-1</seconds>
++          <runAsRoot>true</runAsRoot>
++          <blockOnComplete>true</blockOnComplete>
++          <wrapInInitScript>true</wrapInInitScript>
++          <inboundPorts class="com.google.common.collect.RegularImmutableSet">
++            <int>22</int>
++            <int>5432</int>
++          </inboundPorts>
++          <tags class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <securityGroups class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <blockUntilRunning>true</blockUntilRunning>
++          <userMetadata>
++            <Name>brooklyn-nv6pd3-amp-auto-qa-post-un67-postgresql-ryhi-b87c</Name>
++            <entry key="brooklyn-user">amp</entry>
++            <entry key="brooklyn-app-id">Un679FS8</entry>
++            <entry key="brooklyn-app-name">(auto-qa) postgres @ AWS Singapore</entry>
++            <entry key="brooklyn-entity-id">RyHiwukc</entry>
++            <entry key="brooklyn-entity-name">PostgreSQL Server</entry>
++            <entry key="brooklyn-server-creation-date">2015-09-24-1456</entry>
++          </userMetadata>
++          <nodeNames class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <networks class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <groupNames class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <noKeyPair>false</noKeyPair>
++          <userData class="com.google.common.primitives.Bytes$ByteArrayAsList">
++            <array>I2Nsb3VkLWNvbmZpZwpyZXBvX3VwZ3JhZGU6IG5vbmUK</array>
++            <start>0</start>
++            <end>33</end>
++          </userData>
++          <blockDeviceMappings>
++            <contents>
++              <null/>
++              <null/>
++              <null/>
++              <null/>
++            </contents>
++            <size>0</size>
++          </blockDeviceMappings>
++          <monitoringEnabled>false</monitoringEnabled>
++          <noPlacementGroup>false</noPlacementGroup>
++          <spotOptions>
++            <formParameters class="com.google.common.collect.LinkedHashMultimap" serialization="custom">
++              <unserializable-parents/>
++              <com.google.common.collect.LinkedHashMultimap>
++                <default/>
++                <int>2</int>
++                <int>0</int>
++                <int>0</int>
++              </com.google.common.collect.LinkedHashMultimap>
++            </formParameters>
++            <queryParameters class="com.google.common.collect.LinkedHashMultimap" serialization="custom">
++              <unserializable-parents/>
++              <com.google.common.collect.LinkedHashMultimap>
++                <default/>
++                <int>2</int>
++                <int>0</int>
++                <int>0</int>
++              </com.google.common.collect.LinkedHashMultimap>
++            </queryParameters>
++            <headers class="com.google.common.collect.LinkedHashMultimap" serialization="custom">
++              <unserializable-parents/>
++              <com.google.common.collect.LinkedHashMultimap>
++                <default/>
++                <int>2</int>
++                <int>0</int>
++                <int>0</int>
++              </com.google.common.collect.LinkedHashMultimap>
++            </headers>
++          </spotOptions>
++          <groupIds class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++        </options>
++      </org.jclouds.compute.domain.internal.TemplateImpl>
++    </template>
++    <usedPorts>
++      <set/>
++    </usedPorts>
++    <tags>
++      <set/>
++    </tags>
++  </locationConfig>
++  <locationConfigUnused>
++    <string>displayName</string>
++    <string>config</string>
++    <string>availabilityZone</string>
++    <string>region</string>
++    <string>portforwarding.enabled</string>
++  </locationConfigUnused>
++</location>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-aws-parent-lCYB3mTb
----------------------------------------------------------------------
diff --cc brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-aws-parent-lCYB3mTb
index 0000000,0000000..0d00195
new file mode 100644
--- /dev/null
+++ b/brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-aws-parent-lCYB3mTb
@@@ -1,0 -1,0 +1,78 @@@
++<!--
++    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.
++-->
++<location>
++  <brooklynVersion>0.9.0-20151120.1523</brooklynVersion>
++  <type>org.apache.brooklyn.location.jclouds.JcloudsLocation</type>
++  <id>lCYB3mTb</id>
++  <displayName>AWS Singapore (ap-southeast-1)</displayName>
++  <children>
++    <string>aKEcbxKN</string>
++  </children>
++  <locationConfig>
++    <provider>aws-ec2</provider>
++    <minRam>2048</minRam>
++    <useJcloudsSshInit>false</useJcloudsSshInit>
++    <identity>myidentity</identity>
++    <openIptables>true</openIptables>
++    <credential>mycredential</credential>
++    <machineCreateAttempts>2</machineCreateAttempts>
++    <latitude>1.3000</latitude>
++    <streetAddress>Singapore</streetAddress>
++    <defaultImageId>ap-southeast-1/ami-21c2bd73</defaultImageId>
++    <iso3166>
++      <com.google.common.collect.SingletonImmutableSet>
++        <string>SG</string>
++      </com.google.common.collect.SingletonImmutableSet>
++    </iso3166>
++    <displayName>AWS Singapore (ap-southeast-1)</displayName>
++    <longitude>103.8000</longitude>
++    <hardwareId>m1.medium</hardwareId>
++    <imageId>ap-southeast-1/ami-42bfe910</imageId>
++    <spec.named.name>AWS Singapore</spec.named.name>
++    <spec.original>AWS Singapore</spec.original>
++    <region>ap-southeast-1</region>
++    <spec.final>jclouds:aws-ec2:ap-southeast-1</spec.final>
++    <machineCreationSemaphore>
++      <java.util.concurrent.Semaphore>
++        <sync class="java.util.concurrent.Semaphore$FairSync">
++          <state>2147483647</state>
++        </sync>
++      </java.util.concurrent.Semaphore>
++    </machineCreationSemaphore>
++    <vmInstanceIds>
++      <map>
++        <entry>
++          <locationProxy>aKEcbxKN</locationProxy>
++          <string>ap-southeast-1/i-56fd53f2</string>
++        </entry>
++      </map>
++    </vmInstanceIds>
++    <tags>
++      <set/>
++    </tags>
++  </locationConfig>
++  <locationConfigUnused>
++    <string>latitude</string>
++    <string>streetAddress</string>
++    <string>iso3166</string>
++    <string>displayName</string>
++    <string>longitude</string>
++  </locationConfigUnused>
++  <locationConfigDescription>aws-ec2:ap-southeast-1</locationConfigDescription>
++</location>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-aws-winrm-machine-KYSryzW8
----------------------------------------------------------------------
diff --cc brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-aws-winrm-machine-KYSryzW8
index 0000000,0000000..908e9c9
new file mode 100644
--- /dev/null
+++ b/brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-aws-winrm-machine-KYSryzW8
@@@ -1,0 -1,0 +1,184 @@@
++<!--
++    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.
++-->
++<location>
++  <brooklynVersion>0.9.0-P20151204.1943</brooklynVersion>
++  <type>org.apache.brooklyn.location.jclouds.JcloudsWinRmMachineLocation</type>
++  <id>KYSryzW8</id>
++  <displayName>52.28.153.46</displayName>
++  <parent>fKc0Ofyn</parent>
++  <locationConfig>
++    <jcloudsParent>
++      <locationProxy>fKc0Ofyn</locationProxy>
++    </jcloudsParent>
++    <displayName>52.28.153.46</displayName>
++    <address>52.28.153.46</address>
++    <user>Administrator</user>
++    <node>
++      <org.jclouds.compute.domain.internal.NodeMetadataImpl>
++        <providerId>i-372eda8a</providerId>
++        <name>brooklyn-nxlv9n-amp-myentity-i9fh-myentity-ms-sql-tccu-wgax</name>
++        <location class="org.jclouds.domain.internal.LocationImpl">
++          <scope>ZONE</scope>
++          <id>eu-central-1a</id>
++          <description>eu-central-1a</description>
++          <parent class="org.jclouds.domain.internal.LocationImpl">
++            <scope>REGION</scope>
++            <id>eu-central-1</id>
++            <description>eu-central-1</description>
++            <parent class="org.jclouds.domain.internal.LocationImpl">
++              <scope>PROVIDER</scope>
++              <id>aws-ec2</id>
++              <description>https://ec2.us-east-1.amazonaws.com</description>
++              <iso3166Codes class="com.google.common.collect.RegularImmutableSet">
++                <string>US-VA</string>
++                <string>US-CA</string>
++                <string>US-OR</string>
++                <string>BR-SP</string>
++                <string>IE</string>
++                <string>DE-HE</string>
++                <string>SG</string>
++                <string>AU-NSW</string>
++                <string>JP-13</string>
++              </iso3166Codes>
++              <metadata class="com.google.common.collect.EmptyImmutableBiMap"/>
++            </parent>
++            <iso3166Codes class="com.google.common.collect.SingletonImmutableSet">
++              <string>DE-HE</string>
++            </iso3166Codes>
++            <metadata class="com.google.common.collect.EmptyImmutableBiMap" reference="../parent/metadata"/>
++          </parent>
++          <iso3166Codes class="com.google.common.collect.SingletonImmutableSet" reference="../parent/iso3166Codes"/>
++          <metadata class="com.google.common.collect.EmptyImmutableBiMap" reference="../parent/parent/metadata"/>
++        </location>
++        <userMetadata>
++          <Name>brooklyn-nxlv9n-amp-myentity-i9fh-myentity-ms-sql-tccu-wgax</Name>
++          <entry key="brooklyn-user">amp</entry>
++          <entry key="brooklyn-app-id">I9fhRJIh</entry>
++          <entry key="brooklyn-app-name">MyApp</entry>
++          <entry key="brooklyn-entity-id">tCcU6QL0</entry>
++          <entry key="brooklyn-entity-name">My Entity</entry>
++          <entry key="brooklyn-server-creation-date">2015-11-10-1539</entry>
++        </userMetadata>
++        <id>eu-central-1/i-372eda8a</id>
++        <type>NODE</type>
++        <tags class="com.google.common.collect.EmptyImmutableSet"/>
++        <status>RUNNING</status>
++        <backendStatus>running</backendStatus>
++        <loginPort>5985</loginPort>
++        <publicAddresses class="com.google.common.collect.SingletonImmutableSet">
++          <string>52.28.153.46</string>
++        </publicAddresses>
++        <privateAddresses class="com.google.common.collect.SingletonImmutableSet">
++          <string>172.31.18.175</string>
++        </privateAddresses>
++        <credentials>
++          <identity>Administrator</identity>
++          <credential>mypassword</credential>
++          <authenticateSudo>false</authenticateSudo>
++          <password class="com.google.common.base.Present">
++            <reference class="string">mypassword</reference>
++          </password>
++          <privateKey class="com.google.common.base.Absent"/>
++        </credentials>
++        <group>brooklyn-nxlv9n-amp-myentity-i9fh-myentity-ms-sql-tccu</group>
++        <imageId>eu-central-1/ami-f2f5f9ef</imageId>
++        <hardware class="org.jclouds.compute.domain.internal.HardwareImpl">
++          <providerId>m3.large</providerId>
++          <userMetadata/>
++          <id>m3.large</id>
++          <type>HARDWARE</type>
++          <tags class="com.google.common.collect.EmptyImmutableSet" reference="../../tags"/>
++          <processors class="ImmutableList">
++            <org.jclouds.compute.domain.Processor>
++              <cores>2.0</cores>
++              <speed>3.25</speed>
++            </org.jclouds.compute.domain.Processor>
++          </processors>
++          <ram>7680</ram>
++          <volumes class="ImmutableList">
++            <org.jclouds.compute.domain.internal.VolumeImpl>
++              <type>LOCAL</type>
++              <size>32.0</size>
++              <device>/dev/sdb</device>
++              <bootDevice>false</bootDevice>
++              <durable>false</durable>
++            </org.jclouds.compute.domain.internal.VolumeImpl>
++            <org.jclouds.compute.domain.internal.VolumeImpl>
++              <id>vol-1f1b72c6</id>
++              <type>SAN</type>
++              <device>/dev/sda1</device>
++              <bootDevice>true</bootDevice>
++              <durable>true</durable>
++            </org.jclouds.compute.domain.internal.VolumeImpl>
++          </volumes>
++          <supportsImage class="com.google.common.base.Predicates$AndPredicate">
++            <components>
++              <com.google.common.base.Predicates_-ObjectPredicate>ALWAYS_TRUE</com.google.common.base.Predicates_-ObjectPredicate>
++              <com.google.common.base.Predicates_-OrPredicate>
++                <components>
++                  <org.jclouds.ec2.compute.domain.EC2HardwareBuilder_-RequiresVirtualizationType>
++                    <type>HVM</type>
++                  </org.jclouds.ec2.compute.domain.EC2HardwareBuilder_-RequiresVirtualizationType>
++                  <org.jclouds.ec2.compute.domain.EC2HardwareBuilder_-RequiresVirtualizationType>
++                    <type>PARAVIRTUAL</type>
++                  </org.jclouds.ec2.compute.domain.EC2HardwareBuilder_-RequiresVirtualizationType>
++                </components>
++              </com.google.common.base.Predicates_-OrPredicate>
++              <com.google.common.base.Predicates_-ObjectPredicate>ALWAYS_TRUE</com.google.common.base.Predicates_-ObjectPredicate>
++              <com.google.common.base.Predicates_-ObjectPredicate>ALWAYS_TRUE</com.google.common.base.Predicates_-ObjectPredicate>
++            </components>
++          </supportsImage>
++          <hypervisor>xen</hypervisor>
++          <deprecated>false</deprecated>
++        </hardware>
++        <os>
++          <family>WINDOWS</family>
++          <arch>hvm</arch>
++          <version></version>
++          <description>amazon/Windows_Server-2012-R2_RTM-English-64Bit-Base-2015.10.26</description>
++          <is64Bit>true</is64Bit>
++        </os>
++        <hostname>ip-172-31-18-175</hostname>
++      </org.jclouds.compute.domain.internal.NodeMetadataImpl>
++    </node>
++    <port type="int">5985</port>
++    <password>mypassword</password>
++    <availabilityZone>eu-central-1a</availabilityZone>
++    <region>eu-central-1</region>
++    <callerContext>
++      <null/>
++    </callerContext>
++    <detectMachineDetails type="boolean">true</detectMachineDetails>
++    <portforwarding.enabled type="boolean">false</portforwarding.enabled>
++    <template>
++      <null/>
++    </template>
++    <tags>
++      <set/>
++    </tags>
++  </locationConfig>
++  <locationConfigUnused>
++    <string>displayName</string>
++    <string>availabilityZone</string>
++    <string>region</string>
++    <string>callerContext</string>
++    <string>detectMachineDetails</string>
++    <string>portforwarding.enabled</string>
++  </locationConfigUnused>
++</location>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-aws-winrm-parent-fKc0Ofyn
----------------------------------------------------------------------
diff --cc brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-aws-winrm-parent-fKc0Ofyn
index 0000000,0000000..3b35efc
new file mode 100644
--- /dev/null
+++ b/brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-aws-winrm-parent-fKc0Ofyn
@@@ -1,0 -1,0 +1,75 @@@
++<!--
++    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.
++-->
++<location>
++  <brooklynVersion>0.9.0-P20151204.1943</brooklynVersion>
++  <type>org.apache.brooklyn.location.jclouds.JcloudsLocation</type>
++  <id>fKc0Ofyn</id>
++  <displayName>aws-ec2:eu-central-1</displayName>
++  <children>
++    <string>KYSryzW8</string>
++  </children>
++  <locationConfig>
++    <provider>aws-ec2</provider>
++    <minRam>2048</minRam>
++    <useJcloudsSshInit type="boolean">false</useJcloudsSshInit>
++    <identity>myidentity</identity>
++    <openIptables>true</openIptables>
++    <credential>mycredential</credential>
++    <machineCreateAttempts>2</machineCreateAttempts>
++    <imageNameRegex>Windows_Server-2012-R2_RTM-English-64Bit-Base</imageNameRegex>
++    <hardwareId>m3.large</hardwareId>
++    <templateOptions>
++      <com.google.common.collect.SingletonImmutableBiMap>
++        <entry>
++          <string>mapNewVolumeToDeviceName</string>
++          <ImmutableList>
++            <string>/dev/sda1</string>
++            <int>100</int>
++            <boolean>true</boolean>
++          </ImmutableList>
++        </entry>
++      </com.google.common.collect.SingletonImmutableBiMap>
++    </templateOptions>
++    <region>eu-central-1</region>
++    <spec.final>jclouds:aws-ec2:eu-central-1</spec.final>
++    <spec.original>jclouds:aws-ec2:eu-central-1</spec.original>
++    <machineCreationSemaphore>
++      <java.util.concurrent.Semaphore>
++        <sync class="java.util.concurrent.Semaphore$FairSync">
++          <state>2147483647</state>
++        </sync>
++      </java.util.concurrent.Semaphore>
++    </machineCreationSemaphore>
++    <vmInstanceIds>
++      <map>
++        <entry>
++          <locationProxy>KYSryzW8</locationProxy>
++          <string>eu-central-1/i-372eda8a</string>
++        </entry>
++      </map>
++    </vmInstanceIds>
++    <tags>
++      <set/>
++    </tags>
++  </locationConfig>
++  <locationConfigUnused>
++    <string>machineCreationSemaphore</string>
++  </locationConfigUnused>
++  <locationConfigDescription>aws-ec2:eu-central-1</locationConfigDescription>
++</location>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-azure-machine-VNapYjwp
----------------------------------------------------------------------
diff --cc brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-azure-machine-VNapYjwp
index 0000000,0000000..8d0f0cf
new file mode 100644
--- /dev/null
+++ b/brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-azure-machine-VNapYjwp
@@@ -1,0 -1,0 +1,271 @@@
++<!--
++    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.
++-->
++<location>
++  <brooklynVersion>0.9.0-20151120.1523</brooklynVersion>
++  <type>org.apache.brooklyn.location.jclouds.JcloudsSshMachineLocation</type>
++  <id>VNapYjwp</id>
++  <displayName>40.118.22.49</displayName>
++  <parent>briByOel</parent>
++  <locationConfig>
++    <displayName>40.118.22.49</displayName>
++    <address>
++      <java.net.Inet4Address>40.118.22.49/40.118.22.49</java.net.Inet4Address>
++    </address>
++    <user>amp</user>
++    <privateKeyData>myPrivateKey-----BEGIN RSA PRIVATE KEY-----
++myPrivateKey
++-----END RSA PRIVATE KEY-----
++</privateKeyData>
++    <config>
++      <java.util.Collections_-UnmodifiableMap>
++        <m class="MutableMap">
++          <privateKeyData>-----BEGIN RSA PRIVATE KEY-----
++myPrivateKey
++-----END RSA PRIVATE KEY-----
++</privateKeyData>
++        </m>
++      </java.util.Collections_-UnmodifiableMap>
++    </config>
++    <jcloudsParent>
++      <locationProxy>briByOel</locationProxy>
++    </jcloudsParent>
++    <node>
++      <org.jclouds.compute.domain.internal.NodeMetadataImpl>
++        <providerId>brookly-nwkmys-amp-tomca-q9kd-tomca-tg0g-ddd</providerId>
++        <name>brookly-nwkmys-amp-tomca-q9kd-tomca-tg0g-ddd</name>
++        <location class="org.jclouds.domain.internal.LocationImpl">
++          <scope>REGION</scope>
++          <id>West Europe</id>
++          <description>West Europe</description>
++          <parent class="org.jclouds.domain.internal.LocationImpl">
++            <scope>PROVIDER</scope>
++            <id>azurecompute</id>
++            <description>https://management.core.windows.net/341751b0-f348-45ce-9498-41cc68b4b45f</description>
++            <iso3166Codes class="com.google.common.collect.RegularImmutableSet">
++              <string>US-IA</string>
++              <string>US-VA</string>
++              <string>US-IL</string>
++              <string>US-TX</string>
++              <string>US-CA</string>
++              <string>IE</string>
++              <string>NL</string>
++              <string>HK</string>
++              <string>SG</string>
++              <string>JP-11</string>
++              <string>JP-27</string>
++              <string>BR</string>
++              <string>AU-NSW</string>
++              <string>AU-VIC</string>
++            </iso3166Codes>
++            <metadata class="com.google.common.collect.EmptyImmutableBiMap"/>
++          </parent>
++          <iso3166Codes class="com.google.common.collect.SingletonImmutableSet">
++            <string>NL</string>
++          </iso3166Codes>
++          <metadata class="com.google.common.collect.EmptyImmutableBiMap" reference="../parent/metadata"/>
++        </location>
++        <userMetadata/>
++        <id>brookly-nwkmys-amp-tomca-q9kd-tomca-tg0g-ddd</id>
++        <type>NODE</type>
++        <tags class="com.google.common.collect.EmptyImmutableSet"/>
++        <status>RUNNING</status>
++        <loginPort>22</loginPort>
++        <publicAddresses class="com.google.common.collect.SingletonImmutableSet">
++          <string>40.118.22.49</string>
++        </publicAddresses>
++        <privateAddresses class="com.google.common.collect.SingletonImmutableSet">
++          <string>10.0.0.4</string>
++        </privateAddresses>
++        <credentials>
++          <identity>amp</identity>
++          <credential>-----BEGIN RSA PRIVATE KEY-----
++myPrivateKey
++-----END RSA PRIVATE KEY-----
++</credential>
++          <authenticateSudo>false</authenticateSudo>
++          <password class="com.google.common.base.Absent"/>
++          <privateKey class="com.google.common.base.Present">
++            <reference class="string">-----BEGIN RSA PRIVATE KEY-----
++myPrivateKey
++-----END RSA PRIVATE KEY-----
++</reference>
++          </privateKey>
++        </credentials>
++        <group>brookly-nwkmys-amp-tomca-q9kd-tomca-tg0g</group>
++        <hostname>brookly-nwkmys-amp-tomca-q9kd-tomca-tg0g-ddd</hostname>
++      </org.jclouds.compute.domain.internal.NodeMetadataImpl>
++    </node>
++    <brooklyn.ssh.config.port type="int">22</brooklyn.ssh.config.port>
++    <region>West Europe</region>
++    <callerContext>
++      <null/>
++    </callerContext>
++    <detectMachineDetails type="boolean">true</detectMachineDetails>
++    <portforwarding.enabled type="boolean">false</portforwarding.enabled>
++    <port type="int">22</port>
++    <template>
++      <org.jclouds.compute.domain.internal.TemplateImpl>
++        <image class="org.jclouds.compute.domain.internal.ImageImpl">
++          <providerId>b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_1-LTS-amd64-server-20150123-en-us-30GB</providerId>
++          <name>Ubuntu Server 14.04.1 LTS</name>
++          <userMetadata/>
++          <id>b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_1-LTS-amd64-server-20150123-en-us-30GB</id>
++          <type>IMAGE</type>
++          <tags class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <operatingSystem>
++            <family>UBUNTU</family>
++            <version>14.04.1 LTS</version>
++            <description>Ubuntu Server 14.04.1 LTS (amd64 20150123) for Microsoft Azure. Ubuntu Server is the world&apos;s most popular Linux for cloud environments. Updates and patches for Ubuntu 14.04.1 LTS will be available until 2019-04-17.  Ubuntu Server is the perfect platform for all workloads from web applications to NoSQL databases and Hadoop. For more information see [Ubuntu Cloud|http://www.ubuntu.com/cloud|_blank] and [using Juju to deploy your workloads|http://juju.ubuntu.com|_blank].</description>
++            <is64Bit>true</is64Bit>
++          </operatingSystem>
++          <status>AVAILABLE</status>
++          <description>Ubuntu Server 14.04.1 LTS (amd64 20150123) for Microsoft Azure. Ubuntu Server is the world&apos;s most popular Linux for cloud environments. Updates and patches for Ubuntu 14.04.1 LTS will be available until 2019-04-17.  Ubuntu Server is the perfect platform for all workloads from web applications to NoSQL databases and Hadoop. For more information see [Ubuntu Cloud|http://www.ubuntu.com/cloud|_blank] and [using Juju to deploy your workloads|http://juju.ubuntu.com|_blank].</description>
++          <defaultCredentials>
++            <identity>root</identity>
++            <authenticateSudo>false</authenticateSudo>
++            <password class="com.google.common.base.Absent" reference="../../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/credentials/password"/>
++            <privateKey class="com.google.common.base.Absent" reference="../../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/credentials/password"/>
++          </defaultCredentials>
++        </image>
++        <hardware class="org.jclouds.compute.domain.internal.HardwareImpl">
++          <providerId>BASIC_A2</providerId>
++          <name>BASIC_A2</name>
++          <userMetadata/>
++          <id>BASIC_A2</id>
++          <type>HARDWARE</type>
++          <tags class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <processors class="ImmutableList">
++            <org.jclouds.compute.domain.Processor>
++              <cores>2.0</cores>
++              <speed>2.0</speed>
++            </org.jclouds.compute.domain.Processor>
++          </processors>
++          <ram>3584</ram>
++          <volumes class="ImmutableList"/>
++          <supportsImage class="com.google.common.base.Predicates$ObjectPredicate">ALWAYS_TRUE</supportsImage>
++          <hypervisor>Hyper-V</hypervisor>
++          <deprecated>false</deprecated>
++        </hardware>
++        <location class="org.jclouds.domain.internal.LocationImpl">
++          <scope>REGION</scope>
++          <id>West Europe</id>
++          <description>West Europe</description>
++          <parent class="org.jclouds.domain.internal.LocationImpl">
++            <scope>PROVIDER</scope>
++            <id>azurecompute</id>
++            <description>https://management.core.windows.net/341751b0-f348-45ce-9498-41cc68b4b45f</description>
++            <iso3166Codes class="com.google.common.collect.RegularImmutableSet" reference="../../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/location/parent/iso3166Codes"/>
++            <metadata class="com.google.common.collect.EmptyImmutableBiMap" reference="../../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/location/parent/metadata"/>
++          </parent>
++          <iso3166Codes class="com.google.common.collect.SingletonImmutableSet">
++            <string>NL</string>
++          </iso3166Codes>
++          <metadata class="com.google.common.collect.EmptyImmutableBiMap" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/location/parent/metadata"/>
++        </location>
++        <options class="org.jclouds.azurecompute.options.AzureComputeTemplateOptions">
++          <port>-1</port>
++          <seconds>-1</seconds>
++          <taskName>bootstrap</taskName>
++          <runAsRoot>true</runAsRoot>
++          <blockOnComplete>true</blockOnComplete>
++          <wrapInInitScript>true</wrapInInitScript>
++          <inboundPorts class="com.google.common.collect.RegularImmutableSet">
++            <int>22</int>
++            <int>31880</int>
++            <int>31001</int>
++            <int>8080</int>
++            <int>1099</int>
++            <int>8443</int>
++          </inboundPorts>
++          <script class="org.jclouds.scriptbuilder.domain.StatementList">
++            <statements class="ImmutableList">
++              <org.jclouds.scriptbuilder.statements.login.AdminAccess>
++                <config>
++                  <adminUsername>amp</adminUsername>
++                  <adminFullName>amp</adminFullName>
++                  <adminPublicKey>ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAyRJ8h3uGrGOPo4fe2Fxd47bKE4I7aTwDvs3zkMNDGQZFzVPrldpDLcXG6E9jjAOomhchP7+PKc/FLa3pkv9eA5gX0m8GwYiVzmwYP6k2dvX+hyVD9m68xGMPYdjnu4ytTnnJLaDDqdd7ta/sJLWFbPkup1L6iHgHzJ9Zy1238yAtd7ypmlpvFLUltlqq614dpZq4C3ZuHhylL9gnNKPDYiX1a6XxX8WDkl5QLDMpU+pVp/XtyWvsBJ2j+aIghGSUtYB4kZYKRg9Vw/SskUg2a9NNnGxhox6D5MEw48eYpWoFk1A5qQrfPv5iwhEvbKWq3N600GvcfDwVgqJIK9VksQ== amp@AMP
++</adminPublicKey>
++                  <adminPrivateKey>pa55w0rd</adminPrivateKey>
++                  <adminPassword>pa55w0rd</adminPassword>
++                  <loginPassword>pa55w0rd</loginPassword>
++                  <grantSudoToAdminUser>true</grantSudoToAdminUser>
++                  <installAdminPrivateKey>false</installAdminPrivateKey>
++                  <resetLoginPassword>true</resetLoginPassword>
++                  <cryptFunction class="org.jclouds.compute.functions.Sha512Crypt$Function">INSTANCE</cryptFunction>
++                  <adminCredentials>
++                    <identity>amp</identity>
++                    <credential>c6KeIH4QNmOs-ignored</credential>
++                  </adminCredentials>
++                  <authorizeAdminPublicKey>true</authorizeAdminPublicKey>
++                  <lockSsh>true</lockSsh>
++                </config>
++              </org.jclouds.scriptbuilder.statements.login.AdminAccess>
++            </statements>
++          </script>
++          <tags class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <securityGroups class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <blockUntilRunning>true</blockUntilRunning>
++          <userMetadata>
++            <Name>brookly-nwkmys-amp-tomca-q9kd-tomca-tg0g-pnds</Name>
++            <entry key="brooklyn-user">amp</entry>
++            <entry key="brooklyn-app-id">Q9kDQQ1s</entry>
++            <entry key="brooklyn-app-name">Tomcat Azure EU West</entry>
++            <entry key="brooklyn-entity-id">tg0gDGp0</entry>
++            <entry key="brooklyn-entity-name">Tomcat Server</entry>
++            <entry key="brooklyn-server-creation-date">2015-10-21-1404</entry>
++          </userMetadata>
++          <nodeNames class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <networks class="com.google.common.collect.EmptyImmutableSet" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/tags"/>
++          <virtualNetworkName class="com.google.common.base.Present">
++            <reference class="string">jclouds-virtual-network</reference>
++          </virtualNetworkName>
++          <addressSpaceAddressPrefix class="com.google.common.base.Absent" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/credentials/password"/>
++          <subnetName class="com.google.common.base.Present">
++            <reference class="string">jclouds-1</reference>
++          </subnetName>
++          <subnetAddressPrefix class="com.google.common.base.Absent" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/credentials/password"/>
++          <storageAccountName class="com.google.common.base.Present">
++            <reference class="string">jcloudsbchsfqegxc</reference>
++          </storageAccountName>
++          <storageAccountType class="com.google.common.base.Absent" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/credentials/password"/>
++          <networkSecurityGroupName class="com.google.common.base.Absent" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/credentials/password"/>
++          <reservedIPName class="com.google.common.base.Absent" reference="../../../../node/org.jclouds.compute.domain.internal.NodeMetadataImpl/credentials/password"/>
++        </options>
++      </org.jclouds.compute.domain.internal.TemplateImpl>
++    </template>
++    <usedPorts>
++      <set>
++        <int>31880</int>
++        <int>8443</int>
++        <int>8080</int>
++        <int>31001</int>
++        <int>1099</int>
++      </set>
++    </usedPorts>
++    <tags>
++      <set/>
++    </tags>
++  </locationConfig>
++  <locationConfigUnused>
++    <string>displayName</string>
++    <string>config</string>
++    <string>region</string>
++    <string>portforwarding.enabled</string>
++  </locationConfigUnused>
++</location>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/018a0e15/brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-azure-parent-briByOel
----------------------------------------------------------------------
diff --cc brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-azure-parent-briByOel
index 0000000,0000000..c80f0f2
new file mode 100644
--- /dev/null
+++ b/brooklyn-server/locations/jclouds/src/test/resources/org/apache/brooklyn/location/jclouds/persisted-azure-parent-briByOel
@@@ -1,0 -1,0 +1,65 @@@
++<!--
++    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.
++-->
++<location>
++  <brooklynVersion>0.9.0-20151120.1523</brooklynVersion>
++  <type>org.apache.brooklyn.location.jclouds.JcloudsLocation</type>
++  <id>briByOel</id>
++  <displayName>Microsoft Azure West Europe</displayName>
++  <children>
++    <string>VNapYjwp</string>
++  </children>
++  <locationConfig>
++    <provider>azurecompute</provider>
++    <endpoint>https://management.core.windows.net/341751b0-f348-45ce-9498-41cc68b4b45f</endpoint>
++    <identity>/home/amp/.amp/azure.p12</identity>
++    <jclouds.azurecompute.operation.timeout>120000</jclouds.azurecompute.operation.timeout>
++    <credential>pa55w0rd</credential>
++    <vmNameMaxLength>45</vmNameMaxLength>
++    <displayName>Microsoft Azure West Europe</displayName>
++    <imageId>b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_1-LTS-amd64-server-20150123-en-us-30GB</imageId>
++    <hardwareId>BASIC_A2</hardwareId>
++    <spec.named.name>azure-euwest</spec.named.name>
++    <spec.original>azure-euwest</spec.original>
++    <region>West Europe</region>
++    <spec.final>jclouds:azurecompute:West Europe</spec.final>
++    <machineCreationSemaphore>
++      <java.util.concurrent.Semaphore>
++        <sync class="java.util.concurrent.Semaphore$FairSync">
++          <state>2147483647</state>
++        </sync>
++      </java.util.concurrent.Semaphore>
++    </machineCreationSemaphore>
++    <vmInstanceIds>
++      <map>
++        <entry>
++          <locationProxy>VNapYjwp</locationProxy>
++          <string>brookly-nwkmys-amp-tomca-q9kd-tomca-tg0g-ddd</string>
++        </entry>
++      </map>
++    </vmInstanceIds>
++    <tags>
++      <set/>
++    </tags>
++  </locationConfig>
++  <locationConfigUnused>
++    <string>jclouds.azurecompute.operation.timeout</string>
++    <string>displayName</string>
++  </locationConfigUnused>
++  <locationConfigDescription>azurecompute:West Europe:https://management.core.windows.net/341751b0-f348-45ce-9498-41cc68b4b45f</locationConfigDescription>
++</location>