/**
  * Returns a queued {@link Task} which provisions a machine in the given location and returns that
  * machine. The task can be used as a supplier to subsequent methods.
  */
 protected Task<MachineLocation> provisionAsync(final MachineProvisioningLocation<?> location) {
   return DynamicTasks.queue(
       Tasks.<MachineLocation>builder()
           .displayName("provisioning (" + location.getDisplayName() + ")")
           .body(new ProvisionMachineTask(location))
           .build());
 }
  /**
   * 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);
  }
  /**
   * Some clouds (e.g. Rackspace) give us VMs that have two nics: one for private and one for
   * public. If the private IP is used then it doesn't work, even for a cluster purely internal to
   * Rackspace!
   *
   * <p>TODO Ugly. Need to understand more and find a better fix. Perhaps in Cassandra itself if
   * necessary. Also need to investigate further: - does it still fail if BroadcastAddress is set to
   * private IP? - is `openIptables` opening it up for both interfaces? - for aws->rackspace comms
   * between nodes (thus using the public IP), will it be listening on an accessible port? - ideally
   * do a check, open a server on one port on the machine, see if it is contactable on the public
   * address; and set that as a flag on the cloud
   */
  protected void setCloudPreferredSensorNames() {
    if (detectedCloudSensors.get()) return;
    synchronized (detectedCloudSensors) {
      if (detectedCloudSensors.get()) return;

      MachineProvisioningLocation<?> loc = getProvisioningLocation();
      if (loc != null) {
        try {
          Method method = loc.getClass().getMethod("getProvider");
          method.setAccessible(true);
          String provider = (String) method.invoke(loc);
          String result = "(nothing special)";
          if (provider != null) {
            if (provider.contains("rackspace")
                || provider.contains("cloudservers")
                || provider.contains("softlayer")) {
              /* These clouds have 2 NICs and it has to be consistent, so use public IP here to allow external access;
               * (TODO internal access could be configured to improve performance / lower cost,
               * if we know all nodes are visible to each other) */
              if (getConfig(LISTEN_ADDRESS_SENSOR) == null)
                setConfig(LISTEN_ADDRESS_SENSOR, CassandraNode.ADDRESS.getName());
              if (getConfig(BROADCAST_ADDRESS_SENSOR) == null)
                setConfig(BROADCAST_ADDRESS_SENSOR, CassandraNode.ADDRESS.getName());
              result = "public IP for both listen and broadcast";
            } else if (provider.contains("google-compute")) {
              /* Google nodes cannot reach themselves/each-other on the public IP,
               * and there is no hostname, so use private IP here */
              if (getConfig(LISTEN_ADDRESS_SENSOR) == null)
                setConfig(LISTEN_ADDRESS_SENSOR, CassandraNode.SUBNET_HOSTNAME.getName());
              if (getConfig(BROADCAST_ADDRESS_SENSOR) == null)
                setConfig(BROADCAST_ADDRESS_SENSOR, CassandraNode.SUBNET_HOSTNAME.getName());
              result = "private IP for both listen and broadcast";
            }
          }
          log.debug(
              "Cassandra NICs inferred {} for {}; using location {}, based on provider {}",
              new Object[] {result, this, loc, provider});
        } catch (Exception e) {
          log.debug(
              "Cassandra NICs auto-detection failed for {} in location {}: {}",
              new Object[] {this, loc, e});
        }
      }
      detectedCloudSensors.set(true);
    }
  }
 public String getRackName() {
   String name = getAttribute(CassandraNode.RACK_NAME);
   if (name == null) {
     MachineLocation machine = getMachineOrNull();
     MachineProvisioningLocation<?> provisioningLocation = getProvisioningLocation();
     if (machine != null) {
       name = machine.getConfig(CloudLocationConfig.CLOUD_AVAILABILITY_ZONE_ID);
     }
     if (name == null && provisioningLocation != null) {
       name = provisioningLocation.getConfig(CloudLocationConfig.CLOUD_AVAILABILITY_ZONE_ID);
     }
     if (name == null) {
       name = "UNKNOWN_RACK";
     }
     setAttribute((AttributeSensor<String>) RACK_NAME, name);
   }
   return name;
 }
 // referenced by cassandra-rackdc.properties, read by some of the cassandra snitches
 public String getDatacenterName() {
   String name = getAttribute(CassandraNode.DATACENTER_NAME);
   if (name == null) {
     MachineLocation machine = getMachineOrNull();
     MachineProvisioningLocation<?> provisioningLocation = getProvisioningLocation();
     if (machine != null) {
       name = machine.getConfig(CloudLocationConfig.CLOUD_REGION_ID);
     }
     if (name == null && provisioningLocation != null) {
       name = provisioningLocation.getConfig(CloudLocationConfig.CLOUD_REGION_ID);
     }
     if (name == null) {
       name = "UNKNOWN_DATACENTER";
     }
     setAttribute((AttributeSensor<String>) DATACENTER_NAME, name);
   }
   return name;
 }
    public MachineLocation call() throws Exception {
      // Blocks if a latch was configured.
      entity().getConfig(BrooklynConfigKeys.PROVISION_LATCH);
      final Map<String, Object> 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().sensors().set(SoftwareProcess.PROVISIONING_LOCATION, location);
      Transition expectedState = entity().sensors().get(Attributes.SERVICE_STATE_EXPECTED);

      // BROOKLYN-263: see corresponding code in doStop()
      if (expectedState != null
          && (expectedState.getState() == Lifecycle.STOPPING
              || expectedState.getState() == Lifecycle.STOPPED)) {
        throw new IllegalStateException(
            "Provisioning aborted before even begun for "
                + entity()
                + " in "
                + location
                + " (presumably by a concurrent call to stop");
      }
      entity()
          .sensors()
          .set(AttributesInternal.INTERNAL_PROVISIONING_TASK_STATE, ProvisioningTaskState.RUNNING);

      MachineLocation machine;
      try {
        machine =
            Tasks.withBlockingDetails(
                "Provisioning machine in " + location, new ObtainLocationTask(location, flags));
        entity().sensors().set(INTERNAL_PROVISIONED_MACHINE, machine);
      } finally {
        entity().sensors().remove(AttributesInternal.INTERNAL_PROVISIONING_TASK_STATE);
      }

      if (machine == null) {
        throw new NoMachinesAvailableException(
            "Failed to obtain machine in " + location.toString());
      }
      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;
    }
 public MachineLocation call() throws NoMachinesAvailableException {
   return location.obtain(flags);
 }