/**
   * get Matched Virtual Pools For Pool. This is called to calculate supported vpools during
   * unmanaged objects discovery
   *
   * @param poolUri
   * @return
   */
  public static StringSet getMatchedVirtualPoolsForPool(
      DbClient dbClient, URI poolUri, String isThinlyProvisionedUnManagedObject) {
    StringSet vpoolUriSet = new StringSet();
    // We should match all virtual pools as below:
    // 1) Virtual pools which have useMatchedPools set to true and have the storage pool in their
    // matched pools
    // 2) Virtual pools which have the storage pool in their assigned pools

    URIQueryResultList vpoolMatchedPoolsResultList = new URIQueryResultList();
    dbClient.queryByConstraint(
        ContainmentConstraint.Factory.getMatchedPoolVirtualPoolConstraint(poolUri),
        vpoolMatchedPoolsResultList);
    List<VirtualPool> vPoolsMatchedPools =
        dbClient.queryObject(VirtualPool.class, vpoolMatchedPoolsResultList);
    String provisioningTypeUnManagedObject =
        UnManagedVolume.SupportedProvisioningType.getProvisioningType(
            isThinlyProvisionedUnManagedObject);
    StoragePool storagePool = dbClient.queryObject(StoragePool.class, poolUri);
    for (VirtualPool vPool : vPoolsMatchedPools) {
      if (!VirtualPool.vPoolSpecifiesHighAvailability(vPool)) {
        List<StoragePool> validPools = VirtualPool.getValidStoragePools(vPool, dbClient, true);
        for (StoragePool sPool : validPools) {
          if (sPool.getId().equals(storagePool.getId())
              && provisioningTypeUnManagedObject.equalsIgnoreCase(
                  vPool.getSupportedProvisioningType())) {
            vpoolUriSet.add(vPool.getId().toString());
            break;
          }
        }
      }
    }

    return vpoolUriSet;
  }
  public static void checkStoragePortsNotVisibleForSMI(
      List<StoragePort> discoveredPorts,
      Set<URI> systemsToRunRPConnectivity,
      List<StoragePort> portsToRunNetworkConnectivity,
      Map<URI, StoragePool> poolsToMatchWithVpool,
      DbClient dbClient,
      URI storageSystemId) {
    List<StoragePort> notVisiblePorts =
        checkStoragePortsNotVisible(discoveredPorts, dbClient, storageSystemId);

    // Systems used to run RP connectivity later after runing pool matcher
    if (systemsToRunRPConnectivity != null) {
      systemsToRunRPConnectivity.addAll(
          StoragePoolAssociationHelper.getStorageSytemsFromPorts(notVisiblePorts, null));
    }

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

    // ports used later to run Transport Zone connectivity
    if (portsToRunNetworkConnectivity != null) {
      portsToRunNetworkConnectivity.addAll(notVisiblePorts);
    }
  }
  // Getting all the vpools
  public static StringSet getMatchedVirtualPoolsForPool(DbClient dbClient, URI poolUri) {
    StringSet vpoolUriSet = new StringSet();
    // We should match all virtual pools as below:
    // 1) Virtual pools which have useMatchedPools set to true and have the storage pool in their
    // matched pools
    // 2) Virtual pools which have the storage pool in their assigned pools

    URIQueryResultList vpoolMatchedPoolsResultList = new URIQueryResultList();
    dbClient.queryByConstraint(
        ContainmentConstraint.Factory.getMatchedPoolVirtualPoolConstraint(poolUri),
        vpoolMatchedPoolsResultList);
    List<VirtualPool> vPoolsMatchedPools =
        dbClient.queryObject(VirtualPool.class, vpoolMatchedPoolsResultList);
    StoragePool storagePool = dbClient.queryObject(StoragePool.class, poolUri);
    for (VirtualPool vPool : vPoolsMatchedPools) {
      List<StoragePool> validPools = VirtualPool.getValidStoragePools(vPool, dbClient, true);
      for (StoragePool sPool : validPools) {
        if (sPool.getId().equals(storagePool.getId())) {
          vpoolUriSet.add(vPool.getId().toString());
          break;
        }
      }
    }

    return vpoolUriSet;
  }
  /*
   * (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()));
  }
  public static List<StoragePool> checkStoragePoolsNotVisible(
      List<StoragePool> discoveredPools, DbClient dbClient, URI storageSystemId) {
    List<StoragePool> modifiedPools = new ArrayList<StoragePool>();
    // Get the pools previously discovered
    URIQueryResultList storagePoolURIs = new URIQueryResultList();
    dbClient.queryByConstraint(
        ContainmentConstraint.Factory.getStorageDeviceStoragePoolConstraint(storageSystemId),
        storagePoolURIs);
    Iterator<URI> storagePoolIter = storagePoolURIs.iterator();

    List<URI> existingPoolsURI = new ArrayList<URI>();
    while (storagePoolIter.hasNext()) {
      existingPoolsURI.add(storagePoolIter.next());
    }

    List<URI> discoveredPoolsURI = new ArrayList<URI>();
    for (StoragePool pool : discoveredPools) {
      discoveredPoolsURI.add(pool.getId());
    }

    Set<URI> poolDiff =
        Sets.difference(new HashSet<URI>(existingPoolsURI), new HashSet<URI>(discoveredPoolsURI));

    if (!poolDiff.isEmpty()) {
      Iterator<StoragePool> storagePoolIt =
          dbClient.queryIterativeObjects(StoragePool.class, poolDiff, true);
      while (storagePoolIt.hasNext()) {
        StoragePool pool = storagePoolIt.next();
        modifiedPools.add(pool);
        _log.info(
            "Setting discovery status of pool {} : {} as NOTVISIBLE",
            pool.getLabel(),
            pool.getId());
        pool.setDiscoveryStatus(DiscoveredDataObject.DiscoveryStatus.NOTVISIBLE.name());
        dbClient.persistObject(pool);
      }
    }

    return modifiedPools;
  }
  /** {@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;
    }
  }
  @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) {
        }
      }
    }
  }
  /**
   * create StorageVolume Info Object
   *
   * @param unManagedVolume
   * @param volumeInstance
   * @param unManagedVolumeNativeGuid
   * @param pool
   * @return
   */
  private UnManagedVolume createUnManagedVolume(
      UnManagedVolume unManagedVolume,
      CIMInstance volumeInstance,
      String unManagedVolumeNativeGuid,
      StoragePool pool,
      StorageSystem system,
      String volumeNativeGuid,
      // to make the code uniform, passed in all the below sets as
      // arguments
      Map<String, VolHostIOObject> exportedVolumes,
      Set<String> existingVolumesInCG,
      Map<String, RemoteMirrorObject> volumeToRAGroupMap,
      Map<String, LocalReplicaObject> volumeToLocalReplicaMap,
      Set<String> poolSupportedSLONames,
      Map<String, Object> keyMap) {
    _logger.info("Process UnManagedVolume {}", unManagedVolumeNativeGuid);
    try {
      boolean created = false;
      if (null == unManagedVolume) {
        unManagedVolume = new UnManagedVolume();
        unManagedVolume.setId(URIUtil.createId(UnManagedVolume.class));
        unManagedVolume.setNativeGuid(unManagedVolumeNativeGuid);
        unManagedVolume.setStorageSystemUri(system.getId());
        created = true;
      }

      // reset the auto-tiering info for unmanaged volumes already present
      // in db
      // so that the tiering info is updated correctly later
      if (!created) {
        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.AUTO_TIERING_POLICIES.toString(), "");
        unManagedVolume.putVolumeCharacterstics(
            SupportedVolumeCharacterstics.IS_AUTO_TIERING_ENABLED.toString(), "false");

        // reset local replica info
        unManagedVolume.putVolumeCharacterstics(
            SupportedVolumeCharacterstics.IS_FULL_COPY.name(), FALSE);
        unManagedVolume.putVolumeCharacterstics(
            SupportedVolumeCharacterstics.IS_LOCAL_MIRROR.name(), FALSE);
        unManagedVolume.putVolumeCharacterstics(
            SupportedVolumeCharacterstics.IS_SNAP_SHOT.name(), FALSE);
        unManagedVolume.putVolumeCharacterstics(
            SupportedVolumeCharacterstics.HAS_REPLICAS.name(), FALSE);

        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.REPLICA_STATE.name(), new StringSet());

        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.LOCAL_REPLICA_SOURCE_VOLUME.name(), new StringSet());
        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.SYNC_STATE.name(), new StringSet());
        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.SYNC_TYPE.name(), new StringSet());
        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.SYNCHRONIZED_INSTANCE.name(), new StringSet());

        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.IS_SYNC_ACTIVE.name(), new StringSet());
        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.NEEDS_COPY_TO_TARGET.name(), new StringSet());
        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.TECHNOLOGY_TYPE.name(), new StringSet());
        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.SETTINGS_INSTANCE.name(), new StringSet());

        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.FULL_COPIES.name(), new StringSet());
        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.MIRRORS.name(), new StringSet());
        unManagedVolume
            .getVolumeInformation()
            .put(SupportedVolumeInformation.SNAPSHOTS.name(), new StringSet());
      }

      Map<String, StringSet> unManagedVolumeInformation = new HashMap<String, StringSet>();
      Map<String, String> unManagedVolumeCharacteristics = new HashMap<String, String>();

      if (null != system) {
        StringSet systemTypes = new StringSet();
        systemTypes.add(system.getSystemType());
        unManagedVolumeInformation.put(
            SupportedVolumeInformation.SYSTEM_TYPE.toString(), systemTypes);
      }

      if (exportedVolumes != null && exportedVolumes.containsKey(volumeNativeGuid)) {
        VolHostIOObject obj = exportedVolumes.get(volumeNativeGuid);
        if (null != obj) {
          StringSet bwValues = new StringSet();
          bwValues.add(obj.getHostIoBw());
          if (unManagedVolumeInformation.get(
                  SupportedVolumeInformation.EMC_MAXIMUM_IO_BANDWIDTH.toString())
              == null) {
            unManagedVolumeInformation.put(
                SupportedVolumeInformation.EMC_MAXIMUM_IO_BANDWIDTH.toString(), bwValues);
          } else {
            unManagedVolumeInformation
                .get(SupportedVolumeInformation.EMC_MAXIMUM_IO_BANDWIDTH.toString())
                .replace(bwValues);
          }

          StringSet iopsVal = new StringSet();
          iopsVal.add(obj.getHostIops());

          if (unManagedVolumeInformation.get(SupportedVolumeInformation.EMC_MAXIMUM_IOPS.toString())
              == null) {
            unManagedVolumeInformation.put(
                SupportedVolumeInformation.EMC_MAXIMUM_IOPS.toString(), iopsVal);
          } else {
            unManagedVolumeInformation
                .get(SupportedVolumeInformation.EMC_MAXIMUM_IOPS.toString())
                .replace(iopsVal);
          }
        }
        unManagedVolumeCharacteristics.put(
            SupportedVolumeCharacterstics.IS_VOLUME_EXPORTED.toString(), TRUE);

      } else {
        unManagedVolumeCharacteristics.put(
            SupportedVolumeCharacterstics.IS_VOLUME_EXPORTED.toString(), FALSE);
        StringSet bwValues = new StringSet();
        bwValues.add("0");

        if (unManagedVolumeInformation.get(
                SupportedVolumeInformation.EMC_MAXIMUM_IO_BANDWIDTH.toString())
            == null) {
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.EMC_MAXIMUM_IO_BANDWIDTH.toString(), bwValues);
        } else {
          unManagedVolumeInformation
              .get(SupportedVolumeInformation.EMC_MAXIMUM_IO_BANDWIDTH.toString())
              .replace(bwValues);
        }

        StringSet iopsVal = new StringSet();
        iopsVal.add("0");

        if (unManagedVolumeInformation.get(SupportedVolumeInformation.EMC_MAXIMUM_IOPS.toString())
            == null) {
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.EMC_MAXIMUM_IOPS.toString(), iopsVal);
        } else {
          unManagedVolumeInformation
              .get(SupportedVolumeInformation.EMC_MAXIMUM_IOPS.toString())
              .replace(iopsVal);
        }
      }

      // Set SLOName only for VMAX3 exported volumes
      if (system.checkIfVmax3()) {
        // If there are no slonames defined for a pool or no slo
        // set for a volume, update the tiering_enabled to false.
        if (poolSupportedSLONames.isEmpty() || !keyMap.containsKey(Constants.VOLUMES_WITH_SLOS)) {
          unManagedVolumeCharacteristics.put(
              SupportedVolumeCharacterstics.IS_AUTO_TIERING_ENABLED.toString(),
              Boolean.FALSE.toString());
        } else {
          Map<String, String> volumesWithSLO =
              (Map<String, String>) keyMap.get(Constants.VOLUMES_WITH_SLOS);
          if (volumesWithSLO.containsKey(volumeNativeGuid)) {
            String sloName = volumesWithSLO.get(volumeNativeGuid);
            _logger.debug("formattedSLOName: {}", sloName);
            updateSLOPolicies(
                poolSupportedSLONames,
                unManagedVolumeInformation,
                unManagedVolumeCharacteristics,
                sloName);
          } else {
            unManagedVolumeCharacteristics.put(
                SupportedVolumeCharacterstics.IS_AUTO_TIERING_ENABLED.toString(),
                Boolean.FALSE.toString());
          }
        }
      }

      if (existingVolumesInCG != null && existingVolumesInCG.contains(volumeNativeGuid)) {
        unManagedVolumeCharacteristics.put(
            SupportedVolumeCharacterstics.IS_VOLUME_ADDED_TO_CONSISTENCYGROUP.toString(), TRUE);
      } else {
        unManagedVolumeCharacteristics.put(
            SupportedVolumeCharacterstics.IS_VOLUME_ADDED_TO_CONSISTENCYGROUP.toString(), FALSE);
      }

      Object raidLevelObj;
      boolean isIngestable;
      String isBound;
      String isThinlyProvisioned;
      String isMetaVolume;
      String allocCapacity;
      // Set the attributes for new smis version.
      if (keyMap.containsKey(Constants.IS_NEW_SMIS_PROVIDER)
          && Boolean.valueOf(keyMap.get(Constants.IS_NEW_SMIS_PROVIDER).toString())) {
        unManagedVolume.setLabel(getCIMPropertyValue(volumeInstance, NAME));
        raidLevelObj =
            volumeInstance.getPropertyValue(
                SupportedVolumeInformation.RAID_LEVEL.getAlternateKey());
        isBound =
            getCIMPropertyValue(
                volumeInstance, SupportedVolumeCharacterstics.IS_BOUND.getAlterCharacterstic());
        isIngestable = isVolumeIngestable(volumeInstance, isBound, USAGE);
        isThinlyProvisioned = getCIMPropertyValue(volumeInstance, THINLY_PROVISIONED);
        isMetaVolume =
            getCIMPropertyValue(
                volumeInstance,
                SupportedVolumeCharacterstics.IS_METAVOLUME.getAlterCharacterstic());
        allocCapacity =
            getAllocatedCapacity(volumeInstance, _volumeToSpaceConsumedMap, system.checkIfVmax3());
      } else {
        unManagedVolume.setLabel(getCIMPropertyValue(volumeInstance, SVELEMENT_NAME));
        isBound =
            getCIMPropertyValue(
                volumeInstance, SupportedVolumeCharacterstics.IS_BOUND.getCharacterstic());
        raidLevelObj =
            volumeInstance.getPropertyValue(SupportedVolumeInformation.RAID_LEVEL.getInfoKey());
        isIngestable = isVolumeIngestable(volumeInstance, isBound, SVUSAGE);
        isThinlyProvisioned = getCIMPropertyValue(volumeInstance, EMC_THINLY_PROVISIONED);
        isMetaVolume =
            getCIMPropertyValue(
                volumeInstance, SupportedVolumeCharacterstics.IS_METAVOLUME.getCharacterstic());
        allocCapacity = getCIMPropertyValue(volumeInstance, EMC_ALLOCATED_CAPACITY);
      }

      if (null != raidLevelObj) {
        StringSet raidLevels = new StringSet();
        raidLevels.add(raidLevelObj.toString());
        unManagedVolumeInformation.put(
            SupportedVolumeInformation.RAID_LEVEL.toString(), raidLevels);
      }

      if (null != isBound) {
        unManagedVolumeCharacteristics.put(
            SupportedVolumeCharacterstics.IS_BOUND.toString(), isBound);
      }
      if (null != isThinlyProvisioned) {
        unManagedVolumeCharacteristics.put(
            SupportedVolumeCharacterstics.IS_THINLY_PROVISIONED.toString(), isThinlyProvisioned);
      }

      if (null != isMetaVolume) {
        unManagedVolumeCharacteristics.put(
            SupportedVolumeCharacterstics.IS_METAVOLUME.toString(), isMetaVolume);
      }

      // only Volumes with Usage 2 can be ingestable, other volumes
      // [SAVE,VAULT...] apart from replicas have usage other than 2
      // Volumes which are set EMCIsBound as false cannot be ingested
      if (isIngestable) {
        unManagedVolumeCharacteristics.put(
            SupportedVolumeCharacterstics.IS_INGESTABLE.toString(), TRUE);
      } else {
        unManagedVolumeCharacteristics.put(
            SupportedVolumeCharacterstics.IS_INGESTABLE.toString(), FALSE);
      }

      if (volumeToRAGroupMap.containsKey(unManagedVolume.getNativeGuid())) {
        RemoteMirrorObject rmObj = volumeToRAGroupMap.get(unManagedVolume.getNativeGuid());
        _logger.info("Found RA Object {}", rmObj.toString());
        if (RemoteMirrorObject.Types.SOURCE.toString().equalsIgnoreCase(rmObj.getType())) {
          _logger.info("Found Source, updating targets {}", rmObj.getTargetVolumenativeGuids());
          // setting target Volumes
          if (unManagedVolumeInformation.get(SupportedVolumeInformation.REMOTE_MIRRORS.toString())
              == null) {
            unManagedVolumeInformation.put(
                SupportedVolumeInformation.REMOTE_MIRRORS.toString(),
                rmObj.getTargetVolumenativeGuids());
          } else {
            if (null == rmObj.getTargetVolumenativeGuids()
                || rmObj.getTargetVolumenativeGuids().size() == 0) {
              unManagedVolumeInformation
                  .get(SupportedVolumeInformation.REMOTE_MIRRORS.toString())
                  .clear();
            } else {
              unManagedVolumeInformation
                  .get(SupportedVolumeInformation.REMOTE_MIRRORS.toString())
                  .replace(rmObj.getTargetVolumenativeGuids());
            }
          }
        } else if (RemoteMirrorObject.Types.TARGET.toString().equalsIgnoreCase(rmObj.getType())) {

          _logger.info(
              "Found Target {}, updating copyMode {}, RA Group",
              unManagedVolume.getNativeGuid(),
              rmObj.getCopyMode());
          // setting srdfParent
          StringSet parentVolume = new StringSet();
          parentVolume.add(rmObj.getSourceVolumeNativeGuid());
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.REMOTE_MIRROR_SOURCE_VOLUME.toString(), parentVolume);

          // setting RAGroup
          StringSet raGroup = new StringSet();
          raGroup.add(rmObj.getRaGroupUri().toString());
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.REMOTE_MIRROR_RDF_GROUP.toString(), raGroup);
        }
        // setting Copy Modes
        StringSet copyModes = new StringSet();
        copyModes.add(rmObj.getCopyMode());
        if (unManagedVolumeInformation.get(SupportedVolumeInformation.REMOTE_COPY_MODE.toString())
            == null) {
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.REMOTE_COPY_MODE.toString(), copyModes);
        } else {
          unManagedVolumeInformation
              .get(SupportedVolumeInformation.REMOTE_COPY_MODE.toString())
              .replace(copyModes);
        }
        // setting Volume Type

        StringSet volumeType = new StringSet();
        volumeType.add(rmObj.getType());
        unManagedVolumeInformation.put(
            SupportedVolumeInformation.REMOTE_VOLUME_TYPE.toString(), volumeType);

        unManagedVolumeCharacteristics.put(
            SupportedVolumeCharacterstics.REMOTE_MIRRORING.toString(), TRUE);
      } else {
        unManagedVolumeCharacteristics.put(
            SupportedVolumeCharacterstics.REMOTE_MIRRORING.toString(), FALSE);
      }

      // handle clones, local mirrors and snapshots
      boolean isLocalReplica = false;
      if (volumeToLocalReplicaMap.containsKey(unManagedVolume.getNativeGuid())) {
        _logger.info("Found in localReplicaMap {}", unManagedVolume.getNativeGuid());
        LocalReplicaObject lrObj = volumeToLocalReplicaMap.get(unManagedVolume.getNativeGuid());
        isLocalReplica = lrObj.isReplica();

        // setting targets
        StringSet fullCopies = lrObj.getFullCopies();
        if (fullCopies != null && !fullCopies.isEmpty()) {
          unManagedVolumeInformation.put(SupportedVolumeInformation.FULL_COPIES.name(), fullCopies);
        }

        StringSet mirrors = lrObj.getMirrors();
        if (mirrors != null && !mirrors.isEmpty()) {
          unManagedVolumeInformation.put(SupportedVolumeInformation.MIRRORS.name(), mirrors);
        }

        StringSet snapshots = lrObj.getSnapshots();
        if (snapshots != null && !snapshots.isEmpty()) {
          unManagedVolumeInformation.put(SupportedVolumeInformation.SNAPSHOTS.name(), snapshots);
        }

        if (lrObj.hasReplica()) {
          // set the HAS_REPLICAS property
          unManagedVolumeCharacteristics.put(
              SupportedVolumeCharacterstics.HAS_REPLICAS.name(), TRUE);
        }

        if (LocalReplicaObject.Types.FullCopy.equals(lrObj.getType())) {
          _logger.info("Found Clone {}", unManagedVolume.getNativeGuid());
          // setting clone specific info
          unManagedVolumeCharacteristics.put(
              SupportedVolumeCharacterstics.IS_FULL_COPY.name(), TRUE);
          StringSet sourceVolume = new StringSet();
          sourceVolume.add(lrObj.getSourceNativeGuid());
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.LOCAL_REPLICA_SOURCE_VOLUME.name(), sourceVolume);

          StringSet isSyncActive = new StringSet();
          isSyncActive.add(new Boolean(lrObj.isSyncActive()).toString());
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.IS_SYNC_ACTIVE.name(), isSyncActive);

          StringSet replicaState = new StringSet();
          replicaState.add(lrObj.getReplicaState());
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.REPLICA_STATE.name(), replicaState);
        } else if (LocalReplicaObject.Types.BlockMirror.equals(lrObj.getType())) {
          _logger.info("Found Local Mirror {}", unManagedVolume.getNativeGuid());
          // setting local mirror specific info
          unManagedVolumeCharacteristics.put(
              SupportedVolumeCharacterstics.IS_LOCAL_MIRROR.name(), TRUE);
          StringSet sourceVolume = new StringSet();
          sourceVolume.add(lrObj.getSourceNativeGuid());
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.LOCAL_REPLICA_SOURCE_VOLUME.name(), sourceVolume);

          StringSet syncState = new StringSet();
          syncState.add(lrObj.getSyncState());
          unManagedVolumeInformation.put(SupportedVolumeInformation.SYNC_STATE.name(), syncState);

          StringSet syncType = new StringSet();
          syncType.add(lrObj.getSyncType());
          unManagedVolumeInformation.put(SupportedVolumeInformation.SYNC_TYPE.name(), syncType);

          String syncedInst = lrObj.getSynchronizedInstance();
          if (syncedInst != null) {
            StringSet synchronizedInstance = new StringSet();
            synchronizedInstance.add(syncedInst);
            unManagedVolumeInformation.put(
                SupportedVolumeInformation.SYNCHRONIZED_INSTANCE.name(), synchronizedInstance);
          }
        } else if (LocalReplicaObject.Types.BlockSnapshot.equals(lrObj.getType())) {
          _logger.info("Found Snapshot {}", unManagedVolume.getNativeGuid());
          // setting snapshot specific info
          unManagedVolumeCharacteristics.put(
              SupportedVolumeCharacterstics.IS_SNAP_SHOT.name(), TRUE);
          StringSet sourceVolume = new StringSet();
          sourceVolume.add(lrObj.getSourceNativeGuid());
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.LOCAL_REPLICA_SOURCE_VOLUME.name(), sourceVolume);

          StringSet isSyncActive = new StringSet();
          isSyncActive.add(new Boolean(lrObj.isSyncActive()).toString());
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.IS_SYNC_ACTIVE.name(), isSyncActive);

          StringSet needsCopyToTarget = new StringSet();
          needsCopyToTarget.add(new Boolean(lrObj.isNeedsCopyToTarget()).toString());
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.NEEDS_COPY_TO_TARGET.name(), needsCopyToTarget);

          StringSet technologyType = new StringSet();
          technologyType.add(lrObj.getTechnologyType());
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.TECHNOLOGY_TYPE.name(), technologyType);

          String settingsInst = lrObj.getSettingsInstance();
          if (settingsInst != null) {
            StringSet settingsInstance = new StringSet();
            settingsInstance.add(settingsInst);
            unManagedVolumeInformation.put(
                SupportedVolumeInformation.SETTINGS_INSTANCE.name(), settingsInstance);
          }
        }
      }

      // set volume's isSyncActive
      if (!isLocalReplica) {
        StringSet isSyncActive = new StringSet();
        isSyncActive.add(TRUE);
        unManagedVolumeInformation.put(
            SupportedVolumeInformation.IS_SYNC_ACTIVE.name(), isSyncActive);
      }

      if (null != pool) {
        unManagedVolume.setStoragePoolUri(pool.getId());
        StringSet pools = new StringSet();
        pools.add(pool.getId().toString());
        unManagedVolumeInformation.put(SupportedVolumeInformation.STORAGE_POOL.toString(), pools);
        StringSet driveTypes = pool.getSupportedDriveTypes();
        if (null != driveTypes) {
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.DISK_TECHNOLOGY.toString(), driveTypes);
        }
        StringSet matchedVPools =
            DiscoveryUtils.getMatchedVirtualPoolsForPool(
                _dbClient,
                pool.getId(),
                unManagedVolumeCharacteristics.get(
                    SupportedVolumeCharacterstics.IS_THINLY_PROVISIONED.toString()));
        if (unManagedVolumeInformation.containsKey(
            SupportedVolumeInformation.SUPPORTED_VPOOL_LIST.toString())) {

          _logger.debug("Matched Pools :" + Joiner.on("\t").join(matchedVPools));
          if (null != matchedVPools && matchedVPools.size() == 0) {
            // replace with empty string set doesn't work, hence
            // added explicit code to remove all
            unManagedVolumeInformation
                .get(SupportedVolumeInformation.SUPPORTED_VPOOL_LIST.toString())
                .clear();
          } else {
            // replace with new StringSet
            unManagedVolumeInformation
                .get(SupportedVolumeInformation.SUPPORTED_VPOOL_LIST.toString())
                .replace(matchedVPools);
            _logger.info(
                "Replaced Pools :"
                    + Joiner.on("\t")
                        .join(
                            unManagedVolumeInformation.get(
                                SupportedVolumeInformation.SUPPORTED_VPOOL_LIST.toString())));
          }
        } else {
          unManagedVolumeInformation.put(
              SupportedVolumeInformation.SUPPORTED_VPOOL_LIST.toString(), matchedVPools);
        }
      }

      // set allocated capacity
      if (allocCapacity != null) {
        StringSet allocCapacitySet = new StringSet();
        allocCapacitySet.add(allocCapacity);
        unManagedVolumeInformation.put(
            SupportedVolumeInformation.ALLOCATED_CAPACITY.toString(), allocCapacitySet);
      }

      StringSet provCapacity = new StringSet();
      provCapacity.add(String.valueOf(returnProvisionedCapacity(volumeInstance, keyMap)));
      unManagedVolumeInformation.put(
          SupportedVolumeInformation.PROVISIONED_CAPACITY.toString(), provCapacity);
      injectVolumeInformation(unManagedVolume, volumeInstance, unManagedVolumeInformation);
      injectVolumeCharacterstics(unManagedVolume, volumeInstance, unManagedVolumeCharacteristics);
      unManagedVolume.getUnmanagedExportMasks().clear();
      unManagedVolume.getInitiatorUris().clear();
      unManagedVolume.getInitiatorNetworkIds().clear();
      if (created) {
        _unManagedVolumesInsert.add(unManagedVolume);
      } else {
        _unManagedVolumesUpdate.add(unManagedVolume);
      }

    } catch (Exception e) {
      _logger.error("Exception: ", e);
    }
    return unManagedVolume;
  }
  /*
   * (non-Javadoc)
   *
   * @see com.emc.storageos.volumecontroller.BlockStorageDevice#doCreateVolumes(com.emc.storageos.db.client.model.StorageSystem,
   * com.emc.storageos.db.client.model.StoragePool, java.lang.String, java.util.List,
   * com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper, com.emc.storageos.volumecontroller.TaskCompleter)
   */
  @Override
  public void doCreateVolumes(
      StorageSystem storageSystem,
      StoragePool storagePool,
      String opId,
      List<Volume> volumes,
      VirtualPoolCapabilityValuesWrapper capabilities,
      TaskCompleter taskCompleter)
      throws DeviceControllerException {
    String label = null;
    Long capacity = null;
    boolean isThinVolume = false;
    boolean opCreationFailed = false;
    StringBuilder logMsgBuilder =
        new StringBuilder(
            String.format(
                "Create Volume Start - Array:%s, Pool:%s",
                storageSystem.getSerialNumber(), storagePool.getNativeGuid()));
    for (Volume volume : volumes) {
      logMsgBuilder.append(
          String.format(
              "%nVolume:%s , IsThinlyProvisioned: %s",
              volume.getLabel(), volume.getThinlyProvisioned()));

      if ((label == null) && (volumes.size() == 1)) {
        String tenantName = "";
        try {
          TenantOrg tenant = dbClient.queryObject(TenantOrg.class, volume.getTenant().getURI());
          tenantName = tenant.getLabel();
        } catch (DatabaseException e) {
          log.error("Error lookup TenantOrb object", e);
        }
        label =
            nameGenerator.generate(
                tenantName,
                volume.getLabel(),
                volume.getId().toString(),
                '-',
                HDSConstants.MAX_VOLUME_NAME_LENGTH);
      }

      if (capacity == null) {
        capacity = volume.getCapacity();
      }
      isThinVolume = volume.getThinlyProvisioned();
    }
    log.info(logMsgBuilder.toString());
    try {
      multiVolumeCheckForHitachiModel(volumes, storageSystem);

      HDSApiClient hdsApiClient =
          hdsApiFactory.getClient(
              HDSUtils.getHDSServerManagementServerInfo(storageSystem),
              storageSystem.getSmisUserName(),
              storageSystem.getSmisPassword());
      String systemObjectID = HDSUtils.getSystemObjectID(storageSystem);
      String poolObjectID = HDSUtils.getPoolObjectID(storagePool);
      String asyncTaskMessageId = null;

      // isThinVolume = true, creates VirtualVolumes
      // isThinVolume = false, creates LogicalUnits
      if (isThinVolume) {
        asyncTaskMessageId =
            hdsApiClient.createThinVolumes(
                systemObjectID,
                storagePool.getNativeId(),
                capacity,
                volumes.size(),
                label,
                QUICK_FORMAT_TYPE,
                storageSystem.getModel());
      } else if (!isThinVolume) {
        asyncTaskMessageId =
            hdsApiClient.createThickVolumes(
                systemObjectID,
                poolObjectID,
                capacity,
                volumes.size(),
                label,
                null,
                storageSystem.getModel(),
                null);
      }

      if (asyncTaskMessageId != null) {
        HDSJob createHDSJob =
            (volumes.size() > 1)
                ? new HDSCreateMultiVolumeJob(
                    asyncTaskMessageId,
                    volumes.get(0).getStorageController(),
                    storagePool.getId(),
                    volumes.size(),
                    taskCompleter)
                : new HDSCreateVolumeJob(
                    asyncTaskMessageId,
                    volumes.get(0).getStorageController(),
                    storagePool.getId(),
                    taskCompleter);
        ControllerServiceImpl.enqueueJob(new QueueJob(createHDSJob));
      }
    } catch (final InternalException e) {
      log.error("Problem in doCreateVolumes: ", e);
      opCreationFailed = true;
      taskCompleter.error(dbClient, e);
    } catch (final Exception e) {
      log.error("Problem in doCreateVolumes: ", e);
      opCreationFailed = true;
      ServiceError serviceError =
          DeviceControllerErrors.hds.methodFailed("doCreateVolumes", e.getMessage());
      taskCompleter.error(dbClient, serviceError);
    }
    if (opCreationFailed) {
      for (Volume vol : volumes) {
        vol.setInactive(true);
        dbClient.persistObject(vol);
      }
    }

    logMsgBuilder =
        new StringBuilder(
            String.format(
                "Create Volumes End - Array:%s, Pool:%s",
                storageSystem.getSerialNumber(), storagePool.getNativeGuid()));
    for (Volume volume : volumes) {
      logMsgBuilder.append(String.format("%nVolume:%s", volume.getLabel()));
    }
    log.info(logMsgBuilder.toString());
  }
  /*
   * (non-Javadoc)
   *
   * @see com.emc.storageos.volumecontroller.CloneOperations#createSingleClone(
   * com.emc.storageos.db.client.model.StorageSystem, java.net.URI, java.net.URI,
   * java.lang.Boolean,
   * com.emc.storageos.volumecontroller.TaskCompleter)
   */
  @Override
  public void createSingleClone(
      StorageSystem storageSystem,
      URI sourceObject,
      URI cloneVolume,
      Boolean createInactive,
      TaskCompleter taskCompleter) {
    log.info("START createSingleClone operation");
    boolean isVolumeClone = true;
    try {
      BlockObject sourceObj = BlockObject.fetch(dbClient, sourceObject);
      URI tenantUri = null;
      if (sourceObj
          instanceof BlockSnapshot) { // In case of snapshot, get the tenant from its parent volume
        NamedURI parentVolUri = ((BlockSnapshot) sourceObj).getParent();
        Volume parentVolume = dbClient.queryObject(Volume.class, parentVolUri);
        tenantUri = parentVolume.getTenant().getURI();
        isVolumeClone = false;
      } else { // This is a default flow
        tenantUri = ((Volume) sourceObj).getTenant().getURI();
        isVolumeClone = true;
      }

      Volume cloneObj = dbClient.queryObject(Volume.class, cloneVolume);
      StoragePool targetPool = dbClient.queryObject(StoragePool.class, cloneObj.getPool());
      TenantOrg tenantOrg = dbClient.queryObject(TenantOrg.class, tenantUri);
      // String cloneLabel = generateLabel(tenantOrg, cloneObj);

      CinderEndPointInfo ep =
          CinderUtils.getCinderEndPoint(storageSystem.getActiveProviderURI(), dbClient);
      log.info(
          "Getting the cinder APi for the provider with id "
              + storageSystem.getActiveProviderURI());
      CinderApi cinderApi = cinderApiFactory.getApi(storageSystem.getActiveProviderURI(), ep);

      String volumeId = "";
      if (isVolumeClone) {
        volumeId =
            cinderApi.cloneVolume(
                cloneObj.getLabel(),
                (cloneObj.getCapacity() / (1024 * 1024 * 1024)),
                targetPool.getNativeId(),
                sourceObj.getNativeId());
      } else {
        volumeId =
            cinderApi.createVolumeFromSnapshot(
                cloneObj.getLabel(),
                (cloneObj.getCapacity() / (1024 * 1024 * 1024)),
                targetPool.getNativeId(),
                sourceObj.getNativeId());
      }

      log.debug("Creating volume with the id " + volumeId + " on Openstack cinder node");
      if (volumeId != null) {
        Map<String, URI> volumeIds = new HashMap<String, URI>();
        volumeIds.put(volumeId, cloneObj.getId());
        ControllerServiceImpl.enqueueJob(
            new QueueJob(
                new CinderSingleVolumeCreateJob(
                    volumeId,
                    cloneObj.getLabel(),
                    storageSystem.getId(),
                    CinderConstants.ComponentType.volume.name(),
                    ep,
                    taskCompleter,
                    targetPool.getId(),
                    volumeIds)));
      }
    } catch (InternalException e) {
      String errorMsg = String.format(CREATE_ERROR_MSG_FORMAT, sourceObject, cloneVolume);
      log.error(errorMsg, e);
      taskCompleter.error(dbClient, e);
    } catch (Exception e) {
      String errorMsg = String.format(CREATE_ERROR_MSG_FORMAT, sourceObject, cloneVolume);
      log.error(errorMsg, e);
      ServiceError serviceError =
          DeviceControllerErrors.cinder.operationFailed("createSingleClone", e.getMessage());
      taskCompleter.error(dbClient, serviceError);
    }
  }
  /** {@inheritDoc} */
  @SuppressWarnings("unchecked")
  @Override
  public void processResult(Operation operation, Object resultObj, Map<String, Object> keyMap)
      throws BaseCollectionException {
    final Iterator<CIMInstance> it = (Iterator<CIMInstance>) resultObj;
    profile = (AccessProfile) keyMap.get(Constants.ACCESSPROFILE);
    try {
      _newPoolList = new ArrayList<StoragePool>();
      _updatePoolList = new ArrayList<StoragePool>();
      _dbClient = (DbClient) keyMap.get(Constants.dbClient);
      _cimClient = (WBEMClient) keyMap.get(Constants._cimClient);
      _coordinator = (CoordinatorClient) keyMap.get(Constants.COORDINATOR_CLIENT);
      _eventManager = (RecordableEventManager) keyMap.get(Constants.EVENT_MANAGER);
      _logger.info("StoragePoolProcessor --- event manager: " + _eventManager);
      StorageSystem device = getStorageSystem(_dbClient, profile.getSystemId());
      if (SupportedProvisioningTypes.NONE
          .toString()
          .equalsIgnoreCase(device.getSupportedProvisioningType())) {
        _logger.info(
            "Storage System doesn't support volume creations :" + device.getSerialNumber());
        return;
      }
      Set<String> protocols = (Set<String>) keyMap.get(Constants.PROTOCOLS);
      Map<URI, StoragePool> poolsToMatchWithVpool =
          (Map<URI, StoragePool>) keyMap.get(Constants.MODIFIED_STORAGEPOOLS);
      while (it.hasNext()) {
        CIMInstance poolInstance = null;
        try {
          poolInstance = it.next();

          // Supporting both thick and thin pools
          String[] poolClassNameAndSupportedVolumeTypes =
              determinePoolClassNameAndSupportedVolumeTypes(poolInstance, device);
          if (null != poolClassNameAndSupportedVolumeTypes) {
            String instanceID = getCIMPropertyValue(poolInstance, Constants.INSTANCEID);
            addPath(keyMap, operation.getResult(), poolInstance.getObjectPath());
            StoragePool pool =
                checkStoragePoolExistsInDB(getNativeIDFromInstance(instanceID), _dbClient, device);
            createStoragePool(
                pool,
                poolInstance,
                profile,
                poolClassNameAndSupportedVolumeTypes[0],
                poolClassNameAndSupportedVolumeTypes[1],
                protocols,
                poolsToMatchWithVpool,
                device);

            if (DiscoveredDataObject.Type.vnxblock
                .toString()
                .equalsIgnoreCase(device.getSystemType())) {
              addPath(keyMap, Constants.VNXPOOLS, poolInstance.getObjectPath());
            }

            if (DiscoveredDataObject.Type.vmax
                .toString()
                .equalsIgnoreCase(device.getSystemType())) {
              addPath(keyMap, Constants.VMAXPOOLS, poolInstance.getObjectPath());
              if (!device.checkIfVmax3()) {
                addPath(keyMap, Constants.VMAX2POOLS, poolInstance.getObjectPath());
              }
            }
            // This approach deviates from the existing built plugin framework for plugin
            // Discovery
            // To follow the existing pattern, we need to have different SMI-S calls
            // 1st to get Device StoragePools alone ,and 2nd to get Thin Pools.
            // Its a tradeoff between whether to go with the current plugin design or
            // reduce the number of calls to SMI Provider.
            // I chose the 2nd option.
            if (!poolClassNameAndSupportedVolumeTypes[0].contains(DEVICE_STORAGE_POOL)) {
              addPath(keyMap, Constants.THINPOOLS, poolInstance.getObjectPath());
            }

            addPath(keyMap, Constants.DEVICEANDTHINPOOLS, poolInstance.getObjectPath());
          } else {
            _logger.debug(
                "Skipping Pools other than Unified & Virtual & Device : {}",
                poolInstance.getObjectPath().toString());
          }
        } catch (Exception e) {
          _logger.warn(
              "StoragePool Discovery failed for {}",
              getCIMPropertyValue(poolInstance, Constants.INSTANCEID),
              e);
        }
      }

      _dbClient.createObject(_newPoolList);
      _dbClient.updateAndReindexObject(_updatePoolList);

      // find the pools not visible in this discovery
      List<StoragePool> discoveredPools = new ArrayList<StoragePool>(_newPoolList);
      discoveredPools.addAll(_updatePoolList);
      List<StoragePool> notVisiblePools =
          DiscoveryUtils.checkStoragePoolsNotVisible(discoveredPools, _dbClient, device.getId());
      for (StoragePool notVisiblePool : notVisiblePools) {
        poolsToMatchWithVpool.put(notVisiblePool.getId(), notVisiblePool);
      }
      // If any storage ports on the storage system are in a transport
      // zone, there is an implicit connection to the transport zone
      // varray. We need to add these implicit varray
      // connections for the new storage pool.
      StoragePoolAssociationHelper.setStoragePoolVarrays(device.getId(), _newPoolList, _dbClient);
    } catch (Exception e) {
      _logger.error("StoragePool Discovery failed --> {}", getMessage(e));
    } finally {
      _newPoolList = null;
      _updatePoolList = null;
    }
  }
  /**
   * 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);
      }
    }
  }
 /**
  * Get pool URI
  *
  * @return
  */
 public URI getPoolId() {
   return pool.getId();
 }