public VmwareCloudImage(
      @NotNull final VMWareApiConnector apiConnector,
      @NotNull final VmwareCloudImageDetails imageDetails,
      @NotNull final CloudAsyncTaskExecutor asyncTaskExecutor,
      @NotNull final File idxStorage) {
    super(imageDetails.getNickname(), imageDetails.getNickname());
    myImageDetails = imageDetails;
    myApiConnector = apiConnector;
    myAsyncTaskExecutor = asyncTaskExecutor;
    myInstances.clear();
    myIdxFile = new File(idxStorage, imageDetails.getNickname() + ".idx");
    if (!myIdxFile.exists()) {
      try {
        FileUtil.writeFileAndReportErrors(myIdxFile, "1");
      } catch (IOException e) {
        LOG.warn(
            String.format(
                "Unable to write idx file '%s': %s", myIdxFile.getAbsolutePath(), e.toString()));
      }
    }

    Map<String, VmwareInstance> realInstances = null;
    try {
      realInstances = myApiConnector.listImageInstances(this);
    } catch (VmwareCheckedCloudException e) {
      updateErrors(TypedCloudErrorInfo.fromException(e));
      return;
    }
    if (imageDetails.getBehaviour().isUseOriginal()) {
      final VmwareCloudInstance imageInstance =
          new VmwareCloudInstance(
              this, imageDetails.getSourceName(), VmwareConstants.CURRENT_STATE);
      myInstances.put(myImageDetails.getSourceName(), imageInstance);

      final VmwareInstance vmwareInstance = realInstances.get(imageDetails.getSourceName());
      if (vmwareInstance != null) {
        imageInstance.setStatus(vmwareInstance.getInstanceStatus());
      } else {
        imageInstance.setStatus(InstanceStatus.UNKNOWN);
        imageInstance.updateErrors(
            new TypedCloudErrorInfo("NoVM", "VM doesn't exist: " + imageDetails.getSourceName()));
      }
    } else {
      for (String instanceName : realInstances.keySet()) {
        final VmwareInstance instance = realInstances.get(instanceName);
        final String snapshotName = instance.getSnapshotName();
        VmwareCloudInstance cloudInstance =
            new VmwareCloudInstance(this, instanceName, snapshotName);
        cloudInstance.setStatus(instance.getInstanceStatus());
        myInstances.put(instanceName, cloudInstance);
      }
    }
  }
  public void terminateInstance(@NotNull final VmwareCloudInstance instance) {

    LOG.info("Stopping instance " + instance.getName());
    instance.setStatus(InstanceStatus.SCHEDULED_TO_STOP);
    myAsyncTaskExecutor.executeAsync(
        new VmwareTaskWrapper(
            new Callable<Task>() {
              public Task call() throws Exception {
                return myApiConnector.stopInstance(instance);
              }
            },
            "Stop " + instance.getName()),
        new ImageStatusTaskWrapper(instance) {

          @Override
          public void onComplete() {
            instance.setStatus(InstanceStatus.STOPPED);
            if (myImageDetails
                .getBehaviour()
                .isDeleteAfterStop()) { // we only destroy proper instances.
              deleteInstance(instance);
            }
          }
        });
  }
 @Override
 public void detectNewInstances(final Map<String, AbstractInstance> realInstances) {
   for (String instanceName : realInstances.keySet()) {
     if (myInstances.get(instanceName) == null) {
       final VmwareInstance realInstance = (VmwareInstance) realInstances.get(instanceName);
       final VmwareCloudInstance newInstance =
           new VmwareCloudInstance(this, instanceName, realInstance.getSnapshotName());
       newInstance.setStatus(realInstance.getInstanceStatus());
       myInstances.put(instanceName, newInstance);
     }
   }
 }
  @Override
  public synchronized VmwareCloudInstance startNewInstance(
      @NotNull final CloudInstanceUserData cloudInstanceUserData) throws QuotaException {
    try {
      final VmwareCloudInstance instance = getOrCreateInstance();
      if (instance == null) {
        return null;
      }
      boolean willClone = !myApiConnector.checkVirtualMachineExists(instance.getName());
      LOG.info("Will clone for " + instance.getName() + ": " + willClone);
      if (willClone && myImageDetails.getMaxInstances() <= myInstances.size()) {
        throw new QuotaException(
            String.format(
                "Cannot clone '%s' into '%s' - limit exceeded",
                myImageDetails.getSourceName(), instance.getName()));
      }
      instance.setStatus(InstanceStatus.SCHEDULED_TO_START);
      if (!myInstances.containsKey(instance.getName())) {
        addInstance(instance);
      }
      if (willClone) {
        myAsyncTaskExecutor.executeAsync(
            new VmwareTaskWrapper(
                new Callable<Task>() {
                  public Task call() throws Exception {
                    return myApiConnector.cloneAndStartVm(
                        instance, myImageDetails.getResourcePoolId(), myImageDetails.getFolderId());
                  }
                },
                "Clone and start instance " + instance.getName()),
            new ImageStatusTaskWrapper(instance) {
              @Override
              public void onSuccess() {
                reconfigureVmTask(instance, cloudInstanceUserData);
              }

              @Override
              public void onError(final Throwable th) {
                super.onError(th);
                removeInstance(instance.getName());
              }
            });
      } else {
        startVM(instance, cloudInstanceUserData);
      }
      return instance;
    } catch (QuotaException e) {
      throw e;
    } catch (VmwareCheckedCloudException e) {
      throw new CloudException("Unable to start new instance: " + e.toString());
    }
  }
 @Override
 public void onError(final Throwable th) {
   myInstance.setStatus(InstanceStatus.ERROR);
   if (th != null) {
     myInstance.updateErrors(TypedCloudErrorInfo.fromException(th));
     LOG.warn(
         "An error occurred: "
             + th.getLocalizedMessage()
             + " during processing "
             + myInstance.getName());
   } else {
     myInstance.updateErrors(
         new TypedCloudErrorInfo(
             "Unknown error during processing instance " + myInstance.getName()));
     LOG.warn("Unknown error during processing " + myInstance.getName());
   }
 }
 private synchronized void startVM(
     @NotNull final VmwareCloudInstance instance,
     @NotNull final CloudInstanceUserData cloudInstanceUserData) {
   instance.setStatus(InstanceStatus.STARTING);
   myAsyncTaskExecutor.executeAsync(
       new VmwareTaskWrapper(
           new Callable<Task>() {
             public Task call() throws Exception {
               return myApiConnector.startInstance(
                   instance, instance.getName(), cloudInstanceUserData);
             }
           },
           "Start instance " + instance.getName()),
       new ImageStatusTaskWrapper(instance) {
         @Override
         public void onSuccess() {
           reconfigureVmTask(instance, cloudInstanceUserData);
         }
       });
 }
  public void check_can_start_new_instance_limits() throws RemoteException, InterruptedException {
    final CloudInstanceUserData data =
        new CloudInstanceUserData(
            "aaa",
            "bbbb",
            "localhost",
            10000l,
            "profileDescr",
            Collections.<String, String>emptyMap());
    assertTrue(myImage.canStartNewInstance());
    myImage.startNewInstance(data);
    assertTrue(myImage.canStartNewInstance());
    myImage.startNewInstance(data);
    assertTrue(myImage.canStartNewInstance());
    myImage.startNewInstance(data);
    assertTrue(myImage.canStartNewInstance());
    myImage.startNewInstance(data);
    assertTrue(myImage.canStartNewInstance());
    final VmwareCloudInstance instance2Stop = myImage.startNewInstance(data);
    assertFalse(myImage.canStartNewInstance());
    new WaitFor(5 * 1000) {

      @Override
      protected boolean condition() {
        return instance2Stop.getStatus() == InstanceStatus.RUNNING;
      }
    };
    final FakeVirtualMachine vm2Stop =
        FakeModel.instance().getVirtualMachine(instance2Stop.getName());
    final String result = vm2Stop.powerOffVM_Task().waitForTask();
    assertEquals(Task.SUCCESS, result);
    instance2Stop.setStatus(InstanceStatus.STOPPED);
    assertTrue(myImage.canStartNewInstance());
    System.setProperty(VmwareConstants.CONSIDER_STOPPED_VMS_LIMIT, "true");
    assertFalse(myImage.canStartNewInstance());
    System.getProperties().remove(VmwareConstants.CONSIDER_STOPPED_VMS_LIMIT);
    assertTrue(myImage.canStartNewInstance());
  }