/** {@inheritDoc} */
  @Override
  public void process() throws MigrationCallbackException {
    s_logger.info("Executing BlockSnapshotSession migration callback.");
    try {
      DbClient dbClient = getDbClient();
      List<BlockSnapshotSession> snapshotSessions = new ArrayList<BlockSnapshotSession>();
      Map<URI, Map<String, BlockSnapshotSession>> groupSessionMap = new HashMap<>();
      List<URI> snapshotURIs = dbClient.queryByType(BlockSnapshot.class, true);
      Iterator<BlockSnapshot> snapshotsIter =
          dbClient.queryIterativeObjects(BlockSnapshot.class, snapshotURIs, true);
      while (snapshotsIter.hasNext()) {
        BlockSnapshot snapshot = snapshotsIter.next();
        if (isSnapshotSessionSupported(snapshot)) {
          // Check if this is a group snapshot.
          URI cgURI = snapshot.getConsistencyGroup();
          if (NullColumnValueGetter.isNullURI(cgURI)) {
            // The storage system for the single volume snapshot supports
            // snapshot sessions, then we need to prepare and create a
            // snapshot session for that snapshot and add the snapshot as
            // a linked target for the session.
            BlockSnapshotSession snapshotSession = prepareSnapshotSession(snapshot);
            snapshotSessions.add(snapshotSession);
          } else {
            // Create the group session if necessary and add the snapshot as a
            // linked target for that group session.
            String settingsInstance = snapshot.getSettingsInstance();
            Map<String, BlockSnapshotSession> grpSnapshotSessions = groupSessionMap.get(cgURI);
            if (grpSnapshotSessions != null) {
              BlockSnapshotSession snapshotSession = grpSnapshotSessions.get(settingsInstance);
              if (snapshotSession == null) {
                snapshotSession = prepareSnapshotSession(snapshot);
                grpSnapshotSessions.put(settingsInstance, snapshotSession);
                snapshotSessions.add(snapshotSession);
              } else {
                StringSet linkedTargets = snapshotSession.getLinkedTargets();
                linkedTargets.add(snapshot.getId().toString());
              }
            } else {
              grpSnapshotSessions = new HashMap<String, BlockSnapshotSession>();
              groupSessionMap.put(cgURI, grpSnapshotSessions);
              BlockSnapshotSession snapshotSession = prepareSnapshotSession(snapshot);
              grpSnapshotSessions.put(settingsInstance, snapshotSession);
              snapshotSessions.add(snapshotSession);
            }
          }
        }
      }

      if (!snapshotSessions.isEmpty()) {
        dbClient.createObject(snapshotSessions);
      }
    } catch (Exception e) {
      s_logger.error("Caught exception during BlockSnapshotSession migration", e);
    }
  }
  /**
   * Cleans up instances of {@link BlockSnapshot} that failed to be created. Also, any stale entries
   * in {@link BlockSnapshotSession#getLinkedTargets()} will be cleaned up.
   *
   * @param volume Volume URI to process.
   * @param itemsToUpdate Items to be updated.
   * @param itemsToDelete Items to be deleted.
   */
  @Override
  public void process(
      URI volume, Collection<DataObject> itemsToUpdate, Collection<DataObject> itemsToDelete) {
    List<BlockSnapshot> snapshots =
        CustomQueryUtility.queryActiveResourcesByConstraint(
            getDbClient(),
            BlockSnapshot.class,
            ContainmentConstraint.Factory.getVolumeSnapshotConstraint(volume));
    List<BlockSnapshot> failedSnapshots = new ArrayList<>();
    List<BlockSnapshotSession> updateSessions = new ArrayList<>();

    failedSnapshots.addAll(
        Collections2.filter(
            snapshots,
            new Predicate<BlockSnapshot>() {
              @Override
              public boolean apply(BlockSnapshot snapshot) {
                return Strings.isNullOrEmpty(snapshot.getNativeId());
              }
            }));

    // Removed failed snapshots from any existing sessions
    for (BlockSnapshot failedSnapshot : failedSnapshots) {
      log.info("Removing failed snapshot: {}", failedSnapshot.getLabel());
      List<BlockSnapshotSession> sessions =
          CustomQueryUtility.queryActiveResourcesByConstraint(
              getDbClient(),
              BlockSnapshotSession.class,
              ContainmentConstraint.Factory.getLinkedTargetSnapshotSessionConstraint(
                  failedSnapshot.getId()));

      for (BlockSnapshotSession session : sessions) {
        log.info("Updating existing session: {}", session.getSessionLabel());
        StringSet linkedTargets = session.getLinkedTargets();
        linkedTargets.remove(failedSnapshot.getId().toString());
        updateSessions.add(session);
      }
    }

    itemsToUpdate.addAll(updateSessions);
    itemsToDelete.addAll(failedSnapshots);
  }
 /**
  * Prepare the ViPR BlockSnapshotSession instance for the pass BlockSnapshot instance.
  *
  * @param snapshot A reference to the snapshot.
  * @return A reference to the newly created snapshot session.
  */
 private BlockSnapshotSession prepareSnapshotSession(BlockSnapshot snapshot) {
   s_logger.info("Prepare BlockSnapshotSession for snapshot {}", snapshot.getId());
   BlockSnapshotSession snapshotSession = new BlockSnapshotSession();
   URI snapSessionURI = URIUtil.createId(BlockSnapshotSession.class);
   snapshotSession.setId(snapSessionURI);
   snapshotSession.setSessionLabel(getSessionLabelFromSettingsInstance(snapshot));
   URI cgURI = snapshot.getConsistencyGroup();
   if (NullColumnValueGetter.isNullURI(cgURI)) {
     snapshotSession.setParent(snapshot.getParent());
     snapshotSession.setLabel(snapshot.getLabel());
   } else {
     snapshotSession.setConsistencyGroup(cgURI);
     snapshotSession.setLabel(snapshot.getSnapsetLabel());
     Volume parent = getDbClient().queryObject(Volume.class, snapshot.getParent());
     if (parent != null) {
       snapshotSession.setReplicationGroupInstance(parent.getReplicationGroupInstance());
       snapshotSession.setSessionSetName(parent.getReplicationGroupInstance());
     }
   }
   snapshotSession.setProject(snapshot.getProject());
   snapshotSession.setStorageController(snapshot.getStorageController());
   snapshotSession.setSessionInstance(snapshot.getSettingsInstance());
   StringSet linkedTargets = new StringSet();
   linkedTargets.add(snapshot.getId().toString());
   snapshotSession.setLinkedTargets(linkedTargets);
   return snapshotSession;
 }
  /** {@inheritDoc} */
  @Override
  public void updateStatus(JobContext jobContext) throws Exception {
    JobStatus jobStatus = getJobStatus();
    CloseableIterator<CIMObjectPath> volumeIter = null;
    try {
      DbClient dbClient = jobContext.getDbClient();
      TaskCompleter completer = getTaskCompleter();
      BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, _snapshotURI);
      if (jobStatus == JobStatus.IN_PROGRESS) {
        return;
      }
      if (jobStatus == JobStatus.SUCCESS) {
        s_logger.info(
            "Post-processing successful link snapshot session target {} for task {}",
            snapshot.getId(),
            completer.getOpId());
        // Get the snapshot session to which the target is being linked.
        BlockSnapshotSession snapSession =
            dbClient.queryObject(BlockSnapshotSession.class, completer.getId());

        // Get the snapshot device ID and set it against the BlockSnapshot object.
        BlockObject sourceObj = BlockObject.fetch(dbClient, snapshot.getParent().getURI());
        CIMConnectionFactory cimConnectionFactory = jobContext.getCimConnectionFactory();
        WBEMClient client = getWBEMClient(dbClient, cimConnectionFactory);
        volumeIter =
            client.associatorNames(getCimJob(), null, SmisConstants.CIM_STORAGE_VOLUME, null, null);
        while (volumeIter.hasNext()) {
          // Get the sync volume native device id
          CIMObjectPath volumePath = volumeIter.next();
          s_logger.info("volumePath: {}", volumePath.toString());
          CIMInstance volume = client.getInstance(volumePath, false, false, null);
          String volumeDeviceId =
              volumePath.getKey(SmisConstants.CP_DEVICE_ID).getValue().toString();
          s_logger.info("volumeDeviceId: {}", volumeDeviceId);
          if (volumeDeviceId.equals(sourceObj.getNativeId())) {
            // Don't want the source, we want the linked target.
            continue;
          }
          String volumeElementName =
              CIMPropertyFactory.getPropertyValue(volume, SmisConstants.CP_ELEMENT_NAME);
          s_logger.info("volumeElementName: {}", volumeElementName);
          String volumeWWN = CIMPropertyFactory.getPropertyValue(volume, SmisConstants.CP_WWN_NAME);
          s_logger.info("volumeWWN: {}", volumeWWN);
          String volumeAltName = CIMPropertyFactory.getPropertyValue(volume, SmisConstants.CP_NAME);
          s_logger.info("volumeAltName: {}", volumeAltName);
          StorageSystem system = dbClient.queryObject(StorageSystem.class, getStorageSystemURI());
          snapshot.setNativeId(volumeDeviceId);
          snapshot.setNativeGuid(NativeGUIDGenerator.generateNativeGuid(system, snapshot));
          snapshot.setDeviceLabel(volumeElementName);
          snapshot.setInactive(false);
          snapshot.setIsSyncActive(Boolean.TRUE);
          snapshot.setCreationTime(Calendar.getInstance());
          snapshot.setWWN(volumeWWN.toUpperCase());
          snapshot.setAlternateName(volumeAltName);
          snapshot.setSettingsInstance(snapSession.getSessionInstance());
          commonSnapshotUpdate(
              snapshot,
              volume,
              client,
              system,
              sourceObj.getNativeId(),
              volumeDeviceId,
              false,
              dbClient);
          s_logger.info(
              String.format(
                  "For target volume path %1$s, going to set blocksnapshot %2$s nativeId to %3$s (%4$s). Associated volume is %5$s (%6$s)",
                  volumePath.toString(),
                  snapshot.getId().toString(),
                  volumeDeviceId,
                  volumeElementName,
                  sourceObj.getNativeId(),
                  sourceObj.getDeviceLabel()));
          dbClient.updateObject(snapshot);
        }
      } else if (jobStatus == JobStatus.FAILED || jobStatus == JobStatus.FATAL_ERROR) {
        s_logger.info(
            "Failed to link snapshot session target {} for task {}",
            snapshot.getId(),
            completer.getOpId());
        snapshot.setInactive(true);
        dbClient.updateObject(snapshot);
      }
    } catch (Exception e) {
      setPostProcessingErrorStatus(
          "Encountered an internal error in link snapshot session target job status processing: "
              + e.getMessage());
      s_logger.error(
          "Encountered an internal error in link snapshot session target job status processing", e);
    } finally {
      if (volumeIter != null) {
        volumeIter.close();
      }
      super.updateStatus(jobContext);
    }
  }