/**
   * 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;
  }
  /**
   * find DM or NAS from db using native id
   *
   * @param system
   * @param dbClient
   * @param nativeId
   * @return
   */
  private PhysicalNAS findPhysicalNasByNativeId(
      final StorageSystem system, DbClient dbClient, String nativeId) {
    URIQueryResultList results = new URIQueryResultList();
    PhysicalNAS physicalNas = null;

    // Set storage port details to vNas
    String nasNativeGuid =
        NativeGUIDGenerator.generateNativeGuid(system, nativeId, NativeGUIDGenerator.PHYSICAL_NAS);

    dbClient.queryByConstraint(
        AlternateIdConstraint.Factory.getPhysicalNasByNativeGuidConstraint(nasNativeGuid), results);

    Iterator<URI> iter = results.iterator();
    while (iter.hasNext()) {
      PhysicalNAS tmpNas = dbClient.queryObject(PhysicalNAS.class, iter.next());

      if (tmpNas != null && !tmpNas.getInactive()) {
        physicalNas = tmpNas;
        _logger.info(
            "found physical NAS {}", physicalNas.getNativeGuid() + ":" + physicalNas.getNasName());
        break;
      }
    }
    return physicalNas;
  }
  /**
   * Filters supported vPools in UnManaged Volume based on Auto-Tiering Policy.
   *
   * @param unManagedVolume the UnManaged volume
   * @param policyName the policy name associated with UnManaged volume
   * @param system the system
   * @param dbClient the db client
   */
  public static void filterSupportedVpoolsBasedOnTieringPolicy(
      UnManagedVolume unManagedVolume, String policyName, StorageSystem system, DbClient dbClient) {

    StringSetMap unManagedVolumeInformation = unManagedVolume.getVolumeInformation();
    StringSet supportedVpoolURIs =
        unManagedVolumeInformation.get(SupportedVolumeInformation.SUPPORTED_VPOOL_LIST.toString());
    List<String> vPoolsToRemove = new ArrayList<String>();
    if (supportedVpoolURIs != null) {
      Iterator<String> itr = supportedVpoolURIs.iterator();
      while (itr.hasNext()) {
        String uri = itr.next();
        VirtualPool vPool = dbClient.queryObject(VirtualPool.class, URI.create(uri));
        if (vPool != null && !vPool.getInactive()) {
          // generate unmanaged volume's policyId
          String autoTierPolicyId =
              NativeGUIDGenerator.generateAutoTierPolicyNativeGuid(
                  system.getNativeGuid(),
                  policyName,
                  NativeGUIDGenerator.getTieringPolicyKeyForSystem(system));
          if (!checkVPoolValidForUnManagedVolumeAutoTieringPolicy(
              vPool, autoTierPolicyId, system)) {
            String msg =
                "Removing vPool %s from SUPPORTED_VPOOL_LIST in UnManagedVolume %s "
                    + "since Auto-tiering Policy %s in UnManaged Volume does not match with vPool's (%s)";
            _log.info(
                String.format(
                    msg,
                    new Object[] {
                      uri, unManagedVolume.getId(), autoTierPolicyId, vPool.getAutoTierPolicyName()
                    }));
            vPoolsToRemove.add(uri);
          }
        } else {
          // remove Inactive vPool URI
          vPoolsToRemove.add(uri);
        }
      }
    }
    for (String uri : vPoolsToRemove) { // UnManagedVolume object is persisted by caller
      supportedVpoolURIs.remove(uri);
    }
  }
  /**
   * Allows the user to remove a storage system from the list of decommisioned resources After that
   * corresponding provider should be able to be rescanned and add this system back to the list of
   * managed systems.
   *
   * @param id id the URN of a ViPR Storage provider
   * @param param The storage system details.
   * @brief removes the storage system from the list of decommissioned systems and rescans the
   *     provider.
   * @return An asynchronous task corresponding to the scan job scheduled for the provider.
   * @throws BadRequestException When the system type is not valid or a storage system with the same
   *     native guid already exists.
   * @throws com.emc.storageos.db.exceptions.DatabaseException When an error occurs querying the
   *     database.
   * @throws ControllerException When an error occurs discovering the storage system.
   */
  @PUT
  @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
  @CheckPermission(roles = {Role.SYSTEM_ADMIN})
  @Path("/{id}/storage-systems")
  public TaskResourceRep addStorageSystem(
      @PathParam("id") URI id, StorageSystemProviderRequestParam param) throws ControllerException {
    TaskResourceRep taskRep;
    URIQueryResultList list = new URIQueryResultList();

    ArgValidator.checkFieldNotEmpty(param.getSystemType(), "system_type");
    if (!StorageSystem.Type.isProviderStorageSystem(
        DiscoveredDataObject.Type.valueOf(param.getSystemType()))) {
      throw APIException.badRequests.cannotAddStorageSystemTypeToStorageProvider(
          param.getSystemType());
    }

    StorageProvider provider = _dbClient.queryObject(StorageProvider.class, id);
    ArgValidator.checkEntityNotNull(provider, id, isIdEmbeddedInURL(id));

    ArgValidator.checkFieldNotEmpty(param.getSerialNumber(), "serialNumber");

    String nativeGuid =
        NativeGUIDGenerator.generateNativeGuid(param.getSystemType(), param.getSerialNumber());
    // check for duplicate StorageSystem.

    List<StorageSystem> systems =
        CustomQueryUtility.getActiveStorageSystemByNativeGuid(_dbClient, nativeGuid);
    if (systems != null && !systems.isEmpty()) {
      throw APIException.badRequests.invalidParameterProviderStorageSystemAlreadyExists(
          "nativeGuid", nativeGuid);
    }

    int cleared =
        DecommissionedResource.removeDecommissionedFlag(_dbClient, nativeGuid, StorageSystem.class);
    if (cleared == 0) {
      log.info("Cleared {} decommissioned systems", cleared);
    } else {
      log.info("Did not find any decommissioned systems to clear. Continue to scan.");
    }

    ArrayList<AsyncTask> tasks = new ArrayList<AsyncTask>(1);
    String taskId = UUID.randomUUID().toString();
    tasks.add(new AsyncTask(StorageProvider.class, provider.getId(), taskId));

    BlockController controller = getController(BlockController.class, provider.getInterfaceType());
    DiscoveredObjectTaskScheduler scheduler =
        new DiscoveredObjectTaskScheduler(_dbClient, new ScanJobExec(controller));
    TaskList taskList = scheduler.scheduleAsyncTasks(tasks);
    return taskList.getTaskList().listIterator().next();
  }
  @Override
  public void processResult(Operation operation, Object resultObj, Map<String, Object> keyMap)
      throws BaseCollectionException {
    CloseableIterator<CIMInstance> volumeInstances = null;
    try {
      _dbClient = (DbClient) keyMap.get(Constants.dbClient);
      WBEMClient client = (WBEMClient) keyMap.get(Constants._cimClient);
      _unManagedVolumesUpdate = new ArrayList<UnManagedVolume>();
      CIMObjectPath storagePoolPath = getObjectPathfromCIMArgument(_args);
      String poolNativeGuid = NativeGUIDGenerator.generateNativeGuidForPool(storagePoolPath);
      StoragePool pool = checkStoragePoolExistsInDB(poolNativeGuid, _dbClient);
      if (pool == null) {
        _logger.error(
            "Skipping unmanaged volume discovery of Access Sattes as the storage pool with path {} doesn't exist in ViPR",
            storagePoolPath.toString());
        return;
      }
      EnumerateResponse<CIMInstance> volumeInstanceChunks =
          (EnumerateResponse<CIMInstance>) resultObj;
      volumeInstances = volumeInstanceChunks.getResponses();

      processVolumes(volumeInstances, keyMap, operation);
      while (!volumeInstanceChunks.isEnd()) {
        _logger.debug("Processing Next Volume Chunk of size {}", BATCH_SIZE);
        volumeInstanceChunks =
            client.getInstancesWithPath(
                storagePoolPath,
                volumeInstanceChunks.getContext(),
                new UnsignedInteger32(BATCH_SIZE));
        processVolumes(volumeInstanceChunks.getResponses(), keyMap, operation);
      }
      if (null != _unManagedVolumesUpdate && _unManagedVolumesUpdate.size() > 0) {
        _partitionManager.updateInBatches(
            _unManagedVolumesUpdate, getPartitionSize(keyMap), _dbClient, "UnManagedVolume");
      }

    } catch (Exception e) {
      _logger.error("Discovering Access States of unManaged Volumes failed", e);
    } finally {
      volumeInstances.close();
    }
  }
  /**
   * Creates an instance of new storage port and associates it with the storage system.
   *
   * @param portNetworkId
   * @param transportType
   */
  private void create(String portNetworkId, String transportType) {
    logger.info(
        "Start create storage port for portNetworkId={}" + " and transportType={}",
        portNetworkId,
        transportType);

    StorageHADomain adapter = CinderUtils.getStorageAdapter(storageSystem, dbClient);

    StoragePort port = new StoragePort();
    port.setId(URIUtil.createId(StoragePort.class));
    port.setStorageDevice(storageSystem.getId());
    String portName = generatePortName();
    logger.debug("New storage port name is = {}", portName);
    String nativeGuid =
        NativeGUIDGenerator.generateNativeGuid(storageSystem, portName, NativeGUIDGenerator.PORT);
    port.setNativeGuid(nativeGuid);
    port.setPortNetworkId(portNetworkId);
    port.setStorageHADomain(adapter.getId());

    port.setRegistrationStatus(DiscoveredDataObject.RegistrationStatus.REGISTERED.toString());
    // always treat it as a frontend port
    port.setPortType(PortType.frontend.name());
    port.setOperationalStatus(OperationalStatus.OK.toString());
    port.setTransportType(transportType);
    port.setLabel(portName);
    port.setPortName(portName);
    port.setPortGroup("Cinder-PortGroup");
    port.setCompatibilityStatus(CompatibilityStatus.COMPATIBLE.name());
    port.setDiscoveryStatus(DiscoveryStatus.VISIBLE.name());
    dbClient.createObject(port);

    // Add it to the new ports list
    newStoragePortsList.add(port);

    // Add it to the local list
    allStoragePortsList.add(port);

    logger.info(
        "End create storage port for portNetworkId={}" + " and transportType={}",
        portNetworkId,
        transportType);
  }
  private void setStorageAdapterReference(
      StoragePort port, CIMInstance portInstance, StorageSystem device) {
    String adapterName = null;
    try {
      adapterName = portInstance.getObjectPath().getKey(SYSTEMNAME).getValue().toString();
      Iterable<String> adapterItr =
          Splitter.on(Constants.PATH_DELIMITER_PATTERN).limit(3).split(adapterName);
      URIQueryResultList result = new URIQueryResultList();

      _dbClient.queryByConstraint(
          AlternateIdConstraint.Factory.getStorageHADomainByNativeGuidConstraint(
              NativeGUIDGenerator.generateNativeGuid(
                  device, Iterables.getLast(adapterItr), NativeGUIDGenerator.ADAPTER)),
          result);
      if (result.iterator().hasNext()) {
        URI portGroup = result.iterator().next();
        port.setStorageHADomain(portGroup);
        StorageHADomain haDomain = _dbClient.queryObject(StorageHADomain.class, portGroup);
        port.setPortGroup(haDomain.getAdapterName());
      }
    } catch (Exception e) {
      _logger.warn("Storage Port not found : {}", adapterName);
    }
  }
  /** {@inheritDoc} */
  @SuppressWarnings("unchecked")
  @Override
  public void processResult(Operation operation, Object resultObj, Map<String, Object> keyMap)
      throws BaseCollectionException {
    try {
      final Iterator<CIMInstance> it = (Iterator<CIMInstance>) resultObj;
      profile = (AccessProfile) keyMap.get(Constants.ACCESSPROFILE);
      Set<String> protocols = (Set<String>) keyMap.get(Constants.PROTOCOLS);
      _newPortList = new ArrayList<StoragePort>();
      _updatePortList = new ArrayList<StoragePort>();
      _dbClient = (DbClient) keyMap.get(Constants.dbClient);
      CoordinatorClient coordinator = (CoordinatorClient) keyMap.get(Constants.COORDINATOR_CLIENT);
      Map<URI, StoragePool> poolsToMatchWithVpool =
          (Map<URI, StoragePool>) keyMap.get(Constants.MODIFIED_STORAGEPOOLS);
      Set<URI> systemsToRunRPConnectivity =
          (HashSet<URI>) keyMap.get(Constants.SYSTEMS_RUN_RP_CONNECTIVITY);
      StorageSystem device = _dbClient.queryObject(StorageSystem.class, profile.getSystemId());
      CIMObjectPath storageAdapterPath = getObjectPathfromCIMArgument(args);
      Iterable<String> adapterItr =
          Splitter.on(Constants.PATH_DELIMITER_PATTERN)
              .limit(3)
              .split(storageAdapterPath.getKey(Constants.NAME).getValue().toString());
      String adapterNativeGuid =
          NativeGUIDGenerator.generateNativeGuid(
              device, Iterables.getLast(adapterItr), NativeGUIDGenerator.ADAPTER);
      StorageHADomain haDomain = getStorageAdapter(_dbClient, adapterNativeGuid);
      if (null == haDomain) {
        _logger.info("Adapter Not found");
        return;
      }

      while (it.hasNext()) {
        CIMInstance portInstance = null;
        StoragePort port = null;
        try {
          portInstance = it.next();

          // skip back end ports other than RDF Ports
          if (!HADomainType.REMOTE.name().equalsIgnoreCase(haDomain.getAdapterType())
              && "3".equalsIgnoreCase(getCIMPropertyValue(portInstance, USAGERESTRICTION))) {
            continue;
          }
          // only if its an EthernetPort, as protocolEnd point needs
          // to run only for Ethernet
          // Ports , because SCSI address we don't have it in
          // CIM_LogicalPort Class
          // 2 - Ethernet Port 4 - FC Port
          if ("2".equalsIgnoreCase(getCIMPropertyValue(portInstance, LINKTECHNOLOGY))) {
            port = createStoragePort(null, portInstance, profile, haDomain, false, IP, device);
            checkProtocolAlreadyExists(protocols, ISCSI);
            String deviceId = getCIMPropertyValue(portInstance, DEVICEID);
            /*
             * For SMI-S 8.x, While getting the iSCSI Port details, we use SystemName property
             * (Ex. SYMMETRIX-+-<<SERIAL>>-+-SE-1G-+-0)
             * Where this call just add the deviceId to the KeyMap (i.e SE-1G-+-0).
             * Hence manually constructing the key.
             */
            if (device.getUsingSmis80()) {
              String systemName = getCIMPropertyValue(portInstance, SYSTEM_NAME);
              StringBuffer deviceIdStrBuf = new StringBuffer(systemName);
              deviceIdStrBuf.append(Constants.SMIS80_DELIMITER).append(deviceId);
              deviceId = deviceIdStrBuf.toString();
            }
            _logger.debug("Adding iSCSI Port instance {} to keyMap", deviceId);
            keyMap.put(deviceId, port);
            addPath(keyMap, operation.getResult(), portInstance.getObjectPath());
          } else if ("4".equalsIgnoreCase(getCIMPropertyValue(portInstance, LINKTECHNOLOGY))) {
            port = checkStoragePortExistsInDB(portInstance, device, _dbClient);
            checkProtocolAlreadyExists(protocols, FC);
            createStoragePort(port, portInstance, profile, haDomain, true, FC, device);
          } else {
            _logger.debug("Unsupported Port : {}", getCIMPropertyValue(portInstance, DEVICEID));
          }

        } catch (Exception e) {
          _logger.warn(
              "Port Discovery failed for {}", getCIMPropertyValue(portInstance, DEVICEID), e);
        }
      }
      _dbClient.createObject(_newPortList);
      _dbClient.persistObject(_updatePortList);

      // ports used later to run Transport Zone connectivity
      List<List<StoragePort>> portsUsedToRunTZoneConnectivity =
          (List<List<StoragePort>>) keyMap.get(Constants.STORAGE_PORTS);
      portsUsedToRunTZoneConnectivity.add(_newPortList);
      portsUsedToRunTZoneConnectivity.add(_updatePortList);

      List<StoragePool> modifiedPools =
          StoragePoolAssociationHelper.getStoragePoolsFromPorts(
              _dbClient, _newPortList, _updatePortList);
      for (StoragePool pool : modifiedPools) {
        // pool matcher will be invoked on this pool
        if (!poolsToMatchWithVpool.containsKey(pool.getId())) {
          poolsToMatchWithVpool.put(pool.getId(), pool);
        }
      }

      // Systems used to run RP connectivity later after runing pool matcher
      systemsToRunRPConnectivity.addAll(
          StoragePoolAssociationHelper.getStorageSytemsFromPorts(_newPortList, null));
      systemsToRunRPConnectivity.addAll(
          StoragePoolAssociationHelper.getStorageSytemsFromPorts(_updatePortList, null));

      // discovered ports used later to check for not visible ports
      List<StoragePort> discoveredPorts =
          (List<StoragePort>) keyMap.get(Constants.DISCOVERED_PORTS);
      discoveredPorts.addAll(_newPortList);
      discoveredPorts.addAll(_updatePortList);

      _logger.debug(
          "# Pools used in invoking PoolMatcher during StoragePortProcessor {}",
          Joiner.on("\t").join(poolsToMatchWithVpool.keySet()));
    } catch (Exception e) {
      _logger.error("Port Discovery failed -->{}", getMessage(e));
    } finally {
      _newPortList = null;
      _updatePortList = null;
    }
  }
  /**
   * create StoragePort Record, if not present already, else update only the properties.
   *
   * @param port
   * @param portInstance
   * @throws URISyntaxException
   * @throws IOException
   */
  private StoragePort createStoragePort(
      StoragePort port,
      CIMInstance portInstance,
      AccessProfile profile,
      StorageHADomain haDomain,
      boolean flag,
      String transportType,
      StorageSystem device)
      throws URISyntaxException, IOException {
    boolean newPort = false;
    if (null == port) {
      newPort = true;
      port = new StoragePort();
      port.setId(URIUtil.createId(StoragePort.class));
      // if true, then its FC Port or else its Ethernet, PORTID
      // or ethernet will be updated later in ProtocolEndPoint Processor
      if (flag) {
        port.setPortNetworkId(
            WWNUtility.getWWNWithColons(getCIMPropertyValue(portInstance, PORTID)));
      }
      port.setStorageDevice(profile.getSystemId());
      String portNativeGuid = NativeGUIDGenerator.generateNativeGuid(_dbClient, port);
      port.setNativeGuid(portNativeGuid);
      port.setLabel(portNativeGuid);
      port.setPortGroup(haDomain.getAdapterName());
      port.setStorageHADomain(haDomain.getId());
    }
    setPortType(port, portInstance);
    port.setTransportType(transportType);
    port.setPortName(getCIMPropertyValue(portInstance, PORTNAME));
    port.setCompatibilityStatus(DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name());
    port.setDiscoveryStatus(DiscoveredDataObject.DiscoveryStatus.VISIBLE.name());

    UnsignedInteger16[] operationalStatusCodes =
        (UnsignedInteger16[]) portInstance.getPropertyValue(OPERATIONALSTATUS);
    OperationalStatus operationalStatus = getPortOperationalStatus(operationalStatusCodes);
    if (OperationalStatus.NOT_OK.equals(operationalStatus)) {
      _logger.info(
          "StoragePort {} operationalStatus is NOT_OK. operationalStatusCodes collected from SMI-S :{}",
          port.getId(),
          operationalStatusCodes);
    } else {
      _logger.debug("operationalStatusCodes :{}", operationalStatusCodes);

      // there can be multiple statuses. {OK, Online}, {OK, Stopped}
      if (operationalStatusCodes != null
          && operationalStatusCodes.length > 1
          && Arrays.asList(operationalStatusCodes).contains(stopped_code)) {
        _logger.info(
            "StoragePort {} operational status is {OK, Stopped}. operationalStatusCodes :{}",
            port.getId(),
            operationalStatusCodes);
      }
    }
    port.setOperationalStatus(operationalStatus.name());
    String portSpeed = getCIMPropertyValue(portInstance, SPEED);
    if (null != portSpeed) {
      // SMI returns port speed in bits per sec ?? Is this always true?
      Long portSpeedInBitsPerSec = Long.parseLong(portSpeed);
      Long portSpeedInGbps = portSpeedInBitsPerSec / GB;
      port.setPortSpeed(portSpeedInGbps);
    }
    setCompatibilityByACLXFlag(device, portInstance, port);
    if (flag) {
      if (newPort) {
        _logger.info("Creating port - {}:{}", port.getLabel(), port.getNativeGuid());
        _newPortList.add(port);
      } else {
        _logger.info("Updating port - {}:{}", port.getLabel(), port.getNativeGuid());
        _updatePortList.add(port);
      }
    }
    ;
    return port;
  }
  @Override
  public void processResult(Operation operation, Object resultObj, Map<String, Object> keyMap)
      throws BaseCollectionException {
    CloseableIterator<CIMInstance> volumeInstances = null;
    EnumerateResponse<CIMInstance> volumeInstanceChunks = null;
    CIMObjectPath storagePoolPath = null;
    WBEMClient client = null;
    try {
      _dbClient = (DbClient) keyMap.get(Constants.dbClient);
      client = (WBEMClient) keyMap.get(Constants._cimClient);
      _profile = (AccessProfile) keyMap.get(Constants.ACCESSPROFILE);
      Map<String, VolHostIOObject> exportedVolumes =
          (Map<String, VolHostIOObject>) keyMap.get(Constants.EXPORTED_VOLUMES);
      Set<String> existingVolumesInCG = (Set<String>) keyMap.get(Constants.VOLUMES_PART_OF_CG);
      @SuppressWarnings("unchecked")
      Map<String, RemoteMirrorObject> volumeToRAGroupMap =
          (Map<String, RemoteMirrorObject>) keyMap.get(Constants.UN_VOLUME_RAGROUP_MAP);
      @SuppressWarnings("unchecked")
      Map<String, LocalReplicaObject> volumeToLocalReplicaMap =
          (Map<String, LocalReplicaObject>) keyMap.get(Constants.UN_VOLUME_LOCAL_REPLICA_MAP);
      @SuppressWarnings("unchecked")
      Map<String, Set<String>> vmax2ThinPoolToBoundVolumesMap =
          (Map<String, Set<String>>) keyMap.get(Constants.VMAX2_THIN_POOL_TO_BOUND_VOLUMES);
      Set<String> boundVolumes = null;

      storagePoolPath = getObjectPathfromCIMArgument(_args);
      String poolNativeGuid = NativeGUIDGenerator.generateNativeGuidForPool(storagePoolPath);
      StoragePool pool = checkStoragePoolExistsInDB(poolNativeGuid, _dbClient);
      if (pool == null) {
        _logger.error(
            "Skipping unmanaged volume discovery as the storage pool with path {} doesn't exist in ViPR",
            storagePoolPath.toString());
        return;
      }
      StorageSystem system = _dbClient.queryObject(StorageSystem.class, _profile.getSystemId());
      _unManagedVolumesInsert = new ArrayList<UnManagedVolume>();
      _unManagedVolumesUpdate = new ArrayList<UnManagedVolume>();
      _unManagedExportMasksUpdate = new ArrayList<UnManagedExportMask>();

      // get bound volumes list for VMAX2 Thin pools
      boundVolumes = vmax2ThinPoolToBoundVolumesMap.get(storagePoolPath.toString());

      Set<String> poolSupportedSLONames = (Set<String>) keyMap.get(poolNativeGuid);
      _logger.debug("Pool Supporting SLO Names:{}", poolSupportedSLONames);
      _metaVolumeViewPaths = (List<CIMObjectPath>) keyMap.get(Constants.META_VOLUMES_VIEWS);
      if (_metaVolumeViewPaths == null) {
        _metaVolumeViewPaths = new ArrayList<CIMObjectPath>();
        keyMap.put(Constants.META_VOLUMES_VIEWS, _metaVolumeViewPaths);
      }
      // create empty place holder list for meta volume paths (cannot
      // define this in xml)
      _metaVolumePaths = (List<CIMObjectPath>) keyMap.get(Constants.META_VOLUMES);
      if (_metaVolumePaths == null) {
        _metaVolumePaths = new ArrayList<CIMObjectPath>();
        keyMap.put(Constants.META_VOLUMES, _metaVolumePaths);
      }

      _volumeToSpaceConsumedMap =
          (Map<String, String>) keyMap.get(Constants.VOLUME_SPACE_CONSUMED_MAP);

      // get VolumeInfo Object and inject Fast Policy Name.

      volumeInstanceChunks = (EnumerateResponse<CIMInstance>) resultObj;
      volumeInstances = volumeInstanceChunks.getResponses();

      processVolumes(
          volumeInstances,
          keyMap,
          operation,
          pool,
          system,
          exportedVolumes,
          existingVolumesInCG,
          volumeToRAGroupMap,
          volumeToLocalReplicaMap,
          poolSupportedSLONames,
          boundVolumes);
      while (!volumeInstanceChunks.isEnd()) {
        _logger.info("Processing Next Volume Chunk of size {}", BATCH_SIZE);
        volumeInstanceChunks =
            client.getInstancesWithPath(
                storagePoolPath,
                volumeInstanceChunks.getContext(),
                new UnsignedInteger32(BATCH_SIZE));
        processVolumes(
            volumeInstanceChunks.getResponses(),
            keyMap,
            operation,
            pool,
            system,
            exportedVolumes,
            existingVolumesInCG,
            volumeToRAGroupMap,
            volumeToLocalReplicaMap,
            poolSupportedSLONames,
            boundVolumes);
      }
      if (null != _unManagedVolumesUpdate && _unManagedVolumesUpdate.size() > 0) {
        _partitionManager.updateInBatches(
            _unManagedVolumesUpdate, getPartitionSize(keyMap), _dbClient, UNMANAGED_VOLUME);
      }

      if (null != _unManagedVolumesInsert && _unManagedVolumesInsert.size() > 0) {
        _partitionManager.insertInBatches(
            _unManagedVolumesInsert, getPartitionSize(keyMap), _dbClient, UNMANAGED_VOLUME);
      }

      if (null != _unManagedExportMasksUpdate && _unManagedExportMasksUpdate.size() > 0) {
        _partitionManager.updateInBatches(
            _unManagedExportMasksUpdate,
            getPartitionSize(keyMap),
            _dbClient,
            UNMANAGED_EXPORT_MASK);
      }

      performStorageUnManagedVolumeBookKeeping(pool.getId());

    } catch (Exception e) {
      _logger.error("Processing Storage Volume Information failed :", e);
    } finally {
      _unManagedVolumesInsert = null;
      _unManagedVolumesUpdate = null;
      if (null != volumeInstances) {
        volumeInstances.close();
      }
      if (null != volumeInstanceChunks) {
        try {
          client.closeEnumeration(storagePoolPath, volumeInstanceChunks.getContext());
        } catch (Exception e) {
        }
      }
    }
  }
  /**
   * process the FileShareUsage response of the VNX XML API Server.
   *
   * @param fsUsageList : fileShareUsage map.
   * @param keyMap : attribute map.
   * @param statList : list of stat objects.
   */
  @SuppressWarnings("rawtypes")
  private void processFileShareInfo(
      final List<Object> fsUsageList,
      final Map<String, Object> keyMap,
      final List<Stat> statList,
      DbClient dbClient)
      throws VNXFilePluginException {

    final String serialId = keyMap.get(Constants._serialID).toString();
    Iterator iterator = fsUsageList.iterator();
    keyMap.put(Constants._TimeCollected, System.currentTimeMillis());

    Map<String, Long> fsCapacityMap = new HashMap<String, Long>();

    while (iterator.hasNext()) {
      FileSystemSetUsageStats fsSetUsageStats = (FileSystemSetUsageStats) iterator.next();
      List<Item> fsUsageItems = fsSetUsageStats.getItem();
      _logger.info(
          "Received {} fileShareUsage records at server time {}",
          fsUsageItems.size(),
          fsSetUsageStats.getTime());
      for (Item item : fsUsageItems) {
        if (null == item.getFileSystem()) {
          continue;
        }
        final String nativeGuid =
            NativeGUIDGenerator.generateNativeGuid(
                Type.vnxfile.toString(), serialId, item.getFileSystem());
        Stat stat = _zeroRecordGenerator.injectattr(keyMap, nativeGuid, null);
        if (null != stat) {
          stat.setTimeInMillis((Long) keyMap.get(Constants._TimeCollected));
          stat.setTimeCollected((Long) keyMap.get(Constants._TimeCollected));
          injectProvisionedCapacity(stat, keyMap);
          // The data coming in is in KB. Converting to Bytes
          stat.setAllocatedCapacity(item.getSpaceUsed() * 1024);
          _statsColumnInjector.injectColumns(stat, dbClient);
          statList.add(stat);
          // Persists the file system, only if change in used capacity.
          DbClient client = (DbClient) keyMap.get(Constants.dbClient);
          if (client != null) {
            FileShare fileSystem = client.queryObject(FileShare.class, stat.getResourceId());
            if (fileSystem != null) {
              if (!fileSystem.getInactive()
                  && fileSystem.getUsedCapacity() != stat.getAllocatedCapacity()) {
                fileSystem.setUsedCapacity(stat.getAllocatedCapacity());
                client.persistObject(fileSystem);
              }
            }
          }
        }
        // filesystem and total capacity in Map
        long totalSpace = item.getSpaceTotal();
        String fsNativeId = item.getFileSystem();
        fsCapacityMap.put(fsNativeId, Long.valueOf(totalSpace));
        _logger.info(
            "processFileShareInfo - FileSystem native id  {}  and file system total size{}",
            fsNativeId,
            String.valueOf(totalSpace));
      }
      _logger.info("Filesystems found - {} ", fsCapacityMap.size());
      keyMap.put(VNXFileConstants.FILE_CAPACITY_MAP, fsCapacityMap);
    }
    _logger.info("No. of stat objects: {}", statList.size());
  }
  /** {@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);
    }
  }
  /**
   * Create StoragePool Record, if not present already, else update only the properties.
   *
   * @param pool
   * @param poolInstance
   * @param profile
   * @param poolClassName
   * @param supportedVolumeTypes
   * @param protocols
   * @param poolsToMatchWithVpool
   * @throws URISyntaxException
   * @throws IOException
   */
  private void createStoragePool(
      StoragePool pool,
      CIMInstance poolInstance,
      AccessProfile profile,
      String poolClassName,
      String supportedVolumeTypes,
      Set<String> protocols,
      Map<URI, StoragePool> poolsToMatchWithVpool,
      StorageSystem device)
      throws URISyntaxException, IOException {
    boolean newPool = false;
    boolean modifiedPool = false; // indicates whether to add to modified pools list or not
    if (null == pool) {
      String instanceID = getCIMPropertyValue(poolInstance, Constants.INSTANCEID);
      String nativeIdFromInstance = getNativeIDFromInstance(instanceID);
      newPool = true;
      pool = new StoragePool();
      pool.setId(URIUtil.createId(StoragePool.class));
      pool.setPoolName(getCIMPropertyValue(poolInstance, POOL_ID));
      pool.setNativeId(nativeIdFromInstance);
      pool.setStorageDevice(profile.getSystemId());
      pool.setPoolServiceType(PoolServiceType.block.toString());
      String poolNativeGuid = NativeGUIDGenerator.generateNativeGuid(_dbClient, pool);
      pool.setNativeGuid(poolNativeGuid);
      pool.setLabel(poolNativeGuid);
      // setting default values on Pool Creation for VMAX and VNX
      pool.setMaximumThickVolumeSize(0L);
      pool.setMinimumThickVolumeSize(0L);
      pool.setMaximumThinVolumeSize(0L);
      pool.setMinimumThinVolumeSize(0L);
      if (device.getAutoTieringEnabled()) {
        pool.setAutoTieringEnabled(Boolean.TRUE);
      } else {
        pool.setAutoTieringEnabled(Boolean.FALSE);
      }
      _logger.info(
          String.format(
              "Maximum default limits for volume capacity in storage pool %s / %s : \n   "
                  + "max thin volume capacity: %s, max thick volume capacity: %s ",
              pool.getPoolName(),
              pool.getId(),
              pool.getMaximumThinVolumeSize(),
              pool.getMaximumThickVolumeSize()));

      // set default utilization/subscription limits
      double poolSubscriptionPercent =
          CapacityMatcher.getMaxPoolSubscriptionPercentage(pool, _coordinator);
      double poolUtilizationPercent =
          CapacityMatcher.getMaxPoolUtilizationPercentage(pool, _coordinator);
      pool.setMaxThinPoolSubscriptionPercentage((int) poolSubscriptionPercent);
      pool.setMaxPoolUtilizationPercentage((int) poolUtilizationPercent);
    }

    String maxSubscriptionPercent =
        getCIMPropertyValue(poolInstance, SmisConstants.CP_EMCMAXSUBSCRIPTIONPERCENT);
    _logger.info(
        String.format(
            "Discovered maximum subscription percent of storage pool %s from array : %s ",
            pool.getPoolName(), maxSubscriptionPercent));
    // null value indicates "not available".
    Integer newMaxSubscriptionPercentFromArray =
        maxSubscriptionPercent == null ? null : new Integer(maxSubscriptionPercent);
    _logger.info(
        String.format(
            "New maximum subscription percent of storage pool %s from array : %s ",
            pool.getPoolName(), newMaxSubscriptionPercentFromArray));
    processMaxSubscriptionPercent(
        newMaxSubscriptionPercentFromArray, pool, _dbClient, _eventManager);

    String subscribedCapacity =
        getCIMPropertyValue(poolInstance, SmisConstants.CP_SUBSCRIBEDCAPACITY);
    if (null != subscribedCapacity) {
      pool.setSubscribedCapacity(ControllerUtils.convertBytesToKBytes(subscribedCapacity));
    }
    pool.setFreeCapacity(SmisUtils.getFreeCapacity(poolInstance));
    pool.setTotalCapacity(SmisUtils.getTotalCapacity(poolInstance));
    pool.setPoolClassName(poolClassName);
    pool.setSupportedResourceTypes(supportedVolumeTypes);
    String operationalStatus = determineOperationalStatus(poolInstance);
    if (!newPool
        && (ImplicitPoolMatcher.checkPoolPropertiesChanged(
                pool.getOperationalStatus(), operationalStatus)
            || ImplicitPoolMatcher.checkPoolPropertiesChanged(pool.getProtocols(), protocols)
            || ImplicitPoolMatcher.checkPoolPropertiesChanged(
                pool.getCompatibilityStatus(),
                DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name())
            || ImplicitPoolMatcher.checkPoolPropertiesChanged(
                pool.getDiscoveryStatus(), DiscoveredDataObject.DiscoveryStatus.VISIBLE.name()))) {
      modifiedPool = true;
    }
    pool.addProtocols(protocols);
    pool.setOperationalStatus(operationalStatus);
    pool.setCompatibilityStatus(DiscoveredDataObject.CompatibilityStatus.COMPATIBLE.name());
    pool.setDiscoveryStatus(DiscoveredDataObject.DiscoveryStatus.VISIBLE.name());

    Set<String> diskDrives = new HashSet<String>();
    String driveTypes = getCIMPropertyValue(poolInstance, EMC_DRIVE_TYPE);
    if (null != driveTypes) {
      String driveTypesArr[] = driveTypes.split(SPACE_STR_DELIM);
      if (device.checkIfVmax3()
          && driveTypesArr.length == 1
          && driveTypesArr[0].equals(MIXED_DRIVE_TYPE)) {
        driveTypesArr = getVMAX3PoolDriveTypes(device, poolInstance);
      }
      for (String driveType : driveTypesArr) {
        String driveDisplayName = SupportedDriveTypeValues.getDiskDriveDisplayName(driveType);
        if (null == driveDisplayName) {
          _logger.warn(
              "UnSupported DiskDrive Type : {} resulting in drives not getting discovered for this pool: {}",
              driveType,
              getCIMPropertyValue(poolInstance, Constants.INSTANCEID));
          continue;
        }

        diskDrives.add(driveDisplayName);
      }
      if (!newPool
          && !modifiedPool
          && ImplicitPoolMatcher.checkPoolPropertiesChanged(
              pool.getSupportedDriveTypes(), diskDrives)) {
        modifiedPool = true;
      }
      pool.addDriveTypes(diskDrives);
    }
    _logger.info("Discovered disk drives:[{}] for pool id:{}", driveTypes, pool.getId());

    if (newPool) {
      _newPoolList.add(pool);
      // add new pools to modified pools list to consider them for implicit pool matching.
      if (!poolsToMatchWithVpool.containsKey(pool.getId())) {
        poolsToMatchWithVpool.put(pool.getId(), pool);
      }
    } else {
      _updatePoolList.add(pool);
      // add to modified pool list if pool's property which is required for vPool matcher, has
      // changed.
      // No need to check whether the pool is already there in the list here
      // because this processor is the first to discover pools.
      if (modifiedPool && !poolsToMatchWithVpool.containsKey(pool.getId())) {
        poolsToMatchWithVpool.put(pool.getId(), pool);
      }
    }
  }