Cleaner for the old dangling references
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/59f8e8b4 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/59f8e8b4 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/59f8e8b4 Branch: refs/heads/master Commit: 59f8e8b44c0399ef270da8b53d4b3e6851069cf4 Parents: c7c8db5 Author: Ivana Yovcheva <ivana.yovch...@gmail.com> Authored: Wed Jun 15 19:22:43 2016 +0300 Committer: Ivana Yovcheva <ivana.yovch...@gmail.com> Committed: Tue Jul 19 18:30:15 2016 +0300 ---------------------------------------------------------------------- .../DeleteOrphanedLocationsTransformer.java | 147 ++++++++++- .../brooklyn/launcher/common/BasicLauncher.java | 7 +- .../CleanOrphanedLocationsLiveTest.java | 257 +++++++++++++++++++ .../main/java/org/apache/brooklyn/cli/Main.java | 77 ++++++ 4 files changed, 475 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/59f8e8b4/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/transformer/impl/DeleteOrphanedLocationsTransformer.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/transformer/impl/DeleteOrphanedLocationsTransformer.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/transformer/impl/DeleteOrphanedLocationsTransformer.java index 985ce37..1c9cd9e 100644 --- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/transformer/impl/DeleteOrphanedLocationsTransformer.java +++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/transformer/impl/DeleteOrphanedLocationsTransformer.java @@ -20,13 +20,17 @@ package org.apache.brooklyn.core.mgmt.rebind.transformer.impl; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Set; +import com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList; import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMemento; +import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoRawData; import org.apache.brooklyn.api.mgmt.rebind.mementos.EntityMemento; import org.apache.brooklyn.api.mgmt.rebind.mementos.LocationMemento; import org.apache.brooklyn.core.mgmt.rebind.dto.BrooklynMementoImpl; import org.apache.brooklyn.core.mgmt.rebind.transformer.BrooklynMementoTransformer; +import org.apache.brooklyn.core.mgmt.rebind.transformer.CompoundTransformer; import org.apache.brooklyn.util.collections.MutableList; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.collections.MutableSet; @@ -34,17 +38,49 @@ import org.apache.brooklyn.util.collections.MutableSet; import com.google.common.annotations.Beta; import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import org.apache.brooklyn.util.core.xstream.XmlUtil; +import org.w3c.dom.Node; + +import javax.xml.xpath.XPathConstants; @Beta -public class DeleteOrphanedLocationsTransformer implements BrooklynMementoTransformer { +public class DeleteOrphanedLocationsTransformer extends CompoundTransformer implements BrooklynMementoTransformer { + + protected DeleteOrphanedLocationsTransformer(DeleteOrphanedLocationsTransformer.Builder builder) { + super(builder); + } + + public static Builder builder() { + return new DeleteOrphanedLocationsTransformer.Builder(); + } + + public static class Builder extends CompoundTransformer.Builder { + + public DeleteOrphanedLocationsTransformer build() { + return new DeleteOrphanedLocationsTransformer(this); + } + } + + public BrooklynMementoRawData transform(BrooklynMementoRawData input) { + Map<String, String> locationsToKeep = findLocationsToKeep(input); + + return BrooklynMementoRawData.builder() + .catalogItems(input.getCatalogItems()) + .enrichers(input.getEnrichers()) + .entities(input.getEntities()) + .feeds(input.getFeeds()) + .locations(locationsToKeep) + .policies(input.getPolicies()) + .build(); + } // TODO Work in progress; untested code! - + public BrooklynMemento transform(BrooklynMemento input) throws Exception { Set<String> referencedLocationIds = findReferencedLocationIds(input); Set<String> unreferencedLocationIds = Sets.newLinkedHashSet(); List<String> toCheck = Lists.newLinkedList(input.getLocationIds()); - + while (!toCheck.isEmpty()) { String locationId = toCheck.remove(0); List<String> locationsInHierarchy = MutableList.<String>builder() @@ -52,7 +88,7 @@ public class DeleteOrphanedLocationsTransformer implements BrooklynMementoTransf .addAll(findLocationAncestors(input, locationId)) .addAll(findLocationDescendents(input, locationId)) .build(); - + if (containsAny(referencedLocationIds, locationsInHierarchy)) { // keep them all } else { @@ -60,7 +96,7 @@ public class DeleteOrphanedLocationsTransformer implements BrooklynMementoTransf } toCheck.removeAll(locationsInHierarchy); } - + // TODO What about brooklyn version? return BrooklynMementoImpl.builder() .applicationIds(input.getApplicationIds()) @@ -78,26 +114,113 @@ public class DeleteOrphanedLocationsTransformer implements BrooklynMementoTransf .catalogItems(input.getCatalogItemMementos()) .build(); } - + public boolean containsAny(Collection<?> container, Iterable<?> contenders) { for (Object contender : contenders) { if (container.contains(contender)) return true; } return false; } - + public Set<String> findReferencedLocationIds(BrooklynMemento input) { Set<String> result = Sets.newLinkedHashSet(); - + for (EntityMemento entity : input.getEntityMementos().values()) { result.addAll(entity.getLocations()); } return result; } - + + public Map<String, String> findLocationsToKeep(BrooklynMementoRawData input) { + Map<String, String> locationsToKeep = MutableMap.of(); + Set<String> allReferencedLocations = findAllReferencedLocations(input); + + for (Map.Entry location: input.getLocations().entrySet()) { + if (allReferencedLocations.contains(location.getKey())) { + locationsToKeep.put((String) location.getKey(), (String) location.getValue()); + } + } + return locationsToKeep; + } + + public Set<String> findAllReferencedLocations(BrooklynMementoRawData input) { + Set<String> allReferencedLocations = MutableSet.of(); + + allReferencedLocations.addAll(searchLocationsToKeepInEntities(input.getEntities())); + allReferencedLocations.addAll(searchLocationsToKeepInPolicies(input.getPolicies())); + allReferencedLocations.addAll(searchLocationsToKeepInEnrichers(input.getEnrichers())); + allReferencedLocations.addAll(searchLocationsToKeepInLocations(input.getLocations(), allReferencedLocations)); + + return allReferencedLocations; + } + + private Set<String> searchLocationsToKeepInEntities(Map<String, String> entities) { + Set<String> result = MutableSet.of(); + + for(Map.Entry entity: entities.entrySet()) { + + String prefix = "/entity"; + String location = "/locations/string"; + String locationProxy = "/attributes/softwareservice.provisioningLocation/locationProxy"; + + result.addAll(getAllNodesFromXpath((DTMNodeList) XmlUtil.xpath((String) entity.getValue(), prefix+location, XPathConstants.NODESET))); + result.addAll(getAllNodesFromXpath((DTMNodeList) XmlUtil.xpath((String) entity.getValue(), prefix+locationProxy, XPathConstants.NODESET))); + } + + return result; + } + + private Set<String> searchLocationsToKeepInLocations(Map<String, String> locations, Set<String> alreadyReferencedLocations) { + Set<String> result = MutableSet.of(); + + String prefix = "/location"; + String locationId = "/id"; + String locationChildren = "/children/string"; + String locationParentDirectTag = "/parent"; + String locationParent = "/locationConfig/jcloudsParent/locationProxy"; + String locationProxy = "/locationConfig/vmInstanceIds/map/entry/locationProxy"; + + for (Map.Entry location: locations.entrySet()) { + if (alreadyReferencedLocations.contains(location)) { + result.addAll(getAllNodesFromXpath((DTMNodeList) XmlUtil.xpath((String) location.getValue(), prefix+locationId, XPathConstants.NODESET))); + result.addAll(getAllNodesFromXpath((DTMNodeList) XmlUtil.xpath((String) location.getValue(), prefix+locationChildren, XPathConstants.NODESET))); + result.addAll(getAllNodesFromXpath((DTMNodeList) XmlUtil.xpath((String) location.getValue(), prefix+locationParent, XPathConstants.NODESET))); + result.addAll(getAllNodesFromXpath((DTMNodeList) XmlUtil.xpath((String) location.getValue(), prefix+locationParentDirectTag, XPathConstants.NODESET))); + result.addAll(getAllNodesFromXpath((DTMNodeList) XmlUtil.xpath((String) location.getValue(), prefix+locationProxy, XPathConstants.NODESET))); + } + } + + return result; + } + + private Set<String> searchLocationsToKeepInPolicies(Map<String, String> policies) { + Set<String> result = MutableSet.of(); + + return result; + } + + private Set<String> searchLocationsToKeepInEnrichers(Map<String, String> enrichers) { + Set<String> result = MutableSet.of(); + + return result; + } + + private Set<String> getAllNodesFromXpath(DTMNodeList nodeList) { + Set<String> result = MutableSet.of(); + + int i = 0; + Node nextNode = nodeList.item(i); + while (nextNode != null) { + result.add(nextNode.getTextContent()); + nextNode = nodeList.item(++i); + } + + return result; + } + public Set<String> findLocationAncestors(BrooklynMemento input, String locationId) { Set<String> result = Sets.newLinkedHashSet(); - + String parentId = null; do { LocationMemento memento = input.getLocationMemento(locationId); @@ -107,11 +230,11 @@ public class DeleteOrphanedLocationsTransformer implements BrooklynMementoTransf return result; } - + public Set<String> findLocationDescendents(BrooklynMemento input, String locationId) { Set<String> result = Sets.newLinkedHashSet(); List<String> tovisit = Lists.newLinkedList(); - + tovisit.add(locationId); while (!tovisit.isEmpty()) { LocationMemento memento = input.getLocationMemento(tovisit.remove(0)); http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/59f8e8b4/launcher-common/src/main/java/org/apache/brooklyn/launcher/common/BasicLauncher.java ---------------------------------------------------------------------- diff --git a/launcher-common/src/main/java/org/apache/brooklyn/launcher/common/BasicLauncher.java b/launcher-common/src/main/java/org/apache/brooklyn/launcher/common/BasicLauncher.java index b550a5b..3c74549 100644 --- a/launcher-common/src/main/java/org/apache/brooklyn/launcher/common/BasicLauncher.java +++ b/launcher-common/src/main/java/org/apache/brooklyn/launcher/common/BasicLauncher.java @@ -45,6 +45,7 @@ import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.catalog.internal.CatalogInitialization; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.entity.StartableApplication; +import org.apache.brooklyn.core.entity.factory.ApplicationBuilder; import org.apache.brooklyn.core.entity.trait.Startable; import org.apache.brooklyn.core.internal.BrooklynProperties; import org.apache.brooklyn.core.mgmt.EntityManagementUtils; @@ -60,6 +61,7 @@ import org.apache.brooklyn.core.mgmt.persist.PersistenceObjectStore; import org.apache.brooklyn.core.mgmt.rebind.PersistenceExceptionHandlerImpl; import org.apache.brooklyn.core.mgmt.rebind.RebindManagerImpl; import org.apache.brooklyn.core.mgmt.rebind.transformer.CompoundTransformer; +import org.apache.brooklyn.core.mgmt.rebind.transformer.impl.DeleteOrphanedLocationsTransformer; import org.apache.brooklyn.core.server.BrooklynServerConfig; import org.apache.brooklyn.core.server.BrooklynServerPaths; import org.apache.brooklyn.entity.brooklynnode.BrooklynNode; @@ -373,7 +375,6 @@ public class BasicLauncher<T extends BasicLauncher<T>> { managementContext, destinationLocationSpec, destinationDir); BrooklynPersistenceUtils.writeMemento(managementContext, memento, destinationObjectStore); BrooklynPersistenceUtils.writeManagerMemento(managementContext, planeState, destinationObjectStore); - } catch (Exception e) { Exceptions.propagateIfFatal(e); LOG.debug("Error copying persisted state (rethrowing): " + e, e); @@ -382,6 +383,10 @@ public class BasicLauncher<T extends BasicLauncher<T>> { } } + public void cleanOrphanedLocations(String destinationDir, @Nullable String destinationLocationSpec) { + copyPersistedState(destinationDir, destinationLocationSpec, DeleteOrphanedLocationsTransformer.builder().build()); + } + /** @deprecated since 0.7.0 use {@link #copyPersistedState} instead */ // Make private after deprecation @Deprecated http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/59f8e8b4/launcher/src/test/java/org/apache/brooklyn/launcher/CleanOrphanedLocationsLiveTest.java ---------------------------------------------------------------------- diff --git a/launcher/src/test/java/org/apache/brooklyn/launcher/CleanOrphanedLocationsLiveTest.java b/launcher/src/test/java/org/apache/brooklyn/launcher/CleanOrphanedLocationsLiveTest.java new file mode 100644 index 0000000..cda7cc0 --- /dev/null +++ b/launcher/src/test/java/org/apache/brooklyn/launcher/CleanOrphanedLocationsLiveTest.java @@ -0,0 +1,257 @@ +/* + * 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.launcher; + +import org.apache.brooklyn.api.mgmt.ManagementContext; +import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode; +import org.apache.brooklyn.api.mgmt.rebind.PersistenceExceptionHandler; +import org.apache.brooklyn.api.mgmt.rebind.RebindManager; +import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoRawData; +import org.apache.brooklyn.core.mgmt.ha.OsgiManager; +import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext; +import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; +import org.apache.brooklyn.core.mgmt.persist.BrooklynMementoPersisterToObjectStore; +import org.apache.brooklyn.core.mgmt.persist.BrooklynPersistenceUtils; +import org.apache.brooklyn.core.mgmt.persist.PersistMode; +import org.apache.brooklyn.core.mgmt.persist.PersistenceObjectStore; +import org.apache.brooklyn.core.mgmt.rebind.PersistenceExceptionHandlerImpl; +import org.apache.brooklyn.core.mgmt.rebind.RebindManagerImpl; +import org.apache.brooklyn.core.mgmt.rebind.transformer.impl.DeleteOrphanedLocationsTransformer; +import org.apache.brooklyn.core.server.BrooklynServerConfig; +import org.apache.brooklyn.core.server.BrooklynServerPaths; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.collections.MutableSet; +import org.apache.brooklyn.util.time.Duration; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import org.apache.brooklyn.core.internal.BrooklynProperties; + +import java.util.Map; +import java.util.Set; + +public class CleanOrphanedLocationsLiveTest { + + private String PERSISTED_STATE_PATH_WITH_ORPHANED_LOCATIONS = "/orphaned-locations-test-data/data-with-orphaned-locations"; + private String PERSISTED_STATE_PATH_WITH_MULTIPLE_LOCATIONS_OCCURRENCE = "/orphaned-locations-test-data/fake-multiple-location-for-multiple-search-tests"; + private String PERSISTED_STATE_PATH_WITHOUT_ORPHANED_LOCATIONS = "/orphaned-locations-test-data/data-without-orphaned-locations"; + private String PERSISTED_STATE_DESTINATION_PATH = "/orphaned-locations-test-data/copy-persisted-state-destination"; + + + private String persistenceDirWithOrphanedLocations; + private String persistenceDirWithoutOrphanedLocations; + private String persistenceDirWithMultipleLocationsOccurrence; + private String destinationDir; + private String destinationLocation; + private String localBrooklynProperties; + private String persistenceLocation; + private String highAvailabilityMode; + + private DeleteOrphanedLocationsTransformer transformer; + + private ManagementContext managementContext; + + @BeforeMethod(alwaysRun = true) + public void initialize() throws Exception { + persistenceDirWithOrphanedLocations = getClass().getResource(PERSISTED_STATE_PATH_WITH_ORPHANED_LOCATIONS).getFile(); + persistenceDirWithoutOrphanedLocations = getClass().getResource(PERSISTED_STATE_PATH_WITHOUT_ORPHANED_LOCATIONS).getFile(); + persistenceDirWithMultipleLocationsOccurrence = getClass().getResource(PERSISTED_STATE_PATH_WITH_MULTIPLE_LOCATIONS_OCCURRENCE).getFile(); + + destinationDir = getClass().getResource(PERSISTED_STATE_DESTINATION_PATH).getFile(); + + transformer = DeleteOrphanedLocationsTransformer.builder().build(); + } + + private void initManagementContextAndPersistence(String persistenceDir) { + + BrooklynProperties brooklynProperties = BrooklynProperties.Factory.builderDefault().build(); + brooklynProperties.put(BrooklynServerConfig.MGMT_BASE_DIR.getName(), ""); + + managementContext = new LocalManagementContext(brooklynProperties); + + if (persistenceLocation == null) { + persistenceLocation = brooklynProperties.getConfig(BrooklynServerConfig.PERSISTENCE_LOCATION_SPEC); + } + + persistenceDir = BrooklynServerPaths.newMainPersistencePathResolver(brooklynProperties).location(persistenceLocation).dir(persistenceDir).resolve(); + PersistenceObjectStore objectStore = BrooklynPersistenceUtils.newPersistenceObjectStore(managementContext, persistenceLocation, persistenceDir, + PersistMode.AUTO, HighAvailabilityMode.HOT_STANDBY); + + BrooklynMementoPersisterToObjectStore persister = new BrooklynMementoPersisterToObjectStore( + objectStore, + ((ManagementContextInternal)managementContext).getBrooklynProperties(), + managementContext.getCatalogClassLoader()); + + RebindManager rebindManager = managementContext.getRebindManager(); + + PersistenceExceptionHandler persistenceExceptionHandler = PersistenceExceptionHandlerImpl.builder().build(); + ((RebindManagerImpl) rebindManager).setPeriodicPersistPeriod(Duration.ONE_SECOND); + rebindManager.setPersister(persister, persistenceExceptionHandler); + } + + @Test + public void testSelectionWithOrphanedLocationsInData() { + + Set<String> locationsToKeep = MutableSet.of( + "tjdywoxbji", + "pudtixbw89" + ); + + Set<String> orphanedLocations = MutableSet.of( + "banby1jx4j", + "msyp655po0", + "ppamsemxgo" + ); + + initManagementContextAndPersistence(persistenceDirWithOrphanedLocations); + BrooklynMementoRawData mementoRawData = managementContext.getRebindManager().retrieveMementoRawData(); + + Set<String> allReferencedLocationsFoundByTransformer = transformer.findAllReferencedLocations(mementoRawData); + Map<String, String> locationsToKeepFoundByTransformer = transformer.findLocationsToKeep(mementoRawData); + + Asserts.assertEquals(allReferencedLocationsFoundByTransformer, locationsToKeep); + Asserts.assertEquals(locationsToKeepFoundByTransformer.keySet(), locationsToKeep); + + Map<String, String> locationsNotToKeepDueToTransormer = MutableMap.of(); + locationsNotToKeepDueToTransormer.putAll(mementoRawData.getLocations()); + for (Map.Entry location: locationsToKeepFoundByTransformer.entrySet()) { + locationsNotToKeepDueToTransormer.remove(location.getKey()); + } + Set<String> notReferencedLocationsDueToTransormer = MutableSet.of(); + notReferencedLocationsDueToTransormer.addAll(mementoRawData.getLocations().keySet()); + for (String location: allReferencedLocationsFoundByTransformer) { + notReferencedLocationsDueToTransormer.remove(location); + } + + Asserts.assertEquals(notReferencedLocationsDueToTransormer, orphanedLocations); + Asserts.assertEquals(locationsNotToKeepDueToTransormer.keySet(), orphanedLocations); + + + BrooklynMementoRawData transformedMemento = transformer.transform(mementoRawData); + Asserts.assertFalse(EqualsBuilder.reflectionEquals(mementoRawData, transformedMemento)); + Asserts.assertTrue(EqualsBuilder.reflectionEquals(mementoRawData.getEntities(), transformedMemento.getEntities())); + Asserts.assertTrue(EqualsBuilder.reflectionEquals(mementoRawData.getEnrichers(), transformedMemento.getEnrichers())); + Asserts.assertTrue(EqualsBuilder.reflectionEquals(mementoRawData.getPolicies(), transformedMemento.getPolicies())); + Asserts.assertFalse(EqualsBuilder.reflectionEquals(mementoRawData.getLocations(), transformedMemento.getLocations())); + } + + @Test + public void testSelectionWithoutOrphanedLocationsInData() { + + Set<String> locationsToKeep = MutableSet.of( + "tjdywoxbji", + "pudtixbw89" + ); + + initManagementContextAndPersistence(persistenceDirWithoutOrphanedLocations); + BrooklynMementoRawData mementoRawData = managementContext.getRebindManager().retrieveMementoRawData(); + + Set<String> allReferencedLocationsFoundByTransformer = transformer.findAllReferencedLocations(mementoRawData); + Map<String, String> locationsToKeepFoundByTransformer = transformer.findLocationsToKeep(mementoRawData); + + Asserts.assertEquals(allReferencedLocationsFoundByTransformer, locationsToKeep); + Asserts.assertEquals(locationsToKeepFoundByTransformer.keySet(), locationsToKeep); + + Map<String, String> locationsNotToKeepDueToTransormer = MutableMap.of(); + locationsNotToKeepDueToTransormer.putAll(mementoRawData.getLocations()); + for (Map.Entry location: locationsToKeepFoundByTransformer.entrySet()) { + locationsNotToKeepDueToTransormer.remove(location.getKey()); + } + Set<String> notReferencedLocationsDueToTransormer = MutableSet.of(); + notReferencedLocationsDueToTransormer.addAll(mementoRawData.getLocations().keySet()); + for (String location: allReferencedLocationsFoundByTransformer) { + notReferencedLocationsDueToTransormer.remove(location); + } + + Asserts.assertSize(locationsNotToKeepDueToTransormer.keySet(), 0); + Asserts.assertSize(notReferencedLocationsDueToTransormer, 0); + + BrooklynMementoRawData transformedMemento = transformer.transform(mementoRawData); + Asserts.assertTrue(EqualsBuilder.reflectionEquals(mementoRawData, transformedMemento)); + } + + @Test + public void testCleanedCopiedPersistedState() throws Exception { + + BrooklynLauncher launcher = BrooklynLauncher.newInstance() + .localBrooklynPropertiesFile(localBrooklynProperties) + .brooklynProperties(OsgiManager.USE_OSGI, false) + .persistMode(PersistMode.AUTO) + .persistenceDir(persistenceDirWithOrphanedLocations) + .persistenceLocation(persistenceLocation) + .highAvailabilityMode(HighAvailabilityMode.DISABLED); + + try { + launcher.cleanOrphanedLocations(destinationDir, destinationLocation); + + } catch (Exception e) { + throw new Exception(e); + } finally { + launcher.terminate(); + } + + initManagementContextAndPersistence(destinationDir); + BrooklynMementoRawData mementoRawDataFromCleanedState = managementContext.getRebindManager().retrieveMementoRawData(); + Asserts.assertTrue(mementoRawDataFromCleanedState.getEntities().size() != 0); + Asserts.assertTrue(mementoRawDataFromCleanedState.getLocations().size() != 0); + + + initManagementContextAndPersistence(persistenceDirWithoutOrphanedLocations); + BrooklynMementoRawData mementoRawData = managementContext.getRebindManager().retrieveMementoRawData(); + + Asserts.assertTrue(EqualsBuilder.reflectionEquals(mementoRawData, mementoRawDataFromCleanedState)); + } + + @Test + public void testMultipleLocationOccurrenceInEntity() { + Set<String> allReferencedLocations = MutableSet.of( + "m72nvkl799", + "m72nTYl799", + "m72LKVN799", + "jf4rwubqyb", + "jf4rwuTTTb", + "jf4rwuHHHb", + "m72nvkl799", + "m72nPTUF99", + "m72nhtDr99" + ); + + Set<String> locationsToKeep = MutableSet.of( + "m72nvkl799", + "jf4rwubqyb" + ); + + initManagementContextAndPersistence(persistenceDirWithMultipleLocationsOccurrence); + + BrooklynMementoRawData mementoRawData = managementContext.getRebindManager().retrieveMementoRawData(); + Set<String> allReferencedLocationsFoundByTransformer = transformer.findAllReferencedLocations(mementoRawData); + Map<String, String> locationsToKeepFoundByTransformer = transformer.findLocationsToKeep(mementoRawData); + + Asserts.assertEquals(allReferencedLocationsFoundByTransformer, allReferencedLocations); + Asserts.assertEquals(locationsToKeepFoundByTransformer.keySet(), locationsToKeep); + } + + @AfterMethod + public void cleanCopiedPersistedState() { + + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/59f8e8b4/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java ---------------------------------------------------------------------- diff --git a/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java b/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java index 2d65ad0..53a7829 100644 --- a/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java +++ b/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java @@ -827,6 +827,82 @@ public class Main extends AbstractMain { } } + @Command(name = "clean-orphaned-locations", description = "Removes existing orphaned locations") + public static class CleanOrphanedLocationsCommand extends BrooklynCommandCollectingArgs { + + @Option(name = { "--localBrooklynProperties" }, title = "local brooklyn.properties file", + description = "local brooklyn.properties file, specific to this launch (appending to and overriding global properties)") + public String localBrooklynProperties; + + @Option(name = { "--persistenceDir" }, title = "persistence dir", + description = "The directory to read persisted state (or container name if using an object store)") + public String persistenceDir; + + @Option(name = { "--persistenceLocation" }, title = "persistence location", + description = "The location spec for an object store to read persisted state") + public String persistenceLocation; + + @Option(name = { "--destinationDir" }, required = true, title = "destination dir", + description = "The directory to copy persistence data to without orphaned locations") + public String destinationDir; + + @Option(name = { "--destinationLocation" }, title = "persistence location", + description = "The location spec for an object store to copy data to") + public String destinationLocation; + + @Option(name = { "--transformations" }, title = "transformations", + description = "local transformations file, to be applied to the copy of the data before uploading it") + public String transformations; + + @Override + public Void call() throws Exception { + checkNotNull(destinationDir, "orphanedReferencesTmpDir"); + + BrooklynLauncher launcher; + failIfArguments(); + + try { + log.info("Retrieving and copying persisted state to " + destinationDir + " without orphaned locations"); + + + PersistMode persistMode = PersistMode.AUTO; + HighAvailabilityMode highAvailabilityMode = HighAvailabilityMode.DISABLED; + + launcher = BrooklynLauncher.newInstance() + .localBrooklynPropertiesFile(localBrooklynProperties) + .brooklynProperties(OsgiManager.USE_OSGI, false) + .persistMode(persistMode) + .persistenceDir(persistenceDir) + .persistenceLocation(persistenceLocation) + .highAvailabilityMode(highAvailabilityMode); + + } catch (FatalConfigurationRuntimeException e) { + throw e; + } catch (Exception e) { + throw new FatalConfigurationRuntimeException("Fatal error configuring Brooklyn launch: "+e.getMessage(), e); + } + + try { + launcher.cleanOrphanedLocations(destinationDir, destinationLocation); + } catch (FatalRuntimeException e) { + throw e; + } catch (Exception e) { + Exceptions.propagateIfFatal(e); + log.error("Error retrieving persisted state: "+Exceptions.collapseText(e), e); + Exceptions.propagate(e); + } finally { + try { + launcher.terminate(); + } catch (Exception e2) { + log.warn("Subsequent error during termination: "+e2); + log.debug("Details of subsequent error during termination: "+e2, e2); + } + } + + return null; + } + } + @Command(name = "copy-state", description = "Retrieves persisted state") public static class CopyStateCommand extends BrooklynCommandCollectingArgs { @@ -931,6 +1007,7 @@ public class Main extends AbstractMain { HelpCommand.class, cliInfoCommand(), GeneratePasswordCommand.class, + CleanOrphanedLocationsCommand.class, CopyStateCommand.class, ListAllCommand.class, cliLaunchCommand()