@Override
 public void process() {
   DbClient dbClient = getDbClient();
   List<URI> volumeURIs = dbClient.queryByType(Volume.class, false);
   Iterator<Volume> volumesIter = dbClient.queryIterativeObjects(Volume.class, volumeURIs);
   while (volumesIter.hasNext()) {
     Volume volume = volumesIter.next();
     URI systemURI = volume.getStorageController();
     if (!NullColumnValueGetter.isNullURI(systemURI)) {
       StorageSystem system = dbClient.queryObject(StorageSystem.class, systemURI);
       if ((system != null)
           && (DiscoveredDataObject.Type.vplex.name().equals(system.getSystemType()))) {
         // This is a VPLEX volume. If not already set,
         // set the protocols to FC.
         StringSet protocols = volume.getProtocol();
         if (protocols == null) {
           protocols = new StringSet();
           protocols.add(StorageProtocol.Block.FC.name());
           volume.setProtocol(protocols);
           dbClient.persistObject(volume);
         }
       }
     }
   }
 }
  /**
   * Verify the RP+VPlex consistency group and its volumes have been properly migrated.
   *
   * @throws Exception
   */
  private void verifyRpVplexConsistencyGroupMigration() throws Exception {
    log.info("Verifying RP+VPlex BlockConsistencyGroup and associated volume migration.");
    List<BlockObject> blockObjects = new ArrayList<BlockObject>();

    BlockConsistencyGroup rpVplexPrimaryCg =
        _dbClient.queryObject(BlockConsistencyGroup.class, rpVplexPrimaryConsistencyGroupURI);

    // Verify the RP+VPLEX consistency group was properly migrated
    verifyConsistencyGroupMigration(rpVplexPrimaryCg, Types.RP.name(), Types.VPLEX.name());

    Assert.assertNotNull(
        "The RP+VPlex BlockConsistencyGroup.systemConsistencyGroups field should be populated.",
        rpVplexPrimaryCg.getSystemConsistencyGroups());
    Assert.assertNotNull(
        "The RP+VPlex BlockConsistencyGroup.systemConsistencyGroups field should contain an entry for "
            + protectionSystemURI.toString(),
        rpVplexPrimaryCg.getSystemConsistencyGroups().get(protectionSystemURI.toString()));
    Assert.assertTrue(
        "The RP+VPlex BlockConsistencyGroup.systemConsistencyGroups field should contain a mapping for "
            + protectionSystemURI.toString()
            + "-> ViPR-"
            + rpVplexPrimaryCg.getLabel(),
        rpVplexPrimaryCg
            .getSystemConsistencyGroups()
            .get(protectionSystemURI.toString())
            .contains("ViPR-" + rpVplexPrimaryCg.getLabel()));

    // Verify that primary CG has a mapping reference for each of the VPlex storage system/cg name.
    for (URI rpVplexVolumeId : rpVplexVolumeToCgMapping.keySet()) {
      Volume rpVplexVolume = _dbClient.queryObject(Volume.class, rpVplexVolumeId);
      blockObjects.add(rpVplexVolume);

      // Get the VPlex consistency group
      URI cgUri = rpVplexVolumeToCgMapping.get(rpVplexVolumeId);
      BlockConsistencyGroup vplexCg = _dbClient.queryObject(BlockConsistencyGroup.class, cgUri);

      String cgName = vplexCg.getLabel();
      String clusterName = getVPlexClusterFromVolume(rpVplexVolume);
      String storageSystem = rpVplexVolume.getStorageController().toString();
      String clusterCgName = BlockConsistencyGroupUtils.buildClusterCgName(clusterName, cgName);

      // Verify that primary CG contains the correct mapping
      Assert.assertTrue(
          "The RP+VPlex BlockConsistencyGroup.systemConsistencyGroups field should contain a mapping for "
              + storageSystem
              + "->"
              + clusterCgName,
          rpVplexPrimaryCg.getSystemConsistencyGroups().get(storageSystem).contains(clusterCgName));

      // Verify that the VPlex CG has been marked for deletion
      Assert.assertTrue(
          "The VPlex BlockConsistencyGroup " + vplexCg.getLabel() + "should be inactive.",
          vplexCg.getInactive());
    }

    // Verify the volume migration took place correctly
    verifyBlockObjects(blockObjects);
  }
  /** {@inheritDoc} */
  @Override
  public void validateFullCopyCreateRequest(List<BlockObject> fcSourceObjList, int count) {
    if (fcSourceObjList.size() > 0) {
      URI fcSourceObjURI = fcSourceObjList.get(0).getId();
      if (URIUtil.isType(fcSourceObjURI, BlockSnapshot.class)) {
        // Currently you cannot create a full copy of a VPLEX snapshot.
        throw APIException.badRequests.cantCreateFullCopyForVPlexSnapshot();
      } else {
        // Call super first.
        super.validateFullCopyCreateRequest(fcSourceObjList, count);

        // Platform specific checks.
        for (BlockObject fcSourceObj : fcSourceObjList) {
          Volume fcSourceVolume = (Volume) fcSourceObj;
          StorageSystem system =
              _dbClient.queryObject(StorageSystem.class, fcSourceVolume.getStorageController());
          if (DiscoveredDataObject.Type.vplex.name().equals(system.getSystemType())) {
            // If the volume is a VPLEX volume, then we need to be sure that
            // storage pool of the source backend volume of the VPLEX volume,
            // which is volume used to create the native full copy, supports
            // full copy.
            Volume srcBackendVolume =
                VPlexUtil.getVPLEXBackendVolume(fcSourceVolume, true, _dbClient, true);
            StoragePool storagePool =
                _dbClient.queryObject(StoragePool.class, srcBackendVolume.getPool());
            verifyFullCopySupportedForStoragePool(storagePool);

            // If the full copy source is itself a full copy, it is not
            // detached, and the native full copy i.e., the source side
            // backend volume, is VNX, then creating a full copy of the
            // volume will fail. As such, we prevent it.
            if ((BlockFullCopyUtils.isVolumeFullCopy(fcSourceVolume, _dbClient))
                && (!BlockFullCopyUtils.isFullCopyDetached(fcSourceVolume, _dbClient))) {
              URI backendSystemURI = srcBackendVolume.getStorageController();
              StorageSystem backendSystem =
                  _dbClient.queryObject(StorageSystem.class, backendSystemURI);
              if (DiscoveredDataObject.Type.vnxblock.name().equals(backendSystem.getSystemType())) {
                throw APIException.badRequests.cantCreateFullCopyOfVPlexFullCopyUsingVNX();
              }
            }
          }
        }
      }
    }
  }
  /**
   * Places and prepares the primary copy volumes when copying a VPLEX virtual volume.
   *
   * @param name The base name for the volume.
   * @param copyCount The number of copies to be made.
   * @param srcPrimaryVolume The primary volume of the VPLEX volume being copied.
   * @param srcCapabilities The capabilities of the primary volume.
   * @param volumeDescriptors The list of descriptors.
   * @return A list of the prepared primary volumes for the VPLEX volume copy.
   */
  private List<Volume> prepareFullCopyPrimaryVolumes(
      String name,
      int copyCount,
      Volume srcPrimaryVolume,
      VirtualPoolCapabilityValuesWrapper srcCapabilities,
      List<VolumeDescriptor> volumeDescriptors) {

    List<Volume> copyPrimaryVolumes = new ArrayList<Volume>();

    // Get the placement recommendations for the primary volume copies.
    // Use the same method as is done for native volume copy.
    VirtualArray vArray =
        _dbClient.queryObject(VirtualArray.class, srcPrimaryVolume.getVirtualArray());
    VirtualPool vPool = _dbClient.queryObject(VirtualPool.class, srcPrimaryVolume.getVirtualPool());
    List<VolumeRecommendation> recommendations =
        ((VPlexScheduler) _scheduler)
            .getBlockScheduler()
            .getRecommendationsForVolumeClones(vArray, vPool, srcPrimaryVolume, srcCapabilities);
    if (recommendations.isEmpty()) {
      throw APIException.badRequests.noStorageForPrimaryVolumesForVplexVolumeCopies();
    }

    // Prepare the copy volumes for each recommendation. Again,
    // use the same manner as is done for native volume copy.
    StringBuilder nameBuilder = new StringBuilder(name);
    nameBuilder.append("-0");
    int copyIndex = (copyCount > 1) ? 1 : 0;
    for (VolumeRecommendation recommendation : recommendations) {
      Volume volume =
          StorageScheduler.prepareFullCopyVolume(
              _dbClient,
              nameBuilder.toString(),
              srcPrimaryVolume,
              recommendation,
              copyIndex++,
              srcCapabilities);
      volume.addInternalFlags(Flag.INTERNAL_OBJECT);
      _dbClient.persistObject(volume);
      copyPrimaryVolumes.add(volume);

      // Create the volume descriptor and add it to the passed list.
      VolumeDescriptor volumeDescriptor =
          new VolumeDescriptor(
              VolumeDescriptor.Type.VPLEX_IMPORT_VOLUME,
              volume.getStorageController(),
              volume.getId(),
              volume.getPool(),
              srcCapabilities);
      volumeDescriptors.add(volumeDescriptor);
    }

    return copyPrimaryVolumes;
  }
  /**
   * Verify the VPlex consistency group and its volumes have been properly migrated.
   *
   * @throws Exception
   */
  private void verifyVplexConsistencyGroupMigration() throws Exception {
    log.info("Verifying VPlex BlockConsistencyGroup and associated volume migration.");

    BlockConsistencyGroup vplexCg =
        _dbClient.queryObject(BlockConsistencyGroup.class, vplexConsistencyGroupURI);
    Iterator<Volume> vplexVolumeItr =
        _dbClient.queryIterativeObjects(Volume.class, vplexVolumeURIs);

    // Verify the VPLEX consistency group was properly migrated
    verifyConsistencyGroupMigration(vplexCg, Types.VPLEX.name());

    while (vplexVolumeItr.hasNext()) {
      Volume vplexVolume = vplexVolumeItr.next();
      // Get the VPlex consistency group
      String cgName = vplexCg.getLabel();
      String clusterName = getVPlexClusterFromVolume(vplexVolume);
      String storageSystem = vplexVolume.getStorageController().toString();
      String clusterCgName = BlockConsistencyGroupUtils.buildClusterCgName(clusterName, cgName);

      // Verify that primary CG contains the correct mapping
      Assert.assertNotNull(
          "The VPlex BlockConsistencyGroup.vplexStorageSystemToCg field should be populated.",
          vplexCg.getSystemConsistencyGroups());
      Assert.assertTrue(
          "The VPlex BlockConsistencyGroup.vplexStorageSystemToCg should contain a key for storage system "
              + storageSystem,
          vplexCg.getSystemConsistencyGroups().containsKey(storageSystem));
      Assert.assertTrue(
          "The VPlex BlockConsistencyGroup.vplexStorageSystemToCg field should contain a mapping for "
              + storageSystem
              + "->"
              + clusterCgName,
          vplexCg.getSystemConsistencyGroups().get(storageSystem).contains(clusterCgName));
    }

    // Verify the volume migration took place correctly
    List<BlockObject> blockObjects = new ArrayList<BlockObject>();
    while (vplexVolumeItr.hasNext()) {
      blockObjects.add(vplexVolumeItr.next());
    }

    verifyBlockObjects(blockObjects);
  }
  /**
   * get the volume to be updated after application add and remove operations could be the volume
   * passed in if it's a simple block volume or the vplex virtual volume if it's a backing volume
   *
   * @param voluri uri of volume operated on during add or remove volume from application operation
   * @param dbClient
   * @return the volume to update
   */
  private Volume getVolume(URI voluri, DbClient dbClient) {
    // if this is a vplex volume, update the parent virtual volume
    List<Volume> vplexVolumes =
        CustomQueryUtility.queryActiveResourcesByConstraint(
            dbClient, Volume.class, getVolumesByAssociatedId(voluri.toString()));

    Volume volume = null;

    for (Volume vplexVolume : vplexVolumes) {
      URI storageURI = vplexVolume.getStorageController();
      StorageSystem storage = dbClient.queryObject(StorageSystem.class, storageURI);
      if (DiscoveredDataObject.Type.vplex.name().equals(storage.getSystemType())) {
        volume = vplexVolume;
      }
    }

    if (volume == null) {
      volume = dbClient.queryObject(Volume.class, voluri);
    }
    return volume;
  }
  @Override
  public void doModifyVolumes(
      StorageSystem storage,
      StoragePool storagePool,
      String opId,
      List<Volume> volumes,
      TaskCompleter taskCompleter)
      throws DeviceControllerException {
    StringBuilder logMsgBuilder =
        new StringBuilder(
            String.format(
                "Modify Volume Start - Array:%s, Pool:%s",
                storage.getSerialNumber(), storagePool.getNativeGuid()));

    String systemObjectID = HDSUtils.getSystemObjectID(storage);
    for (Volume volume : volumes) {
      try {
        HDSApiClient hdsApiClient =
            hdsApiFactory.getClient(
                HDSUtils.getHDSServerManagementServerInfo(storage),
                storage.getSmisUserName(),
                storage.getSmisPassword());
        logMsgBuilder.append(
            String.format(
                "%nVolume:%s , IsThinlyProvisioned: %s, tieringPolicy: %s",
                volume.getLabel(),
                volume.getThinlyProvisioned(),
                volume.getAutoTieringPolicyUri()));
        LogicalUnit logicalUnit =
            hdsApiClient.getLogicalUnitInfo(
                systemObjectID, HDSUtils.getLogicalUnitObjectId(volume.getNativeId(), storage));
        String policyName = ControllerUtils.getAutoTieringPolicyName(volume.getId(), dbClient);
        String autoTierPolicyName = null;
        if (policyName.equals(Constants.NONE)) {
          autoTierPolicyName = null;
        } else {
          autoTierPolicyName =
              HitachiTieringPolicy.getPolicy(
                      policyName.replaceAll(
                          HDSConstants.SLASH_OPERATOR, HDSConstants.UNDERSCORE_OPERATOR))
                  .getKey();
        }
        if (null != logicalUnit
            && null != logicalUnit.getLdevList()
            && !logicalUnit.getLdevList().isEmpty()) {
          Iterator<LDEV> ldevItr = logicalUnit.getLdevList().iterator();
          if (ldevItr.hasNext()) {
            LDEV ldev = ldevItr.next();
            String asyncMessageId =
                hdsApiClient.modifyThinVolumeTieringPolicy(
                    systemObjectID,
                    logicalUnit.getObjectID(),
                    ldev.getObjectID(),
                    autoTierPolicyName);
            if (null != asyncMessageId) {
              HDSJob modifyHDSJob =
                  new HDSModifyVolumeJob(
                      asyncMessageId,
                      volume.getStorageController(),
                      taskCompleter,
                      HDSModifyVolumeJob.VOLUME_MODIFY_JOB);
              ControllerServiceImpl.enqueueJob(new QueueJob(modifyHDSJob));
            }
          }
        } else {
          String errorMsg = String.format("No LDEV's found for volume: %s", volume.getId());
          log.info(errorMsg);
          ServiceError serviceError =
              DeviceControllerErrors.hds.methodFailed("doModifyVolumes", errorMsg);
          taskCompleter.error(dbClient, serviceError);
        }
      } catch (final InternalException e) {
        log.error("Problem in doModifyVolumes: ", e);
        taskCompleter.error(dbClient, e);
      } catch (final Exception e) {
        log.error("Problem in doModifyVolumes: ", e);
        ServiceError serviceError =
            DeviceControllerErrors.hds.methodFailed("doModifyVolumes", e.getMessage());
        taskCompleter.error(dbClient, serviceError);
      }
    }
  }
  /**
   * Called to update the job status when the volume expand job completes.
   *
   * @param jobContext The job context.
   */
  public void updateStatus(JobContext jobContext) throws Exception {
    CloseableIterator<CIMObjectPath> associatorIterator = null;
    CloseableIterator<CIMInstance> instanceIterator = null;
    JobStatus jobStatus = getJobStatus();

    try {
      if (jobStatus == JobStatus.IN_PROGRESS) {
        return;
      }

      DbClient dbClient = jobContext.getDbClient();
      CIMConnectionFactory cimConnectionFactory = jobContext.getCimConnectionFactory();
      WBEMClient client = getWBEMClient(dbClient, cimConnectionFactory);

      // If terminal state update storage pool capacity and remove reservation for volume capacity
      // from pool's reserved capacity map.
      if (jobStatus == JobStatus.SUCCESS
          || jobStatus == JobStatus.FAILED
          || jobStatus == JobStatus.FATAL_ERROR) {
        SmisUtils.updateStoragePoolCapacity(dbClient, client, _storagePoolURI);

        StoragePool pool = dbClient.queryObject(StoragePool.class, _storagePoolURI);
        StringMap reservationMap = pool.getReservedCapacityMap();
        URI volumeId = getTaskCompleter().getId();
        // remove from reservation map
        reservationMap.remove(volumeId.toString());
        dbClient.persistObject(pool);
      }

      String opId = getTaskCompleter().getOpId();
      StringBuilder logMsgBuilder =
          new StringBuilder(
              String.format(
                  "Updating status of job %s to %s, task: %s",
                  this.getJobName(), jobStatus.name(), opId));

      if (jobStatus == JobStatus.SUCCESS) {
        VolumeExpandCompleter taskCompleter = (VolumeExpandCompleter) getTaskCompleter();
        Volume volume = dbClient.queryObject(Volume.class, taskCompleter.getId());
        // set requested capacity
        volume.setCapacity(taskCompleter.getSize());
        // set meta related properties
        volume.setTotalMetaMemberCapacity(taskCompleter.getTotalMetaMembersSize());
        volume.setMetaMemberCount(taskCompleter.getMetaMemberCount());
        volume.setMetaMemberSize(taskCompleter.getMetaMemberSize());
        volume.setIsComposite(taskCompleter.isComposite());
        volume.setCompositionType(taskCompleter.getMetaVolumeType());

        // set provisioned capacity
        associatorIterator =
            client.associatorNames(getCimJob(), null, SmisConstants.CIM_STORAGE_VOLUME, null, null);
        if (associatorIterator.hasNext()) {
          CIMObjectPath volumePath = associatorIterator.next();
          CIMInstance volumeInstance = client.getInstance(volumePath, true, false, null);
          if (volumeInstance != null) {
            CIMProperty consumableBlocks =
                volumeInstance.getProperty(SmisConstants.CP_CONSUMABLE_BLOCKS);
            CIMProperty blockSize = volumeInstance.getProperty(SmisConstants.CP_BLOCK_SIZE);
            // calculate provisionedCapacity = consumableBlocks * block size
            Long provisionedCapacity =
                Long.valueOf(consumableBlocks.getValue().toString())
                    * Long.valueOf(blockSize.getValue().toString());
            volume.setProvisionedCapacity(provisionedCapacity);
          }

          // set allocated capacity
          instanceIterator =
              client.referenceInstances(
                  volumePath,
                  SmisConstants.CIM_ALLOCATED_FROM_STORAGEPOOL,
                  null,
                  false,
                  SmisConstants.PS_SPACE_CONSUMED);
          if (instanceIterator.hasNext()) {
            CIMInstance allocatedFromStoragePoolPath = instanceIterator.next();
            CIMProperty spaceConsumed =
                allocatedFromStoragePoolPath.getProperty(SmisConstants.CP_SPACE_CONSUMED);
            if (null != spaceConsumed) {
              volume.setAllocatedCapacity(Long.valueOf(spaceConsumed.getValue().toString()));
            }
          }
        }
        logMsgBuilder.append(
            String.format(
                "%n   Capacity: %s, Provisioned capacity: %s, Allocated Capacity: %s",
                volume.getCapacity(),
                volume.getProvisionedCapacity(),
                volume.getAllocatedCapacity()));
        if (volume.getIsComposite()) {
          logMsgBuilder.append(
              String.format(
                  "%n   Is Meta: %s, Total meta member capacity: %s, Meta member count %s, Meta member size: %s",
                  volume.getIsComposite(),
                  volume.getTotalMetaMemberCapacity(),
                  volume.getMetaMemberCount(),
                  volume.getMetaMemberSize()));
        }

        _log.info(logMsgBuilder.toString());

        // Reset list of meta member volumes in the volume
        if (volume.getMetaVolumeMembers() != null) {
          volume.getMetaVolumeMembers().clear();
        }

        StorageSystem storageSystem =
            dbClient.queryObject(StorageSystem.class, volume.getStorageController());
        // set the RP tag on the volume if the volume is RP protected
        if (volume.checkForRp()
            && storageSystem.getSystemType() != null
            && storageSystem
                .getSystemType()
                .equalsIgnoreCase(DiscoveredDataObject.Type.vmax.toString())) {
          SmisCommandHelper helper = jobContext.getSmisCommandHelper();
          List<CIMObjectPath> volumePathList = new ArrayList<CIMObjectPath>();
          volumePathList.add(helper.getVolumeMember(storageSystem, volume));
          helper.setRecoverPointTag(storageSystem, volumePathList, true);
        }

        dbClient.persistObject(volume);
        // Reset list of meta members native ids in WF data (when meta is created meta members are
        // removed from array)
        WorkflowService.getInstance().storeStepData(opId, new ArrayList<String>());
      }
    } catch (Exception e) {
      _log.error("Caught an exception while trying to updateStatus for SmisVolumeExpandJob", e);
      setPostProcessingErrorStatus(
          "Encountered an internal error during volume expand job status processing : "
              + e.getMessage());
    } finally {
      _metaVolumeTaskCompleter.setLastStepStatus(jobStatus);
      if (associatorIterator != null) {
        associatorIterator.close();
      }
      if (instanceIterator != null) {
        instanceIterator.close();
      }
      super.updateStatus(jobContext);
    }
  }
  /** {@inheritDoc} */
  @Override
  public TaskList resynchronizeCopy(Volume sourceVolume, Volume fullCopyVolume) {
    // Create the task list.
    TaskList taskList = new TaskList();

    // Create a unique task id.
    String taskId = UUID.randomUUID().toString();

    // If the source is in a CG, then we will resynchronize the corresponding
    // full copies for all the volumes in the CG. Since we did not allow
    // full copies for volumes or snaps in CGs prior to Jedi, there should
    // be a full copy for all volumes in the CG.
    Map<URI, Volume> fullCopyMap = getFullCopySetMap(sourceVolume, fullCopyVolume);
    Set<URI> fullCopyURIs = fullCopyMap.keySet();

    // Get the storage system for the source volume.
    StorageSystem sourceSystem =
        _dbClient.queryObject(StorageSystem.class, sourceVolume.getStorageController());
    URI sourceSystemURI = sourceSystem.getId();

    // Create the resynchronize task on the full copy volumes.
    for (URI fullCopyURI : fullCopyURIs) {
      Operation op =
          _dbClient.createTaskOpStatus(
              Volume.class,
              fullCopyURI,
              taskId,
              ResourceOperationTypeEnum.RESYNCHRONIZE_VOLUME_FULL_COPY);
      fullCopyMap.get(fullCopyURI).getOpStatus().put(taskId, op);
      TaskResourceRep fullCopyVolumeTask =
          TaskMapper.toTask(fullCopyMap.get(fullCopyURI), taskId, op);
      taskList.getTaskList().add(fullCopyVolumeTask);
    }

    // Invoke the controller.
    try {
      VPlexController controller =
          getController(VPlexController.class, DiscoveredDataObject.Type.vplex.toString());
      controller.resyncFullCopy(sourceSystemURI, new ArrayList<URI>(fullCopyURIs), taskId);
    } catch (InternalException ie) {
      s_logger.error("Controller error", ie);

      // Update the status for the VPLEX volume copies and their
      // corresponding tasks.
      for (Volume vplexFullCopy : fullCopyMap.values()) {
        Operation op = vplexFullCopy.getOpStatus().get(taskId);
        if (op != null) {
          op.error(ie);
          vplexFullCopy.getOpStatus().updateTaskStatus(taskId, op);
          _dbClient.persistObject(vplexFullCopy);
          for (TaskResourceRep task : taskList.getTaskList()) {
            if (task.getResource().getId().equals(vplexFullCopy.getId())) {
              task.setState(op.getStatus());
              task.setMessage(op.getMessage());
              break;
            }
          }
        }
      }
    }
    return taskList;
  }
  /** {@inheritDoc} */
  @Override
  public TaskList detach(BlockObject fcSourceObj, Volume fullCopyVolume) {
    // If full copy volume is already detached or was never
    // activated, return detach action is completed successfully
    // as done in base class. Otherwise, send detach full copy
    // request to controller.
    TaskList taskList = new TaskList();
    String taskId = UUID.randomUUID().toString();
    if ((BlockFullCopyUtils.isFullCopyDetached(fullCopyVolume, _dbClient))
        || (BlockFullCopyUtils.isFullCopyInactive(fullCopyVolume, _dbClient))) {
      super.detach(fcSourceObj, fullCopyVolume);
    } else {
      // You cannot create a full copy of a VPLEX snapshot, so
      // the source will be a volume.
      Volume sourceVolume = (Volume) fcSourceObj;

      // If the source is in a CG, then we will detach the corresponding
      // full copies for all the volumes in the CG. Since we did not allow
      // full copies for volumes or snaps in CGs prior to Jedi, there should
      // be a full copy for all volumes in the CG.
      Map<URI, Volume> fullCopyMap = getFullCopySetMap(sourceVolume, fullCopyVolume);
      Set<URI> fullCopyURIs = fullCopyMap.keySet();

      // Get the storage system for the source volume.
      StorageSystem sourceSystem =
          _dbClient.queryObject(StorageSystem.class, sourceVolume.getStorageController());
      URI sourceSystemURI = sourceSystem.getId();

      // Create the detach task on the full copy volumes.
      for (URI fullCopyURI : fullCopyURIs) {
        Operation op =
            _dbClient.createTaskOpStatus(
                Volume.class,
                fullCopyURI,
                taskId,
                ResourceOperationTypeEnum.DETACH_VOLUME_FULL_COPY);
        fullCopyMap.get(fullCopyURI).getOpStatus().put(taskId, op);
        TaskResourceRep fullCopyVolumeTask =
            TaskMapper.toTask(fullCopyMap.get(fullCopyURI), taskId, op);
        taskList.getTaskList().add(fullCopyVolumeTask);
      }

      // Invoke the controller.
      try {
        VPlexController controller =
            getController(VPlexController.class, DiscoveredDataObject.Type.vplex.toString());
        controller.detachFullCopy(sourceSystemURI, new ArrayList<URI>(fullCopyURIs), taskId);
      } catch (InternalException ie) {
        s_logger.error("Controller error", ie);

        // Update the status for the VPLEX volume copies and their
        // corresponding tasks.
        for (Volume vplexFullCopy : fullCopyMap.values()) {
          Operation op = vplexFullCopy.getOpStatus().get(taskId);
          if (op != null) {
            op.error(ie);
            vplexFullCopy.getOpStatus().updateTaskStatus(taskId, op);
            _dbClient.persistObject(vplexFullCopy);
            for (TaskResourceRep task : taskList.getTaskList()) {
              if (task.getResource().getId().equals(vplexFullCopy.getId())) {
                task.setState(op.getStatus());
                task.setMessage(op.getMessage());
                break;
              }
            }
          }
        }
      }
    }
    return taskList;
  }
  /**
   * Places and prepares the HA volumes when copying a distributed VPLEX volume.
   *
   * @param name The base name for the volume.
   * @param copyCount The number of copies to be made.
   * @param size The size for the HA volume.
   * @param vplexSystem A reference to the VPLEX storage system.
   * @param vplexSystemProject A reference to the VPLEX system project.
   * @param srcVarray The virtual array for the VPLEX volume being copied.
   * @param srcHAVolume The HA volume of the VPLEX volume being copied.
   * @param taskId The task identifier.
   * @param volumeDescriptors The list of descriptors.
   * @return A list of the prepared HA volumes for the VPLEX volume copy.
   */
  private List<Volume> prepareFullCopyHAVolumes(
      String name,
      int copyCount,
      Long size,
      StorageSystem vplexSystem,
      Project vplexSystemProject,
      VirtualArray srcVarray,
      Volume srcHAVolume,
      String taskId,
      List<VolumeDescriptor> volumeDescriptors) {

    List<Volume> copyHAVolumes = new ArrayList<Volume>();

    // Get the storage placement recommendations for the volumes.
    // Placement must occur on the same VPLEX system
    Set<URI> vplexSystemURIS = new HashSet<URI>();
    vplexSystemURIS.add(vplexSystem.getId());
    VirtualArray haVarray =
        _dbClient.queryObject(VirtualArray.class, srcHAVolume.getVirtualArray());
    VirtualPool haVpool = _dbClient.queryObject(VirtualPool.class, srcHAVolume.getVirtualPool());
    VirtualPoolCapabilityValuesWrapper haCapabilities = new VirtualPoolCapabilityValuesWrapper();
    haCapabilities.put(VirtualPoolCapabilityValuesWrapper.SIZE, size);
    haCapabilities.put(VirtualPoolCapabilityValuesWrapper.RESOURCE_COUNT, copyCount);
    VirtualPool vpool = BlockFullCopyUtils.queryFullCopySourceVPool(srcHAVolume, _dbClient);
    if (VirtualPool.ProvisioningType.Thin.toString()
        .equalsIgnoreCase(vpool.getSupportedProvisioningType())) {
      haCapabilities.put(VirtualPoolCapabilityValuesWrapper.THIN_PROVISIONING, Boolean.TRUE);
      // To guarantee that storage pool for a copy has enough physical
      // space to contain current allocated capacity of thin source volume
      haCapabilities.put(
          VirtualPoolCapabilityValuesWrapper.THIN_VOLUME_PRE_ALLOCATE_SIZE,
          BlockFullCopyUtils.getAllocatedCapacityForFullCopySource(srcHAVolume, _dbClient));
    }
    List<Recommendation> recommendations =
        ((VPlexScheduler) _scheduler)
            .scheduleStorageForImport(
                srcVarray, vplexSystemURIS, haVarray, haVpool, haCapabilities);
    if (recommendations.isEmpty()) {
      throw APIException.badRequests.noStorageForHaVolumesForVplexVolumeCopies();
    }

    // Prepare the HA volumes for the VPLEX volume copy.
    int copyIndex = 1;
    for (Recommendation recommendation : recommendations) {
      VPlexRecommendation haRecommendation = (VPlexRecommendation) recommendation;
      for (int i = 0; i < haRecommendation.getResourceCount(); i++) {
        // Determine the name for the HA volume copy.
        StringBuilder nameBuilder = new StringBuilder(name);
        nameBuilder.append("-1");
        if (copyCount > 1) {
          nameBuilder.append("-");
          nameBuilder.append(copyIndex++);
        }

        // Prepare the volume.
        Volume volume =
            VPlexBlockServiceApiImpl.prepareVolumeForRequest(
                size,
                vplexSystemProject,
                haVarray,
                haVpool,
                haRecommendation.getSourceDevice(),
                haRecommendation.getSourcePool(),
                nameBuilder.toString(),
                null,
                taskId,
                _dbClient);
        volume.addInternalFlags(Flag.INTERNAL_OBJECT);
        _dbClient.persistObject(volume);
        copyHAVolumes.add(volume);

        // Create the volume descriptor and add it to the passed list.
        VolumeDescriptor volumeDescriptor =
            new VolumeDescriptor(
                VolumeDescriptor.Type.BLOCK_DATA,
                volume.getStorageController(),
                volume.getId(),
                volume.getPool(),
                haCapabilities);
        volumeDescriptors.add(volumeDescriptor);
      }
    }

    return copyHAVolumes;
  }