/** {@inheritDoc} */
  @Override
  public void validateSnapshotCreateRequest(Volume requestedVolume, List<Volume> volumesToSnap) {
    // For VMAX3 you cannot have active snap and full copy sessions,
    // so verify there are no active full copies for the volume.
    for (Volume volumeToSnap : volumesToSnap) {
      // Check if the volume to snap is an active full copy.
      if ((BlockFullCopyUtils.isVolumeFullCopy(volumeToSnap, _dbClient))
          && (!BlockFullCopyUtils.isFullCopyDetached(volumeToSnap, _dbClient))
          && (!BlockFullCopyUtils.isFullCopyInactive(volumeToSnap, _dbClient))) {
        throw APIException.badRequests.noSnapshotsForVMAX3VolumeWithActiveFullCopy();
      }

      // Now check if the volume to be snapped is a full copy source
      // that has active full copies.
      StringSet fullCopyIds = volumeToSnap.getFullCopies();
      if ((fullCopyIds != null) && (!fullCopyIds.isEmpty())) {
        Iterator<String> fullCopyIdsIter = fullCopyIds.iterator();
        while (fullCopyIdsIter.hasNext()) {
          URI fullCopyURI = URI.create(fullCopyIdsIter.next());
          Volume fullCopyVolume = _dbClient.queryObject(Volume.class, fullCopyURI);
          if ((fullCopyVolume != null)
              && (!fullCopyVolume.getInactive())
              && (!BlockFullCopyUtils.isFullCopyDetached(fullCopyVolume, _dbClient))
              && (!BlockFullCopyUtils.isFullCopyInactive(fullCopyVolume, _dbClient))) {
            throw APIException.badRequests.noSnapshotsForVMAX3VolumeWithActiveFullCopy();
          }
        }
      }
    }
  }
  private void handleVplexVolumeErrors(DbClient dbClient) {

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

    for (VolumeDescriptor volumeDescriptor :
        VolumeDescriptor.getDescriptors(
            _volumeDescriptors, VolumeDescriptor.Type.VPLEX_VIRT_VOLUME)) {

      Volume volume = dbClient.queryObject(Volume.class, volumeDescriptor.getVolumeURI());

      _log.info("Looking at VPLEX virtual volume {}", volume.getLabel());

      boolean deactivateVirtualVolume = true;
      List<String> livingVolumeNames = new ArrayList<String>();

      _log.info("Its associated volumes are: " + volume.getAssociatedVolumes());
      for (String associatedVolumeUri : volume.getAssociatedVolumes()) {
        Volume associatedVolume =
            dbClient.queryObject(Volume.class, URI.create(associatedVolumeUri));
        if (associatedVolume != null && !associatedVolume.getInactive()) {
          _log.warn(
              "VPLEX virtual volume {} has active associated volume {}",
              volume.getLabel(),
              associatedVolume.getLabel());
          livingVolumeNames.add(associatedVolume.getLabel());
          deactivateVirtualVolume = false;
        }
      }

      if (deactivateVirtualVolume) {
        _log.info(
            "VPLEX virtual volume {} has no active associated volumes, marking for deletion",
            volume.getLabel());
        dbClient.markForDeletion(volume);
      } else {
        String message =
            "VPLEX virtual volume "
                + volume.getLabel()
                + " will not be marked for deletion "
                + "because it still has active associated volumes (";
        message += Joiner.on(",").join(livingVolumeNames) + ")";
        finalMessages.add(message);
        _log.warn(message);
      }
    }

    if (!finalMessages.isEmpty()) {
      String finalMessage = Joiner.on("; ").join(finalMessages) + ".";
      _log.error(finalMessage);
    }
  }
  /**
   * Verify the RP consistency group and its volumes have been properly migrated.
   *
   * @throws Exception
   */
  private void verifyRpConsistencyGroupWithDuplicatesMigration() throws Exception {
    log.info("Verifying duplicate protection sets were cleaned up.");

    List<URI> protSetIds = _dbClient.queryByType(ProtectionSet.class, true);

    List<String> protSetNames = new ArrayList<String>();
    for (URI id : protSetIds) {
      ProtectionSet protSet = _dbClient.queryObject(ProtectionSet.class, id);
      if (protSet == null || protSet.getInactive()) {
        continue;
      }
      if (protSetNames.contains(protSet.getLabel())) {
        // fail duplicate protection set
        Assert.fail("Duplicate protection sets exist after migration " + protSet.getLabel());
      } else {
        protSetNames.add(protSet.getLabel());

        // verify that there are no duplicates or stale volumes on the volume list
        List<String> volumeIds = new ArrayList<String>();
        for (String volId : protSet.getVolumes()) {
          Volume vol = _dbClient.queryObject(Volume.class, URI.create(volId));
          if (vol == null || vol.getInactive()) {
            // fail stale volume on protection set
            Assert.fail(
                String.format(
                    "Stale volume %s exists on protection set %s exist after migration",
                    volId, protSet.getLabel()));
          } else if (volumeIds.contains(volId)) {
            // fail duplicate volume on protection set
            Assert.fail(
                String.format(
                    "Duplicate volume %s exists on protection set %s exist after migration",
                    volId, protSet.getLabel()));
          } else if (vol.getProtectionSet() == null
              || vol.getProtectionSet().getURI() == null
              || !vol.getProtectionSet().getURI().equals(protSet.getId())) {
            // fail volume does not point back to protection set
            Assert.fail(
                String.format(
                    "Volume %s does not point back to protection set %s exist after migration",
                    volId, protSet.getLabel()));
          } else {
            volumeIds.add(volId);
          }
        }
      }
    }

    // make sure there are no dangling protection sets on volumes
    List<URI> volumes = _dbClient.queryByType(Volume.class, true);
    for (URI id : volumes) {
      Volume vol = _dbClient.queryObject(Volume.class, id);
      if (vol.getProtectionSet() != null && vol.getProtectionSet().getURI() != null) {
        ProtectionSet protSet =
            _dbClient.queryObject(ProtectionSet.class, vol.getProtectionSet().getURI());
        if (protSet == null || protSet.getInactive()) {
          // fail dangling protection set
          Assert.fail(
              String.format(
                  "Stale protection set %s on volume %s exists after migration",
                  vol.getProtectionSet().getURI(), id));
        }
      }
    }

    // make sure there are no dangling protection sets on snapshots
    List<URI> snapshots = _dbClient.queryByType(BlockSnapshot.class, true);
    for (URI id : snapshots) {
      BlockSnapshot snapshot = _dbClient.queryObject(BlockSnapshot.class, id);
      if (snapshot.getProtectionSet() != null) {
        ProtectionSet protSet =
            _dbClient.queryObject(ProtectionSet.class, snapshot.getProtectionSet());
        if (protSet == null || protSet.getInactive()) {
          // fail dangling protection set
          Assert.fail(
              String.format(
                  "Stale protection set %s on snapshot %s exists after migration",
                  snapshot.getProtectionSet(), id));
        }
      }
    }
  }
  private void processVolumes(
      CloseableIterator<CIMInstance> volumeInstances, Map<String, Object> keyMap)
      throws IOException {

    List<CIMObjectPath> metaVolumes = new ArrayList<>();
    while (volumeInstances.hasNext()) {
      CIMInstance volumeViewInstance = volumeInstances.next();
      String nativeGuid = getVolumeViewNativeGuid(volumeViewInstance.getObjectPath(), keyMap);

      if (isSnapShot(volumeViewInstance)) {
        BlockSnapshot snapShot = checkSnapShotExistsInDB(nativeGuid, _dbClient);
        if (null == snapShot || snapShot.getInactive()) {
          _logger.debug("Skipping Snapshot, as its not being managed in Bourne");
          continue;
        }
        updateBlockSnapShot(volumeViewInstance, snapShot, keyMap);
        if (_updateSnapShots.size() > BATCH_SIZE) {
          _partitionManager.updateInBatches(
              _updateSnapShots, getPartitionSize(keyMap), _dbClient, BLOCK_SNAPSHOT);
          _updateSnapShots.clear();
        }
      } else if (isMirror(volumeViewInstance)) {
        BlockMirror mirror = checkBlockMirrorExistsInDB(nativeGuid, _dbClient);
        if (null == mirror || mirror.getInactive()) {
          _logger.debug("Skipping Mirror, as its not being managed in Bourne");
          continue;
        }
        updateBlockMirror(volumeViewInstance, mirror, keyMap);
        if (_updateMirrors.size() > BATCH_SIZE) {
          _partitionManager.updateInBatches(
              _updateMirrors, getPartitionSize(keyMap), _dbClient, BLOCK_MIRROR);
          _updateMirrors.clear();
        }
      } else {
        Volume storageVolume = checkStorageVolumeExistsInDB(nativeGuid, _dbClient);
        if (null == storageVolume || storageVolume.getInactive()) {
          continue;
        }
        _logger.debug("Volume managed by Bourne :" + storageVolume.getNativeGuid());
        updateStorageVolume(volumeViewInstance, storageVolume, keyMap);

        // Check if this is a meta volume and if we need to set missing meta volume related
        // properties.
        // This is applicable for meta volumes discovered as unmanaged volumes and ingested prior to
        // vipr controller 2.2 .
        if (storageVolume.getIsComposite()
            && (storageVolume.getCompositionType() == null
                || storageVolume.getCompositionType().isEmpty())) {
          // meta volume is missing meta related data. Need to discover this data and set in the
          // volume.
          metaVolumes.add(volumeViewInstance.getObjectPath());
          _logger.info(
              "Found meta volume in vipr with missing data: {}, name: {}",
              volumeViewInstance.getObjectPath(),
              storageVolume.getLabel());
        }
      }
      if (_updateVolumes.size() > BATCH_SIZE) {
        _partitionManager.updateInBatches(
            _updateVolumes, getPartitionSize(keyMap), _dbClient, VOLUME);
        _updateVolumes.clear();
      }
    }

    // Add meta volumes to the keyMap
    try {
      if (metaVolumes != null && !metaVolumes.isEmpty()) {
        _metaVolumeViewPaths.addAll(metaVolumes);
        _logger.info("Added  {} meta volumes.", metaVolumes.size());
      }
    } catch (Exception ex) {
      _logger.error("Processing meta volumes.", ex);
    }
  }