/**
   * Applies the given security group permissions to the given node with the given compute service.
   *
   * <p>Takes no action if the compute service does not have a security group extension.
   *
   * @param permissions The set of permissions to be applied to the node
   * @param nodeId The id of the node to update
   * @param computeService The compute service to use to apply the changes
   */
  @VisibleForTesting
  Map<String, SecurityGroup> addPermissionsToLocation(
      Iterable<IpPermission> permissions, final String nodeId, ComputeService computeService) {
    if (!computeService.getSecurityGroupExtension().isPresent()) {
      LOG.warn(
          "Security group extension for {} absent; cannot update node {} with {}",
          new Object[] {computeService, nodeId, permissions});
      return ImmutableMap.of();
    }
    final SecurityGroupExtension securityApi = computeService.getSecurityGroupExtension().get();
    final String locationId = computeService.getContext().unwrap().getId();

    // Expect to have two security groups on the node: one shared between all nodes in the location,
    // that is cached in sharedGroupCache, and one created by Jclouds that is unique to the node.
    // Relies on customize having been called before. This should be safe because the arguments
    // needed to call this method are not available until post-instance creation.
    SecurityGroup machineUniqueSecurityGroup = getSecurityGroup(nodeId, securityApi, locationId);
    MutableList<IpPermission> newPermissions = MutableList.copyOf(permissions);
    Iterables.removeAll(newPermissions, machineUniqueSecurityGroup.getIpPermissions());
    MutableMap<String, SecurityGroup> addedSecurityGroups = MutableMap.of();
    for (IpPermission permission : newPermissions) {
      SecurityGroup addedPermission =
          addPermission(permission, machineUniqueSecurityGroup, securityApi);
      addedSecurityGroups.put(addedPermission.getId(), addedPermission);
    }
    return addedSecurityGroups;
  }
  // Suggest at least 15 minutes for timeout
  public static String waitForPasswordOnAws(
      ComputeService computeService, final NodeMetadata node, long timeout, TimeUnit timeUnit)
      throws TimeoutException {
    ComputeServiceContext computeServiceContext = computeService.getContext();
    AWSEC2Api ec2Client = computeServiceContext.unwrapApi(AWSEC2Api.class);
    final WindowsApi client = ec2Client.getWindowsApi().get();
    final String region = node.getLocation().getParent().getId();

    // The Administrator password will take some time before it is ready - Amazon says sometimes 15
    // minutes.
    // So we create a predicate that tests if the password is ready, and wrap it in a retryable
    // predicate.
    Predicate<String> passwordReady =
        new Predicate<String>() {
          @Override
          public boolean apply(String s) {
            if (Strings.isNullOrEmpty(s)) return false;
            PasswordData data = client.getPasswordDataInRegion(region, s);
            if (data == null) return false;
            return !Strings.isNullOrEmpty(data.getPasswordData());
          }
        };

    LOG.info("Waiting for password, for " + node.getProviderId() + ":" + node.getId());
    Predicate<String> passwordReadyRetryable =
        Predicates2.retry(
            passwordReady, timeUnit.toMillis(timeout), 10 * 1000, TimeUnit.MILLISECONDS);
    boolean ready = passwordReadyRetryable.apply(node.getProviderId());
    if (!ready)
      throw new TimeoutException(
          "Password not available for "
              + node
              + " in region "
              + region
              + " after "
              + timeout
              + " "
              + timeUnit.name());

    // Now pull together Amazon's encrypted password blob, and the private key that jclouds
    // generated
    PasswordDataAndPrivateKey dataAndKey =
        new PasswordDataAndPrivateKey(
            client.getPasswordDataInRegion(region, node.getProviderId()),
            node.getCredentials().getPrivateKey());

    // And apply it to the decryption function
    WindowsLoginCredentialsFromEncryptedData f =
        computeServiceContext
            .utils()
            .injector()
            .getInstance(WindowsLoginCredentialsFromEncryptedData.class);
    LoginCredentials credentials = f.apply(dataAndKey);

    return credentials.getPassword();
  }
  /**
   * Removes the given security group permissions from the given node with the given compute
   * service.
   *
   * <p>Takes no action if the compute service does not have a security group extension.
   *
   * @param permissions The set of permissions to be removed from the node
   * @param nodeId The id of the node to update
   * @param computeService The compute service to use to apply the changes
   */
  @VisibleForTesting
  void removePermissionsFromLocation(
      Iterable<IpPermission> permissions, final String nodeId, ComputeService computeService) {
    if (!computeService.getSecurityGroupExtension().isPresent()) {
      LOG.warn(
          "Security group extension for {} absent; cannot update node {} with {}",
          new Object[] {computeService, nodeId, permissions});
      return;
    }

    final SecurityGroupExtension securityApi = computeService.getSecurityGroupExtension().get();
    final String locationId = computeService.getContext().unwrap().getId();
    SecurityGroup machineUniqueSecurityGroup = getSecurityGroup(nodeId, securityApi, locationId);

    for (IpPermission permission : permissions) {
      removePermission(permission, machineUniqueSecurityGroup, securityApi);
    }
  }
  @Override
  public ComputeService findComputeService(ConfigBag conf, boolean allowReuse) {
    String provider = checkNotNull(conf.get(CLOUD_PROVIDER), "provider must not be null");
    String identity =
        checkNotNull(conf.get(CloudLocationConfig.ACCESS_IDENTITY), "identity must not be null");
    String credential =
        checkNotNull(
            conf.get(CloudLocationConfig.ACCESS_CREDENTIAL), "credential must not be null");

    Properties properties = new Properties();
    properties.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, Boolean.toString(true));
    properties.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, Boolean.toString(true));
    properties.setProperty(
        "jclouds.ssh.max-retries",
        conf.getStringKey("jclouds.ssh.max-retries") != null
            ? conf.getStringKey("jclouds.ssh.max-retries").toString()
            : "50");
    // Enable aws-ec2 lazy image fetching, if given a specific imageId; otherwise customize for
    // specific owners; or all as a last resort
    // See https://issues.apache.org/jira/browse/WHIRR-416
    if ("aws-ec2".equals(provider)) {
      // TODO convert AWS-only flags to config keys
      if (groovyTruth(conf.get(IMAGE_ID))) {
        properties.setProperty(PROPERTY_EC2_AMI_QUERY, "");
        properties.setProperty(PROPERTY_EC2_CC_AMI_QUERY, "");
      } else if (groovyTruth(conf.getStringKey("imageOwner"))) {
        properties.setProperty(
            PROPERTY_EC2_AMI_QUERY,
            "owner-id=" + conf.getStringKey("imageOwner") + ";state=available;image-type=machine");
      } else if (groovyTruth(conf.getStringKey("anyOwner"))) {
        // set `anyOwner: true` to override the default query (which is restricted to certain owners
        // as per below),
        // allowing the AMI query to bind to any machine
        // (note however, we sometimes pick defaults in JcloudsLocationFactory);
        // (and be careful, this can give a LOT of data back, taking several minutes,
        // and requiring extra memory allocated on the command-line)
        properties.setProperty(PROPERTY_EC2_AMI_QUERY, "state=available;image-type=machine");
        /*
         * by default the following filters are applied:
         * Filter.1.Name=owner-id&Filter.1.Value.1=137112412989&
         * Filter.1.Value.2=063491364108&
         * Filter.1.Value.3=099720109477&
         * Filter.1.Value.4=411009282317&
         * Filter.2.Name=state&Filter.2.Value.1=available&
         * Filter.3.Name=image-type&Filter.3.Value.1=machine&
         */
      }

      // occasionally can get com.google.common.util.concurrent.UncheckedExecutionException:
      // java.lang.RuntimeException:
      //     security group eu-central-1/jclouds#brooklyn-bxza-alex-eu-central-shoul-u2jy-nginx-ielm
      // is not available after creating
      // the default timeout was 500ms so let's raise it in case that helps
      properties.setProperty(
          EC2Constants.PROPERTY_EC2_TIMEOUT_SECURITYGROUP_PRESENT,
          "" + Duration.seconds(30).toMilliseconds());
    }

    // FIXME Deprecated mechanism, should have a ConfigKey for overrides
    Map<String, Object> extra =
        Maps.filterKeys(conf.getAllConfig(), Predicates.containsPattern("^jclouds\\."));
    if (extra.size() > 0) {
      LOG.warn("Jclouds using deprecated property overrides: " + Sanitizer.sanitize(extra));
    }
    properties.putAll(extra);

    String endpoint = conf.get(CloudLocationConfig.CLOUD_ENDPOINT);
    if (!groovyTruth(endpoint)) endpoint = getDeprecatedProperty(conf, Constants.PROPERTY_ENDPOINT);
    if (groovyTruth(endpoint)) properties.setProperty(Constants.PROPERTY_ENDPOINT, endpoint);

    Map<?, ?> cacheKey =
        MutableMap.builder()
            .putAll(properties)
            .put("provider", provider)
            .put("identity", identity)
            .put("credential", credential)
            .putIfNotNull("endpoint", endpoint)
            .build()
            .asUnmodifiable();

    if (allowReuse) {
      ComputeService result = cachedComputeServices.get(cacheKey);
      if (result != null) {
        LOG.trace(
            "jclouds ComputeService cache hit for compute service, for "
                + Sanitizer.sanitize(properties));
        return result;
      }
      LOG.debug(
          "jclouds ComputeService cache miss for compute service, creating, for "
              + Sanitizer.sanitize(properties));
    }

    Iterable<Module> modules = getCommonModules();

    // Synchronizing to avoid deadlock from sun.reflect.annotation.AnnotationType.
    // See https://github.com/brooklyncentral/brooklyn/issues/974
    ComputeServiceContext computeServiceContext;
    synchronized (createComputeServicesMutex) {
      computeServiceContext =
          ContextBuilder.newBuilder(provider)
              .modules(modules)
              .credentials(identity, credential)
              .overrides(properties)
              .build(ComputeServiceContext.class);
    }
    final ComputeService computeService = computeServiceContext.getComputeService();
    if (allowReuse) {
      synchronized (cachedComputeServices) {
        ComputeService result = cachedComputeServices.get(cacheKey);
        if (result != null) {
          LOG.debug(
              "jclouds ComputeService cache recovery for compute service, for "
                  + Sanitizer.sanitize(cacheKey));
          // keep the old one, discard the new one
          computeService.getContext().close();
          return result;
        }
        LOG.debug(
            "jclouds ComputeService created "
                + computeService
                + ", adding to cache, for "
                + Sanitizer.sanitize(properties));
        cachedComputeServices.put(cacheKey, computeService);
      }
    }
    return computeService;
  }
 public String getApiName() {
   return computeService != null
       ? computeService.getContext().unwrap().getProviderMetadata().getApiMetadata().getId()
       : apiName;
 }
  // TODO: finish me!
  @Test(enabled = false)
  public void
      testCreateNodeUsingVCloud1_0ApiAgainstVCloudDirector1_5WhenVAppTemplateHasNetworkNamedNone()
          throws Exception {

    String group = "group";
    String name = "group-abcd";

    String instantiateXML =
        XMLBuilder.create("InstantiateVAppTemplateParams")
            .a("xmlns", ns)
            .a("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1")
            .a("deploy", "false")
            .a("name", name)
            .a("powerOn", "false")
            .e("Description")
            .up()
            .e("InstantiationParams")
            .e("NetworkConfigSection")
            .e("ovf:Info")
            .t("Configuration parameters for logical networks")
            .up()
            .e("NetworkConfig")
            .a("networkName", "orgNet-jclouds-External") // NOTE not "None"
            .e("Configuration")
            .e("ParentNetwork")
            .a("href", ENDPOINT + "/v1.0/network/" + networkId)
            .up()
            .e("FenceMode")
            .t("bridged")
            .up()
            .up()
            .up()
            .up()
            .up()
            .e("Source")
            .a("href", ENDPOINT + "/v1.0/vAppTemplate/" + templateId)
            .up()
            .e("AllEULAsAccepted")
            .t("true")
            .up()
            .asString(outputProperties);

    HttpRequest version1_0InstantiateWithNetworkNamedSameAsOrgNetwork =
        HttpRequest.builder()
            .method("POST")
            .endpoint(ENDPOINT + "/v1.0/vdc/" + vdcId + "/action/instantiateVAppTemplate")
            .addHeader(HttpHeaders.ACCEPT, "application/vnd.vmware.vcloud.vApp+xml")
            .addHeader("x-vcloud-authorization", sessionToken)
            .addHeader(HttpHeaders.COOKIE, "vcloud-token=" + sessionToken)
            .payload(
                payloadFromStringWithContentType(
                    instantiateXML,
                    "application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml"))
            .build();

    ComputeService compute =
        requestsSendResponses(
            ImmutableMap.<HttpRequest, HttpResponse>builder()
                .put(versionsRequest, versionsResponseFromVCD1_5)
                .put(
                    version1_0LoginRequest,
                    successfulVersion1_0LoginResponseFromVCD1_5WithSingleOrg)
                .put(
                    version1_0GetOrgRequest,
                    successfulVersion1_0GetOrgResponseFromVCD1_5WithSingleTasksListVDCAndNetwork)
                .put(
                    version1_0GetCatalogRequest,
                    successfulVersion1_0GetCatalogResponseFromVCD1_5WithSingleTemplate)
                .put(
                    version1_0GetCatalogItemRequest,
                    successfulVersion1_0GetCatalogItemResponseFromVCD1_5ForTemplate)
                .put(
                    version1_0GetVDCRequest,
                    successfulVersion1_0GetVDCResponseFromVCD1_5WithSingleTemplateAndNetwork)
                .put(
                    version1_0GetVAppTemplateRequest,
                    successfulVersion1_0GetVAppTemplateResponseFromVCD1_5WithSingleVMAndVDCParent)
                .put(
                    version1_0GetOVFForVAppTemplateRequest,
                    successfulVersion1_0GetOVFForVAppTemplateResponseFromVCD1_5WithSingleVM)
                .put(
                    version1_0InstantiateWithNetworkNamedSameAsOrgNetwork,
                    successfulVersion1_0InstantiatedVApp)
                .build());

    InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn starter =
        compute
            .getContext()
            .utils()
            .injector()
            .getInstance(
                InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn.class);

    NodeAndInitialCredentials<VApp> appAndCreds =
        starter.createNodeWithGroupEncodedIntoName(group, name, compute.templateBuilder().build());

    assertEquals(appAndCreds.getNode().getName(), name);
    assertEquals(
        appAndCreds.getCredentials(),
        LoginCredentials.builder().user("root").password("fromVApp").build());
  }
  public void testInstantiateVAppFromTemplateWhenUsingOverriddenNetworkAndFenceMode()
      throws Exception {

    String name = "group-abcd";
    FenceMode fenceMode = FenceMode.NAT_ROUTED;
    URI parentNetwork = URI.create(ENDPOINT + "/v1.0/network/" + "foooooooo");

    String instantiateXML =
        XMLBuilder.create("InstantiateVAppTemplateParams")
            .a("xmlns", ns)
            .a("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1")
            .a("deploy", "false")
            .a("name", name)
            .a("powerOn", "false")
            .e("Description")
            .up()
            .e("InstantiationParams")
            .e("NetworkConfigSection")
            .e("ovf:Info")
            .t("Configuration parameters for logical networks")
            .up()
            .e("NetworkConfig")
            .a("networkName", "jclouds") // NOTE not "None"
            .e("Configuration")
            .e("ParentNetwork")
            .a("href", parentNetwork.toASCIIString())
            .up()
            .e("FenceMode")
            .t(fenceMode.toString())
            .up()
            .up()
            .up()
            .up()
            .up()
            .e("Source")
            .a("href", ENDPOINT + "/v1.0/vAppTemplate/" + templateId)
            .up()
            .e("AllEULAsAccepted")
            .t("true")
            .up()
            .asString(outputProperties);

    HttpRequest version1_0InstantiateWithCustomizedNetwork =
        HttpRequest.builder()
            .method("POST")
            .endpoint(ENDPOINT + "/v1.0/vdc/" + vdcId + "/action/instantiateVAppTemplate")
            .addHeader(HttpHeaders.ACCEPT, "application/vnd.vmware.vcloud.vApp+xml")
            .addHeader("x-vcloud-authorization", sessionToken)
            .addHeader(HttpHeaders.COOKIE, "vcloud-token=" + sessionToken)
            .payload(
                payloadFromStringWithContentType(
                    instantiateXML,
                    "application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml"))
            .build();

    ComputeService compute =
        requestsSendResponses(
            ImmutableMap.<HttpRequest, HttpResponse>builder()
                .put(versionsRequest, versionsResponseFromVCD1_5)
                .put(
                    version1_0LoginRequest,
                    successfulVersion1_0LoginResponseFromVCD1_5WithSingleOrg)
                .put(
                    version1_0GetOrgRequest,
                    successfulVersion1_0GetOrgResponseFromVCD1_5WithSingleTasksListVDCAndNetwork)
                .put(
                    version1_0GetCatalogRequest,
                    successfulVersion1_0GetCatalogResponseFromVCD1_5WithSingleTemplate)
                .put(
                    version1_0GetCatalogItemRequest,
                    successfulVersion1_0GetCatalogItemResponseFromVCD1_5ForTemplate)
                .put(
                    version1_0GetVDCRequest,
                    successfulVersion1_0GetVDCResponseFromVCD1_5WithSingleTemplateAndNetwork)
                .put(
                    version1_0GetVAppTemplateRequest,
                    successfulVersion1_0GetVAppTemplateResponseFromVCD1_5WithSingleVMAndVDCParent)
                .put(
                    version1_0GetOVFForVAppTemplateRequest,
                    successfulVersion1_0GetOVFForVAppTemplateResponseFromVCD1_5WithSingleVM)
                .put(
                    version1_0InstantiateWithCustomizedNetwork,
                    successfulVersion1_0InstantiatedVApp)
                .build());

    InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn starter =
        compute
            .getContext()
            .utils()
            .injector()
            .getInstance(
                InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn.class);

    Template template = compute.templateBuilder().build();
    template
        .getOptions()
        .as(VCloudTemplateOptions.class)
        .parentNetwork(parentNetwork)
        .fenceMode(fenceMode);
    starter.instantiateVAppFromTemplate(name, template);
  }
 /** Always close your service when you're done with it. */
 public void close() throws IOException {
   Closeables.close(computeService.getContext(), true);
 }