From 415d08349cfd8ffb2848cf89f97fc693346f4444 Mon Sep 17 00:00:00 2001 From: Sam Corbett Date: Tue, 21 Jul 2015 12:00:44 +0100 Subject: [PATCH 1/3] Adds suspend and resume interfaces to MachineManagementMixins. And uses them in JcloudsLocation. --- .../location/MachineManagementMixins.java | 45 ++++++++++++++++--- .../location/jclouds/JcloudsLocation.java | 36 ++++++++++++++- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/brooklyn/location/MachineManagementMixins.java b/api/src/main/java/brooklyn/location/MachineManagementMixins.java index 77ffea596c..99038ba2a7 100644 --- a/api/src/main/java/brooklyn/location/MachineManagementMixins.java +++ b/api/src/main/java/brooklyn/location/MachineManagementMixins.java @@ -20,18 +20,28 @@ import java.util.Map; +import com.google.common.annotations.Beta; + +/** + * Defines mixins for interesting locations. + */ public class MachineManagementMixins { - public interface RichMachineProvisioningLocation extends MachineProvisioningLocation, ListsMachines, GivesMachineMetadata, KillsMachines {} - + public interface RichMachineProvisioningLocation extends + MachineProvisioningLocation, ListsMachines, GivesMachineMetadata, KillsMachines {} + public interface ListsMachines { - /** returns map of machine ID to metadata record for all machines known in a given cloud location */ + /** + * @return A map of machine ID to metadata record for all machines known in a given cloud location. + */ Map listMachines(); } public interface GivesMachineMetadata { - /** returns the MachineMetadata for a given (brooklyn) machine location instance, - * or null if not matched */ + /** + * @return the {@link MachineMetadata} for a given (brooklyn) machine location instance, + * or null if not matched. + */ MachineMetadata getMachineMetadata(MachineLocation location); } @@ -55,5 +65,28 @@ public interface MachineMetadata { /** original metadata object, if available; e.g. ComputeMetadata when using jclouds */ Object getOriginalMetadata(); } - + + /** + * Implement to indicate that a location can suspend and resume machines. + */ + @Beta + public interface SuspendResumeLocation extends SuspendsMachines, ResumesMachines {}; + + + @Beta + public interface SuspendsMachines { + /** + * Suspend the indicated machine. + */ + void suspendMachine(MachineLocation location); + } + + @Beta + public interface ResumesMachines { + /** + * Resume the indicated machine. + */ + void resumeMachine(MachineLocation location); + } + } \ No newline at end of file diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java index c7d9f95e35..4916de59a9 100644 --- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java +++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java @@ -126,6 +126,7 @@ import brooklyn.location.MachineLocationCustomizer; import brooklyn.location.MachineManagementMixins.MachineMetadata; import brooklyn.location.MachineManagementMixins.RichMachineProvisioningLocation; +import brooklyn.location.MachineManagementMixins.SuspendsMachines; import brooklyn.location.NoMachinesAvailableException; import brooklyn.location.access.PortForwardManager; import brooklyn.location.access.PortMapping; @@ -187,7 +188,9 @@ * Configuration flags are defined in {@link JcloudsLocationConfig}. */ @SuppressWarnings("serial") -public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation implements JcloudsLocationConfig, RichMachineProvisioningLocation, LocationWithObjectStore { +public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation implements + JcloudsLocationConfig, RichMachineProvisioningLocation, + LocationWithObjectStore, SuspendsMachines { // TODO After converting from Groovy to Java, this is now very bad code! It relies entirely on putting // things into and taking them out of maps; it's not type-safe, and it's thus very error-prone. @@ -1023,6 +1026,35 @@ protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailable } } + // ------------- suspend and resume ------------------------------------ + + /** + * Suspends the given location. + *

+ * Note that this method does not call the lifecycle methods of any + * {@link #getCustomizers(ConfigBag) customizers} attached to this location. + */ + @Override + public void suspendMachine(MachineLocation rawLocation) { + String instanceId = vmInstanceIds.remove(rawLocation); + if (instanceId == null) { + LOG.info("Attempt to suspend unknown machine " + rawLocation + " in " + this); + throw new IllegalArgumentException("Unknown machine " + rawLocation); + } + LOG.info("Suspending machine {} in {}, instance id {}", new Object[]{rawLocation, this, instanceId}); + Exception toThrow = null; + try { + getComputeService().suspendNode(instanceId); + } catch (Exception e) { + toThrow = e; + LOG.error("Problem suspending machine " + rawLocation + " in " + this + ", instance id " + instanceId, e); + } + removeChild(rawLocation); + if (toThrow != null) { + throw Exceptions.propagate(toThrow); + } + } + // ------------- constructing the template, etc ------------------------ private static interface CustomizeTemplateBuilder { @@ -2159,7 +2191,7 @@ public void release(MachineLocation rawMachine) { throw new IllegalArgumentException("Unknown machine "+rawMachine); } JcloudsMachineLocation machine = (JcloudsMachineLocation) rawMachine; - + LOG.info("Releasing machine {} in {}, instance id {}", new Object[] {machine, this, instanceId}); Exception tothrow = null; From f012c9c7e879248b08bee6912d2698ff97e2ed9d Mon Sep 17 00:00:00 2001 From: Sam Corbett Date: Tue, 21 Jul 2015 16:56:13 +0100 Subject: [PATCH 2/3] MachineLifecycleEffectorTasks can suspend as well as stop machines. Leaves it to subclasses to attach the effector to entities. --- .../MachineLifecycleEffectorTasks.java | 127 +++++++++++++++--- 1 file changed, 108 insertions(+), 19 deletions(-) diff --git a/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java index ec3ae1cf63..920e5b9459 100644 --- a/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java +++ b/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java @@ -63,6 +63,7 @@ import brooklyn.event.feed.ConfigToAttributes; import brooklyn.location.Location; import brooklyn.location.MachineLocation; +import brooklyn.location.MachineManagementMixins.SuspendsMachines; import brooklyn.location.MachineProvisioningLocation; import brooklyn.location.NoMachinesAvailableException; import brooklyn.location.basic.AbstractLocation; @@ -72,7 +73,6 @@ import brooklyn.location.basic.SshMachineLocation; import brooklyn.location.cloud.CloudLocationConfig; import brooklyn.management.Task; -import brooklyn.management.TaskFactory; import brooklyn.util.collections.MutableMap; import brooklyn.util.config.ConfigBag; import brooklyn.util.exceptions.Exceptions; @@ -155,8 +155,18 @@ public Effector newStopEffector() { .build(); } + /** @see {@link #newStartEffector()} */ + public Effector newSuspendEffector() { + return Effectors.effector(Void.class, "suspend") + .description("Suspend the process/service represented by an entity") + .parameter(StopSoftwareParameters.STOP_PROCESS_MODE) + .parameter(StopSoftwareParameters.STOP_MACHINE_MODE) + .impl(newSuspendEffectorTask()) + .build(); + } + /** - * Returns the {@link TaskFactory} which supplies the implementation for the start effector. + * Returns the {@link EffectorBody} which supplies the implementation for the start effector. *

* Calls {@link #start(Collection)} in this class. */ @@ -196,7 +206,7 @@ public Void call(ConfigBag parameters) { } /** - * Calls {@link #stop()}. + * Calls {@link #stop(ConfigBag)}. * * @see {@link #newStartEffectorTask()} */ @@ -210,6 +220,21 @@ public Void call(ConfigBag parameters) { }; } + /** + * Calls {@link #suspend(ConfigBag)}. + * + * @see {@link #newStartEffectorTask()} + */ + public EffectorBody newSuspendEffectorTask() { + return new EffectorBody() { + @Override + public Void call(ConfigBag parameters) { + suspend(parameters); + return null; + } + }; + } + protected EntityInternal entity() { return (EntityInternal) BrooklynTaskTags.getTargetOrContextEntity(Tasks.current()); } @@ -576,6 +601,27 @@ public void stop() { * If no errors were encountered call {@link #postStopCustom()} at the end. */ public void stop(ConfigBag parameters) { + doStop(parameters, new Callable>() { + public StopMachineDetails call() { + return stopAnyProvisionedMachines(); + } + }); + } + + /** + * As {@link #stop} but calling {@link #suspendAnyProvisionedMachines} rather than + * {@link #stopAnyProvisionedMachines}. + */ + public void suspend(ConfigBag parameters) { + doStop(parameters, new Callable>() { + @Override + public StopMachineDetails call() throws Exception { + return suspendAnyProvisionedMachines(); + } + }); + } + + protected void doStop(ConfigBag parameters, Callable> stopTask) { preStopConfirmCustom(); log.info("Stopping {} in {}", entity(), entity().getLocations()); @@ -608,11 +654,7 @@ public void stop(ConfigBag parameters) { Task> stoppingMachine = null; if (canStop(stopMachineMode, machine.isAbsent())) { // Release this machine (even if error trying to stop process - we rethrow that after) - stoppingMachine = DynamicTasks.queue("stopping (machine)", new Callable>() { - public StopMachineDetails call() { - return stopAnyProvisionedMachines(); - } - }); + stoppingMachine = DynamicTasks.queue("stopping (machine)", stopTask); DynamicTasks.drain(entity().getConfig(STOP_PROCESS_TIMEOUT), false); @@ -750,7 +792,7 @@ public String toString() { protected abstract String stopProcessesAtMachine(); /** - * Stop the {@link MachineLocation} the entity is provisioned at. + * Stop and release the {@link MachineLocation} the entity is provisioned at. *

* Can run synchronously or not, caller will submit/queue as needed, and will block on any submitted tasks. */ @@ -775,16 +817,63 @@ protected StopMachineDetails stopAnyProvisionedMachines() { return new StopMachineDetails("No machine decommissioning necessary - not a machine ("+machine+")", 0); } - try { - entity().removeLocations(ImmutableList.of(machine)); - entity().setAttribute(Attributes.HOSTNAME, null); - entity().setAttribute(Attributes.ADDRESS, null); - entity().setAttribute(Attributes.SUBNET_HOSTNAME, null); - entity().setAttribute(Attributes.SUBNET_ADDRESS, null); - if (provisioner != null) provisioner.release((MachineLocation)machine); - } catch (Throwable t) { - throw Exceptions.propagate(t); - } + clearEntityLocationAttributes(machine); + provisioner.release((MachineLocation)machine); + return new StopMachineDetails("Decommissioned "+machine, 1); } + + /** + * Suspend the {@link MachineLocation} the entity is provisioned at. + *

+ * Expects the entity's {@link SoftwareProcess#PROVISIONING_LOCATION provisioner} to be capable of + * {@link SuspendsMachines suspending machines}. + * + * @throws java.lang.UnsupportedOperationException if the entity's provisioner cannot suspend machines. + * @see brooklyn.location.MachineManagementMixins.SuspendsMachines + */ + protected StopMachineDetails suspendAnyProvisionedMachines() { + @SuppressWarnings("unchecked") + MachineProvisioningLocation provisioner = entity().getAttribute(SoftwareProcess.PROVISIONING_LOCATION); + + if (Iterables.isEmpty(entity().getLocations())) { + log.debug("No machine decommissioning necessary for " + entity() + " - no locations"); + return new StopMachineDetails<>("No machine suspend necessary - no locations", 0); + } + + // Only release this machine if we ourselves provisioned it (e.g. it might be running other services) + if (provisioner == null) { + log.debug("No machine decommissioning necessary for " + entity() + " - did not provision"); + return new StopMachineDetails<>("No machine suspend necessary - did not provision", 0); + } + + Location machine = getLocation(null); + if (!(machine instanceof MachineLocation)) { + log.debug("No decommissioning necessary for " + entity() + " - not a machine location (" + machine + ")"); + return new StopMachineDetails<>("No machine suspend necessary - not a machine (" + machine + ")", 0); + } + + if (!(provisioner instanceof SuspendsMachines)) { + log.debug("Location provisioner ({}) cannot suspend machines", provisioner); + throw new UnsupportedOperationException("Location provisioner cannot suspend machines: " + provisioner); + } + + clearEntityLocationAttributes(machine); + SuspendsMachines.class.cast(provisioner).suspendMachine(MachineLocation.class.cast(machine)); + + return new StopMachineDetails<>("Suspended " + machine, 1); + } + + /** + * Nulls the attached entity's hostname, address, subnet hostname and subnet address sensors + * and removes the given machine from its locations. + */ + protected void clearEntityLocationAttributes(Location machine) { + entity().removeLocations(ImmutableList.of(machine)); + entity().setAttribute(Attributes.HOSTNAME, null); + entity().setAttribute(Attributes.ADDRESS, null); + entity().setAttribute(Attributes.SUBNET_HOSTNAME, null); + entity().setAttribute(Attributes.SUBNET_ADDRESS, null); + } + } From fcb1af0cbd7eb9799e48471e37934b046330baf3 Mon Sep 17 00:00:00 2001 From: Sam Corbett Date: Tue, 28 Jul 2015 13:46:32 +0100 Subject: [PATCH 3/3] No anonymous inner classes in classes extending MachineLifecycleEffectorTasks --- ...ameServerDriverLifecycleEffectorTasks.java | 2 +- ...reProcessDriverLifecycleEffectorTasks.java | 22 +- .../MachineLifecycleEffectorTasks.java | 359 +++++++++++------- 3 files changed, 233 insertions(+), 150 deletions(-) diff --git a/software/base/src/main/java/brooklyn/entity/basic/SameServerDriverLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/basic/SameServerDriverLifecycleEffectorTasks.java index bb3b1fd415..8b64ddcb86 100644 --- a/software/base/src/main/java/brooklyn/entity/basic/SameServerDriverLifecycleEffectorTasks.java +++ b/software/base/src/main/java/brooklyn/entity/basic/SameServerDriverLifecycleEffectorTasks.java @@ -84,7 +84,7 @@ protected Collection getRequiredOpenPorts(Entity entity) { value = maybeValue.isPresent() ? maybeValue.get() : null; } - Maybe maybePortRange = TypeCoercions.tryCoerce(value, new TypeToken() {}); + Maybe maybePortRange = TypeCoercions.tryCoerce(value, TypeToken.of(PortRange.class)); if (maybePortRange.isPresentAndNonNull()) { PortRange p = maybePortRange.get(); diff --git a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java index a315e847c2..2dcfa7e0ef 100644 --- a/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java +++ b/software/base/src/main/java/brooklyn/entity/basic/SoftwareProcessDriverLifecycleEffectorTasks.java @@ -59,20 +59,30 @@ public void restart(ConfigBag parameters) { return; } - DynamicTasks.queue("pre-restart", new Runnable() { public void run() { - preRestartCustom(); - }}); + DynamicTasks.queue("pre-restart", new PreRestartTask()); log.debug("restart of "+entity()+" appears to have driver and hostname - doing driver-level restart"); entity().getDriver().restart(); restartChildren(parameters); - DynamicTasks.queue("post-restart", new Runnable() { public void run() { + DynamicTasks.queue("post-restart", new PostRestartTask()); + } + + private class PreRestartTask implements Runnable { + @Override + public void run() { + preRestartCustom(); + } + } + + private class PostRestartTask implements Runnable { + @Override + public void run() { postStartCustom(); postRestartCustom(); ServiceStateLogic.setExpectedState(entity(), Lifecycle.RUNNING); - }}); + } } @Override @@ -233,7 +243,7 @@ protected String stopProcessesAtMachine() { if (childException!=null) throw new IllegalStateException(result+"; but error stopping child: "+childException, childException); - + return result; } diff --git a/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java index 920e5b9459..48bb6a43b3 100644 --- a/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java +++ b/software/base/src/main/java/brooklyn/entity/software/MachineLifecycleEffectorTasks.java @@ -171,23 +171,26 @@ public Effector newSuspendEffector() { * Calls {@link #start(Collection)} in this class. */ public EffectorBody newStartEffectorTask() { - return new EffectorBody() { - @Override - public Void call(ConfigBag parameters) { - Collection locations = null; + return new StartEffectorBody(); + } - Object locationsRaw = parameters.getStringKey(LOCATIONS.getName()); - locations = Locations.coerceToCollection(entity().getManagementContext(), locationsRaw); + private class StartEffectorBody extends EffectorBody { + @Override + public Void call(ConfigBag parameters) { + Collection locations = null; - if (locations==null) { - // null/empty will mean to inherit from parent - locations = Collections.emptyList(); - } + Object locationsRaw = parameters.getStringKey(LOCATIONS.getName()); + locations = Locations.coerceToCollection(entity().getManagementContext(), locationsRaw); - start(locations); - return null; + if (locations == null) { + // null/empty will mean to inherit from parent + locations = Collections.emptyList(); } - }; + + start(locations); + return null; + } + } /** @@ -196,13 +199,15 @@ public Void call(ConfigBag parameters) { * @see {@link #newStartEffectorTask()} */ public EffectorBody newRestartEffectorTask() { - return new EffectorBody() { - @Override - public Void call(ConfigBag parameters) { - restart(parameters); - return null; - } - }; + return new RestartEffectorBody(); + } + + private class RestartEffectorBody extends EffectorBody { + @Override + public Void call(ConfigBag parameters) { + restart(parameters); + return null; + } } /** @@ -211,13 +216,15 @@ public Void call(ConfigBag parameters) { * @see {@link #newStartEffectorTask()} */ public EffectorBody newStopEffectorTask() { - return new EffectorBody() { - @Override - public Void call(ConfigBag parameters) { - stop(parameters); - return null; - } - }; + return new StopEffectorBody(); + } + + private class StopEffectorBody extends EffectorBody { + @Override + public Void call(ConfigBag parameters) { + stop(parameters); + return null; + } } /** @@ -226,13 +233,15 @@ public Void call(ConfigBag parameters) { * @see {@link #newStartEffectorTask()} */ public EffectorBody newSuspendEffectorTask() { - return new EffectorBody() { - @Override - public Void call(ConfigBag parameters) { - suspend(parameters); - return null; - } - }; + return new SuspendEffectorBody(); + } + + private class SuspendEffectorBody extends EffectorBody { + @Override + public Void call(ConfigBag parameters) { + suspend(parameters); + return null; + } } protected EntityInternal entity() { @@ -288,11 +297,19 @@ protected void startInLocation(final Location location) { final Supplier locationSF = locationS; preStartAtMachineAsync(locationSF); - DynamicTasks.queue("start (processes)", new Runnable() { public void run() { - startProcessesAtMachine(locationSF); - }}); + DynamicTasks.queue("start (processes)", new StartProcessesAtMachineTask(locationSF)); postStartAtMachineAsync(); - return; + } + + private class StartProcessesAtMachineTask implements Runnable { + private final Supplier machineSupplier; + private StartProcessesAtMachineTask(Supplier machineSupplier) { + this.machineSupplier = machineSupplier; + } + @Override + public void run() { + startProcessesAtMachine(machineSupplier); + } } /** @@ -300,62 +317,89 @@ protected void startInLocation(final Location location) { * and returns that machine. The task can be used as a supplier to subsequent methods. */ protected Task provisionAsync(final MachineProvisioningLocation location) { - return DynamicTasks.queue(Tasks.builder().name("provisioning ("+location.getDisplayName()+")").body( - new Callable() { - public MachineLocation call() throws Exception { - // Blocks if a latch was configured. - entity().getConfig(BrooklynConfigKeys.PROVISION_LATCH); - final Map flags = obtainProvisioningFlags(location); - if (!(location instanceof LocalhostMachineProvisioningLocation)) - log.info("Starting {}, obtaining a new location instance in {} with ports {}", new Object[] {entity(), location, flags.get("inboundPorts")}); - entity().setAttribute(SoftwareProcess.PROVISIONING_LOCATION, location); - MachineLocation machine; - try { - machine = Tasks.withBlockingDetails("Provisioning machine in "+location, new Callable() { - public MachineLocation call() throws NoMachinesAvailableException { - return location.obtain(flags); - }}); - if (machine == null) throw new NoMachinesAvailableException("Failed to obtain machine in "+location.toString()); - } catch (Exception e) { - throw Exceptions.propagate(e); - } + return DynamicTasks.queue(Tasks.builder().name("provisioning (" + location.getDisplayName() + ")").body( + new ProvisionMachineTask(location)).build()); + } - if (log.isDebugEnabled()) - log.debug("While starting {}, obtained new location instance {}", entity(), - (machine instanceof SshMachineLocation ? - machine+", details "+((SshMachineLocation)machine).getUser()+":"+Sanitizer.sanitize(((SshMachineLocation)machine).config().getLocalBag()) - : machine)); - return machine; - } - }).build()); + private class ProvisionMachineTask implements Callable { + final MachineProvisioningLocation location; + + private ProvisionMachineTask(MachineProvisioningLocation location) { + this.location = location; + } + + public MachineLocation call() throws Exception { + // Blocks if a latch was configured. + entity().getConfig(BrooklynConfigKeys.PROVISION_LATCH); + final Map flags = obtainProvisioningFlags(location); + if (!(location instanceof LocalhostMachineProvisioningLocation)) + log.info("Starting {}, obtaining a new location instance in {} with ports {}", new Object[]{entity(), location, flags.get("inboundPorts")}); + entity().setAttribute(SoftwareProcess.PROVISIONING_LOCATION, location); + MachineLocation machine; + try { + machine = Tasks.withBlockingDetails("Provisioning machine in " + location, new ObtainLocationTask(location, flags)); + if (machine == null) + throw new NoMachinesAvailableException("Failed to obtain machine in " + location.toString()); + } catch (Exception e) { + throw Exceptions.propagate(e); + } + + if (log.isDebugEnabled()) + log.debug("While starting {}, obtained new location instance {}", entity(), + (machine instanceof SshMachineLocation ? + machine + ", details " + ((SshMachineLocation) machine).getUser() + ":" + Sanitizer.sanitize(((SshMachineLocation) machine).config().getLocalBag()) + : machine)); + return machine; + } + } + + private static class ObtainLocationTask implements Callable { + final MachineProvisioningLocation location; + final Map flags; + + private ObtainLocationTask(MachineProvisioningLocation location, Map flags) { + this.flags = flags; + this.location = location; + } + + public MachineLocation call() throws NoMachinesAvailableException { + return location.obtain(flags); + } } /** Wraps a call to {@link #preStartCustom(MachineLocation)}, after setting the hostname and address. */ protected void preStartAtMachineAsync(final Supplier machineS) { - DynamicTasks.queue("pre-start", new Runnable() { public void run() { - MachineLocation machine = machineS.get(); + DynamicTasks.queue("pre-start", new PreStartTask(machineS.get())); + } + + private class PreStartTask implements Runnable { + final MachineLocation machine; + private PreStartTask(MachineLocation machine) { + this.machine = machine; + } + public void run() { log.info("Starting {} on machine {}", entity(), machine); Collection oldLocs = entity().getLocations(); if (!oldLocs.isEmpty()) { List oldSshLocs = ImmutableList.copyOf(Iterables.filter(oldLocs, MachineLocation.class)); if (!oldSshLocs.isEmpty()) { // check if existing locations are compatible - log.debug("Entity "+entity()+" had machine locations "+oldSshLocs+" when starting at "+machine+"; checking if they are compatible"); - for (MachineLocation oldLoc: oldSshLocs) { + log.debug("Entity " + entity() + " had machine locations " + oldSshLocs + " when starting at " + machine + "; checking if they are compatible"); + for (MachineLocation oldLoc : oldSshLocs) { // machines are deemed compatible if hostname and address are the same, or they are localhost // this allows a machine create by jclouds to then be defined with an ip-based spec if (!"localhost".equals(machine.getConfig(AbstractLocation.ORIGINAL_SPEC))) { checkLocationParametersCompatible(machine, oldLoc, "hostname", - oldLoc.getAddress().getHostName(), machine.getAddress().getHostName()); + oldLoc.getAddress().getHostName(), machine.getAddress().getHostName()); checkLocationParametersCompatible(machine, oldLoc, "address", - oldLoc.getAddress().getHostAddress(), machine.getAddress().getHostAddress()); + oldLoc.getAddress().getHostAddress(), machine.getAddress().getHostAddress()); } } - log.debug("Entity "+entity()+" old machine locations "+oldSshLocs+" were compatible, removing them to start at "+machine); + log.debug("Entity " + entity() + " old machine locations " + oldSshLocs + " were compatible, removing them to start at " + machine); entity().removeLocations(oldSshLocs); } } - entity().addLocations(ImmutableList.of((Location)machine)); + entity().addLocations(ImmutableList.of((Location) machine)); // elsewhere we rely on (public) hostname being set _after_ subnet_hostname // (to prevent the tiny possibility of races resulting in hostname being returned @@ -397,7 +441,7 @@ protected void preStartAtMachineAsync(final Supplier machineS) } resolveOnBoxDir(entity(), machine); preStartCustom(machine); - }}); + } } /** @@ -480,9 +524,13 @@ protected Map obtainProvisioningFlags(final MachineProvisioningL protected abstract String startProcessesAtMachine(final Supplier machineS); protected void postStartAtMachineAsync() { - DynamicTasks.queue("post-start", new Runnable() { public void run() { + DynamicTasks.queue("post-start", new PostStartTask()); + } + + private class PostStartTask implements Runnable { + public void run() { postStartCustom(); - }}); + } } /** @@ -515,7 +563,7 @@ public void restart() { protected boolean getDefaultRestartStopsMachine() { return false; } - + /** * Default restart implementation for an entity. *

@@ -523,53 +571,54 @@ protected boolean getDefaultRestartStopsMachine() { */ public void restart(ConfigBag parameters) { ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPING); - + RestartMachineMode isRestartMachine = parameters.get(RestartSoftwareParameters.RESTART_MACHINE_TYPED); - if (isRestartMachine==null) + if (isRestartMachine==null) isRestartMachine=RestartMachineMode.AUTO; - if (isRestartMachine==RestartMachineMode.AUTO) - isRestartMachine = getDefaultRestartStopsMachine() ? RestartMachineMode.TRUE : RestartMachineMode.FALSE; + if (isRestartMachine==RestartMachineMode.AUTO) + isRestartMachine = getDefaultRestartStopsMachine() ? RestartMachineMode.TRUE : RestartMachineMode.FALSE; - DynamicTasks.queue("pre-restart", new Runnable() { public void run() { - //Calling preStopCustom without a corresponding postStopCustom invocation - //doesn't look right so use a separate callback pair; Also depending on the arguments - //stop() could be called which will call the {pre,post}StopCustom on its own. - preRestartCustom(); - }}); + // Calling preStopCustom without a corresponding postStopCustom invocation + // doesn't look right so use a separate callback pair; Also depending on the arguments + // stop() could be called which will call the {pre,post}StopCustom on its own. + DynamicTasks.queue("pre-restart", new PreRestartTask()); if (isRestartMachine==RestartMachineMode.FALSE) { - DynamicTasks.queue("stopping (process)", new Callable() { public String call() { - DynamicTasks.markInessential(); - stopProcessesAtMachine(); - DynamicTasks.waitForLast(); - return "Stop of process completed with no errors."; - }}); + DynamicTasks.queue("stopping (process)", new StopProcessesAtMachineTask()); } else { - DynamicTasks.queue("stopping (machine)", new Callable() { public String call() { - DynamicTasks.markInessential(); - stop(ConfigBag.newInstance().configure(StopSoftwareParameters.STOP_MACHINE_MODE, StopMode.IF_NOT_STOPPED)); - DynamicTasks.waitForLast(); - return "Stop of machine completed with no errors."; - }}); + DynamicTasks.queue("stopping (machine)", new StopMachineTask()); } - DynamicTasks.queue("starting", new Runnable() { public void run() { - // startInLocations will look up the location, and provision a machine if necessary - // (if it remembered the provisioning location) - ServiceStateLogic.setExpectedState(entity(), Lifecycle.STARTING); - startInLocations(null); - }}); - + DynamicTasks.queue("starting", new StartInLocationsTask()); restartChildren(parameters); - - DynamicTasks.queue("post-restart", new Runnable() { public void run() { - postRestartCustom(); - }}); + DynamicTasks.queue("post-restart", new PostRestartTask()); DynamicTasks.waitForLast(); ServiceStateLogic.setExpectedState(entity(), Lifecycle.RUNNING); } + private class PreRestartTask implements Runnable { + @Override + public void run() { + preRestartCustom(); + } + } + private class PostRestartTask implements Runnable { + @Override + public void run() { + postRestartCustom(); + } + } + private class StartInLocationsTask implements Runnable { + @Override + public void run() { + // startInLocations will look up the location, and provision a machine if necessary + // (if it remembered the provisioning location) + ServiceStateLogic.setExpectedState(entity(), Lifecycle.STARTING); + startInLocations(null); + } + } + protected void restartChildren(ConfigBag parameters) { // TODO should we consult ChildStartableMode? @@ -577,12 +626,12 @@ protected void restartChildren(ConfigBag parameters) { if (isRestartChildren==null || !isRestartChildren) { return; } - + if (isRestartChildren) { DynamicTasks.queue(StartableMethods.restartingChildren(entity(), parameters)); return; } - + throw new IllegalArgumentException("Invalid value '"+isRestartChildren+"' for "+RestartSoftwareParameters.RESTART_CHILDREN.getName()); } @@ -601,11 +650,7 @@ public void stop() { * If no errors were encountered call {@link #postStopCustom()} at the end. */ public void stop(ConfigBag parameters) { - doStop(parameters, new Callable>() { - public StopMachineDetails call() { - return stopAnyProvisionedMachines(); - } - }); + doStop(parameters, new StopAnyProvisionedMachinesTask()); } /** @@ -613,12 +658,7 @@ public StopMachineDetails call() { * {@link #stopAnyProvisionedMachines}. */ public void suspend(ConfigBag parameters) { - doStop(parameters, new Callable>() { - @Override - public StopMachineDetails call() throws Exception { - return suspendAnyProvisionedMachines(); - } - }); + doStop(parameters, new SuspendAnyProvisionedMachinesTask()); } protected void doStop(ConfigBag parameters, Callable> stopTask) { @@ -629,26 +669,12 @@ protected void doStop(ConfigBag parameters, Callable StopMode stopMachineMode = getStopMachineMode(parameters); StopMode stopProcessMode = parameters.get(StopSoftwareParameters.STOP_PROCESS_MODE); - DynamicTasks.queue("pre-stop", new Callable() { public String call() { - if (entity().getAttribute(SoftwareProcess.SERVICE_STATE_ACTUAL)==Lifecycle.STOPPED) { - log.debug("Skipping stop of entity "+entity()+" when already stopped"); - return "Already stopped"; - } - ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPING); - entity().setAttribute(SoftwareProcess.SERVICE_UP, false); - preStopCustom(); - return null; - }}); + DynamicTasks.queue("pre-stop", new PreStopCustomTask()); Maybe machine = Machines.findUniqueMachineLocation(entity().getLocations()); Task stoppingProcess = null; if (canStop(stopProcessMode, entity())) { - stoppingProcess = DynamicTasks.queue("stopping (process)", new Callable() { public String call() { - DynamicTasks.markInessential(); - stopProcessesAtMachine(); - DynamicTasks.waitForLast(); - return "Stop at machine completed with no errors."; - }}); + stoppingProcess = DynamicTasks.queue("stopping (process)", new StopProcessesAtMachineTask()); } Task> stoppingMachine = null; @@ -696,14 +722,61 @@ protected void doStop(ConfigBag parameters, Callable entity().setAttribute(SoftwareProcess.SERVICE_UP, false); ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPED); - DynamicTasks.queue("post-stop", new Callable() { public Void call() { - postStopCustom(); - return null; - }}); + DynamicTasks.queue("post-stop", new PostStopCustomTask()); if (log.isDebugEnabled()) log.debug("Stopped software process entity "+entity()); } + private class StopAnyProvisionedMachinesTask implements Callable> { + public StopMachineDetails call() { + return stopAnyProvisionedMachines(); + } + } + + private class SuspendAnyProvisionedMachinesTask implements Callable> { + public StopMachineDetails call() { + return suspendAnyProvisionedMachines(); + } + } + + private class StopProcessesAtMachineTask implements Callable { + public String call() { + DynamicTasks.markInessential(); + stopProcessesAtMachine(); + DynamicTasks.waitForLast(); + return "Stop processes completed with no errors."; + } + } + + private class StopMachineTask implements Callable { + public String call() { + DynamicTasks.markInessential(); + stop(ConfigBag.newInstance().configure(StopSoftwareParameters.STOP_MACHINE_MODE, StopMode.IF_NOT_STOPPED)); + DynamicTasks.waitForLast(); + return "Stop of machine completed with no errors."; + } + } + + private class PreStopCustomTask implements Callable { + public String call() { + if (entity().getAttribute(SoftwareProcess.SERVICE_STATE_ACTUAL) == Lifecycle.STOPPED) { + log.debug("Skipping stop of entity " + entity() + " when already stopped"); + return "Already stopped"; + } + ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPING); + entity().setAttribute(SoftwareProcess.SERVICE_UP, false); + preStopCustom(); + return null; + } + } + + private class PostStopCustomTask implements Callable { + public Void call() { + postStopCustom(); + return null; + } + } + public static StopMode getStopMachineMode(ConfigBag parameters) { @SuppressWarnings("deprecation") final boolean hasStopMachine = parameters.containsKey(StopSoftwareParameters.STOP_MACHINE);