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'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'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>