/**
   * Stop and release the {@link MachineLocation} the entity is provisioned at.
   *
   * <p>Can run synchronously or not, caller will submit/queue as needed, and will block on any
   * submitted tasks.
   */
  protected StopMachineDetails<Integer> stopAnyProvisionedMachines() {
    @SuppressWarnings("unchecked")
    MachineProvisioningLocation<MachineLocation> provisioner =
        entity().getAttribute(SoftwareProcess.PROVISIONING_LOCATION);

    if (Iterables.isEmpty(entity().getLocations())) {
      log.debug("No machine decommissioning necessary for " + entity() + " - no locations");
      return new StopMachineDetails<Integer>(
          "No machine decommissioning 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<Integer>(
          "No machine decommissioning 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<Integer>(
          "No machine decommissioning necessary - not a machine (" + machine + ")", 0);
    }

    entity()
        .sensors()
        .set(AttributesInternal.INTERNAL_TERMINATION_TASK_STATE, ProvisioningTaskState.RUNNING);
    try {
      clearEntityLocationAttributes(machine);
      provisioner.release((MachineLocation) machine);
    } finally {
      // TODO On exception, should we add the machine back to the entity (because it might not
      // really be terminated)?
      //      Do we need a better exception hierarchy for that?
      entity().sensors().remove(AttributesInternal.INTERNAL_TERMINATION_TASK_STATE);
    }

    return new StopMachineDetails<Integer>("Decommissioned " + machine, 1);
  }
  /**
   * Suspend the {@link MachineLocation} the entity is provisioned at.
   *
   * <p>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 MachineManagementMixins.SuspendsMachines
   */
  protected StopMachineDetails<Integer> suspendAnyProvisionedMachines() {
    @SuppressWarnings("unchecked")
    MachineProvisioningLocation<MachineLocation> 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);
  }