@Test
    public void testPrepareVmDiskOperation() throws Throwable {
      com.vmware.photon.controller.api.Operation operation =
          com.vmware.photon.controller.api.Operation.DETACH_DISK;

      DiskService.State diskState = new DiskService.State();
      diskState.name = "test-vm-disk-1";
      diskState.projectId = projectId;
      diskState.flavorId =
          flavorDcpBackend.getEntityByNameAndKind("core-100", PersistentDisk.KIND).getId();
      diskState.capacityGb = 64;
      diskState.diskType = DiskType.PERSISTENT;
      diskState.state = DiskState.ATTACHED;

      Operation result = dcpClient.post(DiskServiceFactory.SELF_LINK, diskState);
      DiskService.State createdDiskState = result.getBody(DiskService.State.class);
      String diskId = ServiceUtils.getIDFromDocumentSelfLink(createdDiskState.documentSelfLink);

      List<String> disks = new ArrayList<>();
      disks.add(diskId);
      TaskEntity task = vmDcpBackend.prepareVmDiskOperation(vmId, disks, operation);

      assertThat(task, is(notNullValue()));
      assertThat(task.getState(), is(TaskEntity.State.QUEUED));
      assertThat(task.getSteps().size(), is(1));
      assertThat(task.getSteps().get(0).getOperation(), is(operation));

      try {
        vmDcpBackend.prepareVmDiskOperation(
            vmId, disks, com.vmware.photon.controller.api.Operation.MOCK_OP);
        fail("vmDcpBackend.prepareVmOperation with invalid operation should have failed");
      } catch (NotImplementedException e) {
        // do nothing
      }
    }
    @BeforeMethod
    public void setUp() throws Throwable {

      commonHostAndClientSetup(basicServiceHost, apiFeDcpRestClient);
      commonDataSetup(
          tenantDcpBackend,
          resourceTicketDcpBackend,
          projectDcpBackend,
          flavorDcpBackend,
          flavorLoader);

      vm = new VmService.State();
      vm.name = UUID.randomUUID().toString();
      flavorEntity = flavorDcpBackend.getEntityByNameAndKind("core-100", Vm.KIND);
      vm.flavorId = flavorEntity.getId();
      vm.imageId = UUID.randomUUID().toString();
      vm.projectId = projectId;
      vm.vmState = VmState.CREATING;

      vm.affinities = new ArrayList<>();
      vm.affinities.add(new LocalitySpec("id1", "kind1"));

      Iso iso = new Iso();
      iso.setName(UUID.randomUUID().toString());
      iso.setSize(-1L);
      vm.isos = new ArrayList<>();
      vm.isos.add(iso);

      vm.metadata = new HashMap<>();
      vm.metadata.put("key1", UUID.randomUUID().toString());

      vm.networks = new ArrayList<>();
      vm.networks.add(UUID.randomUUID().toString());
      vm.agent = UUID.randomUUID().toString();
      vm.host = UUID.randomUUID().toString();
      vm.datastore = UUID.randomUUID().toString();
      vm.datastoreName = UUID.randomUUID().toString();

      vm.tags = new HashSet<>();
      vm.tags.add("namespace1:predicate1=value1");
      vm.tags.add("namespace2:predicate2=value2");

      Operation result = dcpClient.post(VmServiceFactory.SELF_LINK, vm);
      createdVm = result.getBody(VmService.State.class);
      vmId = ServiceUtils.getIDFromDocumentSelfLink(createdVm.documentSelfLink);

      DeploymentCreateSpec deploymentCreateSpec = new DeploymentCreateSpec();
      deploymentCreateSpec.setImageDatastores(Collections.singleton(UUID.randomUUID().toString()));
      TaskEntity deploymentTask = deploymentBackend.prepareCreateDeployment(deploymentCreateSpec);

      HostCreateSpec hostCreateSpec = new HostCreateSpec();
      hostCreateSpec.setAddress(vm.host);
      hostCreateSpec.setUsageTags(ImmutableList.of(UsageTag.CLOUD));
      hostCreateSpec.setUsername(UUID.randomUUID().toString());
      hostCreateSpec.setPassword(UUID.randomUUID().toString());
      TaskEntity hostTask =
          hostBackend.prepareHostCreate(hostCreateSpec, deploymentTask.getEntityId());
      hostId = hostTask.getEntityId();
    }
  @Test(expectedExceptions = ConcurrentTaskException.class)
  public void testDeletingAnUploadingImage() throws Throwable {
    TaskEntity task =
        imageBackend.prepareImageUpload(inputStream, "image-name", ImageReplicationType.ON_DEMAND);
    StepEntity step = task.getSteps().get(0);
    ImageEntity image = (ImageEntity) step.getTransientResourceEntities().get(0);

    imageBackend.prepareImageDelete(image.getId());
  }
    @Test
    public void testPrepareVmGetNetworks() throws Throwable {
      TaskEntity task = vmDcpBackend.prepareVmGetNetworks(vmId);

      assertThat(task, is(notNullValue()));
      assertThat(task.getState(), is(TaskEntity.State.QUEUED));
      assertThat(task.getSteps().size(), is(1));
      assertThat(
          task.getSteps().get(0).getOperation(),
          is(com.vmware.photon.controller.api.Operation.GET_NETWORKS));
    }
    @Test
    public void testPrepareVmDetachIso() throws Throwable {
      TaskEntity task = vmDcpBackend.prepareVmDetachIso(vmId);

      assertThat(task, is(notNullValue()));
      assertThat(task.getState(), is(TaskEntity.State.QUEUED));
      assertThat(task.getSteps().size(), is(1));
      assertThat(
          task.getSteps().get(0).getOperation(),
          is(com.vmware.photon.controller.api.Operation.DETACH_ISO));
      assertThat(task.getSteps().get(0).getTransientResourceEntities().get(0), is(vm));
    }
    @Test
    public void testPrepareVmGetMksTicket() throws Throwable {
      vmDcpBackend.updateState(vmDcpBackend.findById(vmId), VmState.STARTED);
      TaskEntity task = vmDcpBackend.prepareVmGetMksTicket(vmId);

      assertThat(task, is(notNullValue()));
      assertThat(task.getState(), is(TaskEntity.State.QUEUED));
      assertThat(task.getSteps().size(), is(1));
      assertThat(
          task.getSteps().get(0).getOperation(),
          is(com.vmware.photon.controller.api.Operation.GET_MKS_TICKET));
    }
  @Test
  public void testPrepareImageDelete() throws Throwable {
    ImageEntity imageInDb = new ImageEntity();
    imageInDb.setName("image-0");
    imageInDb.setState(ImageState.READY);
    imageInDb.setSize(1000L);

    imageDao.create(imageInDb);
    TaskEntity taskDelete = imageBackend.prepareImageDelete(imageInDb.getId());
    assertThat(taskDelete.getSteps().size(), is(2));
    assertThat(taskDelete.getSteps().get(0).getOperation(), is(Operation.DELETE_IMAGE));
    assertThat(taskDelete.getSteps().get(1).getOperation(), is(Operation.DELETE_IMAGE_REPLICAS));
  }
  @Test(dataProvider = "ImageFileNames")
  public void testPrepareImageUploadImageFileNames(String imageFileName) throws ExternalException {
    TaskEntity task =
        imageBackend.prepareImageUpload(inputStream, imageFileName, ImageReplicationType.ON_DEMAND);
    assertThat(task.getSteps().size(), is(1));

    StepEntity step = task.getSteps().get(0);
    assertThat(step.getTransientResourceEntities().size(), is(1));
    assertThat(step.getOperation(), is(Operation.UPLOAD_IMAGE));
    ImageEntity image = (ImageEntity) step.getTransientResourceEntities().get(0);
    assertThat(image.getState(), is(ImageState.CREATING));
    assertThat(image.getName(), is(imageName));
  }
    @Test
    public void testPrepareVmDelete() throws Throwable {
      TaskEntity task = vmDcpBackend.prepareVmDelete(vmId);

      assertThat(task, is(notNullValue()));
      assertThat(task.getState(), is(TaskEntity.State.QUEUED));
      assertThat(task.getSteps().size(), is(1));
      assertThat(
          task.getSteps().get(0).getOperation(),
          is(com.vmware.photon.controller.api.Operation.DELETE_VM));
      assertThat(task.getToBeLockedEntityIds().size(), is(1));
      assertThat(task.getToBeLockedEntityIds().get(0), is(vmId));
    }
  @Test
  public void testPrepareImageUploadOnDemand() throws ExternalException {
    TaskEntity task =
        imageBackend.prepareImageUpload(inputStream, "image-name", ImageReplicationType.ON_DEMAND);
    assertThat(task.getSteps().size(), is(1));

    StepEntity step = task.getSteps().get(0);
    assertThat(step.getTransientResourceEntities().size(), is(1));
    assertThat(step.getOperation(), is(Operation.UPLOAD_IMAGE));
    ImageEntity image = (ImageEntity) step.getTransientResourceEntities().get(0);
    assertThat(image.getName(), is("image-name"));
    assertThat(image.getState(), is(ImageState.CREATING));
    assertThat(image.getReplicationType(), is(ImageReplicationType.ON_DEMAND));
    assertThat(
        (InputStream) step.getTransientResource(ImageUploadStepCmd.INPUT_STREAM), is(inputStream));
  }
    @Test
    public void testPrepareVmOperation() throws Throwable {
      com.vmware.photon.controller.api.Operation operation =
          VmOperation.VALID_OPERATIONS.iterator().next();
      TaskEntity task = vmDcpBackend.prepareVmOperation(vmId, operation);

      assertThat(task, is(notNullValue()));
      assertThat(task.getState(), is(TaskEntity.State.QUEUED));
      assertThat(task.getSteps().size(), is(1));
      assertThat(task.getSteps().get(0).getOperation(), is(operation));

      try {
        vmDcpBackend.prepareVmOperation(vmId, com.vmware.photon.controller.api.Operation.MOCK_OP);
        fail("vmDcpBackend.prepareVmOperation with invalid operation should have failed");
      } catch (NotImplementedException e) {
        // do nothing
      }
    }
  @Test
  public void testTombstone() throws Throwable {
    TaskEntity task =
        imageBackend.prepareImageUpload(inputStream, "image-name", ImageReplicationType.ON_DEMAND);
    StepEntity step = task.getSteps().get(0);
    ImageEntity image = (ImageEntity) step.getTransientResourceEntities().get(0);
    assertThat(image.getState(), is(ImageState.CREATING));

    image.setState(ImageState.READY);
    flushSession();

    TenantEntity tenant = new TenantEntity();
    tenant.setName("t1");
    tenantDao.create(tenant);

    ProjectEntity project = new ProjectEntity();
    project.setTenantId(tenant.getId());
    project.setName("staging");
    projectDao.create(project);

    VmEntity vm = new VmEntity();
    vm.setName("vm-1");
    vm.setProjectId(project.getId());
    vm.setImageId(image.getId());
    vmDao.create(vm);

    flushSession();

    imageBackend.tombstone(image);
    assertThat(image.getState(), is(ImageState.PENDING_DELETE));

    vm = vmDao.findById(vm.getId()).get();
    vmDao.delete(vm);
    flushSession();

    imageBackend.tombstone(image);
    try {
      imageBackend.findById(image.getId());
      fail("Image should be deleted in database");
    } catch (ImageNotFoundException e) {
    }
  }
    @Test
    public void testPrepareSetMetadata() throws Throwable {
      Map<String, String> metadata = new HashMap<>();
      metadata.put("key", "value");

      TaskEntity task = vmDcpBackend.prepareSetMetadata(vmId, metadata);

      assertThat(task, is(notNullValue()));
      assertThat(task.getState(), is(TaskEntity.State.COMPLETED));
      assertThat(task.getSteps().size(), is(0));

      // check that metadata was saved
      VmEntity updatedVm = vmDcpBackend.findById(vmId);
      assertThat(updatedVm, notNullValue());
      assertThat(updatedVm.getMetadata(), is(metadata));

      // make sure that no other fields have changed
      updatedVm.setMetadata(this.vm.getMetadata());
      assertThat(this.vm, is(updatedVm));
    }
    @Test(dataProvider = "vmCreateImageReplicationType")
    public void testPrepareVmCreateImage(ImageReplicationType replicationType) throws Throwable {
      ImageCreateSpec imageCreateSpec = new ImageCreateSpec();
      imageCreateSpec.setName("i1");
      imageCreateSpec.setReplicationType(replicationType);

      TaskEntity task = vmDcpBackend.prepareVmCreateImage(vmId, imageCreateSpec);

      assertThat(task, is(notNullValue()));
      assertThat(task.getState(), is(TaskEntity.State.QUEUED));
      assertThat(task.getSteps().size(), is(2));
      StepEntity step = task.getSteps().get(0);
      assertThat(
          step.getOperation(), is(com.vmware.photon.controller.api.Operation.CREATE_VM_IMAGE));
      assertThat(step.getTransientResourceEntities().size(), is(3));
      assertThat(step.getTransientResourceEntities(Vm.KIND).size(), is(1));
      assertThat(step.getTransientResourceEntities(Image.KIND).size(), is(2));

      assertThat(step.getTransientResourceEntities(Vm.KIND).get(0).getId(), is(vm.getId()));

      ImageEntity image = (ImageEntity) step.getTransientResourceEntities(ImageEntity.KIND).get(0);
      assertThat(image.getName(), is(imageCreateSpec.getName()));
      assertThat(image.getReplicationType(), is(imageCreateSpec.getReplicationType()));
      assertThat(image.getState(), is(ImageState.CREATING));
      assertThat(image.getSize(), is(createdImageState.size));
      assertThat(
          image.getImageSettingsMap(),
          is((Map<String, String>) ImmutableMap.of("n1", "v1", "n2", "v2")));

      ImageEntity vmImage =
          (ImageEntity) step.getTransientResourceEntities(ImageEntity.KIND).get(1);
      assertThat(vmImage.getId(), is(vm.getImageId()));

      step = task.getSteps().get(1);
      assertThat(
          step.getOperation(), is(com.vmware.photon.controller.api.Operation.REPLICATE_IMAGE));
    }
    @BeforeMethod
    public void setUp() throws Throwable {
      commonHostAndClientSetup(basicServiceHost, apiFeDcpRestClient);
      commonDataSetup(
          tenantDcpBackend,
          resourceTicketDcpBackend,
          projectDcpBackend,
          flavorDcpBackend,
          flavorLoader);

      commonVmAndImageSetup(vmDcpBackend, networkDcpBackend);

      vmId = createdVmTaskEntity.getEntityId();
      entityLockDcpBackend.clearTaskLocks(createdVmTaskEntity);
      vm = vmDcpBackend.findById(vmId);
    }
    @Test
    public void testPrepareVmCreate() throws Throwable {
      String vmId = createdVmTaskEntity.getEntityId();
      assertThat(createdVmTaskEntity.getSteps().size(), is(2));
      assertThat(
          createdVmTaskEntity.getSteps().get(0).getOperation(),
          is(com.vmware.photon.controller.api.Operation.RESERVE_RESOURCE));
      assertThat(
          createdVmTaskEntity
              .getSteps()
              .get(0)
              .getTransientResourceEntities(ProjectEntity.KIND)
              .size(),
          is(1));
      assertThat(
          createdVmTaskEntity
              .getSteps()
              .get(0)
              .getTransientResourceEntities(ProjectEntity.KIND)
              .get(0)
              .getId(),
          is(projectId));
      assertThat(
          createdVmTaskEntity.getSteps().get(1).getOperation(),
          is(com.vmware.photon.controller.api.Operation.CREATE_VM));
      assertThat(createdVmTaskEntity.getToBeLockedEntityIds().size(), is(1));
      assertThat(createdVmTaskEntity.getToBeLockedEntityIds().get(0), is(vmId));

      VmEntity vm = vmDcpBackend.findById(vmId);
      assertThat(vm, is(notNullValue()));
      assertThat(getUsage("vm.cost"), is(1.0));
      assertThat(vm.getImageId(), is(imageId));

      assertThat(vm.getAffinities().get(0).getResourceId(), is("disk-id1"));
      assertThat(vm.getAffinities().get(0).getKind(), is("disk"));
      assertThat(vm.getAffinities().get(1).getResourceId(), is("disk-id2"));
      assertThat(vm.getAffinities().get(1).getKind(), is("disk"));

      Set<TagEntity> tags = vm.getTags();
      assertThat(tags.size(), is(2));
      TagEntity tag1 = new TagEntity();
      tag1.setValue("value1");
      TagEntity tag2 = new TagEntity();
      tag2.setValue("value2");
      assertTrue(tags.contains(tag1));
      assertTrue(tags.contains(tag2));

      assertThat(vmCreateSpec.getNetworks().equals(vm.getNetworks()), is(true));
    }
    @Test
    public void testAddTagWhenOriginalTagIsNull() throws Throwable {
      // overwriting the vmCreateSpec tags to empty from non-empty
      vmCreateSpec.setTags(null);

      createdVmTaskEntity = vmDcpBackend.prepareVmCreate(projectId, vmCreateSpec);
      vmId = createdVmTaskEntity.getEntityId();
      entityLockDcpBackend.clearTaskLocks(createdVmTaskEntity);
      vm = vmDcpBackend.findById(vmId);
      int originalSize = vm.getTags().size();
      Tag tag = new Tag("namespace:predicate=value");
      vmDcpBackend.addTag(vm.getId(), tag);
      vm = vmDcpBackend.findById(vm.getId());
      assertThat(vm.getTags().size(), is(1));
      List<String> tagValues =
          vm.getTags().stream().map(t -> t.getValue()).collect(Collectors.toList());
      assertTrue(tagValues.contains(tag.getValue()));
    }
    @Test(dataProvider = "IsoFileNames")
    public void testPrepareVmAttachIso(String isoFileName) throws Throwable {
      TaskEntity task = vmDcpBackend.prepareVmAttachIso(vmId, inputStream, isoFileName);

      assertThat(task, is(notNullValue()));
      assertThat(task.getState(), is(TaskEntity.State.QUEUED));
      assertThat(task.getSteps().size(), is(2));
      assertThat(
          task.getSteps().get(0).getOperation(),
          is(com.vmware.photon.controller.api.Operation.UPLOAD_ISO));
      IsoEntity iso = (IsoEntity) task.getSteps().get(0).getTransientResourceEntities().get(1);
      assertThat(iso.getName(), is(isoName));

      assertThat(task.getToBeLockedEntityIds().size(), is(2));
      assertThat(task.getToBeLockedEntityIds().get(0), is(iso.getId()));
      assertThat(task.getToBeLockedEntityIds().get(1), is(vmId));
    }
  private static void commonVmAndImageSetup(
      VmDcpBackend vmDcpBackend, NetworkDcpBackend networkDcpBackend) throws Throwable {
    AttachedDiskCreateSpec disk1 =
        new AttachedDiskCreateSpecBuilder().name("disk1").flavor("core-100").bootDisk(true).build();
    AttachedDiskCreateSpec disk2 =
        new AttachedDiskCreateSpecBuilder().name("disk2").flavor("core-200").capacityGb(10).build();

    List<LocalitySpec> affinities = new ArrayList<>();
    affinities.add(new LocalitySpec("disk-id1", "disk"));
    affinities.add(new LocalitySpec("disk-id2", "disk"));

    ImageService.State imageServiceState = new ImageService.State();
    imageServiceState.name = "image-1";
    imageServiceState.state = ImageState.READY;
    imageServiceState.size = 1024L * 1024L;
    imageServiceState.replicationType = ImageReplicationType.EAGER;
    imageServiceState.imageSettings = new ArrayList<>();
    ImageService.State.ImageSetting imageSetting = new ImageService.State.ImageSetting();
    imageSetting.name = "n1";
    imageSetting.defaultValue = "v1";
    imageServiceState.imageSettings.add(imageSetting);
    imageSetting = new ImageService.State.ImageSetting();
    imageSetting.name = "n2";
    imageSetting.defaultValue = "v2";
    imageServiceState.imageSettings.add(imageSetting);

    Operation result = dcpClient.post(ImageServiceFactory.SELF_LINK, imageServiceState);

    createdImageState = result.getBody(ImageService.State.class);

    imageId = ServiceUtils.getIDFromDocumentSelfLink(createdImageState.documentSelfLink);

    vmCreateSpec = new VmCreateSpec();
    vmCreateSpec.setName("test-vm");
    vmCreateSpec.setFlavor("core-100");
    vmCreateSpec.setSourceImageId(imageId);
    vmCreateSpec.setAttachedDisks(ImmutableList.of(disk1, disk2));
    vmCreateSpec.setAffinities(affinities);
    vmCreateSpec.setTags(ImmutableSet.of("value1", "value2"));

    List<String> networks = new ArrayList<>();

    List<String> portGroups = new ArrayList<>();
    portGroups.add("p1");
    NetworkCreateSpec networkCreateSpec = new NetworkCreateSpec();
    networkCreateSpec.setName("n1");
    networkCreateSpec.setPortGroups(portGroups);
    TaskEntity networkTask = networkDcpBackend.createNetwork(networkCreateSpec);
    networks.add(networkTask.getEntityId());

    portGroups = new ArrayList<>();
    portGroups.add("p2");
    networkCreateSpec.setName("n2");
    networkCreateSpec.setPortGroups(portGroups);
    networkTask = networkDcpBackend.createNetwork(networkCreateSpec);
    networks.add(networkTask.getEntityId());

    vmCreateSpec.setNetworks(networks);

    createdVmTaskEntity = vmDcpBackend.prepareVmCreate(projectId, vmCreateSpec);
  }