@Override
  public MachineDetails[] startManagementMachines(final long duration, final TimeUnit unit)
      throws TimeoutException, CloudProvisioningException {

    long endTime = System.currentTimeMillis() + unit.toMillis(duration);

    try {
      azureClient.createAffinityGroup(affinityGroup, location, endTime);

      azureClient.createVirtualNetworkSite(addressSpace, affinityGroup, networkName, endTime);

      azureClient.createStorageAccount(affinityGroup, storageAccountName, endTime);

    } catch (final MicrosoftAzureException e) {
      throw new CloudProvisioningException(e);
    } catch (InterruptedException e) {
      throw new CloudProvisioningException(e);
    }

    int numberOfManagementMachines = this.cloud.getProvider().getNumberOfManagementMachines();

    final ExecutorService executorService =
        Executors.newFixedThreadPool(numberOfManagementMachines);

    try {
      return startManagementMachines(endTime, numberOfManagementMachines, executorService);
    } finally {
      executorService.shutdown();
    }
  }
  @Override
  public void stopManagementMachines() throws TimeoutException, CloudProvisioningException {

    long endTime = System.currentTimeMillis() + DEFAULT_SHUTDOWN_DURATION;

    ExecutorService service = Executors.newCachedThreadPool();
    try {
      stopManagementMachines(endTime, service);
    } finally {
      service.shutdown();
    }

    boolean deletedNetwork = false;
    boolean deletedStorage = false;
    Exception first = null;
    if (cleanup) {
      try {
        deletedNetwork = azureClient.deleteVirtualNetworkSite(networkName, endTime);
        logger.fine("deleted virtual network site : " + networkName);
      } catch (final Exception e) {
        first = e;
        logger.warning("failed deleting virtual network site : " + networkName);
        logger.warning(ExceptionUtils.getFullStackTrace(e));
      }
      try {
        deletedStorage = azureClient.deleteStorageAccount(storageAccountName, endTime);
      } catch (final Exception e) {
        logger.warning("failed deleting storage account : " + networkName);
        logger.warning(ExceptionUtils.getFullStackTrace(e));
      }
      if (deletedNetwork && deletedStorage) {
        try {
          azureClient.deleteAffinityGroup(affinityGroup, endTime);
        } catch (final Exception e) {
          logger.warning("failed deleting affinity group : " + affinityGroup);
          logger.warning(ExceptionUtils.getFullStackTrace(e));
        }
      } else {
        logger.warning(
            "not deleting affinity group since "
                + "failed deleting virtual network site and storage account.");
      }
      if (first != null) {
        throw new CloudProvisioningException(first);
      }
    }
  }
  private boolean stopManagementMachine(
      final String hostedServiceName, final String deploymentName, final long endTime)
      throws CloudProvisioningException, TimeoutException {

    try {
      azureClient.deleteVirtualMachineByDeploymentName(hostedServiceName, deploymentName, endTime);
      return true;
    } catch (MicrosoftAzureException e) {
      throw new CloudProvisioningException(e);
    } catch (InterruptedException e) {
      throw new CloudProvisioningException(e);
    }
  }
  /**
   * @param endTime
   * @param service
   * @throws TimeoutException
   * @throws CloudProvisioningException
   */
  private void stopManagementMachines(final long endTime, final ExecutorService service)
      throws TimeoutException, CloudProvisioningException {

    List<Future<?>> futures = new ArrayList<Future<?>>();

    Disks disks = null;
    try {
      disks = azureClient.listOSDisks();
    } catch (MicrosoftAzureException e1) {
      throw new CloudProvisioningException(e1);
    }
    for (Disk disk : disks) {
      AttachedTo attachedTo = disk.getAttachedTo();
      if (attachedTo != null) { // protect against zombie disks
        String roleName = attachedTo.getRoleName();
        if (roleName.startsWith(this.serverNamePrefix)) {
          final String diskName = disk.getName();
          final String deploymentName = attachedTo.getDeploymentName();
          final String hostedServiceName = attachedTo.getHostedServiceName();
          StopManagementMachineCallable task =
              new StopManagementMachineCallable(
                  deploymentName, hostedServiceName, diskName, endTime);
          futures.add(service.submit(task));
        }
      }
    }

    // block until all tasks stop execution
    List<Exception> exceptionOnStopMachines = new ArrayList<Exception>();
    for (Future<?> future : futures) {
      try {
        future.get();
      } catch (final Exception e) {
        exceptionOnStopMachines.add(e);
      }
    }
    if (exceptionOnStopMachines.size() != 0) {
      if (logger.isLoggable(Level.FINEST)) {
        logger.finest("here are all the exception caught from all threads");
        for (Exception e : exceptionOnStopMachines) {
          logger.finest(ExceptionUtils.getFullStackTrace(e));
        }
      }
      throw new CloudProvisioningException(
          "Failed terminating management machines", exceptionOnStopMachines.get(0));
    }
  }
  @Override
  public boolean stopMachine(final String machineIp, final long duration, final TimeUnit unit)
      throws InterruptedException, TimeoutException, CloudProvisioningException {

    if (isStopRequestRecent(machineIp)) {
      return false;
    }

    long endTime = System.currentTimeMillis() + unit.toMillis(duration);

    boolean connectToPrivateIp = this.cloud.getConfiguration().isConnectToPrivateIp();
    try {
      azureClient.deleteVirtualMachineByIp(machineIp, connectToPrivateIp, endTime);
      return true;
    } catch (MicrosoftAzureException e) {
      throw new CloudProvisioningException(e);
    }
  }
 private static synchronized void initRestClient(
     final String subscriptionId,
     final String pathToPfxFile,
     final String pfxPassword,
     final boolean enableWireLog) {
   if (azureClient == null) {
     logger.fine("initializing Azure REST client");
     azureClient =
         new MicrosoftAzureRestClient(
             subscriptionId,
             pathToPfxFile,
             pfxPassword,
             CLOUDIFY_AFFINITY_PREFIX,
             CLOUDIFY_CLOUD_SERVICE_PREFIX,
             CLOUDIFY_STORAGE_ACCOUNT_PREFIX);
     if (enableWireLog) {
       azureClient.setLoggingFilter(logger);
     }
   }
 }
  private MachineDetails startMachine(final long endTime)
      throws TimeoutException, CloudProvisioningException {

    MachineDetails machineDetails = new MachineDetails();
    CreatePersistentVMRoleDeploymentDescriptor desc = null;
    RoleDetails roleAddressDetails = null;
    try {

      desc = new CreatePersistentVMRoleDeploymentDescriptor();
      desc.setRoleName(serverNamePrefix + "_role");
      desc.setDeploymentSlot(deploymentSlot);
      desc.setImageName(imageName);
      desc.setAvailabilitySetName(availabilitySet);
      desc.setAffinityGroup(affinityGroup);

      InputEndpoints inputEndpoints = createInputEndPoints();

      desc.setInputEndpoints(inputEndpoints);
      desc.setNetworkName(networkName);
      desc.setPassword(password);
      desc.setSize(size);
      desc.setStorageAccountName(storageAccountName);
      desc.setUserName(userName);

      logger.info("creating a virtual machine with details : " + desc);

      roleAddressDetails = azureClient.createVirtualMachineDeployment(desc, endTime);

      machineDetails.setPrivateAddress(roleAddressDetails.getPrivateIp());
      machineDetails.setPublicAddress(roleAddressDetails.getPublicIp());
      machineDetails.setMachineId(roleAddressDetails.getId());
      machineDetails.setAgentRunning(false);
      machineDetails.setCloudifyInstalled(false);
      machineDetails.setInstallationDirectory(this.template.getRemoteDirectory());
      machineDetails.setRemoteDirectory(this.template.getRemoteDirectory());
      machineDetails.setRemotePassword(password);
      machineDetails.setRemoteUsername(userName);

      logger.info("virtual machine started and is ready for use : " + machineDetails);

      return machineDetails;
    } catch (final MicrosoftAzureException e) {
      logger.severe("failed creating virtual machine properly" + e.getMessage());

      // this means a cloud service was created and a request for A VM was
      // made.
      if (desc.getHostedServiceName() != null) {
        try {
          // this will also delete the cloud service that was created.
          azureClient.deleteVirtualMachineByDeploymentIfExists(
              desc.getHostedServiceName(), desc.getDeploymentName(), endTime);
        } catch (final Exception e1) {
          if (e1 instanceof TimeoutException) {
            throw new TimeoutException(e1.getMessage());
          } else {
            throw new CloudProvisioningException(e1);
          }
        }
        // this means that a failure happened while trying to create the
        // cloud service. no request for VM was made, so no need to try
        // and delete it.
      } else {
        logger.fine(
            "not attempting to shutdown virtual machine "
                + desc.getRoleName()
                + " since a failure happened while to create a cloud service for this vm.");
      }

      throw new CloudProvisioningException(e);
    } catch (InterruptedException e) {
      throw new CloudProvisioningException(e);
    }
  }