/**
   * Creates the BlockObject BlockSnapshot data.
   *
   * @param name
   * @param numSnapshots
   * @throws Exception
   */
  private void prepareBlockSnapshotData(String name, int numSnapshots) throws Exception {
    // Create the volume for the snapshots
    Volume volume = new Volume();
    URI volumeURI = URIUtil.createId(Volume.class);

    StorageSystem storageSystem = createStorageSystem(false);
    volume.setId(volumeURI);
    volume.setStorageController(storageSystem.getId());
    String volName = "blockSnapshotVolume";
    volume.setLabel(volName);
    BlockConsistencyGroup cg =
        createBlockConsistencyGroup(
            "blockSnapshotConsistencyGroup", storageSystem.getId(), Types.LOCAL.name(), true);
    volume.setConsistencyGroup(cg.getId());
    _dbClient.createObject(volume);

    for (int i = 1; i <= numSnapshots; i++) {
      BlockSnapshot blockSnapshot = new BlockSnapshot();
      URI blockSnapshotURI = URIUtil.createId(BlockSnapshot.class);
      blockSnapshotURIs.add(blockSnapshotURI);
      blockSnapshot.setId(blockSnapshotURI);
      blockSnapshot.setLabel(name + i);
      blockSnapshot.setSnapsetLabel(name + i);
      blockSnapshot.setParent(new NamedURI(volume.getId(), name + i));
      blockSnapshot.addConsistencyGroup(cg.getId().toString());
      _dbClient.createObject(blockSnapshot);
    }
  }
 @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);
         }
       }
     }
   }
 }
  @Override
  public void removeInitiator(
      StorageSystem storage,
      URI exportMaskId,
      List<Initiator> initiators,
      List<URI> targets,
      TaskCompleter taskCompleter)
      throws DeviceControllerException {
    log.info("{} removeInitiator START...", storage.getSerialNumber());
    log.info("Export mask id: {}", exportMaskId);
    log.info("removeInitiator: initiators : {}", initiators);
    log.info("removeInitiator: targets : {}", targets);

    try {

      ExportMask exportMask = dbClient.queryObject(ExportMask.class, exportMaskId);
      List<Volume> volumeList = new ArrayList<Volume>();
      StringMap volumes = exportMask.getUserAddedVolumes();

      for (String vol : volumes.values()) {
        Volume volume = dbClient.queryObject(Volume.class, URI.create(vol));
        volumeList.add(volume);
      }

      detachVolumesFromInitiators(storage, volumeList, initiators);

      taskCompleter.ready(dbClient);
    } catch (final Exception ex) {
      log.error("Problem in RemoveInitiators: ", ex);
      ServiceError serviceError =
          DeviceControllerErrors.cinder.operationFailed("doRemoveInitiators", ex.getMessage());
      taskCompleter.error(dbClient, serviceError);
    }
    log.info("{} removeInitiator END...", storage.getSerialNumber());
  }
  @Override
  public void addVolume(
      StorageSystem storage,
      URI exportMaskId,
      VolumeURIHLU[] volumeURIHLUs,
      TaskCompleter taskCompleter)
      throws DeviceControllerException {
    log.info("{} addVolume START...", storage.getSerialNumber());
    log.info("Export mask id: {}", exportMaskId);
    log.info("addVolume: volume-HLU pairs: {}", volumeURIHLUs);
    log.info("User assigned HLUs will be ignored as Cinder does not support it.");

    try {

      ExportMask exportMask = dbClient.queryObject(ExportMask.class, exportMaskId);
      List<Volume> volumes = new ArrayList<Volume>();
      List<Initiator> initiatorList = new ArrayList<Initiator>();
      // map to store target LUN id generated for each volume
      Map<URI, Integer> volumeToTargetLunMap = new HashMap<URI, Integer>();
      StringMap initiators = exportMask.getUserAddedInitiators();

      for (VolumeURIHLU volumeURIHLU : volumeURIHLUs) {
        URI volumeURI = volumeURIHLU.getVolumeURI();
        Volume volume = dbClient.queryObject(Volume.class, volumeURI);
        volumes.add(volume);
      }
      for (String ini : initiators.values()) {
        Initiator initiator = dbClient.queryObject(Initiator.class, URI.create(ini));
        initiatorList.add(initiator);
      }

      // Map to store volume to initiatorTargetMap
      Map<Volume, Map<String, List<String>>> volumeToFCInitiatorTargetMap =
          new HashMap<Volume, Map<String, List<String>>>();

      attachVolumesToInitiators(
          storage,
          volumes,
          initiatorList,
          volumeToTargetLunMap,
          volumeToFCInitiatorTargetMap,
          exportMask);

      // Update targets in the export mask
      if (!volumeToFCInitiatorTargetMap.isEmpty()) {
        updateTargetsInExportMask(
            storage, volumes.get(0), volumeToFCInitiatorTargetMap, initiatorList, exportMask);
      }
      updateTargetLunIdInExportMask(volumeToTargetLunMap, exportMask);
      dbClient.updateAndReindexObject(exportMask);

      taskCompleter.ready(dbClient);
    } catch (final Exception ex) {
      log.error("Problem in AddVolumes: ", ex);
      ServiceError serviceError =
          DeviceControllerErrors.cinder.operationFailed("doAddVolumes", ex.getMessage());
      taskCompleter.error(dbClient, serviceError);
    }
    log.info("{} addVolume END...", storage.getSerialNumber());
  }
  /**
   * Deactivate Quota directory of file system, this will move the Quota directory to a
   * "marked-for-delete" state
   *
   * <p>NOTE: This is an asynchronous operation.
   *
   * @param id the URN of the QuotaDirectory
   * @param param QuotaDirectory delete param for optional force delete
   * @brief Delete file system Quota Dir
   * @return Task resource representation
   * @throws com.emc.storageos.svcs.errorhandling.resources.InternalException
   */
  @POST
  @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @Path("/{id}/deactivate")
  @CheckPermission(
      roles = {Role.TENANT_ADMIN},
      acls = {ACL.OWN, ACL.ALL})
  public TaskResourceRep deactivateQuotaDirectory(
      @PathParam("id") URI id, QuotaDirectoryDeleteParam param) throws InternalException {

    _log.info("FileService::deactivateQtree Request recieved {}", id);
    String task = UUID.randomUUID().toString();
    ArgValidator.checkFieldUriType(id, QuotaDirectory.class, "id");
    QuotaDirectory quotaDirectory = queryResource(id);
    FileShare fs = queryFileShareResource(quotaDirectory.getParent().getURI());
    ArgValidator.checkFieldNotNull(fs, "filesystem");

    // <TODO> Implement Force delete option when shares and exports for Quota Directory are
    // supported

    Operation op = new Operation();
    op.setResourceType(ResourceOperationTypeEnum.DELETE_FILE_SYSTEM_QUOTA_DIR);
    quotaDirectory.getOpStatus().createTaskStatus(task, op);
    fs.setOpStatus(new OpStatusMap());
    fs.getOpStatus().createTaskStatus(task, op);
    _dbClient.persistObject(fs);
    _dbClient.persistObject(quotaDirectory);

    // Now get ready to make calls into the controller
    StorageSystem device = _dbClient.queryObject(StorageSystem.class, fs.getStorageDevice());
    FileController controller = getController(FileController.class, device.getSystemType());
    try {
      controller.deleteQuotaDirectory(device.getId(), quotaDirectory.getId(), fs.getId(), task);
      // If delete operation is successful, then remove obj from ViPR db by setting inactive=true
      quotaDirectory.setInactive(true);
      _dbClient.persistObject(quotaDirectory);

    } catch (InternalException e) {
      // treating all controller exceptions as internal error for now. controller
      // should discriminate between validation problems vs. internal errors

      throw e;
    }

    auditOp(
        OperationTypeEnum.DELETE_FILE_SYSTEM_QUOTA_DIR,
        true,
        AuditLogManager.AUDITOP_BEGIN,
        quotaDirectory.getLabel(),
        quotaDirectory.getId().toString(),
        fs.getId().toString());

    fs = _dbClient.queryObject(FileShare.class, fs.getId());
    _log.debug(
        "FileService::Quota directory Before sending response, FS ID : {}, Taks : {} ; Status {}",
        fs.getOpStatus().get(task),
        fs.getOpStatus().get(task).getStatus());

    return toTask(quotaDirectory, task, op);
  }
  @Override
  public void deleteSingleVolumeMirror(
      StorageSystem storage, URI mirror, TaskCompleter taskCompleter)
      throws DeviceControllerException {
    _log.info("deleteSingleVolumeMirror operation START");
    try {
      BlockMirror mirrorObj = _dbClient.queryObject(BlockMirror.class, mirror);
      if (storage.checkIfVmax3()) {
        _helper.removeVolumeFromParkingSLOStorageGroup(storage, mirrorObj.getNativeId(), false);
        _log.info(
            "Done invoking remove volume {} from parking SLO storage group",
            mirrorObj.getNativeId());
      }

      CIMObjectPath mirrorPath = _cimPath.getBlockObjectPath(storage, mirrorObj);
      CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storage);
      CIMArgument[] inArgs = _helper.getDeleteMirrorInputArguments(storage, mirrorPath);
      CIMArgument[] outArgs = new CIMArgument[5];
      _helper.invokeMethod(
          storage, configSvcPath, SmisConstants.RETURN_TO_STORAGE_POOL, inArgs, outArgs);
      CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB);
      if (job != null) {
        ControllerServiceImpl.enqueueJob(
            new QueueJob(new SmisBlockDeleteMirrorJob(job, storage.getId(), taskCompleter)));
      }
    } catch (Exception e) {
      _log.info("Problem making SMI-S call: ", e);
      ServiceError serviceError =
          DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage());
      taskCompleter.error(_dbClient, serviceError);
    }
  }
 /*
  * (non-Javadoc)
  *
  * @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportRemoveVolumes(com.emc.storageos.db.client.model.StorageSystem,
  * com.emc.storageos.db.client.model.ExportMask, java.util.List, com.emc.storageos.volumecontroller.TaskCompleter)
  */
 @Override
 public void doExportRemoveVolumes(
     StorageSystem storage, ExportMask exportMask, List<URI> volumes, TaskCompleter taskCompleter)
     throws DeviceControllerException {
   log.info("{} doExportRemoveVolume START ...", storage.getSerialNumber());
   exportMaskOperationsHelper.removeVolume(storage, exportMask.getId(), volumes, taskCompleter);
   log.info("{} doExportRemoveVolume END ...", storage.getSerialNumber());
 }
  @POST
  @Path("/{id}/deactivate")
  @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @CheckPermission(roles = {Role.SYSTEM_ADMIN, Role.RESTRICTED_SYSTEM_ADMIN})
  public Response deleteStorageProvider(@PathParam("id") URI id) {
    // Validate the provider
    ArgValidator.checkFieldUriType(id, StorageProvider.class, "id");
    StorageProvider provider = _dbClient.queryObject(StorageProvider.class, id);
    ArgValidator.checkEntityNotNull(provider, id, isIdEmbeddedInURL(id));
    // Verify the provider can be removed without leaving "dangling" storages.
    StringSet providerStorageSystems = provider.getStorageSystems();
    if (null != providerStorageSystems && !providerStorageSystems.isEmpty()) {
      // First we need to verify that all related storage systems has at least   2 providers
      for (String system : providerStorageSystems) {
        StorageSystem storageSys = _dbClient.queryObject(StorageSystem.class, URI.create(system));
        if (storageSys != null
            && !storageSys.getInactive()
            && storageSys.getProviders() != null
            && storageSys.getProviders().size() == 1) {
          throw APIException.badRequests.cannotDeleteProviderWithManagedStorageSystems(
              storageSys.getId());
        }
      }
      // Next we can clear this provider from storage systems.
      for (String system : providerStorageSystems) {
        StorageSystem storageSys = _dbClient.queryObject(StorageSystem.class, URI.create(system));
        provider.removeStorageSystem(_dbClient, storageSys);
      }
    }

    StringSet decommissionedSystems = provider.getDecommissionedSystems();
    if (null != decommissionedSystems && !decommissionedSystems.isEmpty()) {
      for (String decommissioned : decommissionedSystems) {
        DecommissionedResource oldRes =
            _dbClient.queryObject(DecommissionedResource.class, URI.create(decommissioned));
        if (oldRes != null) {
          _dbClient.markForDeletion(oldRes);
        }
      }
    }

    // Set to inactive.
    _dbClient.markForDeletion(provider);

    auditOp(
        OperationTypeEnum.DELETE_STORAGEPROVIDER,
        true,
        null,
        provider.getId().toString(),
        provider.getLabel(),
        provider.getIPAddress(),
        provider.getPortNumber(),
        provider.getUserName(),
        provider.getInterfaceType());

    return Response.ok().build();
  }
  /*
   * (non-Javadoc)
   *
   * @see
   * com.emc.storageos.volumecontroller.BlockStorageDevice#doWaitForSynchronized
   * (java.lang.Class, com.emc.storageos.db.client.model.StorageSystem,
   * java.net.URI, com.emc.storageos.volumecontroller.TaskCompleter)
   */
  @Override
  public void doWaitForSynchronized(
      Class<? extends BlockObject> clazz,
      StorageSystem storageObj,
      URI target,
      TaskCompleter completer) {
    log.info("START waitForSynchronized for {}", target);

    try {
      Volume targetObj = dbClient.queryObject(Volume.class, target);
      // Source could be either Volume or BlockSnapshot
      BlockObject sourceObj = BlockObject.fetch(dbClient, targetObj.getAssociatedSourceVolume());

      // We split the pair which causes the data to be synchronized.
      // When the split is complete that data is synchronized.
      HDSApiClient hdsApiClient =
          hdsApiFactory.getClient(
              HDSUtils.getHDSServerManagementServerInfo(storageObj),
              storageObj.getSmisUserName(),
              storageObj.getSmisPassword());
      HDSApiProtectionManager hdsApiProtectionManager = hdsApiClient.getHdsApiProtectionManager();
      String replicationGroupObjectID = hdsApiProtectionManager.getReplicationGroupObjectId();
      ReplicationInfo replicationInfo =
          hdsApiProtectionManager.getReplicationInfoFromSystem(
                  sourceObj.getNativeId(), targetObj.getNativeId())
              .first;
      hdsApiProtectionManager.modifyShadowImagePair(
          replicationGroupObjectID,
          replicationInfo.getObjectID(),
          HDSApiProtectionManager.ShadowImageOperationType.split);

      // Update state in case we are waiting for synchronization
      // after creation of a new full copy that was not created
      // inactive.
      String state = targetObj.getReplicaState();
      if (!ReplicationState.SYNCHRONIZED.name().equals(state)) {
        targetObj.setSyncActive(true);
        targetObj.setReplicaState(ReplicationState.SYNCHRONIZED.name());
        dbClient.persistObject(targetObj);
      }

      // Queue job to wait for replication status to move to split.
      ControllerServiceImpl.enqueueJob(
          new QueueJob(
              new HDSReplicationSyncJob(
                  storageObj.getId(),
                  sourceObj.getNativeId(),
                  targetObj.getNativeId(),
                  ReplicationStatus.SPLIT,
                  completer)));
    } catch (Exception e) {
      log.error("Exception occurred while waiting for synchronization", e);
      ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
      completer.error(dbClient, serviceError);
    }
    log.info("completed doWaitForSynchronized");
  }
  @Override
  public void addInitiator(
      StorageSystem storage,
      URI exportMaskId,
      List<Initiator> initiators,
      List<URI> targets,
      TaskCompleter taskCompleter)
      throws DeviceControllerException {
    log.info("{} addInitiator START...", storage.getSerialNumber());
    log.info("Export mask id: {}", exportMaskId);
    log.info("addInitiator: initiators : {}", initiators);
    log.info("addInitiator: targets : {}", targets);

    try {

      ExportMask exportMask = dbClient.queryObject(ExportMask.class, exportMaskId);
      List<Volume> volumeList = new ArrayList<Volume>();
      // map to store target LUN id generated for each volume
      Map<URI, Integer> volumeToTargetLunMap = new HashMap<URI, Integer>();
      StringMap volumes = exportMask.getUserAddedVolumes();

      for (String vol : volumes.values()) {
        Volume volume = dbClient.queryObject(Volume.class, URI.create(vol));
        volumeList.add(volume);
      }

      // Map to store volume to initiatorTargetMap
      Map<Volume, Map<String, List<String>>> volumeToFCInitiatorTargetMap =
          new HashMap<Volume, Map<String, List<String>>>();

      attachVolumesToInitiators(
          storage,
          volumeList,
          initiators,
          volumeToTargetLunMap,
          volumeToFCInitiatorTargetMap,
          exportMask);

      // Update targets in the export mask
      if (!volumeToFCInitiatorTargetMap.isEmpty()) {
        updateTargetsInExportMask(
            storage, volumeList.get(0), volumeToFCInitiatorTargetMap, initiators, exportMask);
        dbClient.updateAndReindexObject(exportMask);
      }

      // TODO : update volumeToTargetLunMap in export mask.?
      // Do we get different LUN ID for the new initiators from the same Host.?
      taskCompleter.ready(dbClient);
    } catch (final Exception ex) {
      log.error("Problem in AddInitiators: ", ex);
      ServiceError serviceError =
          DeviceControllerErrors.cinder.operationFailed("doAddInitiators", ex.getMessage());
      taskCompleter.error(dbClient, serviceError);
    }
    log.info("{} addInitiator END...", storage.getSerialNumber());
  }
  /*
   * (non-Javadoc)
   *
   * @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExpandVolume(com.emc.storageos.db.client.model.StorageSystem,
   * com.emc.storageos.db.client.model.StoragePool, com.emc.storageos.db.client.model.Volume, java.lang.Long,
   * com.emc.storageos.volumecontroller.TaskCompleter)
   */
  @Override
  public void doExpandVolume(
      StorageSystem storageSystem,
      StoragePool storagePool,
      Volume volume,
      Long size,
      TaskCompleter taskCompleter)
      throws DeviceControllerException {

    log.info(
        String.format(
            "Expand Volume Start - Array: %s, Pool: %s, Volume: %s, New size: %d",
            storageSystem.getSerialNumber(), storagePool.getNativeGuid(), volume.getLabel(), size));
    try {
      HDSApiClient hdsApiClient =
          hdsApiFactory.getClient(
              HDSUtils.getHDSServerManagementServerInfo(storageSystem),
              storageSystem.getSmisUserName(),
              storageSystem.getSmisPassword());
      String systemObjectID = HDSUtils.getSystemObjectID(storageSystem);
      String asyncTaskMessageId = null;

      if (volume.getThinlyProvisioned()) {
        asyncTaskMessageId =
            hdsApiClient.modifyThinVolume(
                systemObjectID,
                HDSUtils.getLogicalUnitObjectId(volume.getNativeId(), storageSystem),
                size);
      }

      if (null != asyncTaskMessageId) {
        HDSJob expandVolumeJob =
            new HDSVolumeExpandJob(
                asyncTaskMessageId,
                storageSystem.getId(),
                storagePool.getId(),
                taskCompleter,
                "ExpandVolume");
        ControllerServiceImpl.enqueueJob(new QueueJob(expandVolumeJob));
      }
    } catch (final InternalException e) {
      log.error("Problem in doExpandVolume: ", e);
      taskCompleter.error(dbClient, e);
    } catch (final Exception e) {
      log.error("Problem in doExpandVolume: ", e);
      ServiceError serviceError =
          DeviceControllerErrors.hds.methodFailed("doExpandVolume", e.getMessage());
      taskCompleter.error(dbClient, serviceError);
    }
    log.info(
        String.format(
            "Expand Volume End - Array: %s, Pool: %s, Volume: %s",
            storageSystem.getSerialNumber(), storagePool.getNativeGuid(), volume.getLabel()));
  }
  /**
   * Re-validate the ExportMask
   *
   * <p>This is required to be done as the ExportMask gets updated by reading the cinder export
   * volume response.
   *
   * @param varrayURI
   * @param initiatorPortMap
   * @param mask
   * @param invalidMasks
   * @param directorToInitiatorIds
   * @param idToInitiatorMap
   * @param dbClient
   * @param portWwnToClusterMap
   */
  public void updateZoningMapAndvalidateExportMask(
      URI varrayURI,
      Map<URI, List<StoragePort>> initiatorPortMap,
      URI exportMaskURI,
      Map<String, Set<String>> directorToInitiatorIds,
      Map<String, Initiator> idToInitiatorMap,
      Map<String, String> portWwnToClusterMap,
      StorageSystem vplex,
      StorageSystem array,
      String clusterId,
      String stepId) {

    try {
      WorkflowStepCompleter.stepExecuting(stepId);
      // Export Mask is updated, read it from DB
      ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI);

      // First step would be to update the zoning map based on the connectivity
      updateZoningMap(initiatorPortMap, directorToInitiatorIds, exportMask);

      boolean passed =
          VPlexBackEndOrchestratorUtil.validateExportMask(
              varrayURI,
              initiatorPortMap,
              exportMask,
              null,
              directorToInitiatorIds,
              idToInitiatorMap,
              _dbClient,
              portWwnToClusterMap);

      if (!passed) {
        // Mark this mask as inactive, so that we dont pick it in the next iteration
        exportMask.setInactive(Boolean.TRUE);
        _dbClient.persistObject(exportMask);

        _log.error("Export Mask is not suitable for VPLEX to backend storage system");
        WorkflowStepCompleter.stepFailed(
            stepId,
            VPlexApiException.exceptions.couldNotFindValidArrayExportMask(
                vplex.getNativeGuid(), array.getNativeGuid(), clusterId));
        throw VPlexApiException.exceptions.couldNotFindValidArrayExportMask(
            vplex.getNativeGuid(), array.getNativeGuid(), clusterId);
      }

      WorkflowStepCompleter.stepSucceded(stepId);

    } catch (Exception ex) {
      _log.error("Failed to validate export mask for cinder: ", ex);
      VPlexApiException vplexex =
          DeviceControllerExceptions.vplex.failedToValidateExportMask(exportMaskURI.toString(), ex);
      WorkflowStepCompleter.stepFailed(stepId, vplexex);
    }
  }
  /**
   * This method cleans up UnManaged Volumes in DB, which had been deleted manually from the Array
   * 1. Get All UnManagedVolumes from DB 2. Store URIs of unmanaged volumes returned from the
   * Provider in unManagedVolumesBookKeepingList. 3. If unmanaged volume is found only in DB, but
   * not in unManagedVolumesBookKeepingList, then set unmanaged volume to inactive.
   *
   * <p>DB | Provider
   *
   * <p>x,y,z | y,z.a [a --> new entry has been added but indexes didn't get added yet into DB]
   *
   * <p>x--> will be set to inactive
   *
   * @param storageSystem
   * @param discoveredUnManagedVolumes
   * @param dbClient
   * @param partitionManager
   */
  public static void markInActiveUnManagedVolumes(
      StorageSystem storageSystem,
      Set<URI> discoveredUnManagedVolumes,
      DbClient dbClient,
      PartitionManager partitionManager) {

    _log.info(
        " -- Processing {} discovered UnManaged Volumes Objects from -- {}",
        discoveredUnManagedVolumes.size(),
        storageSystem.getLabel());
    if (discoveredUnManagedVolumes.isEmpty()) {
      return;
    }
    // Get all available existing unmanaged Volume URIs for this array from DB
    URIQueryResultList allAvailableUnManagedVolumesInDB = new URIQueryResultList();
    dbClient.queryByConstraint(
        ContainmentConstraint.Factory.getStorageDeviceUnManagedVolumeConstraint(
            storageSystem.getId()),
        allAvailableUnManagedVolumesInDB);

    Set<URI> unManagedVolumesInDBSet = new HashSet<URI>();
    Iterator<URI> allAvailableUnManagedVolumesItr = allAvailableUnManagedVolumesInDB.iterator();
    while (allAvailableUnManagedVolumesItr.hasNext()) {
      unManagedVolumesInDBSet.add(allAvailableUnManagedVolumesItr.next());
    }

    SetView<URI> onlyAvailableinDB =
        Sets.difference(unManagedVolumesInDBSet, discoveredUnManagedVolumes);

    _log.info("Diff :" + Joiner.on("\t").join(onlyAvailableinDB));
    if (!onlyAvailableinDB.isEmpty()) {
      List<UnManagedVolume> unManagedVolumeTobeDeleted = new ArrayList<UnManagedVolume>();
      Iterator<UnManagedVolume> unManagedVolumes =
          dbClient.queryIterativeObjects(
              UnManagedVolume.class, new ArrayList<URI>(onlyAvailableinDB));

      while (unManagedVolumes.hasNext()) {
        UnManagedVolume volume = unManagedVolumes.next();
        if (null == volume || volume.getInactive()) {
          continue;
        }

        _log.info("Setting unManagedVolume {} inactive", volume.getId());
        volume.setStoragePoolUri(NullColumnValueGetter.getNullURI());
        volume.setStorageSystemUri(NullColumnValueGetter.getNullURI());
        volume.setInactive(true);
        unManagedVolumeTobeDeleted.add(volume);
      }
      if (!unManagedVolumeTobeDeleted.isEmpty()) {
        partitionManager.updateAndReIndexInBatches(
            unManagedVolumeTobeDeleted, 1000, dbClient, UNMANAGED_VOLUME);
      }
    }
  }
 /**
  * Create block volumes and associated local array consistency group.
  *
  * @throws Exception
  */
 private void prepareLocalArrayConsistencyGroupData() throws Exception {
   // Create a non-VPlex storage system
   StorageSystem storageSystem = createStorageSystem(false);
   // Create the block volumes that will be part of the cg
   List<Volume> blockVolumes = createBlockVolumes("blockVolume", 3, storageSystem.getId());
   // Create the consistency group and add the block volumes
   BlockConsistencyGroup localArrayCg =
       createBlockConsistencyGroup(
           "localArrayCg", storageSystem.getId(), Types.LOCAL.name(), true);
   localArrayConsistencyGroupURI = localArrayCg.getId();
   addVolumesToBlockConsistencyGroup(localArrayCg.getId(), blockVolumes);
 }
  /**
   * Detaches volumes from initiators.
   *
   * @param storage the storage
   * @param volumes the volumes
   * @param initiators the initiators
   * @throws Exception the exception
   */
  private void detachVolumesFromInitiators(
      StorageSystem storage, List<Volume> volumes, List<Initiator> initiators) throws Exception {
    CinderEndPointInfo ep = CinderUtils.getCinderEndPoint(storage.getActiveProviderURI(), dbClient);
    log.debug("Getting the cinder APi for the provider with id {}", storage.getActiveProviderURI());
    CinderApi cinderApi = cinderApiFactory.getApi(storage.getActiveProviderURI(), ep);

    List<Initiator> iSCSIInitiators = new ArrayList<Initiator>();
    List<Initiator> fcInitiators = new ArrayList<Initiator>();
    splitInitiatorsByProtocol(initiators, iSCSIInitiators, fcInitiators);
    String host = getHostNameFromInitiators(initiators);

    Map<String, String[]> mapSettingVsValues = getFCInitiatorsArray(fcInitiators);
    String[] fcInitiatorsWwpns = mapSettingVsValues.get(WWPNS);
    String[] fcInitiatorsWwnns = mapSettingVsValues.get(WWNNS);

    for (Volume volume : volumes) {
      // cinder generated volume ID
      String volumeId = volume.getNativeId();

      // for iSCSI
      for (Initiator initiator : iSCSIInitiators) {
        String initiatorPort = initiator.getInitiatorPort();
        log.debug(
            String.format(
                "Detaching volume %s ( %s ) from initiator %s on Openstack cinder node",
                volumeId, volume.getId(), initiatorPort));
        cinderApi.detachVolume(volumeId, initiatorPort, null, null, host);

        // TODO : Do not use Job to poll status till we figure out how
        // to get detach status.
        /*
         * CinderJob detachJob = new CinderDetachVolumeJob(volumeId,
         * volume.getLabel(), storage.getId(),
         * CinderConstants.ComponentType.volume.name(), ep,
         * taskCompleter); ControllerServiceImpl.enqueueJob(new
         * QueueJob(detachJob));
         */
      }

      // for FC
      if (fcInitiatorsWwpns.length > 0) {
        log.debug(
            String.format(
                "Detaching volume %s ( %s ) from initiator %s on Openstack cinder node",
                volumeId, volume.getId(), fcInitiatorsWwpns));
        cinderApi.detachVolume(volumeId, null, fcInitiatorsWwpns, fcInitiatorsWwnns, host);
      }

      // If ITLs are added, remove them
      removeITLsFromVolume(volume);
    }
  }
  /**
   * Invokes the FC or iSCSI ports operation based on the type of the export/attach operation
   *
   * @param attachResponse
   * @throws IOException
   */
  public void invoke(VolumeAttachResponse attachResponse) {
    logger.info(
        "Cinder Storage Port Invoke Operation Started for" + " the storage system : {}",
        storageSystem.getId());

    synchronized (this) {
      try {

        // Get the transport type
        String protocolType = attachResponse.connection_info.driver_volume_type;

        Map<String, List<String>> initiatorTargetMap = null;
        if (CinderConstants.ATTACH_RESPONSE_FC_TYPE.equalsIgnoreCase(protocolType)) {
          initiatorTargetMap = attachResponse.connection_info.data.initiator_target_map;
          if (null != initiatorTargetMap && !initiatorTargetMap.isEmpty()) {
            logger.debug("FC Initiator and Target mappings : {} ", initiatorTargetMap.toString());
            performFCOperation(initiatorTargetMap);
          }
        }

        String iqn = null;
        if (CinderConstants.ATTACH_RESPONSE_ISCSI_TYPE.equalsIgnoreCase(protocolType)) {
          iqn = attachResponse.connection_info.data.target_iqn;
          logger.debug("iSCSI target IQN is :{}", iqn);
          performISCSIOperation(iqn);
        }

        // Update the port to network associations for modified ports and newly created ports.
        if (!modifiedStoragePortsList.isEmpty()) {
          StoragePortAssociationHelper.updatePortAssociations(modifiedStoragePortsList, dbClient);
        }

        if (!newStoragePortsList.isEmpty()) {
          StoragePortAssociationHelper.updatePortAssociations(newStoragePortsList, dbClient);
        }
      } catch (Exception e) {
        logger.error(
            "There is an error while creating/modifying ports after export/attach,"
                + " Reason:"
                + e.getMessage(),
            e);
      } finally {
        // clear modified and new ports list
        modifiedStoragePortsList.clear();
        newStoragePortsList.clear();
      }
    }

    logger.info(
        "Cinder Storage Port Invoke Operation completed for" + " the storage system :{} ",
        storageSystem.getId());
  }
 /*
  * (non-Javadoc)
  *
  * @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportAddInitiator(com.emc.storageos.db.client.model.StorageSystem,
  * com.emc.storageos.db.client.model.ExportMask, com.emc.storageos.db.client.model.Initiator, java.util.List,
  * com.emc.storageos.volumecontroller.TaskCompleter)
  */
 @Override
 public void doExportAddInitiator(
     StorageSystem storage,
     ExportMask exportMask,
     Initiator initiator,
     List<URI> targets,
     TaskCompleter taskCompleter)
     throws DeviceControllerException {
   log.info("{} doExportAddInitiator START ...", storage.getSerialNumber());
   exportMaskOperationsHelper.addInitiator(
       storage, exportMask.getId(), Arrays.asList(initiator), targets, taskCompleter);
   log.info("{} doExportAddInitiator END ...", storage.getSerialNumber());
 }
 /*
  * (non-Javadoc)
  *
  * @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportRemoveInitiators(com.emc.storageos.db.client.model.StorageSystem,
  * com.emc.storageos.db.client.model.ExportMask, java.util.List, java.util.List, com.emc.storageos.volumecontroller.TaskCompleter)
  */
 @Override
 public void doExportRemoveInitiators(
     StorageSystem storage,
     ExportMask exportMask,
     List<Initiator> initiators,
     List<URI> targets,
     TaskCompleter taskCompleter)
     throws DeviceControllerException {
   log.info("{} doExportRemoveInitiator START ...", storage.getSerialNumber());
   exportMaskOperationsHelper.removeInitiator(
       storage, exportMask.getId(), initiators, targets, taskCompleter);
   log.info("{} doExportRemoveInitiator END ...", storage.getSerialNumber());
 }
  /**
   * Gets the instance from the map if already created, otherwise creates one
   *
   * @param system
   * @param response
   * @return
   */
  public static CinderStoragePortOperations getInstance(StorageSystem system, DbClient dbc) {
    CinderStoragePortOperations instance = instancesMap.get(system.getId());
    if (null == instance) {
      synchronized (instancesMap) {
        if (null == instance) {
          instance = new CinderStoragePortOperations(system, dbc);
          instancesMap.put(system.getId(), instance);
        }
      }
    }

    return instance;
  }
  /**
   * Include only Unified,Virtual [Thin] and Device Storage Pools (Thick Pool)
   *
   * @param poolInstance
   * @return String [] array of pool class name (as a first element) and supported volume types (as
   *     a second element)
   */
  private String[] determinePoolClassNameAndSupportedVolumeTypes(
      CIMInstance poolInstance, StorageSystem system) {

    if (StoragePool.PoolClassNames.Clar_DeviceStoragePool.toString()
        .equalsIgnoreCase(poolInstance.getClassName())) {
      return new String[] {
        StoragePool.PoolClassNames.Clar_DeviceStoragePool.toString(),
        StoragePool.SupportedResourceTypes.THICK_ONLY.toString()
      };
    } else if (StoragePool.PoolClassNames.Clar_UnifiedStoragePool.toString()
        .equalsIgnoreCase(poolInstance.getClassName())) {
      return new String[] {
        StoragePool.PoolClassNames.Clar_UnifiedStoragePool.toString(),
        StoragePool.SupportedResourceTypes.THIN_AND_THICK.toString()
      };
    }

    if (!system.checkIfVmax3()) {
      if (StoragePool.PoolClassNames.Symm_DeviceStoragePool.toString()
              .equalsIgnoreCase(poolInstance.getClassName())
          && !SupportedProvisioningTypes.THIN
              .toString()
              .equalsIgnoreCase(system.getSupportedProvisioningType())) {
        return new String[] {
          StoragePool.PoolClassNames.Symm_DeviceStoragePool.toString(),
          StoragePool.SupportedResourceTypes.THICK_ONLY.toString()
        };
      } else if (StoragePool.PoolClassNames.Symm_VirtualProvisioningPool.toString()
              .equalsIgnoreCase(poolInstance.getClassName())
          && !SupportedProvisioningTypes.THICK
              .toString()
              .equalsIgnoreCase(system.getSupportedProvisioningType())) {
        return new String[] {
          StoragePool.PoolClassNames.Symm_VirtualProvisioningPool.toString(),
          StoragePool.SupportedResourceTypes.THIN_ONLY.toString()
        };
      }
    } else {
      // VMAX3 has StorageResourcePools (SRP). These are composed of ThinPools, which we can
      // discover, but would not have write access to. So, we will only discovery SRP pools
      // and skip over other pool discoveries.
      if (StoragePool.PoolClassNames.Symm_SRPStoragePool.toString()
          .equalsIgnoreCase(poolInstance.getClassName())) {
        return new String[] {
          StoragePool.PoolClassNames.Symm_SRPStoragePool.toString(),
          StoragePool.SupportedResourceTypes.THIN_ONLY.toString()
        };
      }
    }
    return null;
  }
  /**
   * Checks the UnManaged Volume's policy with vPool's policy.
   *
   * @param vPool the vPool
   * @param autoTierPolicyId the auto tier policy id on unmanaged volume
   * @param system the system
   * @return true, if matching, false otherwise
   */
  public static boolean checkVPoolValidForUnManagedVolumeAutoTieringPolicy(
      VirtualPool vPool, String autoTierPolicyId, StorageSystem system) {

    _log.debug("Policy Id: {}, vPool: {}", autoTierPolicyId, vPool);
    boolean policyMatching = false;
    String policyIdfromVPool = vPool.getAutoTierPolicyName();
    if (autoTierPolicyId != null) {
      if (policyIdfromVPool != null) {
        if (vPool.getUniquePolicyNames()
            || DiscoveredDataObject.Type.vnxblock.name().equalsIgnoreCase(system.getSystemType())) {
          // Unique Policy names field will not be set for VNX. vPool will have policy name, not the
          // policy's nativeGuid
          policyIdfromVPool =
              NativeGUIDGenerator.generateAutoTierPolicyNativeGuid(
                  system.getNativeGuid(),
                  policyIdfromVPool,
                  NativeGUIDGenerator.getTieringPolicyKeyForSystem(system));
          _log.debug("Policy Id generated: {}", policyIdfromVPool);
        }
        if (autoTierPolicyId.equalsIgnoreCase(policyIdfromVPool)) {
          policyMatching = true;
        }
      }
    } else if ((policyIdfromVPool == null) || (policyIdfromVPool.equalsIgnoreCase("none"))) {
      // if policy is not set in both unmanaged volume and vPool. Note
      // that the value in the vpool could be set to "none".
      policyMatching = true;
    }

    // Default policy for VNX - match volume with default policy to vPool with no policy as well
    if (!policyMatching
        && DiscoveredDataObject.Type.vnxblock.name().equalsIgnoreCase(system.getSystemType())) {
      if (autoTierPolicyId != null
          && autoTierPolicyId.contains(VnxFastPolicy.DEFAULT_START_HIGH_THEN_AUTOTIER.name())
          && policyIdfromVPool == null) {
        policyMatching = true;
      }
    }

    // Default policy for HDS - match volume with default policy to vPool with no policy as well
    if (!policyMatching
        && DiscoveredDataObject.Type.hds.name().equalsIgnoreCase(system.getSystemType())) {
      if (autoTierPolicyId != null
          && autoTierPolicyId.contains(HitachiTieringPolicy.All.name())
          && policyIdfromVPool == null) {
        policyMatching = true;
      }
    }

    return policyMatching;
  }
  @Override
  public void deleteExportMask(
      StorageSystem storage,
      URI exportMaskId,
      List<URI> volumeURIList,
      List<URI> targetURIList,
      List<Initiator> initiatorList,
      TaskCompleter taskCompleter)
      throws DeviceControllerException {
    log.info("{} deleteExportMask START...", storage.getSerialNumber());
    log.info("Export mask id: {}", exportMaskId);

    try {
      // There is no masking concept on Cinder to delete the export mask.
      // But before marking the task completer as ready,
      // detach the volumes from the initiators that are there in the export mask.
      ExportMask exportMask = dbClient.queryObject(ExportMask.class, exportMaskId);
      List<Volume> volumeList = new ArrayList<Volume>();
      StringMap volumes = exportMask.getUserAddedVolumes();
      StringMap initiators = exportMask.getUserAddedInitiators();
      if (volumes != null) {
        for (String vol : volumes.values()) {
          URI volumeURI = URI.create(vol);
          volumeURIList.add(volumeURI);
          Volume volume = dbClient.queryObject(Volume.class, volumeURI);
          volumeList.add(volume);
        }
      }
      if (initiators != null) {
        for (String ini : initiators.values()) {
          Initiator initiatorObj = dbClient.queryObject(Initiator.class, URI.create(ini));
          initiatorList.add(initiatorObj);
        }
      }

      log.info("deleteExportMask: volumes:  {}", volumeURIList);
      log.info("deleteExportMask: assignments: {}", targetURIList);
      log.info("deleteExportMask: initiators: {}", initiatorList);

      detachVolumesFromInitiators(storage, volumeList, initiatorList);

      taskCompleter.ready(dbClient);
    } catch (final Exception ex) {
      log.error("Problem in DetachVolumes: ", ex);
      ServiceError serviceError =
          DeviceControllerErrors.cinder.operationFailed("doDetachVolumes", ex.getMessage());
      taskCompleter.error(dbClient, serviceError);
    }
    log.info("{} deleteExportMask END...", storage.getSerialNumber());
  }
 /*
  * (non-Javadoc)
  *
  * @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportGroupDelete(com.emc.storageos.db.client.model.StorageSystem,
  * com.emc.storageos.db.client.model.ExportMask, com.emc.storageos.volumecontroller.TaskCompleter)
  */
 @Override
 public void doExportGroupDelete(
     StorageSystem storage, ExportMask exportMask, TaskCompleter taskCompleter)
     throws DeviceControllerException {
   log.info("{} doExportGroupDelete START ...", storage.getSerialNumber());
   exportMaskOperationsHelper.deleteExportMask(
       storage,
       exportMask.getId(),
       new ArrayList<URI>(),
       new ArrayList<URI>(),
       new ArrayList<Initiator>(),
       taskCompleter);
   log.info("{} doExportGroupDelete END ...", storage.getSerialNumber());
 }
  /**
   * Determines if the storage system for the passed BlockSnapshot instance supports snapshot
   * sessions.
   *
   * @param snapshot A reference to the snapshot.
   * @return true if the system for the passed snapshot supports snapshot sessions, false otherwise.
   */
  private boolean isSnapshotSessionSupported(BlockSnapshot snapshot) {
    boolean isSupported = false;
    URI systemURI = snapshot.getStorageController();
    StorageSystem system = dbClient.queryObject(StorageSystem.class, systemURI);
    if ((system != null) && (system.checkIfVmax3())) {
      s_logger.info(
          "BlockSnapshotSession supported for snapshot {}:{}",
          snapshot.getId(),
          snapshot.getLabel());
      isSupported = true;
    }

    return isSupported;
  }
  /**
   * Prepare the VPlex volumes and associated consistency group data.
   *
   * @throws Exception
   */
  private void prepareVPlexConsistencyGroupData() throws Exception {
    // Create a VPlex storage system
    StorageSystem storageSystem = createStorageSystem(true);

    // Create the VPlex volumes and add them to the VPlex consistency group
    List<Volume> vplexVolumes = createVPlexVolumes("vplexVolume", 3, storageSystem.getId());
    // Prior to 2.2, VPlex only CGs (nothing to do with RP) did not set the CG type of VPLEX. So we
    // pass false here.
    BlockConsistencyGroup vplexCg =
        createBlockConsistencyGroup("vplexCg", storageSystem.getId(), Types.VPLEX.name(), false);
    // Save a references to the cg for migration verification
    vplexConsistencyGroupURI = vplexCg.getId();
    // Add the VPlex volumes to the VPlex consistency group
    addVolumesToBlockConsistencyGroup(vplexCg.getId(), vplexVolumes);
  }
  @Override
  public void doExportAddVolumes(
      StorageSystem storage,
      ExportMask exportMask,
      Map<URI, Integer> volumes,
      TaskCompleter taskCompleter)
      throws DeviceControllerException {
    log.info("{} doExportAddVolume START ...", storage.getSerialNumber());
    VolumeURIHLU[] volumeLunArray =
        ControllerUtils.getVolumeURIHLUArray(storage.getSystemType(), volumes, dbClient);

    exportMaskOperationsHelper.addVolume(
        storage, exportMask.getId(), volumeLunArray, taskCompleter);
    log.info("{} doExportAddVolume END ...", storage.getSerialNumber());
  }
 @Override
 public void fractureSingleVolumeMirror(
     StorageSystem storage, URI mirror, Boolean sync, TaskCompleter taskCompleter)
     throws DeviceControllerException {
   _log.info("fractureSingleVolumeMirror operation START");
   CloseableIterator<CIMObjectPath> storageSyncRefs = null;
   try {
     BlockMirror mirrorObj = _dbClient.queryObject(BlockMirror.class, mirror);
     CIMObjectPath mirrorPath = _cimPath.getBlockObjectPath(storage, mirrorObj);
     // Get reference to the CIM_StorageSynchronized instance
     storageSyncRefs =
         _helper.getReference(storage, mirrorPath, SmisConstants.CIM_STORAGE_SYNCHRONIZED, null);
     boolean isVmax3 = storage.checkIfVmax3();
     while (storageSyncRefs.hasNext()) {
       CIMObjectPath storageSync = storageSyncRefs.next();
       CIMArgument[] inArgs =
           isVmax3
               ? _helper.getFractureMirrorInputArgumentsWithCopyState(storageSync, sync)
               : _helper.getFractureMirrorInputArguments(storageSync, sync);
       CIMArgument[] outArgs = new CIMArgument[5];
       // Invoke method to fracture the synchronization
       _helper.callModifyReplica(storage, inArgs, outArgs);
       taskCompleter.ready(_dbClient);
     }
   } catch (Exception e) {
     _log.info("Problem making SMI-S call", e);
     ServiceError serviceError = DeviceControllerException.errors.jobFailed(e);
     taskCompleter.error(_dbClient, serviceError);
   } finally {
     if (storageSyncRefs != null) {
       storageSyncRefs.close();
     }
   }
 }
  @Override
  public void deleteSingleVolumeSnapshot(
      StorageSystem storage, URI snapshot, TaskCompleter taskCompleter)
      throws DeviceControllerException {

    try {
      BlockSnapshot snap = _dbClient.queryObject(BlockSnapshot.class, snapshot);
      VNXeApiClient apiClient = getVnxeClient(storage);
      VNXeLunSnap lunSnap = apiClient.getLunSnapshot(snap.getNativeId());
      if (lunSnap != null) {
        VNXeCommandJob job = apiClient.deleteLunSnap(lunSnap.getId());
        if (job != null) {
          ControllerServiceImpl.enqueueJob(
              new QueueJob(
                  new VNXeBlockDeleteSnapshotJob(job.getId(), storage.getId(), taskCompleter)));
        }
      } else {
        // Perhaps, it's already been deleted or was deleted on the array.
        // In that case, we'll just say all is well, so that this operation
        // is idempotent.
        snap.setInactive(true);
        snap.setIsSyncActive(false);
        _dbClient.updateObject(snap);
        taskCompleter.ready(_dbClient);
      }
    } catch (VNXeException e) {
      _log.error("Delete volume snapshot got the exception", e);
      taskCompleter.error(_dbClient, e);
    } catch (Exception ex) {
      _log.error("Delete volume snapshot got the exception", ex);
      ServiceError error = DeviceControllerErrors.vnxe.jobFailed("DeleteSnapshot", ex.getMessage());
      taskCompleter.error(_dbClient, error);
    }
  }
 /*
  * (non-Javadoc)
  *
  * @see com.emc.storageos.volumecontroller.BlockStorageDevice#doExportGroupCreate(com.emc.storageos.db.client.model.StorageSystem,
  * com.emc.storageos.db.client.model.ExportMask, java.util.Map, java.util.List, java.util.List,
  * com.emc.storageos.volumecontroller.TaskCompleter)
  */
 @Override
 public void doExportGroupCreate(
     StorageSystem storage,
     ExportMask exportMask,
     Map<URI, Integer> volumeMap,
     List<Initiator> initiators,
     List<URI> targets,
     TaskCompleter taskCompleter)
     throws DeviceControllerException {
   log.info("{} doExportGroupCreate START ...", storage.getSerialNumber());
   VolumeURIHLU[] volumeLunArray =
       ControllerUtils.getVolumeURIHLUArray(storage.getSystemType(), volumeMap, dbClient);
   exportMaskOperationsHelper.createExportMask(
       storage, exportMask.getId(), volumeLunArray, targets, initiators, taskCompleter);
   log.info("{} doExportGroupCreate END ...", storage.getSerialNumber());
 }
  /** {@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();
              }
            }
          }
        }
      }
    }
  }