/**
   * Filter vpools from the qualified list. rpSource true: Filter out anything other than RP source
   * vpools rpSource false: Filter out RP and SRDF source vpools
   *
   * @param dbClient dbclient
   * @param unManagedVolume unmanaged volume
   * @param personality SOURCE, TARGET, or METADATA
   */
  private void filterProtectedVpools(
      DbClient dbClient, UnManagedVolume unManagedVolume, String personality) {

    if (unManagedVolume.getSupportedVpoolUris() != null
        && !unManagedVolume.getSupportedVpoolUris().isEmpty()) {
      Iterator<VirtualPool> vpoolItr =
          dbClient.queryIterativeObjects(
              VirtualPool.class, URIUtil.toURIList(unManagedVolume.getSupportedVpoolUris()));
      while (vpoolItr.hasNext()) {
        boolean remove = false;
        VirtualPool vpool = vpoolItr.next();

        // If this is an SRDF source vpool, we can filter out since we're dealing with an RP volume
        if (vpool.getProtectionRemoteCopySettings() != null) {
          remove = true;
        }

        // If this is not an RP source, the vpool should be filtered out if:
        // The vpool is an RP vpool (has settings) and target vpools are non-null
        if (vpool.getProtectionVarraySettings() != null
            && ((Volume.PersonalityTypes.TARGET.name().equalsIgnoreCase(personality))
                || Volume.PersonalityTypes.METADATA.name().equalsIgnoreCase(personality))) {
          boolean foundEmptyTargetVpool = false;
          Map<URI, VpoolProtectionVarraySettings> settings =
              VirtualPool.getProtectionSettings(vpool, dbClient);
          for (Map.Entry<URI, VpoolProtectionVarraySettings> setting : settings.entrySet()) {
            if (NullColumnValueGetter.isNullURI(setting.getValue().getVirtualPool())) {
              foundEmptyTargetVpool = true;
              break;
            }
          }

          // If this is a journal volume, also check the journal vpools. If they're not set, we
          // cannot filter out this vpool.
          if (Volume.PersonalityTypes.METADATA.name().equalsIgnoreCase(personality)
              && (NullColumnValueGetter.isNullValue(vpool.getJournalVpool())
                  || NullColumnValueGetter.isNullValue(vpool.getStandbyJournalVpool()))) {
            foundEmptyTargetVpool = true;
          }

          // If every relevant target (and journal for journal volumes) vpool is filled-in, then
          // you would never assign your target volume to this source vpool, so filter it out.
          if (!foundEmptyTargetVpool) {
            remove = true;
          }
        }

        if (Volume.PersonalityTypes.SOURCE.name().equalsIgnoreCase(personality)) {
          if (!VirtualPool.vPoolSpecifiesProtection(vpool)) {
            // If this an RP source, the vpool must be an RP vpool
            remove = true;
          } else if (unManagedVolume
                  .getVolumeInformation()
                  .containsKey(SupportedVolumeInformation.RP_STANDBY_INTERNAL_SITENAME.toString())
              && !VirtualPool.vPoolSpecifiesMetroPoint(vpool)) {
            // Since this is a Source volume with the presence of RP_STANDBY_INTERNAL_SITENAME
            // it indicates that this volume is MetroPoint, if we get here, this is vpool
            // must be filtered out since it's not MP.
            remove = true;
          }
        }

        if (remove) {
          log.info(
              "Removing virtual pool "
                  + vpool.getLabel()
                  + " from supported vpools for unmanaged volume: "
                  + unManagedVolume.getLabel());
          unManagedVolume.getSupportedVpoolUris().remove(vpool.getId().toString());
        }
      }
    }
  }
  /**
   * Creates the RP source volume/journal and the specified number of target/journal volumes.
   *
   * @param volumeName
   * @param numTargets
   */
  private List<Volume> createRpVolumes(
      String volumeName, int numTargets, ProtectionSet protectionSet, boolean isRpVPlex) {
    List<Volume> volumes = new ArrayList<Volume>();

    StringSet associatedVolumes = new StringSet();
    associatedVolumes.add("associatedVol1");

    StorageSystem storageSystem = null;
    if (isRpVPlex) {
      storageSystem = createStorageSystem(true);
    } else {
      storageSystem = createStorageSystem(false);
    }

    String rsetName = "RSet-" + volumeName;

    Volume sourceVolume = new Volume();
    URI sourceVolumeURI = URIUtil.createId(Volume.class);
    volumes.add(sourceVolume);
    sourceVolume.setId(sourceVolumeURI);
    sourceVolume.setLabel(volumeName);
    sourceVolume.setPersonality(Volume.PersonalityTypes.SOURCE.toString());
    sourceVolume.setRSetName(rsetName);
    sourceVolume.setProtectionSet(new NamedURI(protectionSet.getId(), sourceVolume.getLabel()));
    sourceVolume.setStorageController(storageSystem.getId());
    if (isRpVPlex) {
      sourceVolume.setAssociatedVolumes(associatedVolumes);
      sourceVolume.setNativeId(
          "/clusters/cluster-1/virtual-volumes/device_V000195701573-01E7A_vol");
      // Create a VPLEX ViPR BlockConsistencyGroup for the source volume
      BlockConsistencyGroup sourceVolumeCg =
          createBlockConsistencyGroup(
              sourceVolume.getLabel() + "-CG", storageSystem.getId(), Types.VPLEX.name(), true);
      addVolumeToBlockConsistencyGroup(sourceVolumeCg.getId(), sourceVolume);
      rpVplexVolumeToCgMapping.put(sourceVolumeURI, sourceVolumeCg.getId());
    } else {
      rpVolumeURIs.add(sourceVolumeURI);
    }
    _dbClient.createObject(sourceVolume);

    Volume sourceVolumeJournal = new Volume();
    URI sourceVolumeJournalURI = URIUtil.createId(Volume.class);
    volumes.add(sourceVolumeJournal);
    sourceVolumeJournal.setId(sourceVolumeJournalURI);
    sourceVolumeJournal.setLabel(volumeName + RP_SRC_JOURNAL_APPEND);
    sourceVolumeJournal.setPersonality(Volume.PersonalityTypes.METADATA.toString());
    sourceVolumeJournal.setProtectionSet(
        new NamedURI(protectionSet.getId(), sourceVolumeJournal.getLabel()));
    sourceVolumeJournal.setStorageController(storageSystem.getId());
    if (isRpVPlex) {
      sourceVolumeJournal.setAssociatedVolumes(associatedVolumes);
      sourceVolumeJournal.setNativeId(
          "/clusters/cluster-1/virtual-volumes/device_V000195701573-01E7B_vol");
      // Create a VPLEX ViPR BlockConsistencyGroup for the source journal volume
      BlockConsistencyGroup sourceVolumeJournalCg =
          createBlockConsistencyGroup(
              sourceVolumeJournal.getLabel() + "-CG",
              storageSystem.getId(),
              Types.VPLEX.name(),
              true);
      addVolumeToBlockConsistencyGroup(sourceVolumeJournalCg.getId(), sourceVolumeJournal);
      rpVplexVolumeToCgMapping.put(sourceVolumeJournalURI, sourceVolumeJournalCg.getId());
    } else {
      rpVolumeURIs.add(sourceVolumeJournalURI);
    }
    _dbClient.createObject(sourceVolumeJournal);

    for (int i = 1; i <= numTargets; i++) {
      Volume sourceVolumeTarget = new Volume();
      URI sourceVolumeTargetURI = URIUtil.createId(Volume.class);
      volumes.add(sourceVolumeTarget);
      sourceVolumeTarget.setId(sourceVolumeTargetURI);
      sourceVolumeTarget.setLabel(volumeName + RP_TGT_APPEND + "vArray" + i);
      sourceVolumeTarget.setPersonality(Volume.PersonalityTypes.TARGET.toString());
      sourceVolumeTarget.setRSetName(rsetName);
      sourceVolumeTarget.setProtectionSet(
          new NamedURI(protectionSet.getId(), sourceVolumeTarget.getLabel()));
      sourceVolumeTarget.setStorageController(storageSystem.getId());
      if (isRpVPlex) {
        sourceVolumeTarget.setAssociatedVolumes(associatedVolumes);
        sourceVolumeTarget.setNativeId(
            "/clusters/cluster-2/virtual-volumes/device_V000195701573-01E7C_vol" + i);
        // Create a VPLEX ViPR BlockConsistencyGroup for the target volume
        BlockConsistencyGroup sourceVolumeTargetCg =
            createBlockConsistencyGroup(
                sourceVolumeTarget.getLabel() + "-CG",
                storageSystem.getId(),
                Types.VPLEX.name(),
                true);
        addVolumeToBlockConsistencyGroup(sourceVolumeTargetCg.getId(), sourceVolumeTarget);
        rpVplexVolumeToCgMapping.put(sourceVolumeTargetURI, sourceVolumeTargetCg.getId());
      } else {
        rpVolumeURIs.add(sourceVolumeTargetURI);
      }

      _dbClient.createObject(sourceVolumeTarget);

      Volume sourceVolumeTargetJournal = new Volume();
      URI sourceVolumeTargetJournalURI = URIUtil.createId(Volume.class);
      volumes.add(sourceVolumeTargetJournal);
      sourceVolumeTargetJournal.setId(sourceVolumeTargetJournalURI);
      sourceVolumeTargetJournal.setLabel(volumeName + RP_TGT_JOURNAL_APPEND + "vArray" + i);
      sourceVolumeTargetJournal.setPersonality(Volume.PersonalityTypes.METADATA.toString());
      sourceVolumeTargetJournal.setProtectionSet(
          new NamedURI(protectionSet.getId(), sourceVolumeTargetJournal.getLabel()));
      sourceVolumeTargetJournal.setStorageController(storageSystem.getId());
      if (isRpVPlex) {
        sourceVolumeTargetJournal.setAssociatedVolumes(associatedVolumes);
        sourceVolumeTargetJournal.setNativeId(
            "/clusters/cluster-2/virtual-volumes/device_V000195701573-01ED_vol" + i);
        // Create a VPLEX ViPR BlockConsistencyGroup for the source target journal volume
        BlockConsistencyGroup sourceVolumeTargetJournalCg =
            createBlockConsistencyGroup(
                sourceVolumeTargetJournal.getLabel() + "-CG",
                storageSystem.getId(),
                Types.VPLEX.name(),
                true);
        addVolumeToBlockConsistencyGroup(
            sourceVolumeTargetJournalCg.getId(), sourceVolumeTargetJournal);
        rpVplexVolumeToCgMapping.put(
            sourceVolumeTargetJournalURI, sourceVolumeTargetJournalCg.getId());
      } else {
        rpVolumeURIs.add(sourceVolumeTargetJournalURI);
      }
      _dbClient.createObject(sourceVolumeTargetJournal);
    }

    return volumes;
  }
  /**
   * Update (if it exists) the source and target UnManagedVolume objects with RP information needed
   * for ingestion
   *
   * @param unManagedProtectionSet unmanaged protection set
   * @param cg CG response got back from RP system
   * @param rpCopyAccessStateMap Map to hold the access state of the replication sets
   * @param rpWwnToNativeWwn Map of RP volume WWN to native volume WWN - required for XIO but
   *     harmless otherwise
   * @param storageNativeIdPrefixes List of XIO systems discovered in ViPR
   * @param dbClient DB client instance
   */
  private void mapCgSourceAndTargets(
      UnManagedProtectionSet unManagedProtectionSet,
      GetCGsResponse cg,
      Map<String, String> rpCopyAccessStateMap,
      Map<String, String> rpWwnToNativeWwn,
      List<String> storageNativeIdPrefixes,
      DbClient dbClient) {
    for (GetRSetResponse rset : cg.getRsets()) {
      for (GetVolumeResponse volume : rset.getVolumes()) {
        // Find this volume in UnManagedVolumes based on wwn
        UnManagedVolume unManagedVolume =
            findUnManagedVolumeForWwn(volume.getWwn(), dbClient, storageNativeIdPrefixes);

        // Check if this volume is already managed, which would indicate it has already been
        // partially ingested
        Volume managedVolume =
            DiscoveryUtils.checkManagedVolumeExistsInDBByWwn(dbClient, volume.getWwn());

        // Add the WWN to the unmanaged protection set, regardless of whether this volume is
        // unmanaged or not.
        unManagedProtectionSet.getVolumeWwns().add(volume.getWwn());

        if (null == unManagedVolume && null == managedVolume) {
          log.info(
              "Protection Set {} contains unknown Replication Set volume: {}. Skipping.",
              unManagedProtectionSet.getNativeGuid(),
              volume.getWwn());
          continue;
        }

        if (null != managedVolume) {
          log.info(
              "Protection Set {} contains volume {} that is already managed",
              unManagedProtectionSet.getNativeGuid(),
              volume.getWwn());
          // make sure it's in the UnManagedProtectionSet's ManagedVolume ids
          if (!unManagedProtectionSet
              .getManagedVolumeIds()
              .contains(managedVolume.getId().toString())) {
            unManagedProtectionSet.getManagedVolumeIds().add(managedVolume.getId().toString());
          }

          if (!managedVolume.checkInternalFlags(Flag.INTERNAL_OBJECT) && null != unManagedVolume) {
            log.info(
                "Protection Set {} also has an orphaned UnManagedVolume {} that will be removed",
                unManagedProtectionSet.getNativeGuid(),
                unManagedVolume.getLabel());
            // remove the unManagedVolume from the UnManagedProtectionSet's UnManagedVolume ids
            unManagedProtectionSet
                .getUnManagedVolumeIds()
                .remove(unManagedVolume.getId().toString());
            unManagedVolumesToDelete.add(unManagedVolume);
            // because this volume is already managed, we can just continue to the next
            continue;
          }
        }

        // at this point, we have an legitimate UnManagedVolume whose RP properties should be
        // updated
        log.info("Processing Replication Set UnManagedVolume {}", unManagedVolume.forDisplay());

        // Add the unmanaged volume to the list (if it's not there already)
        if (!unManagedProtectionSet
            .getUnManagedVolumeIds()
            .contains(unManagedVolume.getId().toString())) {
          unManagedProtectionSet.getUnManagedVolumeIds().add(unManagedVolume.getId().toString());
        }

        // Update the fields in the UnManagedVolume to reflect RP characteristics
        String personality = Volume.PersonalityTypes.SOURCE.name();
        if (!volume.isProduction()) {
          personality = Volume.PersonalityTypes.TARGET.name();
        }

        updateCommonRPProperties(
            unManagedProtectionSet, unManagedVolume, personality, volume, dbClient);

        // Update other RP properties for source/targets
        // What Replication Set does this volume belong to? (so we can associate sources to
        // targets.)
        // What is the access state.
        StringSet rpAccessState = new StringSet();
        rpAccessState.add(rpCopyAccessStateMap.get(volume.getRpCopyName()));
        unManagedVolume.putVolumeInfo(
            SupportedVolumeInformation.RP_ACCESS_STATE.toString(), rpAccessState);
        StringSet rsetName = new StringSet();
        rsetName.add(rset.getName());
        unManagedVolume.putVolumeInfo(SupportedVolumeInformation.RP_RSET_NAME.toString(), rsetName);

        rpWwnToNativeWwn.put(volume.getWwn(), unManagedVolume.getWwn());

        unManagedVolumesToUpdateByWwn.put(unManagedVolume.getWwn(), unManagedVolume);
      }

      // Now that we've processed all of the sources and targets, we can mark all of the target
      // devices in the source devices.
      for (GetVolumeResponse volume : rset.getVolumes()) {
        // Only process source volumes here.
        if (!volume.isProduction()) {
          continue;
        }

        // Find this volume in UnManagedVolumes based on wwn
        // See if the unmanaged volume is in the list of volumes to update
        // (it should be, unless the backing array has not been discovered)

        UnManagedVolume unManagedVolume = null;
        String wwn = rpWwnToNativeWwn.get(volume.getWwn());
        if (wwn != null) {
          unManagedVolume = findUnManagedVolumeForWwn(wwn, dbClient, storageNativeIdPrefixes);
        }

        if (null == unManagedVolume) {
          log.info(
              "Protection Set {} contains unknown volume: {}. Skipping.",
              unManagedProtectionSet.getNativeGuid(),
              volume.getWwn());
          continue;
        }

        log.info("Linking target volumes to source volume {}", unManagedVolume.forDisplay());
        StringSet rpTargetVolumeIds =
            linkTargetVolumes(
                unManagedProtectionSet,
                unManagedVolume,
                rset,
                rpWwnToNativeWwn,
                storageNativeIdPrefixes,
                dbClient);

        // Add the unmanaged target IDs to the source unmanaged volume
        unManagedVolume.putVolumeInfo(
            SupportedVolumeInformation.RP_UNMANAGED_TARGET_VOLUMES.toString(), rpTargetVolumeIds);

        unManagedVolumesToUpdateByWwn.put(unManagedVolume.getWwn(), unManagedVolume);
      }
    }
  }